Protocol specification: identity and policy
The identity and session handshake and the principal policy protocol.
Published from docs/protocol-specification/identity-and-policy.md (GitHub)
Part of the protocol specification.
Identity And Session Handshake
Registration is a
bootstrap protocol, not only
an account create call. POST /auth/register submits:
userId,organizationId, androotContainerId- signing and encapsulation public keys, each as canonical padded base64 of its fixed-length bytes
- the initial reserved
Adminsgroup policy and direct member envelope - the initial reserved
Membersgroup policy, holding the registering user - the initial signed organization policy state and direct member envelope
- signed create requests for the root container, its metadata document, and the organization metadata container
- optional create requests for the organization profile document, the roster-profile container and document, and initial system containers
The API validates the user key fingerprints, creates the user, organization,
reserved groups, initial principal policies, and every requested container, KEK
state, and metadata or profile document in one transaction, then returns a login
challenge. The initial Admins policy must project the registering user as the
sole admin. The initial Members policy must project the registering user as
admin. The initial organization policy must target the new organization, be
version 1, use key epoch 1, be signed by the registering user, project only
the registering user as admin, and carry a version-2 authority descriptor
committing the exact initial heads of both reserved groups.
The response returns each created container and document head, each acknowledged by hash as described under container mutation conflicts below. The client checks every one against the request it signed before pinning it as a local verification checkpoint. The acknowledged heads of the roster and organization profile documents also seed discovery evidence, and the founding policy heads seed verified recovery progress, so the device fetches neither those documents’ writer projections nor the founding groups’ history. Creating an additional organization follows the same rule.
organizations.adminGroupId is the reserved org-admin authority. Reachability
through it grants org-admin behavior. organizations.memberGroupId is the
reserved org-membership authority. Reachability
through it drives active roster state; disabled
roster rows can remain visible
without access. The organization principal policy remains signed principal
state, but it is not the product role source for org-manager.
Organization read-model and data-usage
denials use organization_presentation_access_denied on both the existing 403
access denial and 404 missing-organization responses. The code gives clients
exact authority to hide and purge that requester’s local presentation projection
without matching diagnostic text. An uncoded, malformed, or unknown failure is
ambiguous: clients report it and retain last-known-good data.
A warm organization read-model client discards its opaque cursor and requests a
fresh snapshot only for the exact 400 organization_read_model_cursor_invalid
failure. Other 400 responses, missing or malformed codes, unknown codes, and
the cursor code on another status retain the last-known-good projection and are
reported instead of triggering reset behavior. Only a valid cursor ahead of the
head converges through a 200 snapshot; others get a delta.
Authentication uses challenge signing:
POST /auth/challengestores a short-lived challenge for a registered signing-key fingerprint.POST /auth/verifyverifies the signature (canonical padded base64 of the ML-DSA-87 signature bytes) over the challenge and this API’s origin, then issues a session token and its first single-use, sixty-second websocket ticket; later connects mint one withPOST /auth/ws-ticket.GET /auth/sessions,DELETE /auth/sessions/:sessionId, andPOST /auth/logoutmanage authenticated session tokens.- Authenticated mutation routes require the session user and fingerprint to match the signer fields embedded in signed access events or write headers.
A 401 session_refresh_required response means the bearer token identified a
missing, expired, or invalid stored session and permits the client to renew and
replay a replayable request. Missing or malformed bearer credentials remain an
uncoded terminal 401; clients never infer refresh behavior from error text.
The session authenticates the transport caller. Signed access events, principal states, and write headers are the protocol proofs that bind mutations and content records to cryptographic authority.
Principal Policy Protocol
Groups and organizations are managed by signed principal-state chains. The
generic policy PUT accepts organization successors only; standalone group
writes fail closed because they cannot advance the signed group directory.
Organization-group successors use
PUT /organizations/:organizationId/groups/:groupId/policy-commit, which
atomically stores the group successor and an organization-policy successor
whose authority descriptor commits the new exact group head.
Group creation uses the same rule: POST /organizations/:organizationId/groups
stores the group row, initial group policy, and matching organization-policy
successor in one transaction, or stores none of them.
A state commits the principal identity,
version and predecessor, key epoch and encapsulation key, membership/projection/envelope
roots, payload hash, member count, signer identity, and timestamp. The API
verifies those commitments, signature, chain, and admin-signer rule before
atomically storing the state, payload, projection, and exact envelope set.
Initial signers are admins in the new projection; successor signers are admins
in the prior projection or, for org-scoped updates, independently proven members
of the reserved Admins group. Clients verify the complete bundle again before
caching or key use.
Public keys and envelope fields use canonical base64 and exact
ML-KEM-1024 sizes: 1568-byte
public keys/ciphertexts and a 3184-byte wrapped secret plus AES-GCM tag.
Envelopes cover the direct projection one-to-one and bind each member id,
recipient fingerprint, KEM ciphertext, and wrapped key. The root does not commit
active state; separate policy validation binds the principal and key epoch.
Principal-state signing and hashing use the domain tearleads.principal-state.
memberEnvelopesRoot is mandatory; a state missing it, required policy
material, or envelope material fails closed.
GET /principals/:principalType/:principalId/policy serves one pinned head
in pages (history transport). Every read
applies live read authorization and verifies the current policy first. An
unpinned discovery read may name the head the client holds with ifVersion
and ifStateHash; when that is the live head, the reply is
{unchanged: true, head} and carries no artifacts, otherwise it is the first
page. The reply proves only freshness: the client authenticates the held head
from its own verified history and re-reads unconditionally on any other reply.