test(testbed): read-bearer helper + pin the negative control (dev-planner's auth-leg alignment)

dev-planner's adapter caught a real read-side asymmetry: the guarded routers
are gated by the TERMINAL token (server.ts:632 passes deps.terminalBearerToken
into transportRoutes), while publishedDaemonEnv sets the AUTH token. They
coincide under this procedure ONLY because index.ts:160 copies auth -> terminal
when the bind is not trusted — i.e. exactly the 0.0.0.0 bind we mandate. So a
direct curl with Authorization: Bearer works as-is, but a host-side rig read
needs the token under OPENRIG_TERMINAL_BEARER_TOKEN.

Adds TERMINAL_BEARER_ENV + rigReadEnv(token, baseUrl) so both consumers use one
helper instead of hand-assembling env, and documents the coupling where it can
be read.

WHY THE NEGATIVE CONTROL IS NOW PINNED: when the terminal token is null the
middleware passes everything through (auth-bearer-token.ts:98-101) — the route
is UNGUARDED. If the bind were ever softened back to loopback while keeping a
bearer, terminalBearerToken would stay null, the guarded probe would answer
without auth, and an auth-probe-only runbook would report a green proving
nothing. The negative control is the assertion that the guard is armed at all,
so the parity suite now fails if the runbook ever loses it.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
v-openrig-buildandClaude Fable 5 committed 2026-08-07 19:36:26 +00:00
1 parent 2b07b3180e
commit ce803b3075
2 files changed
+57

No files matched your search

@@ -44,6 +44,30 @@ export const BIND_VALUE = "0.0.0.0";
/** Env carrying the bearer the bind guard requires (auth-bearer-token.ts:240). */
export const BEARER_ENV = "OPENRIG_AUTH_BEARER_TOKEN";
/**
* Env a host-side `rig` READ needs to reach a guarded route (client.ts's
* terminal-token resolution). DISTINCT FROM `BEARER_ENV` — and the difference is
* load-bearing:
*
* The guarded routers are gated by the TERMINAL token
* (`server.ts:632` passes `deps.terminalBearerToken` into `transportRoutes`), not
* the auth token. Those are separate values in general. They coincide HERE only
* because `index.ts:160` copies auth -> terminal when the bind is NOT trusted
* (loopback/tailscale short-circuit) — i.e. exactly the `0.0.0.0` bind this
* procedure mandates. So under this procedure ONE token value serves both, and:
* • a DIRECT curl carrying `Authorization: Bearer <token>` works as-is;
* • a host-side `rig` read needs the token under THIS env name.
*
* WHY THE NEGATIVE CONTROL IS NOT OPTIONAL: when the terminal token is null the
* middleware PASSES EVERYTHING THROUGH (`auth-bearer-token.ts:98-101`) — an
* unguarded route. If the bind were ever softened to loopback while keeping a
* bearer, `terminalBearerToken` would stay null, the guarded probe would answer
* without auth, and an auth-probe-only runbook would report a green that proves
* nothing. The negative control (same call, NO header, MUST be 401) is what
* detects that state — it is the assertion that the guard is armed at all.
*/
export const TERMINAL_BEARER_ENV = "OPENRIG_TERMINAL_BEARER_TOKEN";
/** Unauthenticated reachability probe. */
export const HEALTH_PATH = "/healthz";
@@ -80,3 +104,18 @@ export function publishedDaemonEnv(bearerToken: string): Record<string, string>
export function publishedDaemonEnvFlags(bearerToken: string): string[] {
return Object.entries(publishedDaemonEnv(bearerToken)).flatMap(([k, v]) => ["-e", `${k}=${v}`]);
}
/**
* Env for a host-side `rig` read against the published daemon: the terminal
* token (see TERMINAL_BEARER_ENV) plus the daemon URL the read should target.
* Use this instead of hand-assembling env in each consumer.
*/
export function rigReadEnv(bearerToken: string, baseUrl: string): Record<string, string> {
if (!bearerToken) {
throw new Error(
"testbed rig-read: a non-empty token is required — a guarded route answers 401 without it, " +
"and a NULL terminal token would leave the route unguarded entirely (see TERMINAL_BEARER_ENV).",
);
}
return { [TERMINAL_BEARER_ENV]: bearerToken, OPENRIG_URL: baseUrl };
}
@@ -8,6 +8,7 @@ import { resolve } from "node:path";
import {
BIND_ENV, BIND_VALUE, BEARER_ENV, L3_HOST_PORT, CONTAINER_PORT,
HEALTH_PATH, GUARDED_PROBE_PATH, publishArg, publishedDaemonEnv,
TERMINAL_BEARER_ENV, rigReadEnv,
} from "./helpers/testbed-published-daemon.js";
const RUNBOOK = readFileSync(
@@ -33,6 +34,23 @@ describe("published-daemon procedure — module/runbook parity", () => {
expect(() => publishArg(0)).toThrow(/explicit positive integer/);
});
it("rig READS carry the TERMINAL token env — distinct name, same value under this procedure", () => {
expect(TERMINAL_BEARER_ENV).toBe("OPENRIG_TERMINAL_BEARER_TOKEN");
expect(TERMINAL_BEARER_ENV).not.toBe(BEARER_ENV); // separate concepts; coincide only via the non-trusted-bind copy
expect(rigReadEnv("t", "http://127.0.0.1:19433")).toEqual({
[TERMINAL_BEARER_ENV]: "t",
OPENRIG_URL: "http://127.0.0.1:19433",
});
expect(() => rigReadEnv("", "http://x")).toThrow(/non-empty token/);
});
it("the runbook keeps the NEGATIVE CONTROL — the only assertion that the guard is armed", () => {
// A null terminal token leaves the guarded route wide open (middleware passes
// through), so an auth-probe-only runbook could go green while proving nothing.
expect(RUNBOOK).toMatch(/NEGATIVE CONTROL/i);
expect(RUNBOOK).toMatch(/401/);
});
it("the env helper refuses an empty bearer — the guard is satisfied, never weakened", () => {
expect(publishedDaemonEnv("t")).toEqual({ [BIND_ENV]: BIND_VALUE, [BEARER_ENV]: "t" });
expect(() => publishedDaemonEnv("")).toThrow(/REFUSES a non-loopback bind/);