Environment Variables
Environment Variables
Section titled “Environment Variables”All ProtocolSoup backend services share a common set of core variables. Individual services add protocol-specific configuration on top.
Core Variables (All Backend Services)
Section titled “Core Variables (All Backend Services)”| Variable | Default | Description |
|---|---|---|
SHOWCASE_BASE_URL |
http://localhost:8080 |
Public URL used in endpoint URLs, redirect URIs, and token issuer claims. In production this must be a pathless HTTPS origin with no trailing slash, query, or fragment. |
SHOWCASE_LISTEN_ADDR |
:8080 |
Address and port the service binds to. |
SHOWCASE_CORS_ORIGINS |
http://localhost:3000,http://localhost:5173 |
Comma-separated list of allowed CORS origins for browser clients. |
SHOWCASE_ENV |
demo |
Runtime environment. |
BUILD_COMMIT |
(empty) | Full deployed source commit exposed by /health. Production deployment workflows set this automatically so conformance evidence can be bound to an immutable build. The wallet harness uses the same variable. |
SHOWCASE_MOCK_IDP |
true |
Enable built-in mock identity provider with demo users and clients. |
SHOWCASE_DEBUG |
false |
Enable debug logging. |
SHOWCASE_PALETTE_DB |
(empty — palette disabled) | Path to the prebuilt palette SQLite index. Required in SHOWCASE_ENV=production when palette search is enabled. Docker images built from Dockerfile.backend and Dockerfile.fly default to /app/palette.db. See Palette content index. |
OAuth 2.0 Federation Service
Section titled “OAuth 2.0 Federation Service”| Variable | Default | Description |
|---|---|---|
OAUTH2_REPLAY_REDIS_URL |
(in-memory in development/tests) | Redis URL for atomic private_key_jwt (client_id, jti) replay reservations through exp + 60s. Also backs the RFC 9449 DPoP proof jti replay stores for both oauth2 and OID4VCI (distinct key prefix and instance from the private_key_jwt store). Production accepts a reachable rediss:// URL, and fails startup without it or when it cannot be reached. Runtime Redis errors fail the token request closed with server_error. Local Compose supplies an isolated Redis instance. Store production credentials as a platform secret. |
SHOWCASE_DPOP_NONCE_REQUIRED |
false |
Enables the RFC 9449 §8 server-provided nonce challenge at authorization-server token endpoints – oauth2’s /oauth2/token and OID4VCI’s own /oid4vci/token. Off by default: DPoP itself remains fully functional without it, since RFC 9449 treats the nonce as optional hardening, not a baseline requirement. When enabled, a proof lacking the current nonce is rejected with use_dpop_nonce and a DPoP-Nonce response header; the client retries with a fresh proof echoing that value. |
SHOWCASE_DPOP_RESOURCE_NONCE_REQUIRED |
false |
Enables the same challenge, independently, at OID4VCI’s resource-server endpoints (/oid4vci/credential, /oid4vci/nonce, /oid4vci/deferred_credential) for tokens bound via DPoP. Kept as a separate switch from SHOWCASE_DPOP_NONCE_REQUIRED because RFC 9449 §8.2 requires the AS and RS nonce spaces to be independent, even where – as with OID4VCI issuing and validating its own tokens – both roles run in the same process. |
SSF_STREAM_CLIENT_SECRET |
(auto-generated) | Secret for the seeded ssf-stream-client confidential client (client_credentials, ssf.read / ssf.manage). Pin as a platform secret in production so Stream Management tokens stay usable across restarts. The current secret is listed at GET /oauth2/demo/clients. |
OpenID Connect (Federation / Showcase)
Section titled “OpenID Connect (Federation / Showcase)”| Variable | Default | Description |
|---|---|---|
OIDC_DYNAMIC_REGISTRATION_ENABLED |
true |
Enables open Dynamic Client Registration at POST /oidc/register and advertises registration_endpoint / request_uri_parameter_supported only when true. |
OIDC_DYNAMIC_REGISTRATION_TTL |
2h |
Lifetime of dynamically registered clients. Expired clients are removed from the in-memory store. |
OIDC_DYNAMIC_REGISTRATION_MAX_CLIENTS |
200 |
Maximum number of live dynamic clients. Further registrations return invalid_client_metadata. |
OIDC_DYNAMIC_REGISTRATION_RATE_LIMIT |
30 |
Maximum registration attempts per source key within the rate window. |
OIDC_DYNAMIC_REGISTRATION_RATE_WINDOW |
1m |
Sliding window used by the registration rate limiter. |
OIDC_PAIRWISE_SUBJECT_SALT |
(generated in-memory when empty) | Durable secret used to derive deterministic OIDC pairwise subjects. Set a platform secret in production so pairwise sub values remain stable across restarts. |
OIDC_KEY_ROTATION_TOKEN |
(empty — disabled) | Bearer token required for POST /oidc/admin/rotate-keys. Leave empty to keep the rotation endpoint disabled. |
Gateway
Section titled “Gateway”| Variable | Default | Description |
|---|---|---|
FEDERATION_SERVICE_URL |
- | Upstream URL for the federation service. |
SCIM_SERVICE_URL |
- | Upstream URL for the SCIM service. |
SSF_SERVICE_URL |
- | Upstream URL for the SSF service. |
SPIFFE_SERVICE_URL |
- | Upstream URL for the SPIFFE service. |
VC_SERVICE_URL |
- | Upstream URL for the VC service (if separate from federation). |
SCIM Service
Section titled “SCIM Service”| Variable | Default | Description |
|---|---|---|
SCIM_API_TOKEN |
- | Bearer token required for all SCIM API requests and outbound client operations. The SCIM service refuses to start without it in production. |
SCIM_RETENTION |
24h |
Go duration after which visitor-created SCIM users and groups are deleted, based on updated_at. 0 disables the reaper. After each sweep, demo users and groups are re-seeded. |
Looking Glass
Section titled “Looking Glass”| Variable | Default | Description |
|---|---|---|
LOOKINGGLASS_SESSION_IDLE_TTL |
30m |
Idle lifetime of an in-memory Looking Glass session, measured from last activity (updated_at). |
LOOKINGGLASS_SESSION_MAX_TTL |
4h |
Absolute lifetime of an in-memory Looking Glass session, measured from creation. Connected websockets are closed on eviction. |
SSF Service
Section titled “SSF Service”| Variable | Default | Description |
|---|---|---|
SSF_RECEIVER_TOKEN |
- | Bearer token the receiver accepts for event delivery. |
SSF_AS_ISSUER |
- | Authorization server iss checked on Stream Management access tokens. |
SSF_AS_JWKS_URI |
- | JWKS URL used to verify Stream Management bearer JWTs. When set, those APIs require Authorization: Bearer or X-Looking-Glass-Session. |
SSF_AS_INTROSPECT_URI |
- | RFC 7662 introspection URL used by the split SSF Transmitter to check Stream Management token revocation. |
SSF_RESOURCE |
SHOWCASE_BASE_URL |
JWT aud the SSF Transmitter expects. The OAuth 2.0 token endpoint issues ssf.read / ssf.manage client-credentials tokens with this audience and a 900 second lifetime. |
FEDERATION_SERVICE_URL |
loopback from listen addr | On the SSF service, federation base URL for CAEP revoke-subject. Compose: http://federation-service:8080. Fly/monolith: http://127.0.0.1:8080. |
SSF_TO_FEDERATION_TOKEN |
- | Shared bearer token; set on both SSF and federation. Production secret — set via fly secrets set, never via committed config. Without it the CAEP session-revoked demo hop returns 401. |
SPIFFE Service
Section titled “SPIFFE Service”| Variable | Default | Description |
|---|---|---|
SPIFFE_AGENT_SOCKET |
/run/spire/sockets/agent/agent.sock |
Path to the SPIRE agent Workload API socket. Required for full SPIRE mode. |
SPIFFE_TRUST_DOMAIN |
protocolsoup.local |
SPIFFE trust domain. |
Wallet Service
Section titled “Wallet Service”| Variable | Default | Description |
|---|---|---|
WALLET_TARGET_BASE_URL |
https://protocolsoup.com |
URL of the VC verifier/issuer service for OID4VP submissions and the default OID4VCI target. |
WALLET_ALLOWED_CORS_ORIGINS |
https://protocolsoup.com,https://www.protocolsoup.com,https://protocolsoup.fly.dev |
Comma-separated allowed browser origins. |
WALLET_DEFAULT_SUBJECT |
did:example:wallet:alice |
Default holder DID seed for demo sessions. It identifies wallet key material, not an issuer identity-record ID; automatic OID4VCI bootstrap uses the issuer’s designated default identity. |
WALLET_DEFAULT_CREDENTIAL_FORMAT |
mso_mdoc |
Default stored and presented credential format when a request does not name a format or configuration. mso_mdoc is the default (the mDL); dc+sd-jwt and the W3C formats remain selectable. |
WALLET_DID_METHOD |
key |
DID method for the wallet holder identifier (e.g. key, web). |
WALLET_DEFAULT_SIGNING_ALG |
ES256 |
Default holder-binding signing algorithm. ES256 (EC P-256) matches the mso_mdoc default; other wallet-supported algorithms apply to the JOSE formats. |
WALLET_SESSION_TTL |
20m |
Go duration for in-memory session lifetime. |
WALLET_STRICT_SESSION_ISOLATION |
true |
Enforce strict session boundaries between demo runs. |
WALLET_ALLOW_EXTERNAL_VERIFIERS |
true |
Allow OID4VP request_uri and OID4VCI endpoint fetches outside the configured target/issuer origin. External destinations must be HTTPS. The wallet resolves DNS, re-validates redirects, and refuses private, loopback, link-local, multicast, or shared-address destinations. Failed request_uri fetches report HTTP status without reflecting upstream bodies. |
WALLET_TRUSTED_VERIFIER_ATTESTATION_ISSUERS |
(empty) |
Comma-separated issuer URLs the wallet trusts for verifier_attestation request validation. |
WALLET_DEVICE_KEY_PATH |
(empty) |
File path for the persistent mso_mdoc holder device key (EC P-256). Empty uses an ephemeral key; set a durable path so stored mdoc device bindings survive restarts. |
WALLET_VERIFIER_X509_TRUST_ANCHOR_PEM |
System roots only | Additional PEM CA trust anchors used when the wallet validates x509_san_dns and x509_hash request-object chains. Certificates carried in x5c are untrusted chain material and are never promoted to roots. Hosted ProtocolSoup CI copies the last certificate from {SHOWCASE_DATA_DIR}/oid4vp/x509_request_signer_chain.pem into this secret. |
WALLET_MDOC_IACA_ROOT_PEM |
(unset) |
PEM-encoded IACA root certificate(s) the wallet trusts before storing an issued mso_mdoc. Without an explicit root the wallet rejects mdoc issuance rather than trusting the credential’s own x5chain. Multiple PEMs are additive. |
WALLET_CLIENT_ATTESTATION_ATTESTER_JWK_JSON |
(unset) |
Attester private JWK (+ x5c) used to mint OAuth-Client-Attestation JWTs. Together with WALLET_CLIENT_ATTESTATION_KEY_ATTESTATION_JWK_JSON, enables the wallet HAIP issuance path. |
WALLET_CLIENT_ATTESTATION_KEY_ATTESTATION_JWK_JSON |
(unset) |
Attester private JWK (+ x5c) used to mint OID4VCI Appendix D key_attestation JWTs for HAIP credential proofs. |
WALLET_OID4VCI_ATTESTED_CLIENT_ID |
protocolsoup-wallet |
Attested client_id / attestation JWT sub when HAIP attestation material is configured. |
WALLET_CLIENT_ATTESTATION_ISSUER |
https://wallet.protocolsoup.com/attester |
iss claim on client attestation JWTs. |
WALLET_KEY_ATTESTATION_KEY_STORAGE |
(unset) |
Comma-separated key_storage attack-potential levels asserted in key attestation (for example iso_18045_moderate). Omit unless the claim is honest for this software wallet. |
WALLET_KEY_ATTESTATION_USER_AUTHENTICATION |
(unset) |
Comma-separated user_authentication attack-potential levels asserted in key attestation. Omit unless honest. |
OID4VP Verifier (VC And Federation Services)
Section titled “OID4VP Verifier (VC And Federation Services)”| Variable | Default | Description |
|---|---|---|
OID4VP_VERIFIER_ATTESTATION_ISSUER |
<SHOWCASE_BASE_URL>/oid4vp/verifier-attestation |
Issuer URL published for verifier attestation metadata and JWKS. |
OID4VP_VERIFIER_ATTESTATION_CLIENT_ID |
verifier_attestation:<SHOWCASE_BASE_URL host> |
Verifier client_id used when client_id_scheme=verifier_attestation. |
OID4VP_VERIFIER_ATTESTATION_PRIVATE_KEY_PEM |
Ephemeral in-memory key | PEM-encoded stable signing key for verifier attestation JWTs and JWKS. Set this in production so attestation validation survives restarts and deploys. |
OID4VP_X509_SANDNS_CLIENT_ID |
x509_san_dns:<SHOWCASE_BASE_URL host> |
Verifier client_id used when client_id_scheme=x509_san_dns. Must match a DNS SAN on the leaf certificate. |
OID4VP_X509_SANDNS_CERT_CHAIN_PEM |
Ephemeral generated chain | PEM-encoded certificate material for x509_san_dns and x509_hash. The request object’s x5c contains the leaf/intermediate chain and excludes the root trust anchor. When unset, a CA + leaf are generated and persisted under {DataDir}/oid4vp when a data directory is configured. |
OID4VP_X509_SANDNS_PRIVATE_KEY_PEM |
Ephemeral key | PEM-encoded private key matching the leaf certificate used for x509_san_dns / x509_hash request signing. When unset, generated alongside the auto-provisioned chain. Set both PEM variables to pin an externally issued certificate. |
OID4VP_SD_JWT_TRUST_ANCHOR_PEM |
(unset) |
PEM CA root(s) trusted for independently issued certificate-backed SD-JWT VC presentations (no first-party issuance lineage). The credential x5c must contain only leaf/intermediate certificates and chain to this independently configured root. First-party HAIP dc+sd-jwt credentials that match issuance lineage do not need this variable: their Token Status List is verified with the same issuer key already bound to the credential. |
OID4VP_SD_JWT_TRUST_ANCHOR_PEM_FILE |
(unset) |
File containing the SD-JWT VC issuer trust anchor PEM when the inline variable is not used. |
MDOC_IACA_ROOT_PEM |
(unset) |
PEM-encoded external ISO/IEC 18013-5 IACA root certificate(s) the verifier trusts for mso_mdoc presentations. These roots are combined with roots loaded from MDOC_IACA_ROOT_PEM_FILE and the local issuer’s persisted iaca_root.pem. |
MDOC_IACA_ROOT_PEM_FILE |
(unset) |
Path to additional IACA root PEM(s). File roots, inline roots, and the issuer-persisted root under SHOWCASE_MDOC_PKI_PATH or {DataDir}/mdoc are additive, allowing a combined issuer/verifier deployment to validate both external and locally issued mdocs. |
SHOWCASE_MDOC_PKI_PATH |
{DataDir}/mdoc (else ephemeral) |
Persistent store directory for the OID4VCI issuer’s ISO/IEC 18013-5 mso_mdoc PKI (IACA root + document-signer chain). The OID4VP verifier loads the same iaca_root.pem here as its mso_mdoc trust anchor. Point this at a durable volume in any deployment that issues and verifies mso_mdoc in the same app. When neither this nor a data dir is set, the issuer PKI is ephemeral (regenerated on each boot) and the verifier has no trust anchor, so every mso_mdoc presentation is denied with mdoc_trust_anchor_unavailable. |
OID4VCI Issuer (VC Service)
Section titled “OID4VCI Issuer (VC Service)”| Variable | Default | Description |
|---|---|---|
MDOC_ISSUER_COUNTRY |
US |
Two-letter country code used in the ISO/IEC 18013-5 issuer PKI (IACA root and document-signer) certificate subjects. |
MDOC_ISSUER_ORG |
ProtocolSoup |
Organization name used in the ISO/IEC 18013-5 issuer PKI certificate subjects. |
OID4VCI_CLIENT_ATTESTATION_TRUST_ANCHOR_PEM |
(unset) |
PEM-encoded CA certificate(s) the issuer trusts to anchor the x5c chain on incoming OAuth 2.0 Attestation-Based Client Authentication JWTs (OAuth-Client-Attestation header, draft-ietf-oauth-attestation-based-client-auth). Required to accept client_attestation client authentication at the token endpoint. When unset, the issuer refuses any request that presents attestation headers (invalid_client) rather than silently downgrading to a weaker method; credential issuer metadata’s token_endpoint_auth_methods_supported also omits attest_jwt_client_auth in that case. |
OID4VCI_KEY_ATTESTATION_TRUST_ANCHOR_PEM |
(unset) |
PEM-encoded CA certificate(s) the issuer trusts to anchor the x5c chain on OID4VCI 1.0 Appendix D.1 Key Attestation JWTs carried in the proof JWT’s key_attestation JOSE header. Required for credential configurations that set key_attestations_required (e.g. MobileDrivingLicenceMsoMdocHAIP). When unset, a credential request against a key-attestation-required configuration is rejected with invalid_proof. |
Usage Notes
Section titled “Usage Notes”SHOWCASE_BASE_URLis the most important variable. It determines the issuer, entity ID, redirect URIs, and metadata URLs across all protocols. Always set it to the origin your clients actually use. Production startup fails unless it is a pathless HTTPS origin such ashttps://protocolsoup.example.com(no trailing slash or subpath). In development, the OAuth RFC 8414 metadata endpoint returns503for an HTTP or pathful base URL rather than publishing a non-conformant issuer.- Production OAuth deployments must set
OAUTH2_REPLAY_REDIS_URLas a secret usingrediss://. Do not place Redis credentials in Compose files,fly.toml, or source control. - When running behind a reverse proxy or load balancer, set
SHOWCASE_BASE_URLto the external URL (e.g.https://protocolsoup.example.com) rather than the internal address. SHOWCASE_CORS_ORIGINSmust include the origin of your frontend. If the UI is athttps://app.example.com, add that origin.- For SCIM, set
SCIM_API_TOKENto any secret string. Clients must includeAuthorization: Bearer <your-token>on every request. - To enable real
verifier_attestationOID4VP flows, publish the verifier issuer URL from the verifier service and add that issuer to the wallet’sWALLET_TRUSTED_VERIFIER_ATTESTATION_ISSUERSallow-list. x509_san_dnsand the HAIPx509_hashprefix work out of the box. When the PEM env vars are unset, an ECDSA P-256 CA + leaf certificate chain is auto-provisioned with the leaf SAN bound to the deployment hostname fromSHOWCASE_BASE_URL; with a data dir it is persisted under{DataDir}/oid4vpand reloaded on restart so thex509_hashclient_idstays stable across restarts (wallets that pin by leaf hash keep a consistent verifier identity). Provide your own PEM chain and key to pin an externally issued certificate.- To verify
mso_mdoc(ISO/IEC 18013-5) presentations, point the verifier at the issuer’s IACA root viaMDOC_IACA_ROOT_PEM(orMDOC_IACA_ROOT_PEM_FILE). The trust anchor is the IACA root, never the document-signer certificate. When the issuer and verifier run in the same deployment, setSHOWCASE_MDOC_PKI_PATH(orSHOWCASE_DATA_DIR) to a durable volume so the issuer persistsiaca_root.pemand the verifier loads it automatically as its trust anchor; without it the PKI is ephemeral and presentations are denied withmdoc_trust_anchor_unavailable.