Files
alknet/docs/architecture/decisions/037-api-keys-dynamic-config.md
glm-5.1 cfc44008d3 Sync architecture specs with Phase 2 research findings
- Add definitions.md: normative terminology disambiguation (Interface, Service,
  Transport, Token, Identity, Domain, Scope, CredentialProvider, etc.)
- Add credentials.md: CredentialProvider trait and CredentialSet enum for
  outbound auth, mirroring IdentityProvider pattern for inbound auth
- Rewrite interface.md: StreamInterface/MessageInterface split (ADR-035),
  InterfaceRequest/InterfaceResponse, HttpInterface/DnsInterface stubs,
  ListenerConfig with Stream/Http/Dns variants, credential presentation table
- Update auth.md: API keys in DynamicConfig (ADR-037), credential presentation
  per (Transport, Interface) pair, ApiKeyEntry struct in AuthPolicy
- Update configuration.md: API keys, ListenerConfig with Http/Dns variants,
  expanded TOML config examples
- Update call-protocol.md: resolve OQ-IF-01 (InterfaceEvent carries
  EventEnvelope + Identity), add MessageInterface awareness to protocol
  adapter layer
- Update overview.md: three-layer model now includes StreamInterface/
  MessageInterface, CredentialProvider/CredentialSet exports, definitions.md
  reference, ADRs 035-037
- Update open-questions.md: resolve OQ-IF-01, OQ-IF-02, add OQ-P2-01
  through OQ-P2-04, add OQ-CP-01 through OQ-CP-04, add OQ-DEF-01,
  OQ-DEF-03, OQ-DEF-08
- Update README.md: add definitions.md, credentials.md, ADRs 035-037,
  phase2 research docs, current state description

Key architectural decisions:
- ADR-035: StreamInterface/MessageInterface split (two Layer 2 traits)
- ADR-036: CredentialProvider as core type (outbound auth, alknet_core::credentials)
- ADR-037: API keys as DynamicConfig auth (hash-verified bearer tokens)
2026-06-09 08:09:45 +00:00

83 lines
3.4 KiB
Markdown

# ADR-037: API Keys as DynamicConfig Auth
## Status
Accepted
## Context
Alknet's token auth uses Ed25519-signed `AuthToken`s — the same key material
used for SSH auth. This is appropriate for interactive clients (browsers, CLI)
that can generate and sign Ed25519 key pairs.
But for service accounts, automation, and simple integrations, Ed25519 key
pairs are inconvenient. A dashboard backend, a CI/CD pipeline, or a monitoring
script needs a simple bearer token that can be stored in an environment variable
or config file without managing cryptographic key pairs.
The HTTP interface (Phase 2+) requires bearer token auth for `Authorization:
Bearer <token>` headers. `AuthToken` works but requires client-side Ed25519
signing. API keys offer a simpler alternative: short bearer tokens verified by
SHA-256 hash lookup, with optional scope restrictions and TTL.
## Decision
Add `[[auth.api_keys]]` section to `DynamicConfig`:
```toml
[[auth.api_keys]]
prefix = "alk_"
hash = "sha256:abc..."
scopes = ["relay:connect", "secrets:derive"]
description = "dashboard service account"
ttl = "30d" # optional
```
`ConfigIdentityProvider::resolve_from_token()` handles both token types:
- If the input starts with the configured prefix (default `alk_`), treat it as
an API key: hash it with SHA-256 and look up the hash in the `api_keys` table.
- Otherwise, treat it as an `AuthToken`: decode, verify Ed25519 signature,
check timestamp, resolve from `authorized_keys`.
Both paths produce the same `Identity` result. In database-backed deployments,
both resolve to the same account UUID.
API keys are stored as SHA-256 hashes (like password hashing — the cleartext
key is never stored, only its hash). The prefix enables O(1) routing between
AuthToken and API key verification without trying both paths.
The full key is provided to the client exactly once (at creation time). Subsequent
verifications only compare hashes.
## Consequences
**Positive**: Simple bearer token auth for HTTP and other non-SSH interfaces.
No cryptographic key management for service accounts. Consistent with industry
practice (Stripe, GitHub, AWS all use prefixed API keys).
**Positive**: Both AuthTokens and API keys go through `resolve_from_token()`.
The caller doesn't need to know which type they're using. This keeps the
authentication layer unified.
**Positive**: Scoped API keys enable fine-grained access control for service
accounts. A monitoring tool gets `["monitoring:read"]`, not full access.
**Negative**: API keys are bearer tokens — anyone who obtains the key has the
associated permissions. The hash storage and optional TTL mitigate but do not
eliminate this risk. Ed25519 AuthTokens remain the preferred auth method for
interactive clients.
**Negative**: API key rotation requires updating `DynamicConfig` (or the
`api_keys` database table). The `ConfigReloadHandle` / `ConfigService` reload
mechanism handles this, but it's a deliberate operation, not automatic.
**Negative**: No rate limiting on API key verification is built into this ADR.
Rate limiting on the HTTP interface is a separate concern.
## References
- ADR-023 (unified auth, shared key material)
- ADR-029 (Identity as core type)
- ADR-030 (static/dynamic config split)
- [auth.md](../auth.md) — Token auth, AuthPolicy, API keys
- [configuration.md](../configuration.md) — DynamicConfig, AuthPolicy
- [research/phase2/interface-model.md](../../research/phase2/interface-model.md) — API keys in config