Skip to main content
Version: 0.3.0

Configuration

All configuration is via environment variables prefixed with CAIRN_. Each subsystem has its own group (CAIRN_<GROUP>_<KEY>), read once at startup. This page lists every variable the server reads, with its default in the second column. A dash means the variable has no default and stays unset unless you provide it.

Only the CAIRN_-prefixed name is read. Up to 0.2.x a missing CAIRN_* variable fell back to the bare name, so host variables such as URL, USERNAME, PASSWORD or SESSION_SECRET could fill Cairn settings; since 0.3.0 they are ignored. Rename any unprefixed variables you relied on before upgrading.

Three variables are required: CAIRN_DATABASE_URL, CAIRN_AUTH_SESSION_SECRET and CAIRN_AUTH_MASTER_ENCRYPTION_KEY. The server refuses to start when one is missing or set to an empty value. An optional variable set to an empty value means empty, not its default: CAIRN_NATS_URL= keeps NATS disabled.

cairn config help prints every variable the config struct reads, with its type, default and whether it is required:

docker run --rm ghcr.io/johnnycube/cairn-core:v0.3.0 config help

The OIDC variables (keyed by provider id) and CAIRN_HTTP_RATE_LIMIT_PER_MIN are read separately and do not appear in that list.

tip

dev.env.example in cairn-core is a working local set for the Compose dev stack. Copy it to dev.env; make dev-server sources it.

Database​

VariableDefaultNotes
CAIRN_DATABASE_URL—required — postgres://user:pass@host:5432/cairn
CAIRN_DATABASE_AUTO_MIGRATEfalserun the goose migrations on boot
CAIRN_DATABASE_MAX_CONNS / ..._MIN_CONNS10 / 2connection pool bounds
CAIRN_DATABASE_STATEMENT_TIMEOUT30sper-statement timeout
CAIRN_DATABASE_LOCK_TIMEOUT5show long a statement waits for a lock
CAIRN_DATABASE_IDLE_IN_TX_TIMEOUT60sends sessions idle inside a transaction
CAIRN_DATABASE_APPLICATION_NAMEcairnshown in pg_stat_activity

HTTP​

VariableDefaultNotes
CAIRN_HTTP_ADDR0.0.0.0:8080listen address
CAIRN_HTTP_PUBLIC_BASE_URLhttp://localhost:8080external URL — used for OAuth redirects, share links and federation
CAIRN_HTTP_CORS_ALLOWED_ORIGINS—comma-separated origins for browser clients served from another origin
CAIRN_HTTP_TLS_CERT_FILE / ..._TLS_KEY_FILE—terminate TLS in-process instead of at a proxy
CAIRN_HTTP_READ_TIMEOUT / ..._WRITE_TIMEOUT / ..._IDLE_TIMEOUT30s / 5m / 120sserver timeouts; the write timeout is long because uploads and exports stream
CAIRN_HTTP_SHUTDOWN_TIMEOUT30sgrace period on SIGTERM
CAIRN_HTTP_RATE_LIMIT_PER_MIN600requests per minute per session (per IP when anonymous); generous so the polling web UI is never throttled. Values that are not a positive number are ignored

Auth​

VariableDefaultNotes
CAIRN_AUTH_SESSION_SECRET—required — signs sessions and tokens; set a long random value
CAIRN_AUTH_MASTER_ENCRYPTION_KEY—required — encrypts provider tokens and other secrets at rest; changing it makes stored credentials unreadable
CAIRN_AUTH_SESSION_TTL720habsolute session lifetime (30 days)
CAIRN_AUTH_SESSION_IDLE_MAX168ha session unused for this long expires (7 days)
CAIRN_AUTH_ARGON2_TIME / ..._MEMORY / ..._THREADS / ..._KEY_LEN2 / 19456 / 1 / 32Argon2id parameters for password hashing (memory in KiB)
CAIRN_AUTH_OAUTH_ENABLEDtrueCairn's own OAuth 2.1 authorization server, used by native apps and MCP
CAIRN_AUTH_OAUTH_ACCESS_TOKEN_TTL1h
CAIRN_AUTH_OAUTH_REFRESH_TOKEN_TTL2160h90 days
CAIRN_AUTH_OAUTH_AUTH_CODE_TTL5m
CAIRN_AUTH_OAUTH_ALLOW_DYNAMIC_REGISTRATIONtruelet clients register themselves (RFC 7591); see OAuth

Sign-in with an identity provider (OIDC)​

Besides passwords and passkeys, users can sign in through any OpenID Connect provider. List the provider ids, then configure each one under its own prefix.

VariableDefaultNotes
CAIRN_OIDC_PROVIDERS—comma-separated ids, e.g. pocketid,google — each id becomes a login button
CAIRN_OIDC_<ID>_ISSUER_URL—required per provider — the OIDC issuer
CAIRN_OIDC_<ID>_CLIENT_ID / ..._CLIENT_SECRET—required per provider — confidential client
CAIRN_OIDC_<ID>_NAMEthe idlabel of the login button
CAIRN_OIDC_<ID>_SCOPESopenid,email,profile
CAIRN_OIDC_<ID>_AUDIENCEthe client idexpected aud claim
CAIRN_OIDC_<ID>_SKIP_AUDIENCE_CHECKfalse
CAIRN_OIDC_<ID>_USE_PKCEtrue
CAIRN_OIDC_<ID>_AUTO_PROVISIONtruecreate an account on first sign-in
CAIRN_OIDC_<ID>_AUTO_PROVISION_ROLEuseruser or admin for auto-provisioned accounts

<ID> is the upper-cased provider id: CAIRN_OIDC_PROVIDERS=google reads CAIRN_OIDC_GOOGLE_ISSUER_URL.

Instance​

VariableDefaultNotes
CAIRN_INSTANCE_PUBLIC_URLhttp://localhost:8080the instance's canonical URL as presented to other instances and in emails
CAIRN_INSTANCE_BOOTSTRAP_ADMIN_EMAIL / ..._PASSWORD—creates the first admin on first boot; harmless to leave set afterwards
CAIRN_INSTANCE_TRUSTED_PROXIES—comma-separated CIDRs whose X-Forwarded-* headers are honored

NATS (control plane)​

VariableDefaultNotes
CAIRN_NATS_URLnats://localhost:4222set it to an empty value to run without NATS: no workers, no scheduler
CAIRN_NATS_CLUSTER_NAME—optional guard against pointing at the wrong cluster
CAIRN_NATS_CLIENT_NAMEcairn-serverconnection name shown in NATS monitoring
CAIRN_NATS_CREDS_FILE / ..._USERNAME / ..._PASSWORD—credentials (prod: nkey/JWT creds file)
CAIRN_NATS_TLS_CA_FILE / ..._TLS_CERT_FILE / ..._TLS_KEY_FILE—TLS to the NATS server
CAIRN_NATS_CONNECT_TIMEOUT / ..._RECONNECT_WAIT / ..._MAX_RECONNECTS10s / 2s / -1-1 reconnects forever
CAIRN_NATS_JOB_ACK_WAIT5mack window of the consumers core creates on CAIRN_RESULTS; also the TTL of the credentials auth-callout issues to workers. Job consumers are created by the workers with their own AckWait (Strava worker 2 min, Garmin worker 30 s), see the provider contract
CAIRN_NATS_JOB_MAX_DELIVER5redeliveries before a job lands in the dead-letter stream

Storage (S3-compatible, optional)​

VariableDefaultNotes
CAIRN_STORAGE_ENDPOINThttp://minio:9000
CAIRN_STORAGE_REGIONus-east-1
CAIRN_STORAGE_BUCKETcairn
CAIRN_STORAGE_ACCESS_KEY_ID / ..._SECRET_ACCESS_KEY—when both are empty, core runs without blob storage: no raw-file uploads, no photos, no cached map snapshots
CAIRN_STORAGE_USE_PATH_STYLEtruepath-style addressing (MinIO, Garage); false for virtual-hosted buckets
CAIRN_STORAGE_PRESIGN_TTL15mlifetime of the presigned upload and download URLs handed to clients and workers
CAIRN_STORAGE_TRANSFER_EXPIRY_DAYS1bucket lifecycle rule for claim-checked worker results under transfer/; core deletes them right after ingest, the rule only reaps orphans

Maps​

The interactive map in the web UI uses OpenFreeMap's key-less Liberty vector style by default. The server renders a static snapshot of every activity (/api/activities/{id}/map.png) for feeds and share links from raster tiles.

VariableDefaultNotes
CAIRN_MAP_TILE_URLhttps://tile.openstreetmap.org/{z}/{x}/{y}.pngraster tile template for the server-rendered snapshot
CAIRN_MAP_USER_AGENT—identifies your instance to the tile provider; the public OSM tile server requires a meaningful value, see its usage policy
CAIRN_MAP_TIMEOUT15sper-tile fetch timeout
VITE_MAP_STYLE_URLOpenFreeMap Libertybuild-time variable of the web UI, not read by the server: point it at your own MapLibre style JSON when you build the image yourself

Geocoder​

Activities get a place name from reverse geocoding their start point.

VariableDefaultNotes
CAIRN_GEOCODER_ENABLEDtrue
CAIRN_GEOCODER_URLhttps://nominatim.openstreetmap.organy Nominatim-compatible endpoint
CAIRN_GEOCODER_EMAIL / ..._USER_AGENT—contact details Nominatim asks for; set them before using the public instance
CAIRN_GEOCODER_MIN_INTERVAL1sminimum spacing between requests (the public instance allows one per second)
CAIRN_GEOCODER_TIMEOUT10s
CAIRN_GEOCODER_BATCH_SIZE / ..._TICK_INTERVAL50 / 2mhow many activities are geocoded per tick

Email​

VariableDefaultNotes
CAIRN_EMAIL_ADAPTERnonenone, smtp, ses, resend or mailgun; none disables verification and reset mails
CAIRN_EMAIL_FROM_ADDRESS / ..._FROM_NAME— / Cairn
CAIRN_EMAIL_SMTP_HOST / ..._SMTP_PORT— / 587
CAIRN_EMAIL_SMTP_USERNAME / ..._SMTP_PASSWORD—
CAIRN_EMAIL_SMTP_STARTTLStrue
CAIRN_EMAIL_SES_REGION—for the ses adapter
CAIRN_EMAIL_API_KEY—for resend and mailgun
CAIRN_EMAIL_MAILGUN_DOMAIN—

Scheduler​

The scheduler drives periodic provider reconciles and webhook drift checks.

VariableDefaultNotes
CAIRN_SCHEDULER_ENABLEDtruedisable on all but one replica if you scale core
CAIRN_SCHEDULER_TICK_INTERVAL60s
CAIRN_SCHEDULER_POLLING_INTERVAL60mhow often each connected account is reconciled
CAIRN_SCHEDULER_WEBHOOK_DRIFT_INTERVAL24hre-checks provider webhook subscriptions
CAIRN_SCHEDULER_BATCH_SIZE100accounts per tick

Federation​

VariableDefaultNotes
CAIRN_FEDERATION_ENABLEDfalseActivityPub federation. Off keeps federation fully dormant: no remote activities in feeds and no "Federated" feed filter. See Social & federation
CAIRN_FEDERATION_ALLOW_PRIVATE_HOSTSfalselift the outbound address guard for federation fetches and deliveries, so instances on private or loopback addresses can reach each other — only for local testing

Quotas and logging​

VariableDefaultNotes
CAIRN_QUOTA_MAX_ACTIVITIES_PER_USER00 means unlimited
CAIRN_LOG_LEVELinfodebug, info, warn or error
CAIRN_LOG_FORMATjsonjson or text

Request limits​

These limits are fixed in the server, not configurable:

RequestLimitOver the limit
Connect-RPC message (web UI, gRPC, gRPC-Web clients)4 MiB per messagerejected by Connect (resource_exhausted)
File upload, POST /api/activities/upload64 MiB file, plus 1 MiB multipart framing400
Attachment, POST /api/activities/{id}/attachments32 MiB file, plus 1 MiB framing and caption400
JSON request bodies of the REST API and MCP requests1 MiB400
Provider webhook deliveries, POST /webhooks/{provider}1 MiB413

A body over the limit is refused, not truncated.

Outbound requests​

Federation fetches and deliveries and notification webhooks go through an address guard that refuses to connect to anything that is not public unicast: loopback, private (RFC 1918, fc00::/7), link-local and every other IANA special-purpose range, among them CGNAT 100.64.0.0/10 (including the 100.100.100.200 cloud metadata endpoint), 0.0.0.0/8, 192.0.0.0/24, the documentation and benchmarking ranges, 240.0.0.0/4, NAT64 (64:ff9b::/96, 64:ff9b:1::/48), 6to4 2002::/16, Teredo (2001::/23) and any IPv6 address outside 2000::/3. IPv4-mapped IPv6 addresses are judged as their IPv4 address. The check runs on the resolved address at dial time, so DNS names that point inward are refused too, and a webhook URL is validated against the same guard when it is saved. Redirects are capped, and every hop is dialed through the same guard.

CAIRN_FEDERATION_ALLOW_PRIVATE_HOSTS=true turns the guard off for federation only (and makes WebFinger lookups use plain HTTP); notification webhooks stay guarded.

Worker variables​

Provider workers are separate processes with their own, smaller set of CAIRN_* variables (NATS connection, enrollment, provider credentials). They are listed on the Providers page.