Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* entry.ts — the wrangler entry (wrangler.jsonc "main"), and NOTHING but the
* mount plate. It re-exports the whole deployed surface of src/worker.ts —
* the default { fetch, scheduled } handler plus the Durable Object classes
* wrangler discovers as named exports of `main` — PLUS the workerd-only
* SuiteGateway entrypoint (the A.8.6.3 egress gateway the SUITE_OUTBOUND
* loopback service binding names).
*
* WHY A SEPARATE FILE: src/exec/gateway.ts imports `cloudflare:workers`,
* which only the workerd runtime resolves — and the vitest suite imports
* src/worker.ts directly (createApp and friends). So the runtime-only import
* lives here, one file ABOVE the module every test resolves, and the test
* graph never sees it (the same entry-only import trick the apis-vin worker
* uses). test/suite-loader-enrollment.test.ts pins this split.
*/
export { default } from './worker.js'
export { DomainCooldown, MonitorSchedulerDO } from './worker.js'
export { SuiteGateway } from './exec/gateway.js'
14 changes: 14 additions & 0 deletions src/exec/cloudflare-workers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Minimal ambient declaration for the `cloudflare:workers` runtime module —
* kept LOCAL and structural (the same stance as WorkerCodeLike in
* src/exec/runner.ts) so the repo keeps compiling without
* @cloudflare/workers-types. Declares only what src/exec/gateway.ts uses;
* the real types come from the workerd runtime at deploy time.
*/
declare module 'cloudflare:workers' {
/** Structural subset of the runtime's WorkerEntrypoint base class. */
export abstract class WorkerEntrypoint<Env = unknown> {
protected env: Env
protected ctx: { waitUntil(promise: Promise<unknown>): void }
}
}
58 changes: 58 additions & 0 deletions src/exec/gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* exec/gateway.ts — the deploy-time face of the A.8.6.3 egress gateway: a
* named `WorkerEntrypoint` of THIS worker, bound back to itself in
* wrangler.jsonc —
*
* "services": [{ "binding": "SUITE_OUTBOUND", "service": "api-qa",
* "entrypoint": "SuiteGateway" }]
*
* — the same same-worker-loopback shape the apis-vin exec rail deployed on
* this account. Every fetch a suite isolate makes rides `globalOutbound` to
* `SuiteGateway.fetch`, and the runner drains the out-of-band refusal record
* through the `drainViolations` RPC on the same binding (auto-detected by
* `workerLoaderExecRunner`'s `hasDrain`).
*
* ALL behavior lives in `createOutboundGateway` (src/exec/runner.ts), which
* is unit-tested without this module: the floor per request AND per redirect
* hop, the marked 403, the parent-owned violation sink. This class is a thin
* mount, nothing more.
*
* THE SINK IS MODULE-LEVEL on purpose: workerd constructs a fresh entrypoint
* INSTANCE per invocation, so an instance field would silently drop the
* record between the isolate's fetches and the runner's later drain. A
* loopback service binding to this same worker is served in-isolate, so the
* module-level gateway is the shared record both sides see. Sharing one sink
* across concurrent runs can only OVER-attribute a violation, which errs in
* the closed direction (a run may be failed by a neighbour's refusal, never
* passed by one) — the trade `createOutboundGateway`'s contract already
* names. If the drain ever crosses isolates (a platform change), the record
* comes back empty and the in-isolate violation channel still fails the run
* for any non-forged suite — degraded, never open.
*
* This module imports `cloudflare:workers`, so ONLY the wrangler entry
* (src/entry.ts) may import it — vitest imports src/worker.ts and never
* resolves this file (the apis-vin entry-only import trick). Enforced by
* test/suite-loader-enrollment.test.ts.
*/
import { WorkerEntrypoint } from 'cloudflare:workers'
import { createOutboundGateway, type OutboundGatewayLike } from './runner.js'
import type { GateViolation } from './dialect.js'

/** The isolate-global gateway instance — the one record both halves share. */
const gateway: OutboundGatewayLike = createOutboundGateway()
Comment on lines +41 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep gateway violations separate for each run

When hosted suites run concurrently in the same Worker isolate, this module-global gateway lets one run drain another run's record: if run A records a refusal and run B calls drainViolations() first, the underlying splice() clears A's violation, causing B to fail while A's later drain is empty. A hostile A that suppresses the in-isolate marker channel—the scenario this out-of-band record is intended to defend—can therefore pass despite forbidden egress. Correlate violations with individual runs or serialize the fetch/drain lifecycle rather than sharing one destructive sink.

Useful? React with 👍 / 👎.


export class SuiteGateway extends WorkerEntrypoint {
/** `globalOutbound` delivery: the floor, per request and per redirect hop. */
async fetch(request: Request): Promise<Response> {
return gateway.fetch(request)
}

/**
* Hand the out-of-band refusal record to the runner and clear it — the
* A.8.6.3 fail-closed half that makes a caught/absorbed refusal still fail
* the run. Exposed as an RPC method so the SAME binding carries both halves.
*/
async drainViolations(): Promise<GateViolation[]> {
return gateway.drainViolations()
}
}
13 changes: 8 additions & 5 deletions src/exec/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@
* whole run fails, never a partial verdict.
*
* FEATURE DETECTION. The Worker Loader binding is an open-beta, paid-plan
* capability. `wrangler.jsonc` documents (but does not enable) the binding so
* every account keeps valid deploys; `worker.ts` wires this runner ONLY when
* `env.SUITE_LOADER` exists. Anywhere the binding — or the outbound gateway —
* capability, ENABLED in `wrangler.jsonc` since the 2026-08-08 account
* enrollment (SUITE_LOADER + the SuiteGateway loopback outbound) — but
* `worker.ts` still wires this runner ONLY when `env.SUITE_LOADER` exists,
* so a config rollback keeps every deploy valid.
* Anywhere the binding — or the outbound gateway —
* is absent, the runner is `unavailableExecRunner(...)`: a card that declares
* `runner: "api.qa/vitest@1"` then FAILS with the reason named (the same
* direction the ratified unknown-runner rule already gives an older
Expand Down Expand Up @@ -195,8 +197,9 @@ export async function gatewayFetch(
* erase it. Prefer one instance per run; a gateway shared across concurrent
* runs can only over-attribute a violation, which errs in the CLOSED
* direction (a run may be failed by a neighbour's refusal, never passed by
* one). At deploy time, expose this from a same-isolate loopback entrypoint
* (`ctx.exports`) so the runner can actually drain it.
* one). At deploy time this is exposed from a same-isolate loopback
* entrypoint (`SuiteGateway`, src/exec/gateway.ts — the SUITE_OUTBOUND
* service binding) so the runner can actually drain it.
*/
export function createOutboundGateway(
realFetch?: (url: string, init?: RequestInit) => Promise<Response>,
Expand Down
99 changes: 88 additions & 11 deletions src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
* GET /.well-known/agents.json
* GET /icp.json
* GET /openapi.json
* GET /health keyless liveness
* GET /health keyless liveness (?exec=1 adds the measured
* api.qa/vitest@1 runner-availability probe)
* GET /offers/attested-run the 402 boundary (a structured offer, not an error)
* GET /self api.qa's live verdict on api.qa (loopback, no network)
* GET /{domain} the public grade page (markdown | HTML+JSON-LD | JSON)
Expand All @@ -21,7 +22,7 @@
*/

import { verifyTarget, rejudge } from './verify.js'
import type { ExecSuiteRunner } from './exec/dialect.js'
import { VITEST_RUNNER, type ExecSuiteRunner } from './exec/dialect.js'
import { unavailableExecRunner, workerLoaderExecRunner, type WorkerLoaderLike } from './exec/runner.js'
import { verifyPinnedSpec, verifySuite, parseSuite, type PinnedReport, type SuiteReport } from './pinned.js'
import { reportMarkdown, pinnedMarkdown, suiteMarkdown } from './render.js'
Expand Down Expand Up @@ -150,10 +151,12 @@ export interface Env {
TS_ROLLUP_CAP?: string
/**
* Dynamic Worker Loader binding (`worker_loaders` in wrangler.jsonc) — the
* `api.qa/vitest@1` isolate runner (A.8.6.3). OPEN-BETA, PAID-PLAN: the
* binding is documented but NOT enabled in the shipped config, so every
* account keeps valid deploys; absent, a card declaring the executable
* dialect fails with a typed `runner-unavailable` reason.
* `api.qa/vitest@1` isolate runner (A.8.6.3). OPEN-BETA, PAID-PLAN:
* ENABLED in the shipped config since 2026-08-08 (the account accepted
* `worker_loaders` that day for the apis-vin exec rail). Still
* feature-detected — on any deployment without the binding, a card
* declaring the executable dialect fails with a typed `runner-unavailable`
* reason, so a config rollback can never crash or silently pass.
*/
SUITE_LOADER?: WorkerLoaderLike
/**
Expand All @@ -162,7 +165,8 @@ export interface Env {
* runner refuses to run rather than inherit this worker's own network
* access (the A.8.6.3 floor). Build it on `createOutboundGateway`
* (src/exec/runner.ts) and expose it from a same-isolate loopback
* entrypoint (`ctx.exports`), so it carries BOTH halves of the floor:
* entrypoint (the SuiteGateway service binding in wrangler.jsonc,
* src/exec/gateway.ts), so it carries BOTH halves of the floor:
* `fetch` (the refusal itself) and `drainViolations` (the out-of-band
* record the runner folds into the verdict — the half that makes a
* caught/absorbed refusal still fail the run, A.8.6.3 fail-closed
Expand Down Expand Up @@ -198,6 +202,37 @@ export interface TickSummary {
const LINKSET =
'</llms.txt>; rel="service-doc", </.well-known/agents.json>; rel="service-desc", </openapi.json>; rel="describedby"'

// ── GET /health?exec=1 — the runner-availability probe (2026-08-08 SUITE_LOADER
// enrollment). The plain /health answer is UNCHANGED (its declared contract is
// graded); the opt-in query adds one attested fact: whether this deployment can
// actually spin an `api.qa/vitest@1` isolate. The probe runs the FIXED,
// server-owned suite below through the same execRunner a verification uses —
// one registered test, no network — so "available" is a measured run, not a
// binding-presence claim. The result is memoized per binding-signature for
// EXEC_PROBE_TTL_MS in isolate-global state, bounding the metered/billed
// isolate spins an unauthenticated caller can trigger.

/** The fixed probe suite — server-owned bytes, registers one test, fetches nothing. */
export const EXEC_PROBE_TESTS = `it('the isolate runs', () => { expect(1).toBe(1) })`
/** How long one probe verdict answers for a given binding signature. */
export const EXEC_PROBE_TTL_MS = 5 * 60_000

/** What /health?exec=1 reports — a typed state, never a crash. */
export interface ExecProbeResult {
runner: typeof VITEST_RUNNER
/** True iff the probe suite actually RAN in a loader isolate just now (or within TTL). */
available: boolean
/** The runner outcome status verbatim: 'ran' | 'failed' | 'runner-unavailable'. */
status: string
/** The typed reason, when not 'ran'. */
reason?: string
probedAtMs: number
/** True when this answer was served from the TTL memo, not a fresh isolate. */
cached: boolean
}

const execProbeMemo = new Map<string, Omit<ExecProbeResult, 'cached'>>()

const DOMAIN_ROUTE = /^\/([a-z0-9-]+(?:\.[a-z0-9-]+)+)$/i

/**
Expand Down Expand Up @@ -335,9 +370,9 @@ export function createApp(
u.startsWith(SELF_ORIGIN) ? loopback(u, init) : (opts.externalFetcher ?? fetch)(u, init)

// The `api.qa/vitest@1` execution seam (A.8.6) — FEATURE-DETECTED. The
// Worker Loader binding is an open-beta, paid-plan capability, so the
// deployment stays valid without it (wrangler.jsonc documents, but does not
// enable, the binding). Three states, all typed and none a crash:
// Worker Loader binding is an open-beta, paid-plan capability; wrangler.jsonc
// enables it (2026-08-08 enrollment), but the code keeps detecting it so a
// config rollback stays valid. Three states, all typed and none a crash:
// binding + outbound gateway present → the isolate runner;
// binding present, outbound absent → runner-unavailable (running
// without a gateway would inherit THIS worker's network, which the
Expand All @@ -351,6 +386,43 @@ export function createApp(
? workerLoaderExecRunner(env.SUITE_LOADER, { outbound: env.SUITE_OUTBOUND })
: unavailableExecRunner()

/**
* GET /health?exec=1 body: run (or answer from the TTL memo) the fixed
* probe suite through execRunner. Memo key = the binding signature, so a
* test's bindingless createApp can never be answered by a bound app's
* cached verdict (and vice versa); production has ONE signature per deploy.
*/
const execProbe = async (): Promise<ExecProbeResult> => {
const key = `${env.SUITE_LOADER ? 'loader' : '-'}:${env.SUITE_OUTBOUND ? 'outbound' : '-'}`
const atMs = now()
const hit = execProbeMemo.get(key)
if (hit && atMs - hit.probedAtMs < EXEC_PROBE_TTL_MS) return { ...hit, cached: true }
const outcome = await execRunner.run({
Comment on lines +398 to +400

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Coalesce concurrent execution probes

When several unauthenticated /health?exec=1 requests arrive after this memo is cold or expired, every request observes the miss before any reaches execProbeMemo.set() and therefore starts its own metered Worker Loader run. A burst can consequently create an unbounded number of billed isolate executions every five minutes per Worker isolate, defeating the stated abuse bound. Cache the in-flight promise before awaiting the runner so concurrent callers share one probe.

Useful? React with 👍 / 👎.

artifactKind: 'document',
testsSource: EXEC_PROBE_TESTS,
origin: SELF_ORIGIN,
vars: {},
environment: 'public',
sandbox: false,
seed: 1,
declarativeRows: 0,
// The probe bytes are server-owned constants, hashed here exactly as a
// card pin would be — a stable content-hash isolate id, so repeat
// probes warm-reuse one isolate instead of minting new ones.
digest: `sha256:${await sha256Hex(EXEC_PROBE_TESTS)}`,
limits: { cpuMs: 5_000, wallMs: 10_000 },
})
const fresh: Omit<ExecProbeResult, 'cached'> = {
runner: VITEST_RUNNER,
available: outcome.status === 'ran' && outcome.results.every((r) => r.status === 'pass'),
status: outcome.status,
...(outcome.status !== 'ran' ? { reason: outcome.reason } : {}),
probedAtMs: atMs,
}
execProbeMemo.set(key, fresh)
return { ...fresh, cached: false }
}

/**
* The actual tick body: claim + re-verify every DUE monitor through the
* SAME attested verifyTarget/verifySuite/cooldown/SSRF path a fetch run
Expand Down Expand Up @@ -672,7 +744,12 @@ export function createApp(
if (path === '/.well-known/agents.json') return json(selfAgentsJson())
if (path === '/icp.json') return json(selfIcpJson())
if (path === '/openapi.json') return json(selfOpenapi())
if (path === '/health') return json({ ok: true, verifier: 'api.qa', version: VERIFIER_VERSION })
if (path === '/health') {
const base = { ok: true, verifier: 'api.qa', version: VERIFIER_VERSION }
// Opt-in runner probe; the plain declared-contract answer is untouched.
if (url.searchParams.get('exec') !== '1') return json(base)
return json({ ...base, exec: await execProbe() })
}
if (path === '/offers/attested-run') return json(selfOffer(), 402)

// Brand assets. These MUST be matched before DOMAIN_ROUTE: that regex
Expand Down
Loading
Loading