Skip to content
Principals and identity

Principals and identity

Every action in Joe is attributed to a principal — a typed, prefixed name that says who is asking. Principals are how the governed-safety invariant keeps its promise that nothing happens anonymously: a request without a resolvable principal does not get past the edge.

Humans authenticate via OIDC

People sign in through a single configured OIDC issuer using a real authorization-code flow with PKCE. On success, Joe mints a principal carrying the reserved user: prefix, derived from the verified email — a user: principal. Joe verifies the identity at the edge once and then carries that principal by internal context; it does not re-authenticate on every internal hop.

Machines authenticate with service-account keys

Joe’s shipped machine clients are the MCP server and the Slack bot; they — along with any other external caller an operator mints a service-account key for, such as a CI integration or a script — present a service-account bearer key. Joe resolves the key to a principal carrying the reserved svc: prefix — a svc:<name> principal. Service accounts are defined in configuration; a duplicate or empty key is a boot failure rather than a silent ambiguity.

Joe’s own autonomous background loop is itself a service principal, svc:agent:core. Treating the agent as a named principal rather than a privileged exception is what lets its reads flow through the same governed path as everyone else’s.

Reserved prefixes and the impersonation guard

The user: and svc: prefixes are reserved: they denote the kind of a principal, and Joe will not let a caller forge one. If an identity provider hands back an email that itself looks like a reserved-prefix principal, Joe rejects it rather than minting a principal that could impersonate another kind. The prefix is a type, and the type is trustworthy because it can only be assigned by Joe, never supplied by the caller.

Why both, and why it matters downstream

Two authentication paths exist because the two kinds of caller are genuinely different: humans get an interactive login with a session; machines get a long-lived key. They converge on the same thing — a typed principal — so that everything downstream (RBAC and zones, the audit record, chat-session ownership) can reason about one uniform notion of “who” without caring how that “who” arrived.

For how to configure an OIDC issuer or define service accounts, see Configuration; to bring a Joe up with identity wired, see Install and Build.