Skip to content

docs(rbac): document how read enforcement is decided and what each client does - #96

Merged
xe-nvdk merged 2 commits into
mainfrom
docs/rbac-read-enforcement-clients
Oct 4, 2026
Merged

xe-nvdk merged 2 commits into
mainfrom
docs/rbac-read-enforcement-clients

Conversation

@xe-nvdk

@xe-nvdk xe-nvdk commented Oct 2, 2026

Copy link
Copy Markdown
Member

⚠️ Hold until released

Do not merge until Arc 26.09.3 is released and the client releases are out (arcli, arc-client-python, the VS Code extension, arc-mcp). Everything here describes behaviour that does not exist in the currently published versions.

Tracking PRs: arcli#41 · arc-mcp#8 · arc-client-python#3 · launchpad#90 · arc-vscode-extension#19

Summary

Two undocumented changes, and two existing claims that became wrong.

RBAC read and write restrictions now take effect — previously a denial fell back to the token's coarse permission list, so no RBAC grant could narrow anything. And the three database listing endpoints (GET /api/v1/databases, /:name, /:name/measurements) now require read permission where they previously required nothing at all.

Corrections to existing content

  • rbac.md Prerequisites said RBAC requires an Enterprise licence. The licence gates management — creating and changing grants. It does not gate enforcement, and deliberately so: if it did, a lapsed trial or a revoked key would switch enforcement off and silently widen every tenant token to full read. Now stated, with the reasoning.
  • rbac.md "Backward compatible — Existing OSS token permissions continue to work" read as though coarse permissions always apply. They apply to a token with no team memberships. A token with memberships is governed by its grants and a denial is final.
  • arcli/commands/db.md said db list / db show take "Any token". They need read.

New content

arc-enterprise/security/rbac.md — the substance of the PR:

  • How enforcement is decided — the three cases in order: coarse admin is allowed as break-glass; a token with team memberships is governed by RBAC with a final denial; a token with no memberships falls back to its coarse permissions. Plus why enforcement never consults the licence, and that a failure to load grants denies rather than falling through.
  • What a scoped token can and cannot list — a table of what a token granted one database gets from each read path. Includes the non-obvious bit: on GET /api/v1/databases/:name the permission check runs before the existence check, so a database the token has no grant for answers 403, and a 404 only ever comes back for one it is granted — which is what stops the endpoint being used to enumerate names.
  • Telling the refusals apart — Arc answers 403 with six different messages meaning different things, and the status code does not distinguish them. Three mean "name a different database" (a normal state, not to be retried), two mean "use a different token", one means Arc's own permission store is unreadable and says nothing about the token.
  • What the client tools do — one line per client, linked.
  • Two best practices: review grants before adding an existing token to a team (it narrows a token an integration may already rely on), and keep a break-glass admin token outside every team.

arc/api-reference/overview.md and the arc-enterprise twin — a callout on the read endpoints and an error table with the real bodies for 401, all three 403s, and 404.

arc/sdks/python/data-management.md and twin — ArcPermissionError, ArcScopedAccessError (carrying .database, empty on the list-everything route, so test against None not truthiness) and ArcPermissionDataUnavailableError; a warn callout that ArcAuthenticationError now means 401 only.

arc/integrations/vscode.md and twin — the read permission it needs, how the explorer and completions degrade for a scoped token, and the new permissions prompt on token creation.

launchpad/using-the-console/tokens.md — the four permission checkboxes are the whole story only while a token belongs to no RBAC team; Launchpad itself is unaffected because it connects with an instance admin token.

Test plan

  • npm run build — 317 static pages generated, no errors
  • npm run typecheck — clean
  • Every internal link target verified to exist (arcli/commands/db, launchpad/getting-started/connecting-to-arc, arc/sdks/python/data-management, arc/integrations/vscode, arc-enterprise/security/rbac) and every anchor verified against the heading it points at
  • Both product trees updated where a page is duplicated (arc/ and arc-enterprise/ for api-reference, python SDK and VS Code)
  • Response bodies, messages and the permission-before-existence ordering checked against the server branch at its current head, not against the PR descriptions

…ient does

Arc's RBAC read and write restrictions now take effect, and the three
database listing endpoints require read permission where they previously
required nothing at all. Neither was documented, and two existing claims
became wrong.

RBAC page:

- New "How enforcement is decided": the three cases (coarse admin as
  break-glass, team memberships authoritative with a final denial, no
  memberships falling back to coarse permissions), that enforcement never
  consults the licence and why keying it off the licence would fail open on
  expiry, and that a failure to load grants denies rather than falls through.
- Corrected the Prerequisites claim that RBAC needs a licence: the licence
  gates management, not enforcement.
- Narrowed the "Backward compatible" bullet, which read as though coarse
  permissions always apply. They apply to a token with no team memberships.
- New "What a scoped token can and cannot list", including that the
  permission check on GET /api/v1/databases/:name runs before the existence
  check, so a database with no grant answers 403 and a 404 only ever comes
  back for one the token is granted — which is what stops the endpoint being
  used to enumerate names.
- New "Telling the refusals apart": Arc answers 403 with six different
  messages that need different responses, and the status code does not
  distinguish them.
- New "What the client tools do", linking each client's behaviour.
- Two best practices: review grants before adding an existing token to a
  team, and keep a break-glass admin token outside every team.

API reference (both trees): the three read endpoints require read permission
and, where reads are restricted per database, a grant; the error table gives
the real bodies for 401, all three 403s and 404.

arcli db: "Any token" was wrong. Documents the scoped answers with the
messages arcli actually prints, and that they are not retried.

Python SDK (both trees): the new ArcPermissionError,
ArcScopedAccessError and ArcPermissionDataUnavailableError, that
ArcAuthenticationError now means 401 only, and that
ArcScopedAccessError.database is empty on the list-everything route.

VS Code (both trees): the read permission it needs, how the explorer and
completions degrade for a scoped token, and the new token permissions prompt.

Launchpad tokens: the four checkboxes are the whole story only while a token
belongs to no RBAC team, and Launchpad itself is unaffected because it
connects with an instance admin token.
Follows the server change that made a role's measurement grants apply to
every request, including ones naming no measurement. Closing that made "may
this caller enumerate inside this database" a separate, weaker question, and
the two per-database listing routes now ask it and then filter the names they
return — so a token granted production.cpu gets 200 and ["cpu"] from them
rather than a 403.

The list-everything routes are unchanged: SHOW DATABASES and
GET /api/v1/databases still refuse a token without a grant covering every
database, because a filtered list of database names leaks the shape of the
deployment and makes a partial answer indistinguishable from a complete one.
The docs now say which listing asks which question instead of implying one
rule for all three.

Also corrected:

- The what-a-scoped-token-can-list table gains a second column for the
  measurement-grant shape, which is the one the change affects, and rows for
  the measurement listings that filter.
- "no grant for" became "no grant inside", since that is now the test.
- A database whose measurements are all filtered out answers 200 with an
  empty list, not 404.
- Permission model: a role carrying measurement grants is restricted to them
  for every request, and both leading and trailing wildcards match.
- arcli db: db show and measurement list need a grant for something inside
  the database, not all of it, so only db list reports being scoped.
@xe-nvdk
xe-nvdk merged commit 78842c2 into main Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant