Documentation
Technical

Architecture

A deep dive into how Keychains.dev works under the hood — identity, permissions, proxy validation, and the security model.

Four layers of trust

Access flows through four layers. Each builds on the previous — compromise at one layer doesn't break the others.

1
Machine Identity
Ed25519 SSH keypairs + rolling challenge-response + hardware fingerprinting. Proves which machine is acting. Challenge nonces are single-use — replayed signatures fail.
2
Permission Request
Stateful, user-approved authorization stored in Redis. Scoped or wildcard mode. Carries version tracking — any mutation bumps the version, invalidating in-flight tokens.
3
Delegate Identity
Immutable bootstrap identity with its own Ed25519 keypair and proof-of-possession. Allows remote systems to act with a reduced scope ceiling derived from the parent.
4
Permission Token
Short-lived JWT (5-15 min) for execution. Contains request ID and version — no scopes. Scopes are resolved from Redis at proxy time, ensuring revocations take effect instantly.

Proxy validation algorithm

Every proxied request goes through this pipeline. All checks are stateful — the proxy is the final authority.

1. Verify JWT signature
2. Fetch Permission Request from Redis by requestId
3. Validate state: status === active, version matches, not expired
4. If delegateId: validate delegate chain + parent status
5. Validate machine challenge state
6. Resolve approvedScopes from Redis (not from token)
7. Check required scopes vs approved:
All present: proceed
Wildcard + missing: 403 + approval_url
Scoped + missing: 403 no recourse
Delegate ceiling exceeded: 403 scope_refused
8. Inject credentials server-side (OAuth tokens / API keys / basic auth)
9. Execute upstream call
10. Write audit log (non-blocking via after())

Wildcard permissions

Wildcard permissions start with zero scopes and grow as the user approves each one. Every scope still requires explicit consent.

1.Agent calls proxy requiring github::repo
2.Proxy returns 403 with approval URL
3.User visits URL, approves github::repo
4.Scope added to approvedScopes, version bumps
5.Agent retries — proxy re-reads Redis — success

Core data model

EntityStorageKey fields
MachineRedisid, userId, publicKey, fingerprint, challengeNonce
PermissionRequestRedisid, machineId, mode, status, requestedScopes, approvedScopes, version
DelegateRedisid, parentPermissionId, mode, publicKey, approvedScopes, ttl
PermissionTokenJWT (stateless)requestId, machineId, delegateId, version, exp
AuditLogMongoDBuserId, machineId, permissionId, provider, method, endpoint, status

Security properties

No secrets in tokens
Permission tokens contain IDs and versions — never scopes, never credentials. Scopes are resolved server-side.
Version pinning
Every mutation bumps the permission version. Tokens with stale versions are rejected. Revocation takes effect in under 1 second.
Cascade revocation
Revoking a parent automatically revokes all delegates. No orphaned access, no cleanup needed.
Rolling challenges
Each machine auth uses a single-use nonce. Concurrent auth from two locations triggers automatic revalidation.
Scope immutability (scoped)
Once approved, scoped permissions can never expand. The set is frozen — auditable and predictable.
Audit everything
Every proxied call is logged in MongoDB with full context. Dashboard provides real-time visibility.

Credential injection

The proxy resolves and injects credentials based on the target API's requirements. Supported methods:

OAuth 2.0 / 2.1
Access tokens refreshed automatically. Token rotation handled server-side.
API Keys
Injected as headers or query params per provider spec.
Basic Auth
Username/password pairs injected as Authorization headers.