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 |