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

QuestionAnswer
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:

  1. 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.
  2. 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.
  3. 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 modeper-action overridecategory policyshipped 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

CapabilityWhy 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 DSLWorkOS, 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