Access Model
What mcpgate ships at the access-control layer, in NIST terms — and what it deliberately does not, and why.
mcpgate ships NIST RBAC0 with the audit axis of RBAC2: fixed audience tiers, per-action risk classification, an admin-approval gate the model cannot self-grant, and per-user attribution in the audit log. RBAC1 (role hierarchies), the rest of RBAC2 (separation of duties, cardinality), custom roles, per-resource ACL, externalised PDP and JIT elevation are deliberate Nos — different problem shapes, served better at a different layer.
Quick-answer table for procurement reviews
| Question | Answer |
|---|---|
| Do you have role-based access control (RBAC)? | Yes — at the RBAC0 level, with audit. Fixed roles (admin / internal / external / viewer), per-action classification, admin-approval gate, per-user audit. Details below. |
| Can we define custom roles? | No, deliberately. The four-tier enum is the shipped surface; new roles require code. Custom roles would import policy-DSL complexity that operators consistently said they don’t want. |
| Role hierarchies (senior inherits junior)? | No. The four tiers are flat. No hierarchical resolution. |
| Separation of Duties (SoD)? | No. Belongs in a workflow engine, not an API gateway. |
| Per-resource ACL (Alice can edit Doc A, not Doc B)? | No, by design. The connected service (Drive, Jira, GitLab, Notion) is authoritative for per-resource permissions. mcpgate passes through with the user’s own credentials; the upstream enforces. We do not duplicate or override that layer. |
| Audit log with user attribution? | Yes. Every tool call is recorded with pseudonymised actor, service, action, bytes in/out, result, timestamp. See Compliance. |
| Externalised PDP (policy in an external service)? | No. Internal PDP. Adds zero latency vs. an external policy round-trip. If you need a vendor-grade external policy engine (WorkOS, Oso, Permit.io), run it alongside. |
| Attribute-based access (ABAC)? | Partial. Via data_scope auto-deny rules and policy-hook parameter matching. Not declarative the way Oso or Permit.io are. |
| Just-in-Time elevation (request → approve → temporary grant)? | No. Explicitly dropped in destructive-action governance — the permanent-enable + manual-revoke workflow we observed in production was simpler and sufficient. Add when a real ask appears. |
What we have — NIST framing
Users
OAuth-authenticated user identities. Every tool call carries the user that issued it; the audit row carries a pseudonymised hash of that user.
Roles
Fixed enum of four tiers, assignable per user from the admin panel:
- admin — full control of gateway configuration
- internal — full access to operator-data and shared services
- external — restricted; auto-denied on operator-data via
data_scope - viewer — read-only / further restricted
Admin can assign users to a tier. Admin cannot define a fifth tier. This is RBAC0 deliberately limited. The constraint is the feature: the operator does not maintain a policy IDE.
Permissions
Three orthogonal layers compose the effective permission on any tool call:
- Action classification (read / write / high-risk) ships with every action via YAML metadata. Read actions execute. Write actions get per-call confirmation. High-risk actions need explicit admin enable through the Compliance → Destructive Actions surface.
- Category policy for the high-risk class — eight rule-based risk categories (Permanent / irreversible, Container destroy, Bulk delete, API passthrough, Scoped, Comment-metadata, Member-access, Recoverable). Admin toggles policy per category; Recoverable never escalates. The mechanism is described in destructive-action governance.
- Per-user policy hooks — YAML hooks match on
actor_group, action pattern, parameters; deny, enrich or route. Two scopes stack: company-wide (config/tool_hooks.yaml) and per-user. See Hooks reference.
Audit (the RBAC2 axis we did keep)
Every tool call writes one audit row: pseudonymised actor, service, action, bytes in/out, result code, timestamp. The row is the single substrate for the Throughput dashboard, the destructive-actions blocked queue, the forensic surface, and the per-user volume page. See Compliance for the layers stacked on top of the audit log.
Permission check (the PEP/PDP shape)
Happens at the gateway gate. The executor reads the effective classification through a strict precedence chain — read-only mode → per-action override → category policy → shipped default — and either runs the call, returns ADMIN_APPROVAL_REQUIRED, or routes through a hook. The PEP (Policy Enforcement Point) is the executor’s check; the PDP (Policy Decision Point) is the category-policy module. Both live in the same process; there is no external policy round-trip on the hot path.
What we deliberately do not have
| Capability | Why we don’t ship it |
|---|---|
| Custom roles | Would import policy-DSL complexity that operators repeatedly said they don’t want. The four-tier enum covers the common shape; anything beyond it tends to grow into an in-house RBAC IDE we are not staffed to maintain. |
| Role hierarchies (RBAC1) | Flat is enough. Senior-inherits-junior introduces resolution complexity (override semantics, conflict rules) for marginal value at our scale. |
| Separation of Duties (RBAC2) | Belongs in a workflow engine where the state machine lives. Gateways enforce a single point; workflow tools enforce sequences. |
| Cardinality constraints | “Only one user holds role X” constraints belong with custom-roles — same reason as above. |
| Per-resource ACL | The connected service is authoritative for per-resource permissions. mcpgate passes through with the user’s own credentials; the upstream service’s permission model (Drive shares, Jira project roles, GitLab branch protection, Notion page-level perms) enforces. We deliberately do not duplicate or override that layer. |
| Externalised PDP | An external policy round-trip adds ~50–100 ms per tool call. Our internal PDP runs in the same process. If you need vendor-grade external policy (WorkOS, Oso, Permit.io), run that vendor — they are good at it, and mcpgate does not try to be. |
| JIT elevation | Explicitly dropped in the destructive-action governance close: the permanent-enable + manual-revoke workflow we observed in production was simpler and sufficient. Will revisit when a real ask appears. |
| Full ABAC matrix | Partially covered by data_scope auto-deny and policy hook attribute matching. Not declarative the way Oso or Permit.io are. The hook layer is the escape hatch where attribute-based logic actually lives today. |
What this shape buys you
The decisions above are coherent, not happenstance. The shape mcpgate ships is a trade:
- One operator runs governance as a weekly habit, not a daily job. Fixed enum + classified actions + admin gate + audit fits five minutes per week of attention.
- No external policy-engine dependency. Internal PDP, no latency tax on the hot path, no SLA dependency on a third-party policy service.
- Single audit substrate. Throughput dashboard, destructive-actions blocked queue, forensic queries and per-user volume all read the same row format.
- Downstream service permissions stay authoritative. A Drive share decision, a Jira project role, a GitLab branch protection rule — those remain in the source of truth where the operator already manages them. mcpgate does not hide them behind a gateway override.
- No false RBAC promises. Procurement does not get a tool that ticks an RBAC box and then fails the demo on custom-roles or per-resource ACL. The boundary is named on this page so the conversation stays honest.
What it does not cover — and where to go
If your environment needs capabilities mcpgate deliberately does not ship, the right answer is to pair us with the specialised tool that does:
- Enterprise per-resource ACL, custom roles with hierarchies, declarative policy DSL → WorkOS, Oso, Permit.io. Run alongside; mcpgate’s hooks can call into them if you need policy externalisation at the gateway layer.
- Cross-system workflow approvals (multi-step state machines with approvers, escalation, parallel branches) → a workflow engine such as Camunda, n8n, or your existing one.
- Per-resource permissions on the connected services themselves → the service’s own permission model: Google Drive shares, Jira project roles + permissions schemes, GitLab member roles + branch protection, Notion page-level permissions, etc.
Related
- Destructive-action governance — the admin-approval gate, the category model, the deliberate-No on audience-tiers-as-matrix that this page formalises
- Compliance — audit log, PII sanitization, throughput-as-DLP
- Notifications — how operator alerts are delivered when something fires
- Hooks reference — policy hooks at company and per-user scope
- Compare pages — how mcpgate’s access model compares to RBAC-positioned gateway products