@byollm/conformance 0.1.0-alpha.4 → 0.1.0-alpha.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -3
- package/dist/audit-cli.d.ts +1 -0
- package/dist/audit-cli.js +25 -0
- package/dist/audit-cli.js.map +1 -0
- package/dist/chunk-JPFAPXWZ.js +469 -0
- package/dist/chunk-JPFAPXWZ.js.map +1 -0
- package/dist/{chunk-EXNALQ5D.js → chunk-SWXTCMPN.js} +288 -51
- package/dist/chunk-SWXTCMPN.js.map +1 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +106 -4
- package/dist/index.js +9 -1
- package/package.json +8 -6
- package/dist/chunk-EXNALQ5D.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/deployment.ts"],"sourcesContent":["import { connect as tlsConnect } from \"node:tls\";\nimport { request as httpsRequest } from \"node:https\";\nimport {\n PROTOCOL_VERSION,\n generateKeys,\n signRequest,\n signSiteRequest,\n} from \"@byollm/protocol\";\n\n/**\n * The deployment posture audit — what an outsider can do to a running relay.\n *\n * ## Why this exists as a separate surface\n *\n * `certify` drives a real daemon against a {@link ConformanceTarget}, and a\n * target may be an in-process handler or an HTTP server: \"deliberately\n * transport-agnostic\", which is the right call for certifying a *protocol*.\n *\n * It is also a blind spot, and byollm_009's ninth finding lived in it. Eight\n * freeze-gate findings came from tests where the site reached the relay by\n * calling `handle()` on an object it held a reference to — and a harness that\n * invokes the system under test directly cannot see anything about how the\n * system is *reached*. The site plane had no authentication at all. Nothing\n * noticed, because nothing in the suite was ever a stranger.\n *\n * So this suite is a stranger. It holds no credential, no key the deployment\n * knows, and no reference to any object inside it. It has a URL, which is\n * exactly what an attacker has. Every check asks the question that form of\n * access makes available:\n *\n * - can I enqueue work into someone's machines?\n * - can I read who is online and what they are holding?\n * - can I make a signature that is *well-formed* and be believed?\n * - is anything served that should not be on the internet?\n * - can I reach a handler by dressing a path up to look like one?\n *\n * ## What this is not\n *\n * Not a penetration test, and not exhaustive — it cannot be, because the next\n * hole will be in whatever gets added next. It is the specific class that has\n * already bitten, turned into something that runs. That is the same move as\n * every other check in this kit: a finding becomes a check so its *shape*\n * cannot recur silently.\n *\n * It is also deliberately **safe to run against production**: nothing here\n * writes, nothing floods, and every request is one an ordinary scanner would\n * make. A posture audit you are nervous about running is one nobody runs.\n */\n\n/** One thing a stranger tried. */\nexport interface PostureCheck {\n /** Stable id, cited in output. */\n readonly id: string;\n /** What a person should understand from a failure. */\n readonly title: string;\n /** MUSTs this exercises, where one applies. Empty is honest, not a gap. */\n readonly cites: readonly string[];\n run(context: PostureContext): Promise<PostureOutcome>;\n}\n\nexport interface PostureContext {\n /** The origin, as an outsider would type it. */\n readonly origin: string;\n /** Where the daemon plane is mounted. */\n readonly basePath: string;\n /** Injectable, so a test can drive this without a network. */\n readonly fetch: typeof fetch;\n /**\n * The origin's own address, behind whatever edge fronts it — `D008`.\n *\n * Optional because most deployments have no separate origin, and a check\n * that guessed one would report a posture it never tested.\n */\n readonly originAddress?: string;\n}\n\nexport interface PostureOutcome {\n readonly passed: boolean;\n /** What actually happened, in a sentence someone can act on. */\n readonly detail: string;\n}\n\nexport interface PostureResult extends PostureOutcome {\n readonly id: string;\n readonly title: string;\n readonly cites: readonly string[];\n}\n\nexport interface PostureReport {\n readonly origin: string;\n readonly passed: boolean;\n readonly results: readonly PostureResult[];\n}\n\nconst STUB = {\n id: \"posture-probe\",\n kind: \"llm.generate\",\n owner: \"nobody\",\n audience: \"self\",\n sizeClass: \"small\",\n streaming: false,\n // Far enough out that a deployment cannot pass by calling it expired.\n deadlineAt: 4_102_444_800_000,\n};\n\n/** Refused, for any reason a server is entitled to refuse a stranger. */\nconst REFUSED = new Set([401, 403, 404]);\n\n/**\n * Refused **by a byollm relay**, rather than by nothing being there.\n *\n * The distinction is the whole difference between a posture audit and a\n * connectivity check, and this suite shipped without it for an hour. Running\n * against `hub.byollm.cloud` before its Ingress had a matching host rule, the\n * load balancer answered 404 to everything from its own error page — and the\n * audit reported 6/7, because 404 is a refusal and every probe got one.\n *\n * A completely dead deployment scored better than a working one. That is the\n * assertion-that-cannot-fail wearing its most convincing disguise: not a check\n * that never fails, but one that passes for a reason unrelated to the property\n * it claims.\n *\n * So a refusal has to be *byollm's* refusal: the protocol answers errors as\n * JSON with an `error` field, and Google's `backend NotFound` page does not.\n */\nasync function refusedByByollm(\n response: Response,\n): Promise<{ ok: boolean; why: string }> {\n if (!REFUSED.has(response.status)) {\n return { ok: false, why: `answered ${String(response.status)}` };\n }\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n return {\n ok: false,\n why:\n `answered ${String(response.status)} but not as byollm — ` +\n `something else is serving this path`,\n };\n }\n const shaped =\n typeof body === \"object\" &&\n body !== null &&\n typeof (body as { error?: unknown }).error === \"string\";\n return shaped\n ? { ok: true, why: `answered ${String(response.status)}` }\n : {\n ok: false,\n why:\n `answered ${String(response.status)} with a body byollm would not ` +\n `send — something else is serving this path`,\n };\n}\n\nconst outcome = (passed: boolean, detail: string): PostureOutcome => ({\n passed,\n detail,\n});\n\n/**\n * The certificate the **origin** serves, read off the connection.\n *\n * Connects to the origin's own address with the pinned hostname in SNI, which\n * is what an edge does when it validates. Certificate verification is off for\n * `D008`'s reason: the question is what this address presents, not whether to\n * trust it.\n *\n * **It must be the origin, not the hostname.** Written first as a plain\n * connection to `hub.byollm.cloud`, which goes *through* Cloudflare and\n * returns Cloudflare's own edge certificate — one that names the host by\n * construction and auto-renews on a ninety-day cycle. `D009` passed on it\n * while the origin's certificate named nothing relevant, which is the exact\n * condition finding 45 describes; `D010` then failed with \"84 days\" and\n * revealed that both were measuring the edge.\n *\n * Two certificates, two purposes: the edge one protects visitors and\n * Cloudflare renews it, and the origin one is what the edge validates and\n * nobody renews automatically. Only the second is this audit's business.\n */\nasync function servedCertificate(\n address: string,\n host: string,\n): Promise<{ names: readonly string[]; validTo: string } | undefined> {\n return new Promise((resolve) => {\n const socket = tlsConnect(\n {\n host: address,\n servername: host,\n port: 443,\n timeout: 15_000,\n rejectUnauthorized: false,\n },\n () => {\n const peer = socket.getPeerCertificate();\n const alt = (peer.subjectaltname ?? \"\")\n .split(\",\")\n .map((entry) => entry.trim().replace(/^DNS:/, \"\"))\n .filter((entry) => entry.length > 0);\n const cn: unknown = peer.subject.CN;\n socket.end();\n resolve({\n names: alt.length > 0 ? alt : [typeof cn === \"string\" ? cn : \"\"],\n validTo: peer.valid_to,\n });\n },\n );\n socket.on(\"error\", () => {\n resolve(undefined);\n });\n socket.on(\"timeout\", () => {\n socket.destroy();\n resolve(undefined);\n });\n });\n}\n\nexport const POSTURE_CHECKS: readonly PostureCheck[] = Object.freeze([\n {\n id: \"D001_SITE_ENQUEUE_REFUSES_UNSIGNED\",\n title: \"an anonymous caller cannot enqueue work in a site's name\",\n cites: [\"REQUESTS_SIGNED_NOT_BEARER\"],\n async run({ origin, fetch: f }) {\n const response = await f(`${origin}/relay/site/enqueue`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n // The version, because the auditor is a site-plane client like any\n // other (§B.4). Without it the relay refuses on the handshake and\n // this check would report \"refused\" for a reason that has nothing to\n // do with signatures — success for an unrelated reason, which is the\n // failure this whole file was written against.\n body: JSON.stringify({\n protocolVersion: PROTOCOL_VERSION,\n siteId: \"any-site\",\n stub: STUB,\n }),\n });\n // The finding, exactly: a relay that accepts this routes unsolicited\n // work to consenting users' private hardware. The payload that follows\n // is sealed by the real site or not at all, so nothing forged *runs* —\n // and dispatch to someone's machine is a breach whatever the\n // ciphertext does.\n const refusal = await refusedByByollm(response);\n return outcome(refusal.ok, `POST /relay/site/enqueue ${refusal.why}`);\n },\n },\n {\n id: \"D002_SITE_READS_REFUSE_UNSIGNED\",\n title: \"an anonymous caller cannot read who is online\",\n cites: [\"REQUESTS_SIGNED_NOT_BEARER\"],\n async run({ origin, fetch: f }) {\n // Reads matter as much as writes here. A blind relay's whole claim is\n // that it holds routing metadata and nothing else — which makes that\n // metadata the entire prize, and \"who is online right now, on which\n // device, holding which lease\" is the shape of it.\n const why: string[] = [];\n let ok = true;\n for (const path of [\"pending\", \"results\"]) {\n const response = await f(\n `${origin}/relay/site/${path}?siteId=any-site&protocolVersion=${PROTOCOL_VERSION}`,\n );\n const refusal = await refusedByByollm(response);\n ok &&= refusal.ok;\n why.push(`${path} ${refusal.why}`);\n }\n return outcome(ok, why.join(\"; \"));\n },\n },\n {\n id: \"D003_DAEMON_PLANE_REFUSES_UNSIGNED\",\n title: \"an anonymous caller cannot claim work\",\n cites: [\"REQUESTS_SIGNED_NOT_BEARER\", \"CLAIM_REQUIRES_CAPABILITY\"],\n async run({ origin, basePath, fetch: f }) {\n const response = await f(`${origin}${basePath}/claim`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n protocolVersion: \"0\",\n runnerId: \"nobody\",\n max: 1,\n capabilities: [{ kind: \"llm.generate\", models: [\"any\"] }],\n }),\n });\n const refusal = await refusedByByollm(response);\n return outcome(refusal.ok, `POST ${basePath}/claim ${refusal.why}`);\n },\n },\n {\n id: \"D004_REFUSES_A_STRANGER_S_VALID_SIGNATURE\",\n title: \"a well-formed signature from an unknown key is not enough\",\n cites: [\"REQUESTS_SIGNED_NOT_BEARER\", \"KEYS_EXCHANGED_AT_CONSENT\"],\n async run({ origin, basePath, fetch: f }) {\n // The check `D001`–`D003` cannot make: everything about these requests\n // is correct except whose key signed them. A deployment that verified\n // signatures without checking *whose* would pass the three above and\n // fail here, and that is a real implementation mistake rather than a\n // hypothetical one — verifying a signature and identifying a signer are\n // two steps, and the second is the one that gets skipped.\n const stranger = generateKeys(Date.now());\n const now = Date.now();\n\n const siteBody = JSON.stringify({\n protocolVersion: PROTOCOL_VERSION,\n siteId: \"any-site\",\n stub: STUB,\n });\n const siteSignature = signSiteRequest(stranger, {\n endpoint: \"enqueue\",\n siteId: \"any-site\",\n issuedAt: now,\n body: siteBody,\n });\n const site = await f(`${origin}/relay/site/enqueue`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-byollm-site\": \"any-site\",\n \"x-byollm-issued-at\": String(siteSignature.issuedAt),\n \"x-byollm-signature\": siteSignature.signature,\n },\n body: siteBody,\n });\n\n const daemonBody = JSON.stringify({\n protocolVersion: \"0\",\n runnerId: \"nobody\",\n max: 1,\n capabilities: [{ kind: \"llm.generate\", models: [\"any\"] }],\n });\n const daemonSignature = signRequest(stranger, {\n endpoint: \"claim\",\n runnerId: \"nobody\",\n issuedAt: now,\n body: daemonBody,\n });\n const daemon = await f(`${origin}${basePath}/claim`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-byollm-runner\": \"nobody\",\n \"x-byollm-issued-at\": String(daemonSignature.issuedAt),\n \"x-byollm-signature\": daemonSignature.signature,\n },\n body: daemonBody,\n });\n\n const siteRefusal = await refusedByByollm(site);\n const daemonRefusal = await refusedByByollm(daemon);\n return outcome(\n siteRefusal.ok && daemonRefusal.ok,\n `signed by a stranger: site ${siteRefusal.why}, daemon ${daemonRefusal.why}`,\n );\n },\n },\n {\n id: \"D011_VERSION_NAMED_ON_BOTH_PLANES\",\n title: \"an unknown protocol version is refused by name, not by accident\",\n cites: [\"VERSION_HANDSHAKE_REQUIRED\"],\n async run({ origin, fetch: f }) {\n // byollm_009 §B.4. A mismatch that arrives as a generic `bad-request`\n // leaves a daemon and a server to discover they disagree by failing,\n // with nothing in the answer naming the disagreement or the fix — which\n // is what this relay did on every endpoint until the handshake landed.\n //\n // **Both planes, because the gap was a whole plane.** The daemon plane\n // had version literals in its schemas and the site plane had none, so a\n // check that probed one would have reported a handshake the other did\n // not have.\n const probes: { where: string; response: Response }[] = [\n {\n where: \"daemon\",\n response: await f(`${origin}/byollm/claim`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ protocolVersion: \"99\", max: 1 }),\n }),\n },\n {\n where: \"site\",\n response: await f(`${origin}/relay/site/enqueue`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n protocolVersion: \"99\",\n siteId: \"any-site\",\n stub: STUB,\n }),\n }),\n },\n ];\n\n const wrong: string[] = [];\n for (const probe of probes) {\n let body: { error?: string; supported?: unknown } = {};\n try {\n body = (await probe.response.json()) as typeof body;\n } catch {\n wrong.push(\n `${probe.where}: answered ${String(probe.response.status)} and not as byollm`,\n );\n continue;\n }\n if (body.error !== \"unsupported-protocol-version\") {\n wrong.push(\n `${probe.where}: answered ${String(probe.response.status)} ${String(body.error)}`,\n );\n continue;\n }\n // Named, not merely refused: the field a client acts on.\n if (!Array.isArray(body.supported)) {\n wrong.push(`${probe.where}: refused without naming what it speaks`);\n }\n }\n\n return outcome(\n wrong.length === 0,\n wrong.length === 0\n ? \"both planes name the version they speak\"\n : wrong.join(\"; \"),\n );\n },\n },\n\n {\n id: \"D005_NO_DEBUG_SURFACE\",\n title: \"the debug page is not on the internet\",\n cites: [],\n async run({ origin, basePath, fetch: f }) {\n // Finding eleven. The relay's debug page shows no prompt or result text\n // — it does not have them — and it does show every routed job, who\n // claimed it, and every lease in flight. Closing the site plane while\n // leaving this open protects the data from one door and not the other.\n const paths = [\"/debug\", `${basePath}/debug`];\n const statuses: number[] = [];\n for (const path of paths) {\n const response = await f(`${origin}${path}`);\n statuses.push(response.status);\n }\n return outcome(\n // **Not merely \"not 200\"** — cloud_009 §3 made the page per-site, so\n // an enabled one answers a probe with no site id `400` rather than\n // rendering. A check that accepted anything but 200 would pass\n // against a deployment whose debug page is one query parameter away,\n // which is finding eleven with a shorter walk.\n //\n // `404` is the only answer that means the route does not exist. The\n // reference relay says exactly that when its debug page is off, and\n // says `400` when it is on and the caller named no site.\n statuses.every((status) => status === 404),\n `debug paths answered ${statuses.map(String).join(\"/\")}`,\n );\n },\n },\n {\n id: \"D006_NO_PATH_DISPATCH\",\n title: \"a handler cannot be reached by dressing a path up to look like it\",\n cites: [\"REQUESTS_SIGNED_NOT_BEARER\"],\n async run({ origin, fetch: f }) {\n // A router that dispatched on the last path segment would serve\n // `/literally/anything/claim`. Worth checking from outside because it\n // is invisible from inside: an in-process harness calls the handler by\n // name and never constructs a URL that could be misread.\n const response = await f(`${origin}/literally/anything/claim`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ protocolVersion: \"0\", max: 1 }),\n });\n const refusal = await refusedByByollm(response);\n return outcome(\n refusal.ok && response.status === 404,\n `POST /literally/anything/claim ${refusal.why}`,\n );\n },\n },\n {\n id: \"D007_TLS_ONLY\",\n title: \"the deployment is reached over TLS, and plaintext is not served\",\n cites: [],\n async run({ origin, fetch: f }) {\n if (!origin.startsWith(\"https://\")) {\n // Not a failure when auditing a local hub on purpose, but never\n // silent: an audit that reports \"posture good\" against an http origin\n // has certified the one thing that matters least.\n return outcome(false, `origin is ${origin} — this audit saw no TLS`);\n }\n // Payloads are sealed end to end and would survive plaintext transport.\n // Request signatures would not: on the wire they are replayable by\n // anyone on the path for the length of the freshness window, and every\n // site-plane and daemon-plane call carries one.\n const plain = origin.replace(\"https://\", \"http://\");\n const response = await f(`${plain}/healthz`, { redirect: \"manual\" });\n const redirected =\n response.status >= 300 &&\n response.status < 400 &&\n (response.headers.get(\"location\") ?? \"\").startsWith(\"https://\");\n return outcome(\n redirected || REFUSED.has(response.status),\n `plaintext answered ${String(response.status)}${\n redirected ? \" and redirects to https\" : \"\"\n }`,\n );\n },\n },\n\n {\n id: \"D008_ORIGIN_NOT_PUBLIC\",\n title: \"the origin answers the edge, and nobody else\",\n cites: [],\n async run({ origin, originAddress }) {\n // cloud_008 findings 44/45. A load balancer has a public address, and\n // an edge in front of it is a convention rather than a boundary until\n // the origin refuses everything else: anyone who resolves the address\n // reaches the hub past every WAF rule, rate limit and bot check the\n // zone applies, and appears in no edge log doing it.\n //\n // Bounded, and not nothing. Requests are signed and payloads sealed, so\n // a stranger cannot forge a claim or read a job — but they can reach\n // `/byollm/pair` and every plane endpoint at whatever rate the origin\n // serves, which is exactly the surface the edge exists to absorb.\n //\n // Skipped rather than failed when no address is supplied: an auditor\n // who does not know where the origin lives cannot ask this, and\n // guessing would be a check that passes for not looking.\n if (originAddress === undefined) {\n // **Fails, not skips.** This file's own rule, and the suite already\n // had a test for it: an audit that drops a check it could not run\n // reports a posture nobody measured, which reads identically to one\n // measured and found good.\n //\n // Written as a pass first, on the reasoning that most deployments\n // have no separate origin — and that test failed it immediately. The\n // reasoning was wrong in the way this whole audit exists to catch:\n // \"probably fine\" and \"verified\" must not print the same.\n return outcome(\n false,\n \"no origin address given — this posture was not measured \" +\n \"(pass the origin's address as the third argument)\",\n );\n }\n\n // Asked the way an attacker would, which `fetch` cannot.\n //\n // The first version of this check used `fetch` and passed for the wrong\n // reason: TLS fails against a bare IP because the certificate does not\n // name it, the request never reaches the load balancer's backend, and a\n // connection error read as \"refused\". It would have reported a green\n // origin with no policy attached at all.\n //\n // Plain HTTP does not work either — the frontend answers `301` before\n // any backend is chosen, so the policy is never consulted.\n //\n // So: HTTPS to the address, with the real host in SNI, and certificate\n // verification **off**. That is not a lapse. The property under test is\n // whether an address answers a stranger, not who the answer comes from,\n // and refusing to look because the certificate does not match the IP is\n // how the first version passed while proving nothing. Nothing else in\n // this kit may copy it.\n const host = new URL(origin).host;\n const status = await new Promise<number | \"refused\">((resolve) => {\n const request = httpsRequest(\n {\n host: originAddress,\n servername: host,\n headers: { host },\n path: \"/readyz\",\n method: \"GET\",\n rejectUnauthorized: false,\n timeout: 15_000,\n },\n (response) => {\n response.resume();\n resolve(response.statusCode ?? 0);\n },\n );\n request.on(\"error\", () => {\n resolve(\"refused\");\n });\n request.on(\"timeout\", () => {\n request.destroy();\n resolve(\"refused\");\n });\n request.end();\n });\n\n if (status === \"refused\") {\n // Nothing answered at all, which is the strongest form of this.\n return outcome(true, \"the origin address refused the connection\");\n }\n return outcome(\n status === 403,\n `the origin answered ${String(status)} to a direct request`,\n );\n },\n },\n\n {\n id: \"D009_CERT_NAMES_THE_PINNED_HOST\",\n title: \"the certificate names the hostname daemons pin\",\n cites: [],\n async run({ origin, originAddress }) {\n // cloud_008 finding 45. The load balancer held one certificate, for the\n // *origin* hostname, and `hub.byollm.cloud` — the name every daemon\n // pins and the edge validates — was not on it. Cloudflare cannot verify\n // a certificate that does not name what it asked for, so the zone sat\n // on plain Full: encrypted to the origin, not checking who the origin\n // is, while every daemon's pinned site keys arrive through it.\n //\n // Nothing said so. \"There is a certificate\" and \"there is a certificate\n // for the name being validated\" printed identically, which is why this\n // check exists rather than a note in a runbook.\n //\n // Asked over TLS with the real SNI and read from the peer, so it is the\n // certificate actually served rather than one somebody configured.\n const host = new URL(origin).host;\n if (originAddress === undefined) {\n return outcome(\n false,\n \"no origin address given — this posture was not measured. \" +\n \"Asking the hostname reads the edge's certificate, which names it \" +\n \"by construction and proves nothing about the origin\",\n );\n }\n const served = await servedCertificate(originAddress, host);\n const names = served?.names;\n\n if (names === undefined) {\n return outcome(\n false,\n `could not read a certificate from ${originAddress}`,\n );\n }\n const covered = names.some(\n (name) =>\n name === host ||\n (name.startsWith(\"*.\") && host.endsWith(name.slice(1))),\n );\n return outcome(\n covered,\n covered\n ? `served a certificate naming ${host}`\n : `the certificate names ${names.join(\", \")} — not ${host}`,\n );\n },\n },\n\n {\n id: \"D010_CERT_HAS_LIFE_LEFT\",\n title: \"the certificate is not about to expire\",\n cites: [],\n async run({ origin, originAddress }) {\n // A long-lived certificate is the kind whose expiry gets written in a\n // note and never looked at again. The one this deployment now depends\n // on runs to 2041, which makes it *more* likely to be forgotten, not\n // less — and it is load-bearing: it is what the edge validates before\n // it will carry a daemon's traffic at all.\n //\n // Measured from the artefact rather than from the note. A date somebody\n // owns has to mean a date something checks.\n //\n // Ninety days: long enough that a renewal is scheduled rather than\n // scrambled, short enough that the warning is still about something\n // real.\n const host = new URL(origin).host;\n if (originAddress === undefined) {\n return outcome(\n false,\n \"no origin address given — this posture was not measured \" +\n \"(the edge's certificate is Cloudflare's to renew, not ours)\",\n );\n }\n const served = await servedCertificate(originAddress, host);\n if (served === undefined) {\n return outcome(\n false,\n `could not read a certificate from ${originAddress}`,\n );\n }\n const days = Math.round(\n (Date.parse(served.validTo) - Date.now()) / 86_400_000,\n );\n return outcome(\n days > 90,\n `the certificate expires ${served.validTo} (${String(days)} days)`,\n );\n },\n },\n]);\n\n/**\n * Audit a running deployment. Holds nothing it was not given a URL for.\n *\n * Every check runs even after one fails, because a posture report's job is to\n * be a complete picture rather than the first thing that went wrong.\n */\nexport async function auditDeployment(options: {\n url: string;\n basePath?: string;\n /** The origin behind the edge, for `D008`. */\n originAddress?: string;\n fetch?: typeof fetch;\n onProgress?: (result: PostureResult) => void;\n}): Promise<PostureReport> {\n const origin = options.url.replace(/\\/+$/, \"\");\n const context: PostureContext = {\n origin,\n basePath: (options.basePath ?? \"/byollm\").replace(/\\/+$/, \"\"),\n ...(options.originAddress === undefined\n ? {}\n : { originAddress: options.originAddress }),\n fetch: options.fetch ?? globalThis.fetch,\n };\n\n const results: PostureResult[] = [];\n for (const check of POSTURE_CHECKS) {\n let result: PostureResult;\n try {\n result = { ...(await check.run(context)), ...describe(check) };\n } catch (error) {\n // A check that cannot complete is a failure, not a skip. An audit that\n // silently drops a probe it could not run reports a posture nobody\n // measured — which is worse than reporting none at all.\n result = {\n ...describe(check),\n passed: false,\n detail: `the probe failed: ${error instanceof Error ? error.message : \"unknown\"}`,\n };\n }\n results.push(result);\n options.onProgress?.(result);\n }\n\n return {\n origin,\n passed: results.every((result) => result.passed),\n results,\n };\n}\n\nconst describe = (check: PostureCheck) => ({\n id: check.id,\n title: check.title,\n cites: check.cites,\n});\n\nexport function formatPostureReport(report: PostureReport): string {\n const lines = [`deployment posture — ${report.origin}`, \"\"];\n for (const result of report.results) {\n lines.push(` ${result.passed ? \"ok \" : \"FAIL\"} ${result.id}`);\n lines.push(` ${result.title}`);\n lines.push(` ${result.detail}`);\n if (result.cites.length > 0) {\n lines.push(` cites ${result.cites.join(\", \")}`);\n }\n }\n const passed = report.results.filter((result) => result.passed).length;\n lines.push(\n \"\",\n report.passed\n ? `${String(passed)}/${String(report.results.length)} — a stranger got nowhere.`\n : `${String(passed)}/${String(report.results.length)} — a stranger got somewhere. See FAIL above.`,\n \"\",\n );\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -203,8 +203,10 @@ async function pairDaemon(target, options) {
|
|
|
203
203
|
owner: result.pairing.owner,
|
|
204
204
|
identity: {
|
|
205
205
|
keys: () => deviceIdentity.load(Date.now()),
|
|
206
|
-
// Pinned at pairing, exactly as a real daemon does
|
|
207
|
-
|
|
206
|
+
// Pinned at pairing, exactly as a real daemon does — the set the
|
|
207
|
+
// upstream answered with, keyed by each site's identity key id
|
|
208
|
+
// (cloud_009 §5). A direct site is one entry.
|
|
209
|
+
sites: new Map(Object.entries(result.pairing.sites))
|
|
208
210
|
},
|
|
209
211
|
daemonVersion: "conformance",
|
|
210
212
|
loaded,
|
|
@@ -220,10 +222,9 @@ async function pairDaemon(target, options) {
|
|
|
220
222
|
allowlist,
|
|
221
223
|
runnerId: result.pairing.runnerId,
|
|
222
224
|
owner: result.pairing.owner,
|
|
223
|
-
token: result.pairing.token,
|
|
224
225
|
keys: await deviceIdentity.load(Date.now()),
|
|
225
226
|
identityKeys: () => deviceIdentity.load(Date.now()),
|
|
226
|
-
sitePinned: result.pairing.
|
|
227
|
+
sitePinned: Object.values(result.pairing.sites)[0],
|
|
227
228
|
home,
|
|
228
229
|
ingress,
|
|
229
230
|
spend,
|
|
@@ -309,6 +310,37 @@ async function claimOne(target, daemon) {
|
|
|
309
310
|
if (!job) throw new Error("claim returned no jobs");
|
|
310
311
|
return job;
|
|
311
312
|
}
|
|
313
|
+
async function claimRaw(target, daemon, capabilityOverride) {
|
|
314
|
+
const capabilities = capabilityOverride ?? await daemon.runner.detectCapabilities();
|
|
315
|
+
const body = JSON.stringify({
|
|
316
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
317
|
+
runnerId: daemon.runnerId,
|
|
318
|
+
capabilities,
|
|
319
|
+
max: 10
|
|
320
|
+
});
|
|
321
|
+
const signature = signRequest(daemon.keys, {
|
|
322
|
+
endpoint: "claim",
|
|
323
|
+
runnerId: daemon.runnerId,
|
|
324
|
+
issuedAt: Date.now(),
|
|
325
|
+
body
|
|
326
|
+
});
|
|
327
|
+
const response = await target.fetch(
|
|
328
|
+
new Request(`${target.origin}/byollm/claim`, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
headers: {
|
|
331
|
+
"content-type": "application/json",
|
|
332
|
+
"x-byollm-runner": signature.runnerId,
|
|
333
|
+
"x-byollm-issued-at": String(signature.issuedAt),
|
|
334
|
+
"x-byollm-signature": signature.signature
|
|
335
|
+
},
|
|
336
|
+
body
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
if (response.status !== 200) {
|
|
340
|
+
throw new Error(`claim answered ${String(response.status)}`);
|
|
341
|
+
}
|
|
342
|
+
return (await response.json()).jobs;
|
|
343
|
+
}
|
|
312
344
|
async function releaseLease(target, daemon, jobId, leaseId) {
|
|
313
345
|
const body = JSON.stringify({
|
|
314
346
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -383,7 +415,11 @@ async function postResult(target, daemon, input) {
|
|
|
383
415
|
const keys = await daemon.identityKeys();
|
|
384
416
|
const sealer = input.sealWith ?? keys;
|
|
385
417
|
const envelope = await seal({
|
|
386
|
-
|
|
418
|
+
// `{ outcome, ran }` — cloud_008 §2.5.
|
|
419
|
+
plaintext: JSON.stringify({
|
|
420
|
+
outcome: input.outcome,
|
|
421
|
+
ran: { model: "test-model", backendClass: "http", durationMs: 1 }
|
|
422
|
+
}),
|
|
387
423
|
senderKeys: sealer,
|
|
388
424
|
recipientEncryptionPublic: daemon.sitePinned.encryption,
|
|
389
425
|
context: {
|
|
@@ -401,11 +437,9 @@ async function postResult(target, daemon, input) {
|
|
|
401
437
|
protocolVersion: PROTOCOL_VERSION,
|
|
402
438
|
runnerId: daemon.runnerId,
|
|
403
439
|
jobId: input.jobId,
|
|
440
|
+
leaseId: input.leaseId,
|
|
404
441
|
envelope,
|
|
405
|
-
disposition: input.disposition ?? input.outcome.outcome
|
|
406
|
-
model: "conformance-model",
|
|
407
|
-
backendClass: "http",
|
|
408
|
-
durationMs: 1
|
|
442
|
+
disposition: input.disposition ?? input.outcome.outcome
|
|
409
443
|
});
|
|
410
444
|
const signature = signRequest(daemon.keys, {
|
|
411
445
|
endpoint: "result",
|
|
@@ -562,6 +596,24 @@ var CHECKS = [
|
|
|
562
596
|
daemon.backend.seen.length > before,
|
|
563
597
|
"the advertised kind never reached the backend"
|
|
564
598
|
);
|
|
599
|
+
const chat = await target.enqueue({
|
|
600
|
+
kind: "llm.chat",
|
|
601
|
+
payload: { messages: [{ role: "user", content: "not for you" }] },
|
|
602
|
+
owner: "alice"
|
|
603
|
+
});
|
|
604
|
+
const generateOnly = await claimRaw(target, daemon, [
|
|
605
|
+
{
|
|
606
|
+
kind: "llm.generate",
|
|
607
|
+
backendId: "openai-http",
|
|
608
|
+
backendClass: "http",
|
|
609
|
+
model: "echo-model",
|
|
610
|
+
offerScope: "self"
|
|
611
|
+
}
|
|
612
|
+
]);
|
|
613
|
+
assert(
|
|
614
|
+
!generateOnly.some((offered) => offered.id === chat.id),
|
|
615
|
+
"a server offered `llm.chat` to a claim advertising only `llm.generate`"
|
|
616
|
+
);
|
|
565
617
|
} finally {
|
|
566
618
|
await daemon.dispose();
|
|
567
619
|
}
|
|
@@ -579,7 +631,7 @@ var CHECKS = [
|
|
|
579
631
|
owner: "alice"
|
|
580
632
|
});
|
|
581
633
|
dead.backend.hangMs = 6e4;
|
|
582
|
-
await dead
|
|
634
|
+
const firstLease = await claimOne(target, dead);
|
|
583
635
|
await waitFor(
|
|
584
636
|
async () => {
|
|
585
637
|
const state = await target.job(job.id);
|
|
@@ -594,12 +646,43 @@ var CHECKS = [
|
|
|
594
646
|
label: "alive"
|
|
595
647
|
});
|
|
596
648
|
try {
|
|
597
|
-
await alive
|
|
598
|
-
|
|
599
|
-
|
|
649
|
+
const reclaimed = await claimOne(target, alive);
|
|
650
|
+
assert(
|
|
651
|
+
reclaimed.id === job.id,
|
|
652
|
+
"the reclaiming daemon did not get the job"
|
|
653
|
+
);
|
|
654
|
+
const late = await postResult(target, dead, {
|
|
655
|
+
jobId: job.id,
|
|
656
|
+
leaseId: firstLease.lease.id,
|
|
657
|
+
outcome: { outcome: "ok", text: "from the machine that vanished" }
|
|
600
658
|
});
|
|
659
|
+
const lateBody = await late.json().catch(() => ({}));
|
|
660
|
+
assert(
|
|
661
|
+
lateBody.accepted !== true,
|
|
662
|
+
"a site accepted a result from a runner whose lease had lapsed"
|
|
663
|
+
);
|
|
664
|
+
const midflight = await target.job(job.id);
|
|
665
|
+
assert(
|
|
666
|
+
!midflight?.outcome,
|
|
667
|
+
"a lapsed holder's result was recorded over a live grant"
|
|
668
|
+
);
|
|
669
|
+
const proper = await postResult(target, alive, {
|
|
670
|
+
jobId: job.id,
|
|
671
|
+
leaseId: reclaimed.lease.id,
|
|
672
|
+
outcome: { outcome: "ok", text: "from the machine that took over" }
|
|
673
|
+
});
|
|
674
|
+
assert(
|
|
675
|
+
proper.status === 200,
|
|
676
|
+
`the reclaiming daemon could not finish the job (${String(proper.status)})`
|
|
677
|
+
);
|
|
678
|
+
const final = await target.job(job.id);
|
|
679
|
+
assert(
|
|
680
|
+
final?.outcome?.text === "from the machine that took over",
|
|
681
|
+
"the reclaimed job did not record the current holder's result"
|
|
682
|
+
);
|
|
601
683
|
} finally {
|
|
602
684
|
await alive.dispose();
|
|
685
|
+
await dead.dispose();
|
|
603
686
|
}
|
|
604
687
|
}
|
|
605
688
|
},
|
|
@@ -667,12 +750,10 @@ var CHECKS = [
|
|
|
667
750
|
(await target.job(refused.id))?.state !== "ok",
|
|
668
751
|
"a named job ran without the daemon's local allowlist admitting it"
|
|
669
752
|
);
|
|
670
|
-
const
|
|
671
|
-
await bob.runner.tick();
|
|
672
|
-
await sleep(50);
|
|
753
|
+
const reoffered = await claimRaw(target, bob);
|
|
673
754
|
assert(
|
|
674
|
-
|
|
675
|
-
"a
|
|
755
|
+
!reoffered.some((job) => job.id === refused.id),
|
|
756
|
+
"a server re-offered a job to the runner that refused it"
|
|
676
757
|
);
|
|
677
758
|
await bob.allowlist.add(
|
|
678
759
|
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
@@ -744,7 +825,12 @@ var CHECKS = [
|
|
|
744
825
|
{
|
|
745
826
|
id: "C008_REVOCATION",
|
|
746
827
|
title: "a revoked daemon stops mid-queue",
|
|
747
|
-
|
|
828
|
+
// Both halves, and this check already proved both: the daemon learns it
|
|
829
|
+
// is revoked (`REVOCATION_HONORED`), *and* the upstream leaves the job
|
|
830
|
+
// queued rather than granting it (`REVOCATION_IMMEDIATE`). The second
|
|
831
|
+
// assertion was here and cited nothing — which is how a MUST comes to be
|
|
832
|
+
// declared in a spec, absent from the registry, and tested all along.
|
|
833
|
+
musts: ["REVOCATION_HONORED", "REVOCATION_IMMEDIATE"],
|
|
748
834
|
async run(target) {
|
|
749
835
|
const daemon = await pairDaemon(target, { owner: "alice" });
|
|
750
836
|
try {
|
|
@@ -809,32 +895,60 @@ var CHECKS = [
|
|
|
809
895
|
payload: prompt("once"),
|
|
810
896
|
owner: "alice"
|
|
811
897
|
});
|
|
812
|
-
await daemon
|
|
813
|
-
|
|
814
|
-
|
|
898
|
+
const claimed = await claimOne(target, daemon);
|
|
899
|
+
assert(claimed.id === job.id, "the harness could not claim its job");
|
|
900
|
+
const first = await postResult(target, daemon, {
|
|
901
|
+
jobId: job.id,
|
|
902
|
+
leaseId: claimed.lease.id,
|
|
903
|
+
outcome: { outcome: "ok", text: "the answer that counts" }
|
|
815
904
|
});
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
905
|
+
assert(
|
|
906
|
+
first.status === 200,
|
|
907
|
+
`a site refused the first result (${String(first.status)})`
|
|
908
|
+
);
|
|
909
|
+
const replay = await postResult(target, daemon, {
|
|
910
|
+
jobId: job.id,
|
|
911
|
+
leaseId: claimed.lease.id,
|
|
912
|
+
outcome: { outcome: "ok", text: "SECOND ANSWER" }
|
|
913
|
+
});
|
|
914
|
+
assert(
|
|
915
|
+
replay.status === 200,
|
|
916
|
+
`a replayed result was rejected rather than ignored (${String(replay.status)})`
|
|
917
|
+
);
|
|
918
|
+
const body = await replay.json();
|
|
919
|
+
assert(
|
|
920
|
+
body.accepted === false,
|
|
921
|
+
"a site reported a replayed result as newly accepted"
|
|
922
|
+
);
|
|
923
|
+
assert(
|
|
924
|
+
body.duplicate === true,
|
|
925
|
+
"a replay from the device that finished the job was not called a duplicate"
|
|
831
926
|
);
|
|
832
|
-
void response;
|
|
833
927
|
const after = await target.job(job.id);
|
|
834
928
|
assert(
|
|
835
|
-
after?.outcome?.text ===
|
|
836
|
-
|
|
929
|
+
after?.outcome?.text === "the answer that counts",
|
|
930
|
+
`a second result overwrote the first (${String(after?.outcome?.text)})`
|
|
837
931
|
);
|
|
932
|
+
const stranger = await pairDaemon(target, { owner: "alice" });
|
|
933
|
+
try {
|
|
934
|
+
const foreign = await postResult(target, stranger, {
|
|
935
|
+
jobId: job.id,
|
|
936
|
+
leaseId: claimed.lease.id,
|
|
937
|
+
outcome: { outcome: "ok", text: "not this device's to answer" }
|
|
938
|
+
});
|
|
939
|
+
const foreignBody = await foreign.json().catch(() => ({}));
|
|
940
|
+
assert(
|
|
941
|
+
foreignBody["duplicate"] !== true,
|
|
942
|
+
"a site told a device that never held this job it was a duplicate"
|
|
943
|
+
);
|
|
944
|
+
const stillFirst = await target.job(job.id);
|
|
945
|
+
assert(
|
|
946
|
+
stillFirst?.outcome?.text === "the answer that counts",
|
|
947
|
+
"a stranger's result overwrote a terminal job"
|
|
948
|
+
);
|
|
949
|
+
} finally {
|
|
950
|
+
await stranger.dispose();
|
|
951
|
+
}
|
|
838
952
|
} finally {
|
|
839
953
|
await daemon.dispose();
|
|
840
954
|
}
|
|
@@ -949,7 +1063,11 @@ var CHECKS = [
|
|
|
949
1063
|
{
|
|
950
1064
|
id: "C014_RESULT_PROVENANCE",
|
|
951
1065
|
title: "a community result arrives marked untrusted, a self result does not",
|
|
952
|
-
|
|
1066
|
+
// `PROVENANCE_NAMES_DEVICE` supersedes `RESULT_PROVENANCE` — a
|
|
1067
|
+
// strengthening rather than a rename. C030 is the other half: a label
|
|
1068
|
+
// means nothing unless a result whose signature does not verify against
|
|
1069
|
+
// the granted device is refused rather than recorded.
|
|
1070
|
+
musts: ["PROVENANCE_NAMES_DEVICE"],
|
|
953
1071
|
async run(target) {
|
|
954
1072
|
const bob = await pairDaemon(target, { owner: "bob", offer: "public" });
|
|
955
1073
|
try {
|
|
@@ -1031,7 +1149,13 @@ var CHECKS = [
|
|
|
1031
1149
|
{
|
|
1032
1150
|
id: "C016_UNAUTHENTICATED_REFUSED",
|
|
1033
1151
|
title: "the protocol endpoints refuse an unknown token",
|
|
1034
|
-
|
|
1152
|
+
// `CONSENT_BEFORE_ROUTE` on this plane. A relay has a consent record; a
|
|
1153
|
+
// direct site has pairing, and it is the same obligation — an upstream
|
|
1154
|
+
// routes to a device it has a record binding, and there is no discovery
|
|
1155
|
+
// path by which an unbound device receives work. Every endpoint is
|
|
1156
|
+
// checked rather than just `claim`, which is what makes it the absence
|
|
1157
|
+
// of a path rather than the absence of one door.
|
|
1158
|
+
musts: ["PAIR_ONE_USER", "CONSENT_BEFORE_ROUTE"],
|
|
1035
1159
|
async run(target) {
|
|
1036
1160
|
for (const endpoint of ["claim", "heartbeat", "result", "release"]) {
|
|
1037
1161
|
const response = await target.fetch(
|
|
@@ -1054,7 +1178,15 @@ var CHECKS = [
|
|
|
1054
1178
|
{
|
|
1055
1179
|
id: "C017_METERED_DEFAULTS_SELF",
|
|
1056
1180
|
title: "a paid backend is not shared until its owner says so, with a ceiling",
|
|
1057
|
-
|
|
1181
|
+
// `EFFECTIVE_OFFER_ONLY` too: bob asks for `public`, what reaches the
|
|
1182
|
+
// server is `self`, and the server acts on what it was told rather than
|
|
1183
|
+
// on what was wanted. That *is* the effective-offer rule, proved here
|
|
1184
|
+
// without being named.
|
|
1185
|
+
musts: [
|
|
1186
|
+
"METERED_DEFAULTS_SELF",
|
|
1187
|
+
"COST_NOT_CONFIGURABLE",
|
|
1188
|
+
"EFFECTIVE_OFFER_ONLY"
|
|
1189
|
+
],
|
|
1058
1190
|
async run(target) {
|
|
1059
1191
|
const bob = await pairDaemon(target, {
|
|
1060
1192
|
owner: "bob",
|
|
@@ -1471,7 +1603,7 @@ var CHECKS = [
|
|
|
1471
1603
|
};
|
|
1472
1604
|
const pending = await poll();
|
|
1473
1605
|
assert(
|
|
1474
|
-
pending["
|
|
1606
|
+
pending["sites"] === void 0,
|
|
1475
1607
|
"a pending poll disclosed the site's keys before anyone approved"
|
|
1476
1608
|
);
|
|
1477
1609
|
await target.approvePairing(pairing.userCode, "alice");
|
|
@@ -1480,8 +1612,19 @@ var CHECKS = [
|
|
|
1480
1612
|
approved["status"] === "approved",
|
|
1481
1613
|
`poll after approval said "${String(approved["status"])}"`
|
|
1482
1614
|
);
|
|
1483
|
-
const
|
|
1484
|
-
assert(
|
|
1615
|
+
const offered = approved["sites"];
|
|
1616
|
+
assert(
|
|
1617
|
+
typeof offered === "object" && offered !== null,
|
|
1618
|
+
"the approval carried no sites to pin"
|
|
1619
|
+
);
|
|
1620
|
+
const parsed = Object.values(offered).map(
|
|
1621
|
+
(value) => PublicIdentity.safeParse(value)
|
|
1622
|
+
);
|
|
1623
|
+
assert(
|
|
1624
|
+
parsed.length > 0 && parsed.every((entry) => entry.success),
|
|
1625
|
+
"the approval carried no usable site identity"
|
|
1626
|
+
);
|
|
1627
|
+
const site = parsed[0];
|
|
1485
1628
|
assert(
|
|
1486
1629
|
verifyPublicIdentity(site.data),
|
|
1487
1630
|
"the site's encryption key is not signed by the identity it presented"
|
|
@@ -1754,7 +1897,11 @@ var CHECKS = [
|
|
|
1754
1897
|
{
|
|
1755
1898
|
id: "C030_SITE_REFUSES_UNSIGNED_RESULTS",
|
|
1756
1899
|
title: "a site refuses a result not signed by the device that ran it",
|
|
1757
|
-
|
|
1900
|
+
// The proof-of-possession half of `PROVENANCE_NAMES_DEVICE`: attribution
|
|
1901
|
+
// by a signature that verifies against the device the lease was granted
|
|
1902
|
+
// to, rather than by a key id carried beside the result. Carrying an id
|
|
1903
|
+
// is not proving possession, and a forger writes whatever it likes.
|
|
1904
|
+
musts: ["ENVELOPE_SEALED_AND_SIGNED", "PROVENANCE_NAMES_DEVICE"],
|
|
1758
1905
|
async run(target) {
|
|
1759
1906
|
const daemon = await pairDaemon(target, { owner: "alice" });
|
|
1760
1907
|
try {
|
|
@@ -1768,6 +1915,7 @@ var CHECKS = [
|
|
|
1768
1915
|
const relay = generateKeys(Date.now());
|
|
1769
1916
|
const forged = await postResult(target, daemon, {
|
|
1770
1917
|
jobId: job.id,
|
|
1918
|
+
leaseId: claimed.lease.id,
|
|
1771
1919
|
outcome: { outcome: "ok", text: "an answer the device never gave" },
|
|
1772
1920
|
sealWith: relay
|
|
1773
1921
|
});
|
|
@@ -1782,6 +1930,7 @@ var CHECKS = [
|
|
|
1782
1930
|
);
|
|
1783
1931
|
const real = await postResult(target, daemon, {
|
|
1784
1932
|
jobId: job.id,
|
|
1933
|
+
leaseId: claimed.lease.id,
|
|
1785
1934
|
outcome: { outcome: "ok", text: "the genuine answer" }
|
|
1786
1935
|
});
|
|
1787
1936
|
assert(
|
|
@@ -1790,6 +1939,7 @@ var CHECKS = [
|
|
|
1790
1939
|
);
|
|
1791
1940
|
const lying = await postResult(target, daemon, {
|
|
1792
1941
|
jobId: job.id,
|
|
1942
|
+
leaseId: claimed.lease.id,
|
|
1793
1943
|
outcome: {
|
|
1794
1944
|
outcome: "error",
|
|
1795
1945
|
code: "backend-error",
|
|
@@ -1806,11 +1956,98 @@ var CHECKS = [
|
|
|
1806
1956
|
await daemon.dispose();
|
|
1807
1957
|
}
|
|
1808
1958
|
}
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
id: "C032_SERVER_REFUSES_TO_OFFER",
|
|
1962
|
+
title: "a claim is not answered with work the claimer may not run",
|
|
1963
|
+
musts: ["AUDIENCE_BOTH_SIDES"],
|
|
1964
|
+
async run(target) {
|
|
1965
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "public" });
|
|
1966
|
+
try {
|
|
1967
|
+
const priv = await target.enqueue({
|
|
1968
|
+
kind: "llm.generate",
|
|
1969
|
+
payload: prompt("alice's own machines only"),
|
|
1970
|
+
owner: "alice",
|
|
1971
|
+
audience: "self"
|
|
1972
|
+
});
|
|
1973
|
+
const offered = await claimRaw(target, bob);
|
|
1974
|
+
assert(
|
|
1975
|
+
!offered.some((job) => job.id === priv.id),
|
|
1976
|
+
"a server offered a `self` job to a device its owner does not own"
|
|
1977
|
+
);
|
|
1978
|
+
const shared = await target.enqueue({
|
|
1979
|
+
kind: "llm.generate",
|
|
1980
|
+
payload: prompt("anyone may run this"),
|
|
1981
|
+
owner: "alice",
|
|
1982
|
+
audience: "public"
|
|
1983
|
+
});
|
|
1984
|
+
const second = await claimRaw(target, bob);
|
|
1985
|
+
assert(
|
|
1986
|
+
second.some((job) => job.id === shared.id),
|
|
1987
|
+
"a server withheld a `public` job from a public-offering device"
|
|
1988
|
+
);
|
|
1989
|
+
} finally {
|
|
1990
|
+
await bob.dispose();
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
id: "C031_ROSTER_NOT_DISCLOSED",
|
|
1996
|
+
title: "a claimed stub carries no list of who may run the job",
|
|
1997
|
+
musts: ["ROSTER_NOT_DISCLOSED"],
|
|
1998
|
+
async run(target) {
|
|
1999
|
+
const daemon = await pairDaemon(target, { owner: "alice" });
|
|
2000
|
+
try {
|
|
2001
|
+
const job = await target.enqueue({
|
|
2002
|
+
kind: "llm.generate",
|
|
2003
|
+
payload: prompt("who else is on this roster"),
|
|
2004
|
+
owner: "alice",
|
|
2005
|
+
audience: "named",
|
|
2006
|
+
// The site restricts the job to people who are not this daemon's
|
|
2007
|
+
// owner. A stub that carried the list would be handing a routing
|
|
2008
|
+
// party the membership of alice's group.
|
|
2009
|
+
audienceAllow: ["alice", "carol", "erin"]
|
|
2010
|
+
});
|
|
2011
|
+
const claimed = await claimOne(target, daemon);
|
|
2012
|
+
assert(
|
|
2013
|
+
claimed.id === job.id,
|
|
2014
|
+
"the harness could not claim its own named job"
|
|
2015
|
+
);
|
|
2016
|
+
const asRecord = claimed;
|
|
2017
|
+
assert(
|
|
2018
|
+
asRecord["audienceAllow"] === void 0,
|
|
2019
|
+
"a claimed stub carried audienceAllow"
|
|
2020
|
+
);
|
|
2021
|
+
const parsed = ClaimedStub.safeParse(claimed);
|
|
2022
|
+
assert(
|
|
2023
|
+
parsed.success,
|
|
2024
|
+
"the claim response is not a valid stub, so its fields prove nothing"
|
|
2025
|
+
);
|
|
2026
|
+
const wire = JSON.stringify(claimed);
|
|
2027
|
+
for (const member of ["carol", "erin"]) {
|
|
2028
|
+
assert(
|
|
2029
|
+
!wire.includes(member),
|
|
2030
|
+
`a claimed stub disclosed roster member "${member}"`
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
assert(
|
|
2034
|
+
claimed.audience === "named",
|
|
2035
|
+
"the stub lost the audience routing decides on"
|
|
2036
|
+
);
|
|
2037
|
+
assert(
|
|
2038
|
+
typeof claimed.owner === "string" && claimed.owner.length > 0,
|
|
2039
|
+
"the stub lost the owner"
|
|
2040
|
+
);
|
|
2041
|
+
} finally {
|
|
2042
|
+
await daemon.dispose();
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
1809
2045
|
}
|
|
1810
2046
|
];
|
|
1811
2047
|
|
|
1812
2048
|
// src/certify.ts
|
|
1813
2049
|
import {
|
|
2050
|
+
kindsOf,
|
|
1814
2051
|
MUSTS,
|
|
1815
2052
|
MUST_IDS,
|
|
1816
2053
|
mustsVerifiedBy
|
|
@@ -1854,7 +2091,7 @@ function uncoveredMusts(checks = CHECKS) {
|
|
|
1854
2091
|
return mustsVerifiedBy("conformance").filter((id) => !covered.has(id));
|
|
1855
2092
|
}
|
|
1856
2093
|
function miscoveredMusts(checks = CHECKS) {
|
|
1857
|
-
return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => MUSTS[id].
|
|
2094
|
+
return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => !kindsOf(MUSTS[id]).includes("conformance")).sort();
|
|
1858
2095
|
}
|
|
1859
2096
|
var VERIFICATION_NOTE = "(`adversarial` = proved by the reference daemon's own suites; `construction` = true by code shape; `operator` = a deployment claim, verifiable only by audit or source. None is asserted by this run.)";
|
|
1860
2097
|
function formatReport(report) {
|
|
@@ -1882,13 +2119,13 @@ function formatReport(report) {
|
|
|
1882
2119
|
}
|
|
1883
2120
|
}
|
|
1884
2121
|
const elsewhere = MUST_IDS.filter(
|
|
1885
|
-
(id) => MUSTS[id].
|
|
2122
|
+
(id) => !kindsOf(MUSTS[id]).includes("conformance")
|
|
1886
2123
|
);
|
|
1887
2124
|
if (elsewhere.length > 0) {
|
|
1888
2125
|
lines.push("");
|
|
1889
2126
|
lines.push(" Verified elsewhere, not by this kit:");
|
|
1890
2127
|
for (const kind of ["adversarial", "construction", "operator"]) {
|
|
1891
|
-
const ids = elsewhere.filter((id) => MUSTS[id].
|
|
2128
|
+
const ids = elsewhere.filter((id) => kindsOf(MUSTS[id]).includes(kind));
|
|
1892
2129
|
if (ids.length === 0) continue;
|
|
1893
2130
|
lines.push(` ${kind}: ${ids.join(", ")}`);
|
|
1894
2131
|
}
|
|
@@ -1911,4 +2148,4 @@ export {
|
|
|
1911
2148
|
miscoveredMusts,
|
|
1912
2149
|
formatReport
|
|
1913
2150
|
};
|
|
1914
|
-
//# sourceMappingURL=chunk-
|
|
2151
|
+
//# sourceMappingURL=chunk-SWXTCMPN.js.map
|