Skip to content

Configuration Reference

Environment variables and runtime settings for Stuff Stash.

Use this page when you are wiring Stuff Stash into a real environment or checking which values local Compose sets for you.

The API reads configuration from environment variables at startup.

Type Accepted values
Boolean true, false, 1, 0, yes, no, on, off
Duration Go duration strings such as 500ms, 10s, 2h
Integer Positive base-10 integers
List Comma-separated values, trimmed and deduplicated

Most legacy primitive settings fall back to their defaults when invalid. Strict settings, including MCP enablement and export limits, reject invalid values at startup. Unsupported adapter modes, missing required adapter settings, and invalid enabled provider settings also fail startup.

Variable Default Purpose
STUFF_STASH_HTTP_ADDR :8080 Address the API listens on.
STUFF_STASH_HTTP_READ_HEADER_TIMEOUT 5s Maximum time to read request headers.
STUFF_STASH_HTTP_READ_TIMEOUT 15s Maximum time to read a request.
STUFF_STASH_HTTP_WRITE_TIMEOUT 30s Maximum time to write a response.
STUFF_STASH_HTTP_IDLE_TIMEOUT 60s Keep-alive idle timeout.
STUFF_STASH_HTTP_MAX_JSON_BODY_BYTES 1048576 Maximum JSON request body size.
STUFF_STASH_CORS_ALLOWED_ORIGINS empty Comma-separated allowed browser and WebSocket origins. Behind TLS termination, include both the frontend origin and the public HTTPS API origin used by native voice clients.
Variable Default Purpose
STUFF_STASH_HTTP_RATE_LIMIT_ENABLED true Enables API HTTP rate limiting.
STUFF_STASH_HTTP_RATE_LIMIT_REQUESTS 1200 Request budget per rate-limit window.
STUFF_STASH_HTTP_RATE_LIMIT_WINDOW 1m Rate-limit accounting window.
STUFF_STASH_HTTP_RATE_LIMIT_BURST 600 Burst allowance.
Variable Default Purpose
STUFF_STASH_AUTH_MODE local-dev Authentication adapter. Use local-dev or oidc.
STUFF_STASH_OIDC_ISSUER empty OIDC issuer URL when STUFF_STASH_AUTH_MODE=oidc.
STUFF_STASH_OIDC_CLIENT_ID empty Primary expected OIDC audience/client ID.
STUFF_STASH_OIDC_CLIENT_IDS empty Additional accepted OIDC client IDs, comma-separated.
STUFF_STASH_OIDC_CLI_CLIENT_ID empty Public CLI client ID. Also added to the API’s accepted audiences.
STUFF_STASH_OIDC_CLI_SCOPES openid,email,profile,offline_access Comma-separated scopes requested by CLI sign-in; must include openid.
STUFF_STASH_OIDC_CLI_DEVICE_AUTH_ENABLED false Offers device-code sign-in when the provider also advertises support.
STUFF_STASH_OIDC_MOBILE_CLIENT_ID empty Public native mobile OIDC client ID advertised to the mobile app.
STUFF_STASH_OIDC_MOBILE_REDIRECT_URI stuffstash://auth/callback Native redirect URI advertised to the mobile app.
STUFF_STASH_OIDC_MOBILE_SCOPES openid,email,profile,offline_access Comma-separated scopes requested by mobile sign-in.

STUFF_STASH_OIDC_CLIENT_ID is included in the accepted client ID set even when STUFF_STASH_OIDC_CLIENT_IDS is also configured.

CLI sign-in settings are published at /auth/cli/config when OIDC and a CLI client ID are configured. Register a public client with your identity provider. Browser sign-in uses PKCE and an ephemeral 127.0.0.1 callback. Enable device code support in both your provider and the API for sign-in from a headless computer. The provider must issue ID tokens; access tokens alone cannot authenticate to this API.

The bundled Dex configuration includes stuff-stash-cli-local. Its omitted redirectURIs enables Dex’s public-client loopback and device-flow handling. Configure the matching CLI client ID on the API to use it.

MCP is disabled by default. See Connect an Inventory Agent for client requirements, authentication, and available read tools.

Variable Default Purpose
STUFF_STASH_MCP_ENABLED false Enables /mcp. Use true or false; unrecognized values fail startup.
STUFF_STASH_MCP_AUTH_MODE empty Must explicitly match the API authentication mode when enabled.
STUFF_STASH_MCP_PUBLIC_URL empty Canonical HTTPS endpoint ending in /mcp. Explicit local development permits loopback HTTP.

MCP uses the API’s body-size, rate-limit, CORS, and authentication settings. STUFF_STASH_HTTP_WRITE_TIMEOUT also bounds each MCP request’s application work.

See Export Your Inventory for formats and included data.

Variable Default Purpose
STUFF_STASH_EXPORT_MAX_RECORDS 10000 Maximum records per exported collection.
STUFF_STASH_EXPORT_MAX_BYTES 67108864 Maximum encoded export size in bytes.

Both limits must be positive integers. Invalid values fail startup; exports that exceed either bound fail without publishing a partial file.

Variable Default Purpose
STUFF_STASH_AUTHZ_MODE memory Authorization adapter. Use memory or spicedb.
STUFF_STASH_SPICEDB_ENDPOINT empty SpiceDB gRPC endpoint.
STUFF_STASH_SPICEDB_PRESHARED_KEY empty SpiceDB preshared key. Empty is only suitable for local serve-testing.
STUFF_STASH_SPICEDB_TLS_ENABLED true Enables TLS for SpiceDB connections.
STUFF_STASH_SPICEDB_CA_PATH empty Optional CA certificate path for self-signed or private SpiceDB TLS.
STUFF_STASH_SPICEDB_BOOTSTRAP_SCHEMA false Bootstraps the checked-in SpiceDB schema on startup.
STUFF_STASH_SPICEDB_SCHEMA_PATH deploy/spicedb/schema.zed Schema file used when bootstrapping.
Variable Default Purpose
STUFF_STASH_REPOSITORY_MODE memory Repository adapter. Use memory, postgres, or sqlite.
STUFF_STASH_DATABASE_DSN empty Postgres DSN or SQLite file path/DSN when using durable repository modes.

postgres and sqlite require STUFF_STASH_DATABASE_DSN. SQLite will create the parent directory for file-backed database paths.

SQLite is an API runtime mode, not the current Docker Compose self-hosting topology. The Compose stack uses the Postgres migration job and Postgres service unless a future SQLite-specific Compose file wires schema setup and a durable SQLite file mount explicitly.

Variable Default Purpose
STUFF_STASH_AUTHORIZATION_OUTBOX_DRAIN_LIMIT 25 Authorization outbox events claimed per drain.
STUFF_STASH_AUTHORIZATION_OUTBOX_DRAIN_INTERVAL 10s Background authorization outbox drain interval.
STUFF_STASH_AUTHORIZATION_OUTBOX_CLAIM_LEASE 30s Lease duration for claimed authorization outbox events.
STUFF_STASH_BLOB_DELETION_OUTBOX_DRAIN_LIMIT 25 Blob-deletion outbox events claimed per drain.
STUFF_STASH_BLOB_DELETION_OUTBOX_DRAIN_INTERVAL 10s Background blob-deletion outbox drain interval.
STUFF_STASH_BLOB_DELETION_OUTBOX_CLAIM_LEASE 30s Lease duration for claimed blob-deletion outbox events.
STUFF_STASH_BLOB_DELETION_OUTBOX_MAX_ATTEMPTS 5 Attempts before blob-deletion work is treated as terminal.
STUFF_STASH_IMPORT_JOB_TIMEOUT_SECONDS 900 Seconds before stored import source credentials are considered expired.
STUFF_STASH_IMPORT_CREDENTIAL_VACUUM_INTERVAL_SECONDS 60 Seconds between background cleanup passes for expired import source credentials.
STUFF_STASH_INVITATION_TTL 168h Default inventory invitation token lifetime.
STUFF_STASH_INVITATION_PUBLIC_BASE_URL empty Public web URL used to create clickable inventory invitation links. Required to create invitations; use HTTPS outside explicit local development.
STUFF_STASH_INVITATION_ALLOW_INSECURE_LOCAL_HTTP false Allows a loopback or private RFC 1918 LAN HTTP invitation URL for explicit local development only. Do not enable in deployed environments.
STUFF_STASH_DEFAULT_PAGE_LIMIT 50 Default API collection page size.
STUFF_STASH_MAX_PAGE_LIMIT 100 Maximum accepted API collection page size.
Variable Default Purpose
STUFF_STASH_BLOB_STORAGE_MODE filesystem Blob storage adapter. Use filesystem or s3.
STUFF_STASH_BLOB_STORAGE_PATH .stuffstash/blobs Local filesystem blob path.
STUFF_STASH_MAX_ATTACHMENT_BYTES 26214400 Maximum attachment size in bytes.
STUFF_STASH_S3_ENDPOINT empty S3-compatible endpoint host and port, without scheme.
STUFF_STASH_S3_PUBLIC_ENDPOINT empty Browser-reachable S3-compatible endpoint for direct uploads. Defaults to STUFF_STASH_S3_ENDPOINT when empty.
STUFF_STASH_S3_ACCESS_KEY empty S3 access key.
STUFF_STASH_S3_SECRET_KEY empty S3 secret key.
STUFF_STASH_S3_BUCKET empty S3 bucket name.
STUFF_STASH_S3_REGION garage S3 region value.
STUFF_STASH_S3_SECURE true Uses HTTPS for S3-compatible storage when true.

Set STUFF_STASH_S3_SECURE=false only for trusted local plain-HTTP storage, such as local Garage verification.

When browser clients upload directly to S3-compatible storage, the public endpoint must be reachable from the browser and the bucket must allow CORS for the web origin. For local Garage this usually means:

  • STUFF_STASH_S3_ENDPOINT=garage:3900 for API-to-Garage traffic.
  • STUFF_STASH_S3_PUBLIC_ENDPOINT=<shared-dns-hostname>:3900 for browser-to-Garage traffic.
  • a bucket CORS rule that allows the web origin to use GET and POST and exposes ETag.
Variable Default Purpose
STUFF_STASH_VOICE_DEV_FAKE_ENABLED false Enables local development fake speech, language, and speech output providers.
STUFF_STASH_VOICE_GOOGLE_ENABLED false Enables Google realtime voice provider adapters.
STUFF_STASH_VOICE_PROVIDER_HTTP_TIMEOUT 60s HTTP timeout for configured realtime voice provider calls.
STUFF_STASH_GOOGLE_CLOUD_PROJECT empty Google Cloud project ID. Required when Google voice providers are enabled.
STUFF_STASH_GOOGLE_CLOUD_LOCATION us-central1 Google Cloud location for Gemini.
STUFF_STASH_GOOGLE_GEMINI_MODEL gemini-2.5-flash Gemini model for server-configured speech recognition and conversation. Explicit settings override this default.
STUFF_STASH_GOOGLE_TTS_LANGUAGE_CODE en-US Google Text-to-Speech language code.
STUFF_STASH_GOOGLE_TTS_VOICE_NAME en-US-Standard-C Google Text-to-Speech voice name.
STUFF_STASH_GOOGLE_CREDENTIAL_MODE adc Google credential source. Use adc or access_token.
STUFF_STASH_GOOGLE_ACCESS_TOKEN empty Static Google access token when credential mode is access_token.

When STUFF_STASH_VOICE_GOOGLE_ENABLED=true, Google configuration is validated at startup. Google voice providers take precedence over development fakes when both are enabled.

STUFF_STASH_COMPATIBLE_PROVIDER_ENDPOINTS is a comma-separated allowlist of complete API base URLs. It is empty by default, so compatible provider calls are disabled until an operator allows an endpoint. See Compatible Language Providers for setup and supported capabilities.

Variable Default Purpose
STUFF_STASH_PROVIDER_CREDENTIAL_KEY_ID empty Identifier stored with sealed tenant provider credentials.
STUFF_STASH_PROVIDER_CREDENTIAL_KEY empty Base64-encoded 32-byte AES-GCM key for sealing provider credentials and temporary import source material.

If active provider credentials exist, startup fails closed unless a valid provider credential sealing key is configured.

Generate a key with openssl rand -base64 32. Keep the key stable for a given deployment. Rotating or losing it can make stored provider credentials unreadable.

The web app reads /config.json at runtime. It is not configured through STUFF_STASH_* environment variables inside the browser bundle. The web container generates this file at startup; set STUFF_STASH_WEB_MEDIA_MAX_BYTES to override its upload limit. When unset, it uses STUFF_STASH_MAX_ATTACHMENT_BYTES, then the 25 MiB default.

Field Required Purpose
apiBaseUrl yes Public API base URL. Trailing slashes are trimmed.
oidcIssuer yes Browser-visible OIDC issuer URL. Trailing slashes are trimmed.
oidcClientId yes Browser OIDC client ID.
oidcRedirectUri yes Browser redirect URI after OIDC sign-in.
mediaUploadPolicy.supportedContentTypes no Allowed upload content types. Defaults to JPEG, PNG, WebP, and PDF.
mediaUploadPolicy.maxBytes no Client upload limit. Defaults to 26214400 (25 MiB).

Example:

{
"apiBaseUrl": "https://api.example.test",
"oidcIssuer": "https://accounts.example.test",
"oidcClientId": "stuff-stash-web",
"oidcRedirectUri": "https://stuffstash.example.test/callback",
"mediaUploadPolicy": {
"supportedContentTypes": ["image/jpeg", "image/png", "image/webp", "application/pdf"],
"maxBytes": 26214400
}
}

Keep the web upload policy aligned with STUFF_STASH_MAX_ATTACHMENT_BYTES; the API remains authoritative.

The mobile app asks for a Stuff Stash instance URL, reads the API’s public mobile authentication metadata, and signs in with the configured OIDC provider. These Expo public variables are optional seeds and deployment-specific build settings; the general TestFlight build does not need them.

Mobile booleans accept 1, true, yes, 0, false, and no.

Variable Required Purpose
EXPO_PUBLIC_STUFF_STASH_API_BASE_URL no Optional API base URL seed shown on first launch.
EXPO_PUBLIC_STUFF_STASH_TENANT_ID no Optional initial tenant selection hint.
EXPO_PUBLIC_STUFF_STASH_VOICE_DIAGNOSTICS_ENABLED no Enables mobile voice developer diagnostics.
EXPO_PUBLIC_STUFF_STASH_INVITATION_ORIGIN no Optional HTTPS web origin whose /invitations/accept links open in a deployment-specific installed build. The general TestFlight build omits it because users select their server during onboarding.
EXPO_PUBLIC_STUFF_STASH_INVITATION_ALLOW_INSECURE_LOCAL_HTTP no Explicitly permits a configured loopback or private RFC 1918 HTTP invitation origin in non-production builds for browser acceptance from local devices.
STUFF_STASH_MOBILE_REQUIRE_INVITATION_LINKS no Set to true for a deployment-specific build that must include invitation universal/app links. The build fails if the invitation origin is missing.

The web server publishes the platform association documents at /.well-known/apple-app-site-association and /.well-known/assetlinks.json. Set STUFF_STASH_MOBILE_IOS_APP_ID to the built app’s Team ID and bundle ID, and set STUFF_STASH_MOBILE_ANDROID_SHA256_CERT_FINGERPRINT to the SHA-256 fingerprint of the certificate that signs the Android build. Both signing identities are deployment-specific. When an iOS App ID or Android fingerprint is not configured, the server publishes an empty relationship for that platform, so it leaves web links in the browser instead of claiming an identity that cannot be verified.

The mobile invitation origin must use standard HTTPS on port 443 and must match the origin used in generated invitation links for verified native app linking. Associated domains cannot encode a custom port. With the explicit insecure-local switch, a non-production build may trust loopback or private RFC 1918 HTTP for browser acceptance, but it emits no iOS associated-domain or Android verified app-link declaration. The default self-host :8081 origin is suitable for browser and local custom-scheme testing; put the web service on standard HTTPS (directly or through a reverse proxy) before building a mobile app that claims its links. Apple must also be able to retrieve the association document over publicly trusted HTTPS. Private hostnames and self-signed certificates are appropriate for custom-scheme development, not production universal-link verification.

Variable Default Purpose
STUFF_STASH_DOCS_SITE https://elsell.github.io Public site origin for Astro builds.
STUFF_STASH_DOCS_BASE /stuffstash/ Base path for production or preview docs builds.

These variables are conveniences for local Compose. They are not API runtime settings unless they map to a STUFF_STASH_* variable above.

Variable Default Purpose
STUFF_STASH_HTTP_PORT 8080 Host port mapped to the API container.
POSTGRES_DB stuffstash Local Compose Postgres database name.
POSTGRES_USER stuffstash Local Compose Postgres user.
POSTGRES_PASSWORD stuffstash-local Local Compose Postgres password.
POSTGRES_PORT 5432 Host port mapped to Postgres.
SPICEDB_GRPC_PORT 50051 Host port mapped to SpiceDB.
DEX_HTTP_PORT 5556 Host port mapped to Dex in the OIDC Compose override.
GARAGE_IMAGE pinned digest Garage image override. Must stay digest-pinned.
STUFF_STASH_API_IMAGE pinned digest Published API image used by self-host Compose. Release automation updates this digest.
STUFF_STASH_WEB_IMAGE pinned digest Published static web image used by self-host Compose. Release automation updates this digest.
GARAGE_S3_PORT 3900 Host port mapped to the Garage S3 API.
GO_BUILDER_IMAGE pinned digest API builder image override. Must stay digest-pinned.
RUNTIME_IMAGE pinned digest API runtime image override. Must stay digest-pinned.
NODE_BUILDER_IMAGE pinned digest Web builder image override. Must stay digest-pinned.
WEB_RUNTIME_IMAGE pinned digest Web runtime image override. Must stay digest-pinned.
PNPM_VERSION 11.0.7 pnpm version used by the web image build.

Evaluation workers run saved test suites against configured models using isolated inventory fixtures. Each API process runs one suite at a time by default. Keep that default for a small local model; increase concurrency only when the provider has capacity. A run keeps its selected workflow, cases and provider configuration.

Variable Default Purpose
STUFF_STASH_EVALUATION_WORKER_ENABLED true Disable processing on API replicas that should leave queued work to other replicas.
STUFF_STASH_EVALUATION_CONCURRENCY 1 Concurrent suites per process, from 1 to 8.
STUFF_STASH_EVALUATION_DRAIN_LIMIT 10 Maximum runs discovered per batch, from 1 to 100.
STUFF_STASH_EVALUATION_INTERVAL 5s Queue polling interval; batches never overlap.
STUFF_STASH_EVALUATION_POLL_INTERVAL 2s How often active runs check cancellation and access.
STUFF_STASH_EVALUATION_LEASE_GRACE 30s Extra time beyond a case deadline for recording progress.
STUFF_STASH_EVALUATION_MAX_ATTEMPTS 2 Maximum claims after interrupted processing, from 1 to 10.

For Google server ADC evaluations, also set STUFF_STASH_GOOGLE_ADC_CREDENTIAL_VERSION to a non-secret revision label. Change it whenever you replace the mounted ADC credential or account. Normal token refresh does not need a new label. This lets queued evaluations detect credential changes without storing or hashing secret material. Ordinary voice use does not require this label.

The API checks registered users’ expiration reminders in the background. It does not require the web or mobile app to stay open. Personal reminder settings determine which milestones appear in each user’s inbox.

Variable Default Purpose
STUFF_STASH_NOTIFICATION_WORKER_ENABLED true Run background expiration checks.
STUFF_STASH_NOTIFICATION_POLL_INTERVAL 5s Pause between bounded pages of work; minimum 100ms.
STUFF_STASH_NOTIFICATION_PAGE_SIZE 100 Assets evaluated per recipient page, from 1 to 100.
STUFF_STASH_NOTIFICATION_PAGE_TIMEOUT 30s Time allowed for one page; minimum 1s.

Larger inventories take multiple pages. Restarts and retries preserve existing notifications without generating duplicate milestones. These controls govern inbox generation; mobile push delivery requires separate provider configuration.

The notification inbox works independently of mobile push. Push delivery is off until a provider is configured. Mount credentials as read-only secret files; do not put their contents in environment variables or source control.

Set STUFF_STASH_PUSH_SERVER_ID to the public API URL used by the mobile app. For iOS, enable STUFF_STASH_PUSH_APNS_ENABLED and provide STUFF_STASH_PUSH_APNS_KEY_ID, STUFF_STASH_PUSH_APNS_TEAM_ID, STUFF_STASH_PUSH_APNS_TOPIC (the app bundle identifier), and STUFF_STASH_PUSH_APNS_KEY_FILE (a mounted APNs signing key). Production APNs is the default, including TestFlight. Set STUFF_STASH_PUSH_APNS_PRODUCTION=false only for a development app signed for the APNs sandbox.

For Android, enable STUFF_STASH_PUSH_FCM_ENABLED and provide STUFF_STASH_PUSH_FCM_PROJECT_ID and STUFF_STASH_PUSH_FCM_CREDENTIALS_FILE. The latter must contain a Google service account credential authorized to send Firebase Cloud Messaging messages for that project. The default STUFF_STASH_PUSH_FCM_CHANNEL_ID is expiration, matching the mobile app. Enabled providers with missing or invalid credentials prevent startup.

Delivery polls every five seconds, claims ten notifications per page, and allows 30 seconds per page under a one-minute lease. These can be configured with STUFF_STASH_PUSH_POLL_INTERVAL, STUFF_STASH_PUSH_PAGE_SIZE, STUFF_STASH_PUSH_PAGE_TIMEOUT, and STUFF_STASH_PUSH_LEASE. The page timeout must be shorter than the lease. Retries default to six attempts, starting at one minute with a 15-minute backoff cap. Configure these with STUFF_STASH_PUSH_MAX_ATTEMPTS, STUFF_STASH_PUSH_RETRY_INITIAL_DELAY, and STUFF_STASH_PUSH_RETRY_MAXIMUM_DELAY. Provider retry deadlines may extend that backoff. Duration values use Go notation, such as 30s or 5m.

These settings enable asset label downloads and scanning in the app and website. The CLI connector delivers queued labels to registered printers. Invalid values fail startup. An empty label base disables label operations.

Variable Default Purpose
STUFF_STASH_PUBLIC_WEB_BASE_URL empty; web origin in self-host Compose HTTPS web address for connector approval and label links when no separate label base is set.
STUFF_STASH_LABEL_BASE_URL public web base Optional stable HTTPS label address, including any path prefix. No credentials, query, or fragment.
STUFF_STASH_LABEL_RENDER_TTL 1h Private preview/download lifetime; at most 24h.
STUFF_STASH_LABEL_RENDER_MAX_BYTES 1048576 Maximum stored artifact size; at most 16 MiB.
STUFF_STASH_LABEL_CLEANUP_INTERVAL 1m Remove expired previews at this interval; at most 1h. Access expires even before cleanup runs.
STUFF_STASH_LABEL_MAX_PIXELS 4000000 Maximum raster pixels; at most 16 million.
STUFF_STASH_LABEL_MAX_URL_BYTES 2048 Maximum QR URL bytes; at most 4096. Dense QRs can still exceed a label’s readable capacity.
STUFF_STASH_LABEL_MAX_TITLE_RUNES 2048 Maximum title characters accepted by rendering; at most 4096.
STUFF_STASH_LABEL_MAX_REFERENCE_RUNES 128 Maximum reference characters; at most 512.

Use the one-time bootstrap command after database migration. Rendering currently supports the built-in font’s Latin, Greek, and Cyrillic coverage; unsupported text returns an error rather than printing replacement boxes. QR-only labels can omit the title.