authsome is the local auth layer for AI agents — it answers which agent, acting on behalf of whom, accessed what credential, and was that allowed.
Each module has one job. identity/, auth/, vault/, and audit/ are leaf modules — they import nothing from this codebase and can be used and tested in isolation. server/ is the only composition root.
identity/ ←─┐
auth/ ←─┤
vault/ ←─┤ server/ ←── cli/ (via HTTP, not Python import)
audit/ ←─┘ ←── proxy/ (via HTTP, not Python import)
Think of this as the OpenID Connect layer. Handles key material, DIDs, and proof-of-possession tokens.
Owns:
- Ed25519 key pair generation and serialization (
local.py) did:keyDID derivation from public keys (local.py)IdentityMetadatamodel — client-side cached state for a local identityIdentityRegistrationmodel — the server's record of a registered handle/DID binding- PoP JWT creation and validation (
proof.py) ClaimStatus,PrincipalRecord,VaultRecord,IdentityClaimRecord,PrincipalVaultBindingRecord— shared domain models
Does not own:
- Filesystem-backed registries (those are server state, not identity primitives)
- Client config management (that is
cli/territory) - Principal/vault lifecycle decisions (that is
server/territory)
Imports nothing from this codebase. Used by: server/, cli/
Think of this as the OAuth 2.0 protocol library. Each flow takes provider config and credentials in, returns tokens out. No storage, no audit, no identity imports.
Owns:
- OAuth 2.0 flows: PKCE, Device Code, DCR+PKCE (
flows/) - API key collection flow (
flows/api_key.py) - Flow base class and token refresh logic
- Provider models:
ProviderDefinition,OAuthConfig,ApiKeyConfig, bundled provider JSON - Credential models:
ConnectionRecord,ProviderClientRecord,ProviderMetadataRecord,ProviderStateRecord AuthSession— transient flow session state
Does not own:
- Credential persistence (that is
vault/+server/territory) - Audit logging (that is
audit/+server/territory) - Proxy route catalog building
- Server registry reads
Imports nothing from this codebase. Used by: server/
server/ owns CredentialService (currently called AuthService) — the stateful coordinator that wires auth/ flows with vault/ storage and audit/ logging. It is the only place where flows, storage, and audit are combined.
CredentialService is constructed per-request by the server with (vault, identity, principal_id, vault_id) and calls auth/ flows to execute protocols, vault/ to persist results, and audit/ to record events.
Current state:
AuthServicelives inauth/and importsvault/andaudit/directly. Moving it toserver/(TODOS phase E) makesauth/a true leaf.
Think of this as the secrets layer. Encrypts and decrypts credential blobs transparently.
Owns:
Vault— AES-256-GCM encrypted KV wrapper overAsyncKeyValueVaultCrypto— key management (local file, OS keyring)- Encrypted get/put/delete/list over named collections
Does not own:
- Server filesystem layout or path resolution
- Registry lookups
- Business logic about which vault belongs to which principal
Imports nothing from this codebase. Imported by: auth/, server/
Think of this as the audit instrumentation layer. Defines what happened; server/ decides where it goes.
Owns:
AuditEventdomain model — mandatory fields:identity,principal_id,provider,connection; optional:method,path,status,metadatalog()/alog()— emit anAuditEventas an OTelLogRecordviaget_logger_provider()- Translation from
AuditEvent→ OTelLogRecord
Does not own:
- Storage — no file I/O, no database
- Provider lifecycle (
setup()/clear()removed — owned byserver/) - Knowledge of where events are routed
Imports: opentelemetry-api only (no SDK, no storage). Imports nothing from this codebase. Imported by: server/, proxy/
Think of this as the daemon process. Wires identity + auth + vault + audit together. Owns all server-side persistence.
Owns:
server/registries.py— all filesystem-backed registry implementations:IdentityRegistry(handle → DID)PrincipalRegistry(principal_id → email)VaultRegistry(vault_id → handle)IdentityClaimRegistry(identity → principal + ClaimStatus)PrincipalVaultBindingRegistry(principal → default vault)
server/ownership.py—OwnershipResolver(local and hosted variants),ResolvedOwnershipserver/identity_bootstrap.py— deployment-specific identity registration behaviorserver/dependencies.py— infrastructure wiring (paths, store, vault, config)server/app.py— FastAPI application factory and lifespanserver/routes/— HTTP API surfaceserver/schemas.py— API response schemasserver/audit_store.py—SQLiteLogExporter(OTelLogExporterimpl) +AuditStorequery interface;LoggerProviderlifecycle (setup at startup, shutdown at teardown)server/routes/audit.py—GET /audit/events(filtered, paginated admin read)POST /audit/events— ingest endpoint for proxy-side external AuditEvents; server enrichesprincipal_idfrom PoP JWT
All filesystem interaction for server-owned state lives here. No other module writes to server-owned paths.
Imported by: nothing (top of the import graph)
A mitmproxy-based HTTPS proxy. Intercepts outgoing agent requests and injects auth headers.
Owns:
proxy/server.py— mitmproxy addon that intercepts requestsproxy/runner.py— background thread lifecycleproxy/router.py—RouteMatch/RouteResolutiontypesproxy/certs.py— CA certificate management
Does not own:
- Credential loading (asks the server)
- Route catalog construction (asks the server)
- Provider definitions
- Audit storage — ships External AuditEvents to server via
POST /audit/events(fire-and-forget); does not callaudit.log()directly
Imported by: cli/
Click-based CLI and HTTP client. Everything here is a client to the server HTTP API.
Owns:
cli/main.py— Click command treecli/client.py—RuntimeClient(async HTTP client for daemon requests, attaches PoP JWT)cli/client_config.py— client-owned config (active_identity,vault_id, proxy settings)cli/context.py—CliRuntimewiring containercli/daemon_control.py— start/stop the daemon process
Does not own:
- Server registry operations
- Direct vault or store access
- Identity key generation (delegates to
identity/, result stored by CLI viaidentity/local.py)
Imported by: nothing (entry point)
Identity: The cryptographic agent — Ed25519 key pair, did:key DID, and human-readable Handle. Created locally; registered with the daemon. Is not a credential namespace.
Handle: Human-readable name for an Identity (e.g., brisk-boldly-clearly-1234). Used as sub in PoP JWTs.
DID: did:key Ed25519 identifier derived from the Identity's public key. Used as iss in PoP JWTs.
PoP JWT: Short-lived (60 s) Proof-of-Possession token signed with the Identity's Ed25519 private key. Bound to htm, htu, body_sha256. Sent as Authorization: PoP <token>.
Principal: Non-cryptographic logical partition (human or team) that owns Vaults. Identified by an opaque PrincipalId (e.g., principal_abc123def456). Has no cryptographic key. Carries exactly one PrincipalRole.
Avoid: User, account, PrincipalHandle, profile
PrincipalId: Opaque stable identifier for a Principal. Never the email or handle — those can change; the PrincipalId cannot. Avoid: principal_handle, principal_name, username
PrincipalRole: Authorization tier for a Principal. Either admin or user. The first Principal created on a server is always admin; all subsequent Principals are user. Stored as a column on the Principal record — not in environment variables or a separate table.
Avoid: permission level, access level, user type
Vault: Named credential store owned by exactly one Principal. Identified by an opaque VaultId (e.g., vault_a1b2c3d4e5f6). All credential store keys are prefixed vault:<vault_id>:....
Avoid: credential store, token store, secret store, profile store
VaultId: Opaque stable identifier for a Vault. Used as the storage key segment. Stable across naming changes. Avoid: vault_name, vault_handle
VaultHandle: Human-readable name for a Vault (e.g., default). Used in UIs and CLI; the VaultId is authoritative in storage.
IdentityClaimRecord: Binding from an Identity (Handle) to a Principal (PrincipalId) with a ClaimStatus. Created during authsome init --email. Vault access is gated until the claim is accepted.
Avoid: Claim, IdentityRegistration (as claim), join request
ClaimStatus: Lifecycle state: pending → accepted | rejected.
Local mode: authsome init creates an Identity, auto-accepts its claim under the implicit local Principal, and creates the default Vault. No email required.
Hosted mode: authsome init --email manoj@example.com creates an Identity, creates or finds the Principal by email, and registers an IdentityClaimRecord with claim_status = pending. A human reviews the claim in the UI and accepts or rejects it. All vault operations return 403 until the claim is accepted.
- An Identity is a cryptographic agent. It does not own credentials directly.
- An Identity claims a Principal via an IdentityClaimRecord. Claim must be
acceptedfor vault access. - A Principal owns one or more Vaults via PrincipalVaultBindingRecords. The server resolves the default Vault before constructing
AuthService. - A Vault contains zero or more Connections, each scoped to one Provider.
- Multiple Identities may share one Vault by claiming the same Principal.
- A ConnectionRecord belongs to exactly one Vault.
vault:<vault_id>:...is the key prefix. - ClientCredentials are server-scoped — one
ProviderClientRecordper Provider, shared across all Vaults.
AuthService is a per-request credential lifecycle object constructed by the server:
AuthService(vault=vault, identity=handle, principal_id=pid, vault_id=vid, deployment_mode=mode)identity— agent Handle, used for audit logging onlyprincipal_id— resolved byOwnershipResolverfrom the PoP JWT subjectvault_id— resolved fromPrincipalVaultBindingRegistryby the server before constructing AuthServicevault— the encrypted KV store; AuthService reads/writes only through this
AuthService does not query registries, does not know about server filesystem paths, and does not build proxy route catalogs.
Every AuditEvent carries identity (the agent Handle) and principal_id (the PrincipalId). Both are required — every auditable action has an acting agent and an owning principal.
External AuditEvent: An event produced by the proxy layer — records an outbound HTTP call an agent made through the proxy to a third-party API (e.g., a call to api.github.com). Classified by provider and connection. Mandatory fields: identity, principal_id, provider, connection. Optional fields: HTTP method, path, response status.
Avoid: proxy event, API event, outbound event
Internal AuditEvent: An event produced by the server layer — records credential lifecycle operations (login, logout, token refresh, revocation) and auth flow steps. Avoid: server event, auth event, lifecycle event
Audit delivery: External AuditEvents are shipped from the proxy to the server via POST /audit/events (fire-and-forget, best-effort). The proxy does not write to a local audit file. The server is the single source of truth for all audit events. principal_id is resolved server-side from the PoP JWT on the ingest request — the proxy does not need to supply it.
- "PrincipalHandle" — retired. The Principal is now identified by an opaque
PrincipalId. Do not use PrincipalHandle in new code. - "VaultHandle" — the human-readable display name. Do not use VaultHandle as a storage key; use VaultId.
- "Claim" — use
IdentityClaimRecordfor the binding object; use "claim" (lowercase) only as a verb. - "identity=server" — a temporary hack in
app.pywhereAuthServiceis instantiated at startup without a real identity. This is a known violation to be removed. - "credential" — use Connection for the full authenticated session; use access token / API key for the individual secret.