role-based access control, separation-of-duties and dual-control reference
Diátaxis quadrant: Reference. Audience: DPOs, security auditors, and IT-governance reviewers verifying who can do what on the admin plane of humanymous Gate.
This page is the authoritative lookup for the admin-plane access model in the reference build of humanymous Gate (“Gate” after first mention): the four roles, the capability each role carries, the exact capability-resolution rules, and which actions require a distinct second identity (dual-control). It is descriptive, not a walkthrough. For the admin API surface each capability maps onto, see the CLI, config & per-route policy reference. For the operational procedures these controls gate, see the Kill-switch and bans runbook and the Right-to-erasure (crypto-shred) runbook.
Note: This repository is a reference implementation, not a production-hardened build. The role model, capability rules, and dual-control constraints below are enforced by the reference binary. Transport auth defaults to dev bearer tokens (see Authentication and trust boundary); client mutual Transport Layer Security is available via
-admin-mtls-ca(still bearer + mutual Transport Layer Security together). External IdP/SSO remains a production responsibility.
Gate is the reverse-proxy enforcement layer. Its admin plane is served on a separate authenticated admin listener (-admin-addr, default 127.0.0.1:8445 (loopback)), cross-origin to the public edge. The public edge does not serve /__hmn/admin/* — it 404s that prefix.
Roles and capabilities
The reference defines four roles (internal/gate/adminauth.go). Each role carries a fixed set of named capabilities. Capabilities are the primitives; roles are bundles of them.
| Role | canRead |
canOperate |
canApprove |
canApproveErasure |
|---|---|---|---|---|
| Auditor | yes | — | — | — |
| Operator | yes | yes | — | — |
| Approver | yes | — | yes | — |
| data protection officer | yes | yes | yes | yes |
- Auditor — read-only. Can read every admin view; cannot request, approve, or commit any action.
- Operator — read plus operate. Can request bans and erasure, lift bans, and cancel an erasure during its hold window.
- Approver — read plus approve. Can commit (approve) a pending ban or kill-switch action initiated by someone else.
- data protection officer — read, operate, approve, and approve-erasure. The only role that can commit a right-to-erasure.
What each capability grants
| Capability | Grants |
|---|---|
canRead |
Read access to all admin GET views (bans, integrity, audit, incidents, policy, approvals, erasures, whoami). |
canOperate |
Request a ban or erasure, lift a ban, cancel an erasure during its hold window. |
canApprove |
Commit a pending dual-control action — a permanent/CIDR ban or the kill switch. |
canApproveErasure |
Commit a right-to-erasure. Held by the data protection officer role only. |
Capability-resolution rules
The reference resolves each capability from the caller’s role exactly as follows. These are the precise rules — do not infer capability from role name alone.
| Capability | Held by |
|---|---|
canRead |
Auditor or Operator or Approver or data protection officer (every role) |
canOperate |
Operator or data protection officer |
canApprove |
Approver or data protection officer |
canApproveErasure |
data protection officer only |
Consequences worth stating explicitly:
- A data protection officer is the only single role that holds
canOperate,canApprove, andcanApproveErasureat once. Even so, a data protection officer cannot self-commit a dual-control action — see Dual-control. - An Approver holds
canApprovebut notcanApproveErasure. A generic Approver therefore cannot commit an erasure. - An Operator holds
canOperatebut neithercanApprovenorcanApproveErasure. An Operator can request but cannot commit any dual-control action.
Dual-control (distinct committer)
Some actions require two distinct identities: one to request and a different one to commit. Dual-control means the committing identity must not be the requesting identity, and must hold the correct approving capability for that action class.
The two-principal flow — request, pending, distinct commit — resolves like this:
sequenceDiagram
participant R as "Requester (Operator or data protection officer)"
participant G as "Gate admin plane"
participant C as "Committer (distinct identity)"
R->>G: "request action (canOperate)"
G->>G: "record pending action"
G-->>R: "pending: approvalId, needsRole"
Note over G: "requester cannot self-commit"
C->>G: "commit approvalId"
G->>G: "check distinct identity AND approving capability"
alt "permanent / CIDR ban or kill switch"
Note over C: "needs approval capability"
G-->>C: "committed"
else "right-to-erasure"
Note over C: "erasure approval: data protection officer only"
G-->>C: "committed"
else "same identity as requester"
G-->>C: "denied — self-commit barred"
end
| Action | Requesting capability | Committing role (distinct identity) | Single- or dual-control |
|---|---|---|---|
| Temporary ban (add) | canOperate (Operator or data protection officer) |
— | Single-control |
| Ban lift | canOperate (Operator or data protection officer) |
— | Single-control |
| Permanent / CIDR ban | canOperate (Operator or data protection officer) |
distinct Approver (canApprove) |
Dual-control |
| Kill switch (node-local) | canOperate (Operator or data protection officer) |
distinct Approver (canApprove) |
Dual-control |
| Right-to-erasure (crypto-shred) | canOperate (Operator or data protection officer) |
distinct data protection officer (canApproveErasure) |
Dual-control |
Key constraints:
- A generic Approver cannot commit an erasure. Erasure commit requires
canApproveErasure, which only the data protection officer role holds. An Approver approving a ban or the kill switch is not sufficient for an erasure. - The requester cannot approve their own action. The committer must be a distinct identity, even when a single data protection officer would technically hold both the requesting and committing capabilities.
- Temporary bans and ban lifts are single Operator actions — no second identity is required. Bulk ban is temporary-only; permanent and CIDR bans are rejected from the bulk path and must go through the dual-control single-ban path.
Warning: The kill switch is node-local and applies to every route on that process. It is a dual-control action committed by a distinct Approver. A fleet must coordinate the state externally.
Warning: Cryptographic erasure (crypto-shred — destroying the per-subject linkage key) is irreversible once committed. It is data protection officer-gated and dual-control: the committer must be a distinct data protection officer. See the Right-to-erasure (crypto-shred) runbook.
Authentication and trust boundary
The controls below back the role model. Each maps to a separation-of-duties (SoD) principle in the next section.
- Server-derived actor identity. The acting identity is derived by the server from the presented token. Request-body actor fields are ignored — a caller cannot name themselves someone else in a request body.
- Constant-time bearer compare. Authentication is bearer (
Authorization: Bearer <token>). Tokens are compared in constant time, so a comparison does not leak token bytes through timing. - Deny-by-default (404). A missing or invalid token returns 404, not 401/403 — the admin surface is non-discoverable to an unauthenticated caller.
/__hmn/admin/*is not served on the public edge at all. - Meta-audited access. Every authenticated admin access is recorded to the audit log as
admin.accessbefore the request is served. Reads and actions alike are logged, so the audit trail shows who looked at what, not only who changed what.
Note: The reference authenticates admin callers with dev bearer tokens (
HMN_ADMIN_TOKENS, or random per-boot tokens printed at startup). Optional client mutual Transport Layer Security is enabled with-admin-mtls-ca(required in addition to the bearer token). External IdP/SSO fronting is still a production responsibility.
How each control maps to an SoD principle
| Control | Separation-of-duties principle it satisfies |
|---|---|
| Four capability-scoped roles (Auditor / Operator / Approver / data protection officer) | Least privilege — a role carries only the capabilities its function needs; read-only Auditors cannot act. |
canApproveErasure held by data protection officer only |
Segregation of a sensitive function — the most consequential, irreversible action (crypto-shred) is confined to one accountable role. |
| Requester ≠ committer on permanent/CIDR bans, kill switch, erasure | Two-person control — no single identity can both initiate and commit a high-impact action. |
| Server-derived actor identity (body actor fields ignored) | Non-repudiation / accountability — the recorded actor cannot be spoofed by the caller. |
| Constant-time bearer compare | Credential-handling hygiene — authentication does not leak secret material through timing side channels. |
| Deny-by-default 404 on missing/invalid token | Deny-by-default / non-discoverability — the admin surface is closed unless a valid credential opens it. |
admin.access meta-audit before serving |
Complete, tamper-evident audit trail — every access, read or write, is attributable and logged before it takes effect. |
Worked authorization outcomes
The table reads a few concrete request/role combinations against the rules above.
| Caller role | Action | Outcome | Why |
|---|---|---|---|
| Auditor | Read /__hmn/admin/audit |
Allowed | canRead held by every role. |
| Auditor | Request a temporary ban | Denied | Auditor lacks canOperate. |
| Operator | Request a permanent ban | Accepted as a request; not committed | Requires a distinct Approver to commit. |
| Operator | Commit their own permanent-ban request | Denied | Operator lacks canApprove, and self-commit is barred. |
| Approver | Commit an Operator’s permanent-ban request | Committed | Distinct identity with canApprove. |
| Approver | Commit an erasure | Denied | Erasure commit requires canApproveErasure (data protection officer only). |
| data protection officer | Commit another identity’s erasure request | Committed | Distinct data protection officer holds canApproveErasure. |
| data protection officer | Commit their own erasure request | Denied | Committer must be a distinct identity. |
Related
- CLI, config & per-route policy reference — the admin API endpoints, initiating roles, and dual-control columns each capability maps onto.
- Kill-switch and bans runbook — the procedures for bans and the node-local kill switch.
- Right-to-erasure (crypto-shred) runbook — the data protection officer-gated, dual-control erasure procedure.
- enforcement rules, verdicts and signal reference — what the enforcement actions these roles govern actually do.