Skip to content

Configuration

Enrollment Modes

ModeDescriptionUse Case
openAny ATProto user can enrollPublic services, testing
allowlistOnly approved users/PDS endpointsCommunity deployments

Allowlist Configuration

By DID — allow specific users:

bash
STRATOS_ALLOWED_DIDS="did:plc:user1,did:plc:user2"

By PDS endpoint — allow all users from specific PDS instances:

bash
STRATOS_ALLOWED_PDS_ENDPOINTS="https://community-pds.example.com"

Both can be combined — a user is allowed if they match either list.

Domain Boundaries

Restrict which domain names can appear in record boundaries:

bash
STRATOS_ALLOWED_DOMAINS="general,fanart"

These are bare domain names. At startup the service qualifies them with its own DID, so "fanart" becomes "did:web:stratos.example.com/fanart". Clients must send the fully-qualified form.

Service Enrollments

Grant downstream services (AppViews, indexers) access to a set of boundaries without an OAuth flow. Service enrollments are declared in configuration, which is treated as the source of truth: on every startup the service reconciles the declared entries into the enrollment store (upserting them with isService = true) and prunes any service rows that are no longer declared. User enrollments are never affected.

Provide the enrollments either inline or via a JSON file:

bash
# Inline JSON
STRATOS_SERVICE_ENROLLMENTS='[{"did":"did:web:appview.example.com","boundaries":["engineering"]}]'

# Or a path to a JSON file (takes precedence when both are set)
STRATOS_SERVICE_ENROLLMENTS_FILE="/etc/stratos/service-enrollments.json"

The file (or inline value) is a JSON array of entries:

json
[
  {
    "did": "did:web:appview.example.com",
    "boundaries": ["engineering", "leadership"]
  }
]

Each entry requires a non-empty, unique did and at least one boundary. Boundaries may be supplied as bare domain names (qualified at startup against the service DID, exactly like STRATOS_ALLOWED_DOMAINS) or in fully-qualified form. Every boundary must be a member of STRATOS_ALLOWED_DOMAINS; otherwise startup fails fast with a validation error.

Write Rate Limiter

Per-DID write throttling to protect MST commit performance under burst traffic:

bash
STRATOS_WRITE_RATE_MAX_WRITES=300      # writes allowed in window
STRATOS_WRITE_RATE_WINDOW_MS=60000     # rolling window (ms)
STRATOS_WRITE_RATE_COOLDOWN_MS=10000   # cooldown after limit exceeded
STRATOS_WRITE_RATE_COOLDOWN_JITTER_MS=1000  # jitter to avoid synchronized retries

Repository Import

bash
# Default: 256 MiB
STRATOS_IMPORT_MAX_BYTES=268435456

Signing Keys

Service key — signs every repo commit. Auto-generated on first start, or supply via:

bash
STRATOS_SIGNING_KEY_HEX="<hex-encoded-secp256k1-private-key>"

User keys — each enrolled user receives a P-256 keypair generated at enrollment time. Private keys are stored at {dataDir}/actors/{prefix}/{did}/signing_key. The public key and a service attestation are published in the enrollment record on the user's PDS.

Attestation lifecycle:

  • Generated at enrollment with the user's initial boundaries.
  • Regenerated when boundaries change.
  • Deleted on unenrollment. The enrollment record and all associated actor data (records, blobs) are permanently deleted from the service (hard delete).

Database Storage Backend

BackendDescriptionUse Case
sqlite (default)Per-actor SQLite databasesSingle-node, development
postgresPostgreSQL with per-actor schemasProduction, high availability
bash
# SQLite (default)
STORAGE_BACKEND="sqlite"
STRATOS_DATA_DIR="/var/lib/stratos/data"

# PostgreSQL — connection URL
STORAGE_BACKEND="postgres"
STRATOS_POSTGRES_URL="postgres://user:pass@localhost:5432/stratos"

# PostgreSQL — individual params (useful with AWS Secrets Manager)
STORAGE_BACKEND="postgres"
STRATOS_PG_HOST="db.example.com"
STRATOS_PG_PORT="5432"
STRATOS_PG_USERNAME="stratos"
STRATOS_PG_PASSWORD="secret"
STRATOS_PG_DBNAME="stratos"

When both STRATOS_POSTGRES_URL and STRATOS_PG_* variables are set, the URL takes precedence.

Blob Storage

ProviderDescriptionUse Case
local (default)Local filesystemSingle-node, development
s3S3-compatible storageProduction, multi-node

Local Storage

bash
STRATOS_BLOB_STORAGE="local"
STRATOS_DATA_DIR="/var/lib/stratos/data"

Blobs are stored under {STRATOS_DATA_DIR}/blobs/{did}/{cid}.

S3-Compatible Storage

bash
STRATOS_BLOB_STORAGE="s3"
STRATOS_S3_BUCKET="my-stratos-blobs"
STRATOS_S3_REGION="us-east-1"

# Explicit credentials (or use IAM roles — no keys needed)
STRATOS_S3_ACCESS_KEY="AKIAIOSFODNN7EXAMPLE"
STRATOS_S3_SECRET_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

# For MinIO or other S3-compatible services
STRATOS_S3_ENDPOINT="http://minio.local:9000"

S3 key structure:

text
stratos/blocks/{did}/{cid}       # Permanent blobs
stratos/tmp/{did}/{key}          # Temporary uploads
stratos/quarantine/{did}/{cid}   # Taken-down blobs

Migrating from Disk to S3

  1. Stop the Stratos service.

  2. Sync blobs to S3:

    bash
    aws s3 sync /var/lib/stratos/data/blobs/ s3://my-bucket/stratos/blocks/ \
      --exclude "temp/*" --exclude "quarantine/*"
  3. Update config to STRATOS_BLOB_STORAGE="s3".

  4. Restart the service.

DPoP Authentication

Stratos validates DPoP tokens using JWKS-based signature verification (not token introspection):

  1. Client sends Authorization: DPoP <token> + DPoP: <proof> headers.
  2. Stratos fetches and caches the PDS's JWKS from /.well-known/oauth-authorization-server.
  3. Stratos verifies the token signature using the JWKS.
  4. Stratos verifies the DPoP proof is bound to the token (cnf.jkt claim).

Complete Environment Variable Reference

VariableRequiredDefaultDescription
STRATOS_SERVICE_DIDService DID (did:web:<host>)
STRATOS_PUBLIC_URLPublic base URL
STRATOS_ALLOWED_DOMAINSComma-separated allowed boundary names
STRATOS_RESERVED_DOMAINgeneralReserved all-members domain, force-included in every enrollment and implicitly part of the allowed domains.
STRATOS_SPACE_CREDENTIAL_TTL_SECONDS7200TTL (seconds) of issued space credentials (getSpaceCredential)
STRATOS_SPACE_APP_ACCESS— (all open)JSON array of per-space client-attestation app-gating rules (open or allowList)
STRATOS_SPACE_APP_ACCESS_FILEPath to a JSON file with the same shape as STRATOS_SPACE_APP_ACCESS (merged with it)
STRATOS_CLIENT_JWKS_CACHE_TTL_MS300000In-memory TTL (ms) for resolved client JWKS during client attestation
STRATOS_PORT3100HTTP listen port
STRATOS_SERVICE_FRAGMENTatproto_pnsFragment for the service entry in the DID document (and the source.service DID+fragment returned on hydration)
STRATOS_DATA_DIR./dataBase data directory (sqlite)
STORAGE_BACKENDsqlitesqlite or postgres
STRATOS_POSTGRES_URLFull Postgres DSN
STRATOS_PG_ACTOR_POOL_SIZEActor transaction pool size
STRATOS_PG_ADMIN_POOL_SIZEAdmin/schema pool size
BSKY_DB_POSTGRES_URLPostgres DSN for the bundled indexer's AppView (bsky) database. Required by the indexer.
STRATOS_SYNC_TOKENAuth token the bundled indexer uses to subscribe to Stratos sync streams. Required by the indexer.
STRATOS_BLOB_STORAGElocallocal or s3
STRATOS_S3_BUCKETS3 bucket name
STRATOS_S3_REGIONS3 region
STRATOS_S3_ENDPOINTS3-compatible endpoint override
STRATOS_ENROLLMENT_MODEallowlistopen or allowlist
STRATOS_ALLOWED_DIDSComma-separated allowed DIDs
STRATOS_ALLOWED_PDS_ENDPOINTSComma-separated allowed PDS URLs
STRATOS_AUTO_ENROLL_DOMAINSall allowedDomains assigned to new users
STRATOS_PLC_URLhttps://plc.directoryDID PLC resolver URL
STRATOS_OAUTH_CLIENT_IDOAuth client metadata URL
STRATOS_OAUTH_REDIRECT_URIOAuth callback URI
STRATOS_ALLOWED_REDIRECT_ORIGINS— (none)Comma-separated origins the enrollment flow may redirect back to (e.g. https://app.example). Only needed for a client that publishes no client metadata document; a client that publishes one proves its own target. Loopback is allowed automatically in dev mode.
STRATOS_SIGNING_KEY_HEXauto-generatedService secp256k1 key
STRATOS_IMPORT_MAX_BYTES268435456Max CAR import size
STRATOS_WRITE_RATE_MAX_WRITES300Per-DID write limit
STRATOS_WRITE_RATE_WINDOW_MS60000Rate limit window
LOG_LEVELinfodebug, info, warn, error