Appearance
Error codes
Like system.devices, the security ops do not use string error codes; they encode failures as { ok: false, error?: string } returns or HTTP status codes for HTTP endpoints.
system.auth — bus op failures
Session ops
| Op | Failure return | Cause |
|---|---|---|
auth.session.validate | { ok: false, error: 'invalid' } | bad signature, wrong issuer, expired, or jti revoked |
auth.session.create | (throws if secret too short) | construction-time, not runtime |
auth.session.revoke | { ok: true, revoked: false } | no extractable token in headers |
Identity ops
| Op | Failure return | Cause |
|---|---|---|
auth.identity.resolve | { authenticated: false, principalId: null, ... } | no valid session in headers |
auth.principal.ensure | { ok: false, error: '...' } | invalid issuer/subject; principal store error |
auth.principal.get | { ok: false } | principal id not found |
Host Link ops
| Op | Failure return | Cause |
|---|---|---|
auth.hostLink.create | (throws) | missing required principalId, spaceId, or authorityRoot |
auth.hostLink.get | { ok: false } | not found, or scope filter rejected |
auth.hostLink.refresh | { ok: false } | not found, scope mismatch, or status revoked |
auth.hostLink.revoke | { ok: true, alreadyRevoked: true } | already revoked (idempotent) |
auth.hostLink.verifyHeartbeat | { ok: false } | bearer hash mismatch, status revoked, or no heartbeatTokenHash set |
Device-flow ops
| Op | Failure return | Cause |
|---|---|---|
auth.device.start | (always succeeds) | n/a |
auth.device.status | { ok: false } | not found |
auth.device.approve | { ok: false, error: '...' } | not found, status cancelled/exchanged, or principal namespace required |
auth.device.poll | { ok: true, status: '<current>' } | reflects current state; not an error |
auth.device.exchange | { ok: false } | not in approved state |
auth.device.cancel | { ok: true } | always succeeds (idempotent) |
Pair ops
Symmetric to device-flow ops with pairRequestId instead of deviceCode/userCode.
Namespace ops
| Op | Failure return | Cause |
|---|---|---|
auth.namespace.check | { ok: false, available: false, error: 'Invalid public namespace' } | doesn't match PUBLIC_NAMESPACE_PATTERN |
auth.namespace.checkRouteKey | similar | doesn't match ROUTE_KEY_PATTERN, is reserved, or DNS-like |
auth.namespace.claim | { ok: false, error: 'Principal not found: <id>' } | principal doesn't exist |
{ ok: false, error: 'domain.* namespaces require DNS verification' } | domain.* not yet implemented | |
{ ok: false, error: 'system.* namespaces are reserved' } | reserved | |
{ ok: false, error: 'Public namespace already claimed: <ns>' } | duplicate | |
{ ok: false, error: 'Route key already claimed: <key>' } | duplicate | |
{ ok: false, error: 'Parent namespace is not delegated to <id>' } | nested claim under another principal's parent | |
auth.namespace.resolve | { ok: true, claim: null } | not claimed |
Google OIDC ops
| Op | Failure return | Cause |
|---|---|---|
auth.google.status | { ok: true, configured: false } | no Google client configured |
auth.google.login | { ok: false, error: 'not configured' } | Google client missing |
auth.google.callback | { ok: false, error: 'state expired' } | pending login pruned |
{ ok: false, error: 'token exchange failed' } | upstream OAuth error | |
{ ok: false, error: 'invalid id_token' } | signature/iss/aud/exp check failed | |
{ ok: false, error: 'email not verified' } | email_verified !== true |
system.factotum — bus op failures
| Op | Failure return | Cause |
|---|---|---|
credential.store | { ok: false } | provider/type missing or type-specific required fields missing |
credential.get | { found: false } | no row for provider |
credential.delete | { ok: false } | no row for provider |
credential.refresh | { ok: false } | not OAuth, no refresh token, or expired |
credential.validate | { ok: false, error: '...' } | API call returned non-2xx; specific 401/403/network messages included |
credential.import-detected | (always succeeds) | returns empty detected: [] if nothing found |
credential.import | { ok: false } | source/provider mismatch or file unreadable |
credential.lease | { ok: false, error: 'provider is required' } | missing provider |
{ ok: false, error: 'No credential found for provider: X' } | also emits factotum.needkey | |
credential.materialize | { ok: false, error: 'leaseId is required' } | missing leaseId |
{ ok: false, error: 'Lease not found or expired' } | lease consumed or never existed | |
{ ok: false, error: 'Lease expired' } | TTL passed | |
{ ok: false, error: 'Credential no longer available' } | row deleted between lease and materialize |
HTTP endpoint statuses
The HTTP gateway (@open-matrix/system-gateway-http) translates auth failures to HTTP status codes:
| Endpoint | Status | Cause |
|---|---|---|
| any auth-required | 401 | no session cookie / Bearer token |
| any auth-required | 403 | session valid but principal lacks capability for this resource |
/api/auth/login/google | 302 | redirect to Google (success) |
/api/auth/callback/google | 302 | redirect to returnTo after success |
/api/auth/callback/google | 4xx | code exchange failed |
/_auth/host-link/heartbeat | 200 | success |
/_auth/host-link/heartbeat | 401 | no/bad bearer |
/_auth/host-link/heartbeat | 403 | bearer valid but link revoked, or body identity mismatch |
/_auth/host-link/heartbeat | 404 | hostLinkId not found |
Common return-shape conventions
Every op:
- Returns
{ ok: true, ... }on success. - Returns
{ ok: false, error?: string }on failure (or returnsnullfrom store methods that the bus layer wraps). - Returns
{ ok: true, found: false }for "not found" if the op is conceptually a query (e.g.credential.get).
Callers MUST check ok === true before treating the data as authoritative. Callers SHOULD log the error string on the unhappy path before surfacing to the user.
See also
- Token schema — what valid tokens look like.
- Claim schema — namespace validation.
- Grant schema — Host Link / pairing record.
- docs-devices Reference / Error codes — Devices-side error codes.
Source:
projects/matrix-3/packages/system-auth/src/host-auth.ts(return paths in stores);projects/matrix-3/packages/system-factotum/dist/index.js(return paths inFactotumActor).