Skip to content

fix(proxy): correct the databases-route claim and pin non-2xx passthrough - #90

Merged
xe-nvdk merged 1 commit into
mainfrom
fix/databases-listing-403-handling
Oct 4, 2026
Merged

xe-nvdk merged 1 commit into
mainfrom
fix/databases-listing-403-handling

Conversation

@xe-nvdk

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

Copy link
Copy Markdown
Member

Summary

Arc's three database listing routes (GET /api/v1/databases, /:name, /:name/measurements) previously carried no middleware at all. They now require read permission and, where the server restricts reads per database, a grant for the database being listed.

Launchpad's exposure is narrow and worth stating plainly, because it is smaller than it first looks:

  • Nothing in the UI calls those routes. Every database read goes through SHOW DATABASES / SHOW TABLES on POST /api/v1/query (arcClient.getDatabases), and createDatabase is a POST, which was already admin-gated. So there is no broken picker to fix here, and I have not invented one.
  • The proxy forwards the instance's stored admin token, so Arc answers every proxied request as an admin — which Arc allows as break-glass regardless of grants. A scoped 403 is therefore not reachable through Launchpad today.
  • What is reachable: an instance with no stored token now gets 401 on those routes where it used to get 200.

That leaves two real things, both done here.

1. The allowlist note was about to become wrong

MEMBER_READ_PREFIXES carries an important note — each entry must name a route Arc serves without admin auth, because the proxy injects the admin token and this list is the only gate. It said GET databases (handleList, no admin). That is no longer what Arc does.

The entry itself stays correct (readAuth is not adminAuth, so the routes remain member-reachable), but what a member gets back changes, and the note now says so rather than quietly describing a server that no longer exists.

2. Non-2xx passthrough is now pinned

The proxy already forwards the upstream status and body verbatim — that is what lets a caller tell the new answers apart. It was untested, so it is now pinned: 401, both 403 wordings (no read permission, and the per-database denial for a named database and for the list-everything route), 404 and 200 all arrive unchanged.

A 502 stays reserved for the proxy's own failures (unreachable instance, oversized response). Turning an upstream 403 into one would report a scoped token as a Launchpad fault. Also pinned: a 403 is not retried against the next resolved IP — a refusal is a decision, not a transport failure, and retrying only doubles the load and the audit entries.

Companion to the Arc server change on fix/rbac-measurement-where-and-databases-auth; see that branch's release notes under "RBAC read and write restrictions were unreachable, and the database listings had no authorization".

Test plan

  • npm test — 237 passed (6 files), 8 of them new
  • npm run check — 0 errors, 10 warnings (unchanged from main)
  • New non-2xx passthrough block in src/lib/server/arcProxy.test.ts, using the file's existing real-http.createServer upstream rather than a mock, as every other test there does
  • README: the instance token has to be an admin token, and Arc answers with that token's reach

…ough

The proxy's member allowlist carries a note that each entry must name a route
Arc serves without admin auth, because the proxy injects the instance's admin
token and this list is therefore the only gate. It said "GET databases
(handleList, no admin)". Arc's three database listing routes now require read
permission and, where the server restricts reads per database, a grant for the
database being listed, so that claim is stale.

The entry itself stays correct — readAuth is not adminAuth, so the routes
remain member-reachable — but what a member gets back changes, and the note
now says so: the proxy forwards the instance's stored token, so an instance
with no stored token answers 401 where it used to answer 200, and a token Arc
restricts to particular databases gets 403 on the list-everything route rather
than a filtered list.

Added tests pinning that the proxy forwards an upstream non-2xx status and
body verbatim rather than flattening it: 401, both 403 wordings (no read
permission, and the per-database denial for a named database and for the
list-everything route), 404 and 200 all arrive unchanged, so a caller can tell
"re-authenticate" from "you are scoped, name a database" from "no such
database". A 502 stays reserved for the proxy's own failures — turning an
upstream 403 into one would report a scoped token as a Launchpad fault. Also
pinned that a 403 is not retried against the next resolved IP: a refusal is a
decision, not a transport failure.

README: the instance token has to be an admin token, and Arc answers with that
token's reach.
@xe-nvdk
xe-nvdk merged commit fbaac08 into main Oct 4, 2026
3 checks passed
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