Skip to content

Configuration

Everything is an environment variable prefixed ARKHE_, or an entry in a .env file. arkhe check validates a configuration and stops on anything missing.

Secrets have no defaults, on purpose

ARKHE_TOKEN_SECRET, ARKHE_SESSION_SECRET and KC_ADMIN_PASSWORD will refuse to start rather than fall back to a value. A default that works is a default that ships to production unnoticed.

Role

Default
ARKHE_RESOLVER false Run as a resolver. A minter has no resolution endpoint and a resolver has no minting endpoint — so the two scale separately and the resolver can point at a read-only replica
ARKHE_DEBUG false
ARKHE_ALLOWED_HOSTS * Comma-separated. Narrowing it rejects requests whose Host does not match with 400 (the default * installs nothing — where a proxy terminates, it usually checks this already, and rejecting twice makes failures harder to attribute)

Database

Default
ARKHE_DATABASE_URL postgresql+psycopg://arkhe@localhost/arkhe
ARKHE_READ_DATABASE_URL — Read-only connection for the resolver. Falls back to the above
ARKHE_DB_POOL_SIZE 5 Connections a process keeps. It multiplies by the worker count
ARKHE_DB_MAX_OVERFLOW 10 Extra connections under load, added to POOL_SIZE (15 per process by default)
ARKHE_DB_POOL_RECYCLE 0 Seconds before a connection is remade; 0 never remakes it. Behind anything that drops idle connections, set it below that idle timeout
ARKHE_DB_PRE_PING true Check a connection is alive before lending it. Turning it off roughly halves the database round trips per resolution (measured 1.60 → 0.82). Only turn it off where the database is close and a dropped connection may surface as an error

Verify migrations on PostgreSQL

SQLite tolerates things PostgreSQL does not — notably the circular reference between manager and shoulder, and the order in which tables are created. Both slipped through a SQLite-only check during development.

Authentication — API

Default
ARKHE_AUTH apikey,oidc Combinable, tried in order. apikey / oauth2 / oidc
ARKHE_TOKEN_SECRET — Signing key for oauth2. 32 bytes or more (RFC 7518 §3.2)
ARKHE_TOKEN_TTL 3600
ARKHE_TOKEN_ISSUER —
ARKHE_OIDC_ISSUER — Required for oidc
ARKHE_OIDC_AUDIENCE — Audience required of API access tokens. Not the ID token's — those carry admin_client_id
ARKHE_OIDC_JWKS_URL — Discovered from the issuer if unset

See Authentication for which mechanism suits what.

Authentication — the admin interface

Default
ARKHE_ADMIN_LOGIN bearer bearer / password / oidc / proxy
ARKHE_SESSION_SECRET — Required for anything but bearer. 32 bytes or more
ARKHE_SESSION_TTL 28800 Eight hours
ARKHE_SESSION_SECURE true Leave on when serving over HTTPS
ARKHE_ADMIN_CLIENT_ID — Required for oidc
ARKHE_ADMIN_CLIENT_SECRET —
ARKHE_ADMIN_SCOPE openid profile email
ARKHE_PROXY_USER_HEADER X-Forwarded-User For proxy

proxy mode requires closing the direct path

If anything can reach arkhe without passing the proxy, anyone can forge the header. Use a NetworkPolicy on Kubernetes, or listen only on 127.0.0.1.

Resolution

Default
ARKHE_GLOBAL_RESOLVER https://n2t.net Where an unknown NAAN is forwarded (D2)
ARKHE_RAW_URI_HEADER — Header carrying the raw request URI, so a bare ? can be detected. A ? with no query string is indistinguishable otherwise — even in ASGI
ARKHE_RESOLVE_UNPUBLISHED false Also resolve ARKs that are not published yet. For a resolver inside a closed network, where a name minted in that network is worthless if the network's own resolver will not resolve it

Never on a public face

?info and ?? need no authentication. If a resolver with this on can be reached from the open network, the existence, title and target of objects that are not public yet go straight out. Turn it on only where the reach itself is closed.

Minting

Default
ARKHE_BULK_LIMIT 1000 Rows per bulk request. Split larger loads and use request_id so a broken batch can simply be resent
ARKHE_HOLD_MAX_DAYS 90 Longest a redirect may be held. Requiring an expiry is not enough — "a year from now" is indistinguishable from permanent, so there is a ceiling. Extending means setting it again, which the audit log records

Behind a proxy

Default
ARKHE_TRUSTED_PROXIES 0 How many proxies in front to believe. X-Forwarded-For is a header anyone can set, so by default it is ignored and the peer address is recorded — recording a forged value is worse than recording a coarse one, because an audit log full of attacker-written strings is the worst outcome. With n proxies in front, set n: the n-th value from the right is taken. Never the leftmost — that one the client wrote
ARKHE_LOG_LEVEL INFO DEBUG / INFO / WARNING. Logs are JSON lines carrying the request id