Grants
A grant binds a user to one permission on one target:target_kind is one of credential, host, group, set, backup, or global. target_id is
null for global grants and required for every other kind.
Grants are soft-revoked — revoking sets revoked_at rather than deleting the row, so the trail
survives. A user may hold only one active grant per permission/target combination.
Admins bypass grant resolution entirely.
Groups cascade
A group contains resources, not users. Granting a user a permission on a group cascades to every resource in it at resolve time, so adding a host to a group immediately extends existing group-targeted grants to that host.Groups are groups of resources. Custos has no concept of a user group or team.
Global vs scoped permissions
Creation is global — there is no resource to scope it to yet. Everything else is scoped to a single resource, or to a group containing it.How host.access reaches a host
Granting or revoking host.access recomputes the affected host’s authorized-key snapshot and pushes
it immediately over the daemon’s live WebSocket — directly for a host target, fanned out for a group
target. Offline hosts reconcile on their next connect.
User status
Suspending a user kills their sessions and pushes fresh snapshots, cutting SSH as well as API access. It is reversible. Removing a user deletes their login identities and revokes their grants and sessions, but keeps the user row, their keys, and their log entries so the audit trail stays readable.Permission requests
A member withoutcredential.read on a credential can request it
(POST /credentials/{id}/permission-requests); an admin reviews it
(PATCH /permission-requests/{id}). Approval creates the grant.