@byollm/protocol 0.1.0-alpha.8 → 0.1.0-alpha.80
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/ABOUT-SHORT.md +7 -0
- package/ABOUT.md +60 -0
- package/README.md +106 -4
- package/dist/index.d.ts +1130 -132
- package/dist/index.js +1411 -329
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audience.ts","../src/backends.ts","../src/kinds.ts","../src/job.ts","../src/envelope.ts","../src/keys.ts","../src/signing.ts","../src/musts.ts","../src/wire.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { type BackendCost } from \"./backends.js\";\n\n/**\n * Who may run a job, declared by the app that enqueued it.\n *\n * - `self` — only the job owner's own daemon.\n * - `named` — a daemon whose owner has explicitly allowed this (server, user)\n * pair in their *local* allowlist (byollm_001 Rev 1 §B).\n * - `public` — any daemon offering `public` compute.\n */\nexport const Audience = z.enum([\"self\", \"named\", \"public\"]);\nexport type Audience = z.infer<typeof Audience>;\n\n/**\n * What a daemon backend is willing to run, declared by the machine's owner.\n * Same three values as {@link Audience}, but the two are independent axes —\n * a job runs only where both agree ({@link MUSTS.AUDIENCE_BOTH_SIDES}).\n */\nexport const OfferScope = z.enum([\"self\", \"named\", \"public\"]);\nexport type OfferScope = z.infer<typeof OfferScope>;\n\n/** All audience values, in widening order. */\nexport const AUDIENCES = Object.freeze(Audience.options);\n/** All offer scopes, in widening order. */\nexport const OFFER_SCOPES = Object.freeze(OfferScope.options);\n\n/**\n * Why a job was refused. Distinct codes because byollm_002 requires that\n * different truths never share a message — \"no matching work\" and \"refused on\n * principle\" are not the same event, and a volunteer debugging their setup\n * needs to know which one happened.\n */\nexport const MatchRefusal = z.enum([\n /** The daemon advertises no capability for this kind. */\n \"no-capability\",\n /** Job is `self` but this daemon belongs to a different user. */\n \"audience-self-other-owner\",\n /** Job is `named` but this daemon's local allowlist does not admit the owner. */\n \"not-locally-allowed\",\n /** Job is `named`/`public` but the server's own allowlist excludes this runner. */\n \"not-in-server-allowlist\",\n /** The backend offers only `self` and the job belongs to someone else. */\n \"offer-scope-too-narrow\",\n /** The matched backend is subscription-class, which is locked to `self`. */\n \"subscription-self-lock\",\n /** The backend spends the owner's money and they have not agreed to share it. */\n \"metered-no-spend-consent\",\n /** The backend is shared but has spent its ceiling for now. */\n \"metered-ceiling-reached\",\n]);\nexport type MatchRefusal = z.infer<typeof MatchRefusal>;\n\n/** The outcome of an audience match. */\nexport type MatchResult =\n | { readonly ok: true }\n | { readonly ok: false; readonly refusal: MatchRefusal };\n\nconst ALLOWED: MatchResult = Object.freeze({ ok: true as const });\nconst refuse = (refusal: MatchRefusal): MatchResult =>\n Object.freeze({ ok: false as const, refusal });\n\n/** What the owner has agreed to spend on other people's work, if anything. */\nexport interface SpendConsent {\n /** The owner explicitly acknowledged that sharing this backend costs money. */\n readonly acknowledged: boolean;\n /** Their ceiling. Absent means no ceiling was set, which is not consent. */\n readonly ceilingReached?: boolean;\n}\n\n/**\n * The effective offer scope of a backend.\n *\n * Three rules, applied at the one place both the daemon's config loader and\n * its matcher call, so no code path can observe a scope wider than the cost\n * class allows:\n *\n * - `subscription` is locked to `self` regardless of config\n * ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}) — someone else's terms.\n * - `metered` narrows to `self` unless the owner has explicitly acknowledged\n * the spend ({@link MUSTS.METERED_DEFAULTS_SELF}) — their money.\n * - `free` passes through — their electricity.\n *\n * Note the asymmetry: subscription can never be widened, metered can be\n * widened deliberately. Conflating those was byollm_007's bug.\n */\nexport function effectiveOfferScope(\n configured: OfferScope,\n cost: BackendCost,\n spend?: SpendConsent,\n): OfferScope {\n if (cost === \"subscription\") return \"self\";\n if (cost === \"metered\" && spend?.acknowledged !== true) return \"self\";\n return configured;\n}\n\n/** The job-side facts a match needs. */\nexport interface MatchJob {\n /** The app's id for the user who enqueued the job. */\n readonly owner: string;\n /** Who the app says may run it. */\n readonly audience: Audience;\n /**\n * Optional server-side restriction on which runner owners may take a\n * `named` job. Defence in depth only — the daemon's local allowlist is the\n * enforcing side ({@link MUSTS.NAMED_LOCAL_ALLOWLIST}).\n */\n readonly audienceAllow?: readonly string[] | undefined;\n}\n\n/** The daemon-side facts a match needs. */\nexport interface MatchDaemon {\n /** The app's id for the user this daemon is paired to. */\n readonly owner: string;\n /** Effective scope of the backend that would run the job. */\n readonly offerScope: OfferScope;\n /** Who pays for that backend's tokens. */\n readonly cost: BackendCost;\n /** What the owner agreed to spend on others, for a `metered` backend. */\n readonly spend?: SpendConsent | undefined;\n /**\n * Does this daemon's *local* allowlist admit the given owner for the server\n * origin the job came from? Supplied as a predicate so the protocol package\n * stays free of file I/O; the daemon passes its allowlist, the server\n * passes a conservative `() => true` because it cannot know a remote\n * daemon's local list and must not pretend to.\n */\n readonly locallyAllows: (owner: string) => boolean;\n}\n\n/**\n * Decide whether a job may run on a daemon.\n *\n * Both sides must agree ({@link MUSTS.AUDIENCE_BOTH_SIDES}):\n * 1. the job's audience must admit the daemon's owner, and\n * 2. the backend's offer scope must admit the job's owner.\n *\n * The full nine-way matrix (three audiences × three offer scopes) is asserted\n * by the conformance kit. The function is pure and total so both the daemon\n * and the server can run the identical rule — the daemon refuses, and the\n * server refuses too (byollm_003 §Server-side MUSTs).\n *\n * @example\n * ```ts\n * const result = matchAudience(\n * { owner: \"alice\", audience: \"named\" },\n * {\n * owner: \"bob\",\n * offerScope: \"named\",\n * cost: \"free\",\n * locallyAllows: (o) => o === \"alice\",\n * },\n * );\n * // result.ok === true\n * ```\n */\nexport function matchAudience(job: MatchJob, daemon: MatchDaemon): MatchResult {\n const sameOwner = job.owner === daemon.owner;\n\n // --- Side 1: does the job's audience admit this daemon's owner? --------\n if (job.audience === \"self\" && !sameOwner) {\n return refuse(\"audience-self-other-owner\");\n }\n if (\n job.audience === \"named\" &&\n !sameOwner &&\n job.audienceAllow !== undefined &&\n !job.audienceAllow.includes(daemon.owner)\n ) {\n return refuse(\"not-in-server-allowlist\");\n }\n\n // --- Side 2: does the backend's offer scope admit the job's owner? -----\n // The lock is re-applied rather than trusted: a caller that passed a\n // widened scope for a subscription backend gets a refusal, not obedience.\n const scope = effectiveOfferScope(\n daemon.offerScope,\n daemon.cost,\n daemon.spend,\n );\n\n if (sameOwner) {\n // A daemon always runs its own owner's work, at any scope.\n return ALLOWED;\n }\n\n // Order matters for the message, not the outcome: all three refuse, and a\n // volunteer debugging their setup needs to know which truth applies.\n if (daemon.cost === \"subscription\") {\n return refuse(\"subscription-self-lock\");\n }\n if (daemon.cost === \"metered\") {\n if (daemon.spend?.acknowledged !== true) {\n return refuse(\"metered-no-spend-consent\");\n }\n if (daemon.spend.ceilingReached === true) {\n return refuse(\"metered-ceiling-reached\");\n }\n }\n\n switch (scope) {\n case \"self\":\n return refuse(\"offer-scope-too-narrow\");\n case \"named\":\n // byollm_001 Rev 1 §B: the daemon's own list decides, not the server's.\n return daemon.locallyAllows(job.owner)\n ? ALLOWED\n : refuse(\"not-locally-allowed\");\n case \"public\":\n return ALLOWED;\n }\n}\n\n/**\n * Human-readable refusal text for the daemon's log and the trust UI.\n * Each refusal reads as a distinct truth — byollm_002's \"four different\n * truths that must never share a message\" applied to the audience axis.\n */\nexport const REFUSAL_MESSAGES: Readonly<Record<MatchRefusal, string>> =\n Object.freeze({\n \"no-capability\":\n \"no backend on this machine is configured and healthy for that job kind\",\n \"audience-self-other-owner\":\n \"the job is private to its owner and this machine is paired to someone else\",\n \"not-locally-allowed\":\n \"the job's owner is not on this machine's allowlist (byollm allow <server> <user>)\",\n \"not-in-server-allowlist\":\n \"the app restricted this job to named runners and this machine is not one of them\",\n \"offer-scope-too-narrow\":\n \"this backend is offered to its owner only (byollm offer <backend> named|public to widen)\",\n \"subscription-self-lock\":\n \"subscription-backed models run their owner's work only — this is a protocol rule, not a setting\",\n \"metered-no-spend-consent\":\n \"this backend bills its owner per token, and they have not agreed to spend it on other people's work\",\n \"metered-ceiling-reached\":\n \"this backend is shared but has reached the spend ceiling its owner set\",\n });\n","import { z } from \"zod\";\n\n/**\n * How a backend reaches its model — the taxonomy introduced in byollm_001\n * Rev 1 §A, because the two classes have different threat surfaces.\n *\n * - `http`: an OpenAI-compatible HTTP server (Ollama, `mlx_lm.server`,\n * llama.cpp server, vLLM, and every hosted provider that speaks the same\n * wire format). Spawns nothing, so byollm_004 §2's argv, stdin, env and\n * sandbox requirements are not applicable by construction. Its threat\n * surface is SSRF-shaped and bounded by {@link MUSTS.HTTP_BASE_URL_SAFE}.\n * - `process`: spawns a binary (`claude` CLI today, `mlx_lm.lora` for a\n * future `train.*` kind). All of byollm_004 §2 is mandatory here.\n */\nexport const BackendClass = z.enum([\"http\", \"process\"]);\nexport type BackendClass = z.infer<typeof BackendClass>;\n\n/**\n * Who pays, and how — byollm_007.\n *\n * This replaced a two-valued `account` field that conflated two unrelated\n * constraints and, in doing so, left a hole: `openai-http` was \"open\", but it\n * accepts an API key, so an owner could point it at a paid endpoint, offer it\n * `public`, and donate their credit balance to strangers. The community\n * budgets cap job *count*, not spend.\n *\n * - `free` — local compute. Costs electricity, not money. Shareable.\n * - `metered` — per-token billing against the owner's account. Legal to\n * share and ruinous to share by accident.\n * - `subscription` — a vendor account whose terms forbid third-party work.\n * Sharing is a terms violation, not merely expensive.\n */\nexport const BackendCost = z.enum([\"free\", \"metered\", \"subscription\"]);\nexport type BackendCost = z.infer<typeof BackendCost>;\n\n/** The immutable facts about a backend that the protocol reasons over. */\nexport interface BackendDescriptor {\n /** Stable backend id, as written in `byollm.config.json`. */\n readonly id: string;\n /** Human-readable name for the trust UI. */\n readonly label: string;\n /** Determines which isolation requirements apply. */\n readonly class: BackendClass;\n /**\n * Who pays. Fixed here for every named provider and **not overridable by\n * configuration** ({@link MUSTS.COST_NOT_CONFIGURABLE}) — `openai` is\n * metered because it is, and no setting changes that.\n *\n * `null` only for the generic {@link BACKENDS.\"openai-http\"} entry, whose\n * cost is inferred from its base URL instead\n * ({@link MUSTS.REMOTE_IS_NEVER_FREE}).\n */\n readonly cost: BackendCost | null;\n /**\n * Which adversarial corpus byollm_004 §5 runs against this backend. A\n * backend cannot be registered without one — the coverage check in the\n * adversarial suite enforces it.\n */\n readonly adversarialCorpus: \"process\" | \"http\";\n /**\n * Where this provider lives, when that is knowable. Owner config may\n * override it; a provider with no default requires one to be given.\n */\n readonly defaultBaseUrl?: string;\n}\n\nconst backend = (b: BackendDescriptor): BackendDescriptor => Object.freeze(b);\n\n/**\n * The backend registry.\n *\n * **Providers are entries, not implementations.** Every HTTP-class provider\n * below shares the single `openai-http` transport, because they all speak\n * OpenAI-compatible `/v1/chat/completions`. An entry adds a stable id, a cost\n * class the owner cannot override, and a default base URL. Adding a provider\n * is therefore one line and no new code — which is why the adversarial corpus\n * still covers all of them, and why a PR adding one is reviewable at a glance.\n */\nexport const BACKENDS = Object.freeze({\n // -- free: local compute, costs electricity ------------------------------\n ollama: backend({\n id: \"ollama\",\n label: \"Ollama (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:11434/v1\",\n }),\n mlx: backend({\n id: \"mlx\",\n label: \"MLX (mlx_lm.server, local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8080/v1\",\n }),\n llamacpp: backend({\n id: \"llamacpp\",\n label: \"llama.cpp server (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8080/v1\",\n }),\n vllm: backend({\n id: \"vllm\",\n label: \"vLLM (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8000/v1\",\n }),\n lmstudio: backend({\n id: \"lmstudio\",\n label: \"LM Studio (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:1234/v1\",\n }),\n jan: backend({\n id: \"jan\",\n label: \"Jan (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:1337/v1\",\n }),\n localai: backend({\n id: \"localai\",\n label: \"LocalAI (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8080/v1\",\n }),\n\n // -- metered: the owner's money, per token -------------------------------\n /**\n * Note the pair: `anthropic` and {@link BACKENDS.\"claude-cli\"} reach the\n * same vendor and land in different cost classes. That is not an\n * inconsistency — it is the axis working. One bills a key per token, the\n * other runs under a personal plan whose terms cover one person's work. Who\n * pays and under what terms is the question; which company is not.\n */\n anthropic: backend({\n id: \"anthropic\",\n label: \"Anthropic (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.anthropic.com/v1\",\n }),\n openai: backend({\n id: \"openai\",\n label: \"OpenAI (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.openai.com/v1\",\n }),\n gemini: backend({\n id: \"gemini\",\n label: \"Google Gemini (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n }),\n grok: backend({\n id: \"grok\",\n label: \"xAI Grok (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.x.ai/v1\",\n }),\n groq: backend({\n id: \"groq\",\n label: \"Groq (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.groq.com/openai/v1\",\n }),\n openrouter: backend({\n id: \"openrouter\",\n label: \"OpenRouter (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://openrouter.ai/api/v1\",\n }),\n together: backend({\n id: \"together\",\n label: \"Together AI (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.together.xyz/v1\",\n }),\n deepseek: backend({\n id: \"deepseek\",\n label: \"DeepSeek (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.deepseek.com/v1\",\n }),\n mistral: backend({\n id: \"mistral\",\n label: \"Mistral (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.mistral.ai/v1\",\n }),\n\n // -- the escape hatch ----------------------------------------------------\n \"openai-http\": backend({\n id: \"openai-http\",\n label: \"Any OpenAI-compatible server\",\n class: \"http\",\n // Unknown until the base URL is known: local means free, remote means\n // metered, and the owner does not get to say otherwise\n // ({@link MUSTS.REMOTE_IS_NEVER_FREE}).\n cost: null,\n adversarialCorpus: \"http\",\n }),\n\n // -- subscription: someone else's terms ----------------------------------\n \"claude-cli\": backend({\n id: \"claude-cli\",\n label: \"Claude CLI (your subscription)\",\n class: \"process\",\n cost: \"subscription\",\n adversarialCorpus: \"process\",\n }),\n} as const satisfies Record<string, BackendDescriptor>);\n\n/** The id of a registered backend. */\nexport type BackendId = keyof typeof BACKENDS;\n\n/** All registered backend ids — the adversarial coverage check iterates this. */\nexport const BACKEND_IDS = Object.freeze(Object.keys(BACKENDS) as BackendId[]);\n\nexport const BackendIdSchema = z.enum(\n BACKEND_IDS as [BackendId, ...BackendId[]],\n);\n\n/** Narrow an arbitrary string to a registered backend id. */\nexport function isBackendId(value: string): value is BackendId {\n return Object.hasOwn(BACKENDS, value);\n}\n\n/**\n * Look up a backend descriptor.\n *\n * @throws if the id is not registered — an unregistered backend has no\n * adversarial corpus, so refusing is the safe direction.\n */\nexport function backendDescriptor(id: BackendId): BackendDescriptor {\n return BACKENDS[id];\n}\n\n/**\n * Is this host local enough that compute there is free?\n *\n * Loopback and the private ranges only. This is the rule that makes\n * {@link MUSTS.REMOTE_IS_NEVER_FREE} enforceable rather than a promise: an\n * owner cannot reach a paid API through the generic backend and call it free,\n * because \"free\" is derived from the address, not from what the config claims.\n *\n * **What this cannot see.** The address is all it reads. A proxy on\n * `127.0.0.1` forwarding to a paid API classes as `free` and nothing\n * downstream will contradict it. That is deliberate: standing up a relay is\n * an act by the machine's owner against their own account, and the threat\n * model here is a hostile *job*, not an owner routing around a rule that\n * exists to protect them. What this catches is the accident — a remote paid\n * endpoint offered `public` because nobody thought about the bill. See\n * `docs/security.md` §4a.\n */\nexport function isLocalHost(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/^\\[|\\]$/g, \"\");\n if (host === \"localhost\" || host.endsWith(\".localhost\")) return true;\n if (host === \"::1\") return true;\n if (host.startsWith(\"127.\")) return true;\n if (host.startsWith(\"10.\")) return true;\n if (host.startsWith(\"192.168.\")) return true;\n if (/^172\\.(1[6-9]|2\\d|3[01])\\./.test(host)) return true;\n // Unique local addresses (fc00::/7).\n if (/^f[cd]/.test(host)) return true;\n return false;\n}\n\n/**\n * The cost class of a configured backend instance.\n *\n * For every named provider this is whatever the registry says, full stop\n * ({@link MUSTS.COST_NOT_CONFIGURABLE}). For the generic `openai-http` entry\n * it is inferred from the base URL, and a base URL that cannot be parsed is\n * treated as `metered` — the expensive side, because guessing \"free\" wrong\n * costs the owner money.\n */\nexport function resolveCost(\n id: BackendId,\n baseUrl: string | undefined,\n): BackendCost {\n const declared = BACKENDS[id].cost;\n if (declared !== null) return declared;\n if (baseUrl === undefined) return \"metered\";\n try {\n return isLocalHost(new URL(baseUrl).hostname) ? \"free\" : \"metered\";\n } catch {\n return \"metered\";\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Upper bounds on payload size, enforced at the schema so oversized input is\n * refused at parse time rather than somewhere deeper.\n *\n * byollm_004 §4 requires stricter limits for community (`named`/`public`)\n * jobs; those are applied on top of these by the daemon's budget check, which\n * knows the job's audience. These are the absolute ceilings for any job.\n */\nexport const PAYLOAD_LIMITS = Object.freeze({\n /** Max characters in any single text field. */\n maxTextChars: 1_000_000,\n /** Max messages in an `llm.chat` conversation. */\n maxMessages: 256,\n /** Max characters across the whole payload. */\n maxTotalChars: 4_000_000,\n});\n\n/**\n * A conversation turn. `role` is a closed enum — it is routing *within the\n * model call*, not routing of the call, so it cannot select a backend.\n */\nexport const ChatMessage = z.object({\n role: z.enum([\"system\", \"user\", \"assistant\"]),\n content: z.string().max(PAYLOAD_LIMITS.maxTextChars),\n});\nexport type ChatMessage = z.infer<typeof ChatMessage>;\n\n/**\n * Payload for `llm.generate`.\n *\n * @remarks\n * Text only, deliberately. byollm_004 §1 states the payload is \"data handed\n * to a model, never configuration and never a command\", so v0 carries no\n * sampling parameters, no model name, no base URL and no flags — those are\n * owner-side route config. A future `params` field with an explicit closed\n * allowlist and owner-set clamps is reserved; adding a field later is\n * non-breaking, removing one is not.\n */\nexport const GeneratePayload = z\n .object({\n prompt: z.string().min(1).max(PAYLOAD_LIMITS.maxTextChars),\n system: z.string().max(PAYLOAD_LIMITS.maxTextChars).optional(),\n })\n .strict();\nexport type GeneratePayload = z.infer<typeof GeneratePayload>;\n\n/** Payload for `llm.chat`. Text only, for the same reason as {@link GeneratePayload}. */\nexport const ChatPayload = z\n .object({\n messages: z.array(ChatMessage).min(1).max(PAYLOAD_LIMITS.maxMessages),\n system: z.string().max(PAYLOAD_LIMITS.maxTextChars).optional(),\n })\n .strict();\nexport type ChatPayload = z.infer<typeof ChatPayload>;\n\n/**\n * The job kinds a v1 daemon has handlers for.\n *\n * Kinds are resolved against handlers baked into the daemon\n * ({@link MUSTS.KIND_TYPED_ONLY}); an unknown kind is refused, never guessed.\n * Adding a kind is a protocol change with its own spec and threat review —\n * notably any kind that needs tools, which byollm_004 §2 forbids as a payload\n * flag.\n */\nexport const JobKind = z.enum([\"llm.generate\", \"llm.chat\"]);\nexport type JobKind = z.infer<typeof JobKind>;\n\n/** All v1 job kinds. */\nexport const JOB_KINDS = Object.freeze(JobKind.options);\n\n/** A payload discriminated by its kind. */\nexport const KindedPayload = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"llm.generate\"), payload: GeneratePayload }),\n z.object({ kind: z.literal(\"llm.chat\"), payload: ChatPayload }),\n]);\nexport type KindedPayload = z.infer<typeof KindedPayload>;\n\n/** The payload type for a given kind. */\nexport type PayloadFor<K extends JobKind> = K extends \"llm.generate\"\n ? GeneratePayload\n : ChatPayload;\n\n/** Narrow an arbitrary string to a known job kind. */\nexport function isJobKind(value: string): value is JobKind {\n return (JOB_KINDS as readonly string[]).includes(value);\n}\n\n/**\n * Total character weight of a payload, used by the daemon's community budget\n * check and by the server's payload-size limits.\n */\nexport function payloadTextLength(kinded: KindedPayload): number {\n if (kinded.kind === \"llm.generate\") {\n return kinded.payload.prompt.length + (kinded.payload.system?.length ?? 0);\n }\n const messages = kinded.payload.messages.reduce(\n (sum, m) => sum + m.content.length,\n 0,\n );\n return messages + (kinded.payload.system?.length ?? 0);\n}\n","import { z } from \"zod\";\nimport { Audience } from \"./audience.js\";\nimport { BackendClass } from \"./backends.js\";\nimport { ChatPayload, GeneratePayload, JobKind } from \"./kinds.js\";\n\n/**\n * The job lifecycle, made explicit by byollm_001 Rev 1 §D because the most\n * user-visible failure mode — \"nothing is running my job\" — was previously\n * unspecified.\n *\n * ```text\n * queued ──claim──▶ claimed ──start──▶ running ──▶ ok | error | canceled\n * │ │ │\n * │ └──lease expiry─────┘\n * │ ▼\n * │ queued (reclaimable, no loss)\n * └──ttl elapsed──▶ expired\n * ```\n */\nexport const JobState = z.enum([\n \"queued\",\n \"claimed\",\n \"running\",\n \"ok\",\n \"error\",\n \"canceled\",\n \"expired\",\n]);\nexport type JobState = z.infer<typeof JobState>;\n\n/** States from which a job never moves again. */\nexport const TERMINAL_STATES = Object.freeze([\n \"ok\",\n \"error\",\n \"canceled\",\n \"expired\",\n] as const satisfies readonly JobState[]);\n\n/** Is this a state the job can never leave? */\nexport function isTerminal(state: JobState): boolean {\n return (TERMINAL_STATES as readonly JobState[]).includes(state);\n}\n\n/**\n * The legal transitions. Held as data so the store adapters and the\n * conformance kit agree on one definition rather than three implementations.\n */\nconst TRANSITIONS: Readonly<Record<JobState, readonly JobState[]>> =\n Object.freeze({\n queued: [\"claimed\", \"expired\", \"canceled\"],\n // A claimed job returns to `queued` when its lease expires un-renewed\n // ({@link MUSTS.LEASE_RECLAIMABLE}).\n claimed: [\"running\", \"queued\", \"canceled\", \"error\"],\n running: [\"ok\", \"error\", \"canceled\", \"queued\"],\n ok: [],\n error: [],\n canceled: [],\n expired: [],\n });\n\n/** May a job move from `from` to `to`? */\nexport function canTransition(from: JobState, to: JobState): boolean {\n return TRANSITIONS[from].includes(to);\n}\n\n/** A lease: the right to work on a job until `expiresAt`. */\nexport const Lease = z.object({\n /**\n * Identifies *this* grant, not just its holder.\n *\n * A runner can hold a job, release it, and claim it again — three leases,\n * one runner id. Without an id for the grant itself, a lease-scoped request\n * names a mutable target ambiguously, and a replayed release from the first\n * grant lands on the third: the job returns to the queue while the daemon\n * is mid-execution, and the work runs twice on the owner's hardware.\n *\n * That was a live hole, found in review after signed requests shipped. The\n * signature scheme's replay argument rests on endpoints being idempotent —\n * and release *is*, per lease, but not across leases, because nothing in\n * the request said which one.\n */\n id: z.string().min(1),\n /** The runner holding the lease. */\n runnerId: z.string().min(1),\n /** Epoch milliseconds after which the claim is void. */\n expiresAt: z.number().int().positive(),\n});\nexport type Lease = z.infer<typeof Lease>;\n\n/** Payload union as it appears on a job record. */\nexport const JobPayload = z.union([GeneratePayload, ChatPayload]);\nexport type JobPayload = z.infer<typeof JobPayload>;\n\n/**\n * A job as the daemon receives it from `/byollm/claim`.\n *\n * Note what is absent: no model, no backend, no base URL, no flags, no path.\n * Those come from the machine owner's config only\n * ({@link MUSTS.NO_PAYLOAD_ROUTING}). The wire shape is the first place that\n * rule is enforced — there is no field to carry them.\n */\nexport const ClaimedJob = z\n .object({\n id: z.string().min(1),\n kind: JobKind,\n payload: JobPayload,\n audience: Audience,\n /** The app's id for the user who enqueued it. */\n owner: z.string().min(1),\n /** Runner owners the app restricted a `named` job to, if any. */\n audienceAllow: z.array(z.string().min(1)).optional(),\n lease: Lease,\n })\n .strict();\nexport type ClaimedJob = z.infer<typeof ClaimedJob>;\n\n/**\n * The provenance that travels with every result to the delivery seam.\n *\n * byollm_003 Rev 1: a `named`/`public` result is attacker-controlled text.\n * The app must never render volunteer output as its own AI's answer without\n * knowing that is what it is ({@link MUSTS.RESULT_PROVENANCE}).\n */\nexport const ResultProvenance = z\n .object({\n /** The audience the job ran under. */\n audience: Audience,\n /** The runner that produced it. */\n runnerId: z.string().min(1),\n /** The runner owner's id in this app's namespace. */\n runnerOwner: z.string().min(1),\n /** Which backend class produced it — an HTTP call or a sandboxed spawn. */\n backendClass: BackendClass,\n /** The model the runner reports having used. */\n model: z.string().min(1),\n /**\n * False only for `self` jobs. When true the app MUST treat `text` as\n * untrusted third-party content.\n */\n untrusted: z.boolean(),\n })\n .strict();\nexport type ResultProvenance = z.infer<typeof ResultProvenance>;\n\n/**\n * Build provenance for a completed job. `untrusted` is derived, never\n * supplied, so no caller can mark volunteer output as first-party.\n */\nexport function provenanceFor(input: {\n audience: Audience;\n runnerId: string;\n runnerOwner: string;\n backendClass: BackendClass;\n model: string;\n}): ResultProvenance {\n return {\n audience: input.audience,\n runnerId: input.runnerId,\n runnerOwner: input.runnerOwner,\n backendClass: input.backendClass,\n model: input.model,\n untrusted: input.audience !== \"self\",\n };\n}\n\n/** Successful outcome. */\nexport const JobResultOk = z\n .object({\n outcome: z.literal(\"ok\"),\n text: z.string(),\n /** Optional reference to a stored artifact; never a local path. */\n artifactUrl: z.url().optional(),\n })\n .strict();\n\n/** Failed outcome. `code` is a stable machine string; `message` is for humans. */\nexport const JobResultError = z\n .object({\n outcome: z.literal(\"error\"),\n code: z.string().min(1),\n message: z.string().min(1),\n /** Whether the app may reasonably re-enqueue. */\n retryable: z.boolean(),\n })\n .strict();\n\n/** Cancelled outcome, reported by the daemon after honoring a cancel. */\nexport const JobResultCanceled = z\n .object({\n outcome: z.literal(\"canceled\"),\n })\n .strict();\n\nexport const JobOutcome = z.discriminatedUnion(\"outcome\", [\n JobResultOk,\n JobResultError,\n JobResultCanceled,\n]);\nexport type JobOutcome = z.infer<typeof JobOutcome>;\n\n/** A completed job as delivered to the app, provenance attached. */\nexport const DeliveredResult = z\n .object({\n jobId: z.string().min(1),\n state: JobState,\n outcome: JobOutcome.optional(),\n provenance: ResultProvenance.optional(),\n })\n .strict();\nexport type DeliveredResult = z.infer<typeof DeliveredResult>;\n\n/**\n * How big a payload is, in buckets — byollm_009 §6.\n *\n * A relay routes without reading, and matching a job to a machine needs some\n * notion of size. Buckets rather than byte counts because the exact figure is\n * a stronger fingerprint than the routing decision requires, and because a\n * bucket survives compression and encoding changes that an exact count does\n * not.\n *\n * `unbounded` exists for streamed jobs, which have no size when they start.\n * It is reserved now rather than added later: byollm_009 §8.1 — adding a\n * field to a published envelope is the v2 break all over again.\n */\nexport const SizeClass = z.enum([\"small\", \"medium\", \"large\", \"unbounded\"]);\nexport type SizeClass = z.infer<typeof SizeClass>;\n\n/** Where the bucket boundaries sit, in characters of payload text. */\nexport const SIZE_CLASS_LIMITS = Object.freeze({\n small: 4_000,\n medium: 64_000,\n large: Number.POSITIVE_INFINITY,\n});\n\n/**\n * The most a payload in this bucket can be.\n *\n * Used where a decision must be made from a stub, before the payload has been\n * fetched — a budget check, for instance. Charging the bucket's ceiling is the\n * conservative direction: it refuses slightly too eagerly rather than\n * admitting work that turns out larger than the budget allowed.\n *\n * `unbounded` returns `Infinity`, which fails every ceiling. That is correct\n * until byollm_006 defines how a streamed job is budgeted — failing closed on\n * a case nobody has designed beats inventing an allowance for it.\n */\nexport function sizeClassCeiling(sizeClass: SizeClass): number {\n if (sizeClass === \"unbounded\") return Number.POSITIVE_INFINITY;\n return SIZE_CLASS_LIMITS[sizeClass];\n}\n\n/** Bucket a payload by its text length. */\nexport function sizeClassOf(textChars: number): SizeClass {\n if (textChars <= SIZE_CLASS_LIMITS.small) return \"small\";\n if (textChars <= SIZE_CLASS_LIMITS.medium) return \"medium\";\n return \"large\";\n}\n\n/**\n * Everything an upstream may see about a job — byollm_009 §6.\n *\n * **This list is exhaustive and normative.** It is a commitment about the\n * metadata surface, not an accident of what the implementation happens to\n * send: an upstream that requires more has exceeded the protocol, and an\n * endpoint that emits more has leaked past it\n * ({@link MUSTS.STUB_METADATA_EXHAUSTIVE}).\n *\n * What is absent is the point. No payload, no model, no prompt, no result.\n * `kind` is here because capability matching happens upstream; if a later\n * revision moves matching to the daemon, `kind` moves into the ciphertext.\n */\nexport const JobStub = z\n .object({\n id: z.string().min(1),\n kind: JobKind,\n /** The app's id for the user who enqueued it. */\n owner: z.string().min(1),\n audience: Audience,\n audienceAllow: z.array(z.string().min(1)).optional(),\n sizeClass: SizeClass,\n /** Reserved for byollm_006. False until streaming exists. */\n streaming: z.boolean(),\n /** Epoch ms after which the work is pointless; bounds ciphertext retention. */\n deadlineAt: z.number().int().positive(),\n })\n .strict();\nexport type JobStub = z.infer<typeof JobStub>;\n\n/** A stub, plus the lease the claiming runner now holds for it. */\nexport const ClaimedStub = JobStub.extend({ lease: Lease }).strict();\nexport type ClaimedStub = z.infer<typeof ClaimedStub>;\n","import { createPrivateKey, createPublicKey, type KeyObject } from \"node:crypto\";\nimport sodium from \"libsodium-wrappers\";\nimport { z } from \"zod\";\nimport { signWith, verifyWith, type StoredKeys } from \"./keys.js\";\n\n/**\n * Sealed, signed envelopes — byollm_009 §6.\n *\n * ## Signed, then sealed\n *\n * An earlier draft specified a bare sealed box. That was wrong in the\n * direction that matters, and the reasoning is kept because someone will\n * propose it again: `crypto_box_seal` is **anonymous-sender by\n * construction** — it derives from an ephemeral keypair and discards the\n * secret — so the recipient can decrypt but learns nothing about who sent it.\n * Both public keys here are public by definition; the upstream distributed\n * them. Any holder of one can therefore produce an envelope that opens\n * cleanly, and a relay holds both.\n *\n * So every envelope is **signed with the sender's Ed25519 identity key, then\n * sealed to the recipient's X25519 encryption key**. The recipient opens it,\n * then verifies against the identity it pinned at consent. An envelope that\n * does not verify is refused, not run.\n *\n * Three details earn their place:\n *\n * - **Both key ids are inside the signature**, so an envelope cannot be\n * lifted from one recipient and replayed to another, nor re-signed by a\n * third party claiming authorship.\n * - **`direction` is inside it**, so a payload envelope can never be replayed\n * as a result envelope.\n * - **Sign-then-encrypt, not encrypt-then-sign.** The signature lives\n * *inside* the ciphertext, so a relay never accumulates a non-repudiable\n * record of who sent what to whom. Signing the outside would hand it\n * exactly the attestation trail `RELAY_BLIND` exists to deny.\n *\n * ## Why libsodium\n *\n * byollm_009 §2: established primitives, no novel constructions. A sealed box\n * is a specific reviewed construction — ephemeral key agreement with a\n * BLAKE2b-derived nonce — and rebuilding it from lower-level pieces is\n * precisely the thing that rule forbids. The keys themselves are Node's,\n * which interoperate: raw X25519 is raw X25519.\n */\n\n/** libsodium is WASM and initialises asynchronously. */\nlet readied: Promise<void> | undefined;\nexport async function cryptoReady(): Promise<void> {\n readied ??= sodium.ready;\n await readied;\n}\n\n/**\n * How long a sealed payload is worth keeping, from creation.\n *\n * Bound into every envelope and recomputed when one is opened, so it lives\n * here rather than in the two places that need it. Two copies of a value the\n * signature depends on is the same bug as two clock readings: it works until\n * they disagree, and then nothing can be opened.\n *\n * Not a job's TTL. That answers how long the *work* is worth doing, belongs\n * to the app and the store, and may legitimately differ per deployment.\n */\nexport const ENVELOPE_MAX_AGE_MS = 24 * 60 * 60_000;\n\n/** Which leg an envelope belongs to. Bound into the signature. */\nexport const EnvelopeDirection = z.enum([\"payload\", \"result\"]);\nexport type EnvelopeDirection = z.infer<typeof EnvelopeDirection>;\n\nexport const SealedEnvelope = z\n .object({\n /** Base64url `crypto_box_seal` output over the signed plaintext. */\n ciphertext: z.string().min(1),\n /** Who this was sealed to — the recipient checks it is them. */\n recipientKeyId: z.string().min(1),\n /** Who signed it — the recipient checks this against its pin. */\n senderKeyId: z.string().min(1),\n direction: EnvelopeDirection,\n /**\n * When this ciphertext stops being worth keeping.\n *\n * Carried *on* the envelope rather than recomputed by the opener. An\n * earlier version derived it from the job's creation time, which meant\n * two systems had to agree on a timestamp to the millisecond — and they\n * did not, once a real database rounded it. A bound value that has to be\n * reconstructed is a bound value that eventually is not.\n *\n * Not trusted as written: it is also inside the signature, so a changed\n * deadline fails to verify.\n */\n deadlineAt: z.number().int().positive(),\n })\n .strict();\nexport type SealedEnvelope = z.infer<typeof SealedEnvelope>;\n\n/** Everything the signature covers besides the plaintext itself. */\nexport interface EnvelopeContext {\n readonly jobId: string;\n readonly senderKeyId: string;\n readonly recipientKeyId: string;\n readonly deadlineAt: number;\n readonly direction: EnvelopeDirection;\n}\n\n/** The bytes signed inside the envelope. */\nfunction signedBody(context: EnvelopeContext, plaintext: string): Buffer {\n return Buffer.from(\n JSON.stringify({\n v: \"byollm/v1/envelope\",\n jobId: context.jobId,\n senderKeyId: context.senderKeyId,\n recipientKeyId: context.recipientKeyId,\n deadlineAt: context.deadlineAt,\n direction: context.direction,\n plaintext,\n }),\n \"utf8\",\n );\n}\n\nconst rawX25519 = (key: KeyObject, part: \"x\" | \"d\"): Uint8Array => {\n const jwk = key.export({ format: \"jwk\" });\n const value = part === \"x\" ? jwk.x : jwk.d;\n if (typeof value !== \"string\") throw new Error(\"not an X25519 key\");\n return new Uint8Array(Buffer.from(value, \"base64url\"));\n};\n\n/** Seal a plaintext to a recipient, signed by the sender's identity. */\nexport async function seal(input: {\n plaintext: string;\n senderKeys: StoredKeys;\n recipientEncryptionPublic: string;\n context: EnvelopeContext;\n}): Promise<SealedEnvelope> {\n await cryptoReady();\n\n const body = signedBody(input.context, input.plaintext);\n const signature = signWith(input.senderKeys, body);\n const inner = JSON.stringify({ body: body.toString(\"base64url\"), signature });\n\n const recipient = new Uint8Array(\n Buffer.from(input.recipientEncryptionPublic, \"base64url\"),\n );\n const ciphertext = sodium.crypto_box_seal(\n new Uint8Array(Buffer.from(inner, \"utf8\")),\n recipient,\n );\n\n return {\n ciphertext: Buffer.from(ciphertext).toString(\"base64url\"),\n recipientKeyId: input.context.recipientKeyId,\n senderKeyId: input.context.senderKeyId,\n direction: input.context.direction,\n deadlineAt: input.context.deadlineAt,\n };\n}\n\n/** Why an envelope was refused. Never distinguished to a remote caller. */\nexport type EnvelopeFailure =\n | \"not-for-us\"\n | \"unopenable\"\n | \"malformed\"\n | \"bad-signature\"\n | \"context-mismatch\";\n\nexport type OpenResult =\n | { readonly ok: true; readonly plaintext: string }\n | { readonly ok: false; readonly reason: EnvelopeFailure };\n\n/**\n * Open an envelope and verify it came from the pinned sender.\n *\n * Every failure returns rather than throws: this runs on input from the\n * network, and a crash here is a denial of service on the delivery path.\n *\n * The context is checked against the signature, not merely read from the\n * envelope. An envelope carries its own claims about who sent it and to\n * whom — believing those would authenticate the attacker's assertion rather\n * than the sender's key.\n */\nexport async function open(input: {\n envelope: SealedEnvelope;\n recipientKeys: StoredKeys;\n senderIdentityPublic: string;\n /** The deadline is taken from the envelope and checked against its signature. */\n expected: Omit<EnvelopeContext, \"deadlineAt\">;\n}): Promise<OpenResult> {\n await cryptoReady();\n const { envelope, expected } = input;\n\n // Cheap structural checks first, before any crypto.\n if (\n envelope.recipientKeyId !== expected.recipientKeyId ||\n envelope.senderKeyId !== expected.senderKeyId ||\n envelope.direction !== expected.direction\n ) {\n return { ok: false, reason: \"not-for-us\" };\n }\n\n let inner: string;\n try {\n const priv = createPrivateKey({\n key: Buffer.from(input.recipientKeys.encryptionPrivate, \"base64\"),\n type: \"pkcs8\",\n format: \"der\",\n });\n const pub = createPublicKey(priv);\n const opened = sodium.crypto_box_seal_open(\n new Uint8Array(Buffer.from(envelope.ciphertext, \"base64url\")),\n rawX25519(pub, \"x\"),\n rawX25519(priv, \"d\"),\n );\n inner = Buffer.from(opened).toString(\"utf8\");\n } catch {\n // Wrong recipient, tampered ciphertext, or garbage. One reason, because\n // the difference is not something the sender is entitled to learn.\n return { ok: false, reason: \"unopenable\" };\n }\n\n let parsed: { body?: unknown; signature?: unknown };\n try {\n parsed = JSON.parse(inner) as { body?: unknown; signature?: unknown };\n } catch {\n return { ok: false, reason: \"malformed\" };\n }\n if (typeof parsed.body !== \"string\" || typeof parsed.signature !== \"string\") {\n return { ok: false, reason: \"malformed\" };\n }\n\n const body = Buffer.from(parsed.body, \"base64url\");\n if (!verifyWith(input.senderIdentityPublic, body, parsed.signature)) {\n // Opened, but not from the key we pinned. This is the injection case: a\n // relay can produce a well-formed sealed box for any public key it holds.\n return { ok: false, reason: \"bad-signature\" };\n }\n\n let claims: Record<string, unknown>;\n try {\n claims = JSON.parse(body.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n return { ok: false, reason: \"malformed\" };\n }\n\n // The signature is valid over *something*; this checks it is valid over\n // what we asked for. Without it a genuinely signed envelope for another\n // job, recipient or leg would verify here.\n if (\n claims[\"jobId\"] !== expected.jobId ||\n claims[\"senderKeyId\"] !== expected.senderKeyId ||\n claims[\"recipientKeyId\"] !== expected.recipientKeyId ||\n claims[\"deadlineAt\"] !== envelope.deadlineAt ||\n claims[\"direction\"] !== expected.direction\n ) {\n return { ok: false, reason: \"context-mismatch\" };\n }\n if (typeof claims[\"plaintext\"] !== \"string\") {\n return { ok: false, reason: \"malformed\" };\n }\n\n return { ok: true, plaintext: claims[\"plaintext\"] };\n}\n","import {\n createHash,\n createPrivateKey,\n createPublicKey,\n generateKeyPairSync,\n sign,\n verify,\n type KeyObject,\n} from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * Device and site keys — byollm_009 §3.\n *\n * **Two keypairs per party, and the split is load-bearing.** An Ed25519\n * *identity* key signs; an X25519 *encryption* key receives sealed envelopes.\n * The encryption key is signed by the identity key, and **the identity key is\n * what gets pinned**. So \"who sent this\" and \"who can read this\" are answered\n * by different keys — which is what lets an encryption key rotate without\n * re-establishing trust, and what byollm_009 §6's signed-then-sealed envelope\n * depends on.\n *\n * **No new dependency.** byollm_009 §2 says established primitives only, via\n * libsodium. Everything *this* module needs — Ed25519 signing, X25519 key\n * generation — Node provides natively, and using it costs nothing and adds no\n * install weight to a daemon that must land fast on a stranger's laptop.\n *\n * libsodium becomes necessary at envelope v2, where sealing does. That is a\n * real dependency decision and it belongs in the change that needs it: a\n * sealed box is a specific reviewed construction, and rebuilding it out of\n * Node primitives is exactly the \"novel construction\" §2 rules out. Deferring\n * the dependency is not the same as deferring the rule.\n */\n\n/** A public identity, as it travels on the wire. All values base64url. */\nexport const PublicIdentity = z\n .object({\n /** Raw Ed25519 public key. The pinned one. */\n identity: z.string().min(1),\n /** Raw X25519 public key, for sealing to this party. */\n encryption: z.string().min(1),\n /**\n * Ed25519 signature over the encryption key, by the identity key.\n *\n * This is what stops an upstream substituting an encryption key of its\n * own while relaying a genuine identity: the receiver pins the identity\n * and refuses any encryption key not signed by it.\n */\n encryptionSig: z.string().min(1),\n })\n .strict();\nexport type PublicIdentity = z.infer<typeof PublicIdentity>;\n\n/** Private key material, as stored on disk. Never leaves the machine. */\nexport const StoredKeys = z\n .object({\n version: z.literal(1),\n identityPublic: z.string().min(1),\n identityPrivate: z.string().min(1),\n encryptionPublic: z.string().min(1),\n encryptionPrivate: z.string().min(1),\n encryptionSig: z.string().min(1),\n createdAt: z.number().int().positive(),\n })\n .strict();\nexport type StoredKeys = z.infer<typeof StoredKeys>;\n\n/** Domain separator, so a signature over an encryption key cannot be\n * replayed as a signature over anything else. */\nconst ENCRYPTION_KEY_CONTEXT = \"byollm/v1/encryption-key\";\n\nfunction rawPublic(key: KeyObject): string {\n const jwk = key.export({ format: \"jwk\" });\n const x = jwk.x;\n if (typeof x !== \"string\") throw new Error(\"key has no raw public component\");\n return x;\n}\n\nfunction importPublic(raw: string, crv: \"Ed25519\" | \"X25519\"): KeyObject {\n return createPublicKey({ key: { kty: \"OKP\", crv, x: raw }, format: \"jwk\" });\n}\n\nfunction importPrivate(stored: string): KeyObject {\n return createPrivateKey({\n key: Buffer.from(stored, \"base64\"),\n type: \"pkcs8\",\n format: \"der\",\n });\n}\n\nconst exportPrivate = (key: KeyObject): string =>\n key.export({ type: \"pkcs8\", format: \"der\" }).toString(\"base64\");\n\n/** Generate a fresh pair of keypairs and bind them together. */\nexport function generateKeys(now: number): StoredKeys {\n const identity = generateKeyPairSync(\"ed25519\");\n const encryption = generateKeyPairSync(\"x25519\");\n const encryptionPublic = rawPublic(encryption.publicKey);\n\n return {\n version: 1,\n identityPublic: rawPublic(identity.publicKey),\n identityPrivate: exportPrivate(identity.privateKey),\n encryptionPublic,\n encryptionPrivate: exportPrivate(encryption.privateKey),\n encryptionSig: sign(\n null,\n Buffer.from(`${ENCRYPTION_KEY_CONTEXT}:${encryptionPublic}`),\n identity.privateKey,\n ).toString(\"base64url\"),\n createdAt: now,\n };\n}\n\n/** The public half, for the wire. */\nexport function publicIdentityOf(keys: StoredKeys): PublicIdentity {\n return {\n identity: keys.identityPublic,\n encryption: keys.encryptionPublic,\n encryptionSig: keys.encryptionSig,\n };\n}\n\n/**\n * Check that an encryption key really belongs to the identity presenting it.\n *\n * Called on everything received, including from an upstream we otherwise\n * trust — the point of pinning the identity is that nothing else needs to be\n * trusted, and that only holds if this is checked every time rather than at\n * first sight.\n */\nexport function verifyPublicIdentity(identity: PublicIdentity): boolean {\n try {\n return verify(\n null,\n Buffer.from(`${ENCRYPTION_KEY_CONTEXT}:${identity.encryption}`),\n importPublic(identity.identity, \"Ed25519\"),\n Buffer.from(identity.encryptionSig, \"base64url\"),\n );\n } catch {\n // A malformed key is a failed verification, not a crash. This runs on\n // input from the network.\n return false;\n }\n}\n\n/** Sign arbitrary bytes with an identity key. */\nexport function signWith(keys: StoredKeys, data: Uint8Array): string {\n return sign(null, data, importPrivate(keys.identityPrivate)).toString(\n \"base64url\",\n );\n}\n\n/** Verify bytes against a raw Ed25519 public key. */\nexport function verifyWith(\n identityPublic: string,\n data: Uint8Array,\n signature: string,\n): boolean {\n try {\n return verify(\n null,\n data,\n importPublic(identityPublic, \"Ed25519\"),\n Buffer.from(signature, \"base64url\"),\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Crockford base32: no `I`, `L`, `O` or `U`, so a fingerprint read aloud\n * cannot be mis-heard as a different one, and cannot spell anything.\n */\nconst ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n/**\n * A fingerprint a human can compare out loud.\n *\n * 120 bits of SHA-256 over the raw identity key, as six groups of four. Long\n * enough that grinding a colliding key is not worth anyone's afternoon, short\n * enough to read down a phone line — which is the whole point. A fingerprint\n * nobody can be bothered to compare provides no security at all, so\n * legibility is a security property here, not a nicety.\n *\n * Formatted with a `BYOLLM-` prefix so a pasted fingerprint is recognisable\n * out of context, in a support thread or a screenshot.\n */\nexport function fingerprint(identityPublic: string): string {\n const digest = createHash(\"sha256\")\n .update(Buffer.from(identityPublic, \"base64url\"))\n .digest();\n\n let bits = 0;\n let value = 0;\n let out = \"\";\n for (const byte of digest.subarray(0, 15)) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += ALPHABET.charAt((value >>> (bits - 5)) & 31);\n bits -= 5;\n }\n }\n\n const groups = out.match(/.{1,4}/g) ?? [];\n return `BYOLLM-${groups.join(\"-\")}`;\n}\n\n/** The short id used in envelopes and provenance. Stable, and comparable. */\nexport const keyId = (identityPublic: string): string =>\n fingerprint(identityPublic);\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { signWith, verifyWith, type StoredKeys } from \"./keys.js\";\n\n/**\n * Request signing — byollm_009 §4.2.\n *\n * Every authenticated call is signed by the calling device's identity key.\n * There is no bearer token on the daemon plane: possession of a file no\n * longer grants access, possession of a *key* does, and the key never leaves\n * the machine.\n *\n * ## Why this is not the server-issued nonce the spec first described\n *\n * byollm_009 §4.2 says \"the upstream issues a nonce; the daemon signs it\".\n * Implementing that costs one of two things: a round trip before every\n * request, or server-side session state — and sessions reintroduce a bearer\n * credential, which is the thing being removed.\n *\n * Signing *the request itself* gets the same property without either, because\n * of something the protocol already guarantees. A captured signature is valid\n * only for the exact request it covers — same endpoint, same runner, same\n * body — and every authenticated endpoint here is idempotent by design:\n * `RESULT_IDEMPOTENT` makes a replayed result a no-op, a replayed claim from\n * the same runner returns what that runner already holds, and heartbeat and\n * release are idempotent in effect. So a replay inside the freshness window\n * gains an attacker nothing they could not obtain by forwarding the original,\n * which a relay can do anyway.\n *\n * That is the whole argument, and it is worth stating because it rests\n * entirely on the endpoints being idempotent. Two ways that can fail, and the\n * second is the one that actually bit:\n *\n * 1. **A future endpoint that is not idempotent cannot use this scheme\n * unchanged** — it would need a server-issued nonce.\n * 2. **Idempotence must hold per *addressed instance*, not per endpoint.** A\n * request that names a mutable target — a lease, a session, a\n * subscription — must name the *instance*, or a replay lands on a\n * different one than the sender meant and the endpoint's idempotence buys\n * nothing. `release` was idempotent per lease and ambiguous across them:\n * it named a job and a runner, both of which survive a\n * claim-release-reclaim cycle, so a replayed release yanked a later grant.\n * Fixed by giving a lease its own id and requiring it.\n *\n * The rule for anything added later: if a signed request can be replayed onto\n * a target that has changed underneath it, the request has to say which\n * target it meant.\n */\n\n/** How far a request's timestamp may be from the server's clock. */\nexport const MAX_CLOCK_SKEW_MS = 120_000;\n\n/** The signed material a request carries. */\nexport const RequestSignature = z\n .object({\n /** Which runner is calling. The server looks up its pinned identity. */\n runnerId: z.string().min(1),\n /** Epoch ms, bounded by {@link MAX_CLOCK_SKEW_MS}. */\n issuedAt: z.number().int().positive(),\n /** Base64url Ed25519 signature over {@link canonicalRequest}. */\n signature: z.string().min(1),\n })\n .strict();\nexport type RequestSignature = z.infer<typeof RequestSignature>;\n\n/**\n * The exact bytes both sides sign and verify.\n *\n * Newline-separated with a version prefix and a domain separator. Every field\n * that decides what the request *does* is in here: leave one out and it\n * becomes something an intermediary can change without breaking the\n * signature.\n *\n * The body is included by hash rather than by value, so signing does not\n * depend on both sides serialising JSON identically — which they would not.\n */\nexport function canonicalRequest(input: {\n endpoint: string;\n runnerId: string;\n issuedAt: number;\n body: string;\n}): Buffer {\n const digest = createHash(\"sha256\").update(input.body, \"utf8\").digest(\"hex\");\n return Buffer.from(\n [\n \"byollm/v1/request\",\n input.endpoint,\n input.runnerId,\n String(input.issuedAt),\n digest,\n ].join(\"\\n\"),\n \"utf8\",\n );\n}\n\n/** Sign an outgoing request with this machine's identity key. */\nexport function signRequest(\n keys: StoredKeys,\n input: { endpoint: string; runnerId: string; issuedAt: number; body: string },\n): RequestSignature {\n return {\n runnerId: input.runnerId,\n issuedAt: input.issuedAt,\n signature: signWith(keys, canonicalRequest(input)),\n };\n}\n\n/**\n * The same scheme, for the party at the other end: a **site** calling a relay.\n *\n * A site talking to a relay is in exactly the daemon's position — an outbound\n * caller with an identity keypair the other side already pins — so it gets the\n * daemon's authentication rather than a second scheme. Bearer tokens for the\n * site plane were the alternative, and they would have reintroduced the\n * credential-in-a-file that §4.2 removed from the daemon plane, on the plane\n * that carries *every* site's traffic.\n *\n * Two things make this safe to build on the same canonical string:\n *\n * 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never\n * `enqueue`. The daemon plane's `result` and the site plane's `results` are\n * one character apart, and a naming collision between planes must not be\n * what stands between a signature and a replay onto the wrong handler. The\n * prefix is applied *inside* these helpers, so the two ends cannot disagree\n * about it — the alternative is two implementations of one bound value,\n * which is this project's most-repeated bug.\n * 2. **The caller slot carries the site id.** `canonicalRequest` names that\n * field `runnerId` because the daemon plane got there first; here it holds\n * the site id, and the verifier looks the key up in the projection's site\n * registry rather than its device registry. The two registries never share\n * an entry, so a device signature cannot authenticate as a site.\n *\n * §4.2's replay argument carries over **only because the site plane's writes\n * are idempotent per addressed instance**, which is a property that had to be\n * built rather than found: `enqueue` reset a job of the same id, so a replayed\n * enqueue inside the freshness window returned a claimed job to the queue and\n * threw away a device's live lease. Identical in shape to the `release` bug\n * above, on the other plane. Anything added to the site plane later must be\n * idempotent by the instance it names, or this scheme does not cover it.\n */\nexport function signSiteRequest(\n keys: StoredKeys,\n input: { endpoint: string; siteId: string; issuedAt: number; body: string },\n): RequestSignature {\n return signRequest(keys, {\n endpoint: siteEndpoint(input.endpoint),\n runnerId: input.siteId,\n issuedAt: input.issuedAt,\n body: input.body,\n });\n}\n\n/** Verify a site's call against the identity the control plane registered. */\nexport function verifySiteRequest(input: {\n identityPublic: string;\n endpoint: string;\n body: string;\n signature: RequestSignature;\n now: number;\n maxSkewMs?: number;\n}): SignatureFailure | null {\n return verifyRequest({\n ...input,\n endpoint: siteEndpoint(input.endpoint),\n });\n}\n\n/** The one place the site plane's domain separator is written. */\nconst siteEndpoint = (endpoint: string): string => `site/${endpoint}`;\n\n/** Why a signed request was refused. Never returned to the caller verbatim. */\nexport type SignatureFailure = \"stale\" | \"bad-signature\";\n\n/**\n * Verify a signed request against a runner's pinned identity key.\n *\n * Freshness is checked in **both** directions. A clock far ahead is as much a\n * problem as one behind: it would let a captured request stay replayable long\n * after it was made, which is the one thing the window exists to bound.\n */\nexport function verifyRequest(input: {\n identityPublic: string;\n endpoint: string;\n body: string;\n signature: RequestSignature;\n now: number;\n maxSkewMs?: number;\n}): SignatureFailure | null {\n const skew = input.maxSkewMs ?? MAX_CLOCK_SKEW_MS;\n if (Math.abs(input.now - input.signature.issuedAt) > skew) return \"stale\";\n\n const ok = verifyWith(\n input.identityPublic,\n canonicalRequest({\n endpoint: input.endpoint,\n runnerId: input.signature.runnerId,\n issuedAt: input.signature.issuedAt,\n body: input.body,\n }),\n input.signature.signature,\n );\n return ok ? null : \"bad-signature\";\n}\n","/**\n * The normative MUSTs of protocol v0, as data.\n *\n * byollm_001 requires that \"every MUST above has a conformance test id\n * referenced inline\". Keeping the MUSTs as a frozen registry rather than\n * prose is what makes that requirement *checkable*: the conformance kit\n * imports {@link MUSTS} and fails if any id has no test asserting it, so a\n * new MUST cannot be added without a test and a test cannot silently drift\n * away from the statement it claims to prove.\n *\n * Ids are stable and public — third-party servers cite them in their\n * certification output.\n */\n\n/** Which side of the wire is obliged to enforce a given MUST. */\nexport type MustEnforcer = \"daemon\" | \"server\" | \"both\";\n\n/**\n * How a MUST is actually verified — which is not the same question as who\n * enforces it, and is the one that decides what \"byollm-compatible\" means.\n *\n * The conformance kit's credibility rests on an implicit claim that every\n * MUST is checkable. Ten of them were not, and the kit reported that honestly\n * while nothing acted on it. Making the kind explicit turns \"uncovered\" from\n * a number needing a paragraph of explanation into a number that should be\n * zero.\n *\n * - `conformance` — the kit asserts it against *any* implementation. This is\n * the strong kind: a third party runs the suite and learns something.\n * - `adversarial` — proved by the reference daemon's own suites in this repo\n * (the hostile-payload corpus, or its unit tests). Real verification, and\n * it runs in CI — but it proves things about *our* daemon, not about\n * someone else's, so the kit cannot carry it.\n * - `construction` — true by the shape of the code, where a test could only\n * sample. A reviewer verifies it; a suite cannot.\n * - `operator` — a claim about how someone runs a deployment, verifiable only\n * by audit or by reading source. The honest category, and the one that\n * exists so a property nobody can check from outside is *labelled* as such\n * rather than laundered by association with the checkable ones.\n */\nexport type MustVerification =\n \"conformance\" | \"adversarial\" | \"construction\" | \"operator\";\n\n/** A single normative requirement of the protocol. */\nexport interface Must {\n /** Stable public id, cited by conformance output. */\n readonly id: string;\n /** The requirement, in MUST language. */\n readonly statement: string;\n /** Which implementation is obliged to enforce it. */\n readonly enforcedBy: MustEnforcer;\n /**\n * How this is verified. `conformance` is the only kind the kit can assert;\n * see {@link MustVerification} for why the others exist.\n */\n readonly verifiedBy: MustVerification;\n /** Spec section this was adjudicated in. */\n readonly source: string;\n}\n\nconst must = (m: Must): Must => Object.freeze(m);\n\n/**\n * Every normative MUST in protocol v0.\n *\n * @remarks\n * Grouped by concern for readability; the conformance kit treats this as a\n * flat set. Adding an entry here without a corresponding conformance test is\n * a CI failure, by design.\n */\nexport const MUSTS = Object.freeze({\n // ---- Pairing and identity -------------------------------------------\n PAIR_ONE_USER: must({\n id: \"PAIR_ONE_USER\",\n statement:\n \"A runner token MUST be bound to exactly one user; a daemon MUST refuse \" +\n \"work not attributable to its paired user.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n PAIR_INTERACTIVE: must({\n id: \"PAIR_INTERACTIVE\",\n statement:\n \"Pairing MUST be interactive (device-code approval in the app's own \" +\n \"session); a long-lived pasted secret MUST NOT be accepted as pairing.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.1\",\n }),\n PAIR_CODE_EXPIRES: must({\n id: \"PAIR_CODE_EXPIRES\",\n statement:\n \"An unapproved device code MUST expire and MUST NOT be redeemable after \" +\n \"expiry.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.1\",\n }),\n\n // ---- Typed job kinds --------------------------------------------------\n VERSION_HANDSHAKE_REQUIRED: must({\n id: \"VERSION_HANDSHAKE_REQUIRED\",\n statement:\n \"Every protocol request MUST declare a protocol version, and a server \" +\n \"MUST refuse an absent or unsupported one with a structured error \" +\n \"naming what it supports — never a generic parse failure.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §4\",\n }),\n KEYS_EXCHANGED_AT_CONSENT: must({\n id: \"KEYS_EXCHANGED_AT_CONSENT\",\n statement:\n \"Pairing MUST exchange both parties' public identities; each side MUST \" +\n \"verify that the encryption key is signed by the identity presenting \" +\n \"it, and MUST pin the identity. Keys MUST NOT be delivered before \" +\n \"approval.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §5\",\n }),\n REQUESTS_SIGNED_NOT_BEARER: must({\n id: \"REQUESTS_SIGNED_NOT_BEARER\",\n statement:\n \"Every authenticated request MUST be signed by the calling device's \" +\n \"pinned identity key, over the endpoint, the runner id, a timestamp \" +\n \"and the exact request body. A server MUST NOT accept a bearer \" +\n \"credential in place of a signature.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §4.2\",\n }),\n LEASE_SCOPED_BY_GRANT: must({\n id: \"LEASE_SCOPED_BY_GRANT\",\n statement:\n \"A lease-scoped request MUST name the lease it acts on, and a server \" +\n \"MUST apply it only to that lease. Naming the job and the runner is \" +\n \"not sufficient: both survive a claim-release-reclaim cycle.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §4.2\",\n }),\n STUB_METADATA_EXHAUSTIVE: must({\n id: \"STUB_METADATA_EXHAUSTIVE\",\n statement:\n \"A claim MUST answer with stubs carrying exactly the enumerated \" +\n \"fields and no payload. An endpoint MUST NOT emit a stub carrying \" +\n \"others, and an upstream MUST NOT require any.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §6\",\n }),\n ENVELOPE_SEALED_AND_SIGNED: must({\n id: \"ENVELOPE_SEALED_AND_SIGNED\",\n statement:\n \"A stored payload MUST be sealed, and MUST be signed by the sender's \" +\n \"identity key. An endpoint MUST refuse an envelope whose signature \" +\n \"does not verify against the identity it pinned.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §6\",\n }),\n KIND_TYPED_ONLY: must({\n id: \"KIND_TYPED_ONLY\",\n statement:\n \"Job kinds MUST resolve against handlers baked into the daemon. A daemon \" +\n \"MUST refuse an unknown kind rather than guess.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Jobs are typed data\",\n }),\n KIND_NO_CODE: must({\n id: \"KIND_NO_CODE\",\n statement:\n \"A server MUST NOT be able to convey code, a shell string, or a path to \" +\n \"execute; payloads are data handed to a model only.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Jobs are typed data; byollm_004 §1\",\n }),\n\n // ---- Capability and claiming -----------------------------------------\n CLAIM_REQUIRES_CAPABILITY: must({\n id: \"CLAIM_REQUIRES_CAPABILITY\",\n statement:\n \"A daemon MUST NOT be given a job whose kind is absent from its \" +\n \"advertised capability matrix.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n CAPABILITY_IS_DETECTED: must({\n id: \"CAPABILITY_IS_DETECTED\",\n statement:\n \"An advertised capability matrix MUST be the intersection of owner \" +\n \"config and detected, healthy reality — never config alone.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_002 §Routing\",\n }),\n CLAIM_ATOMIC: must({\n id: \"CLAIM_ATOMIC\",\n statement:\n \"Claiming MUST be atomic: a job MUST NOT be handed to two runners \" +\n \"concurrently.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.2\",\n }),\n\n // ---- Leases -----------------------------------------------------------\n LEASE_HONORED: must({\n id: \"LEASE_HONORED\",\n statement:\n \"A daemon MUST stop work on a job whose lease it has failed to renew, \" +\n \"and MUST NOT report a result for an expired lease it no longer holds.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n LEASE_RECLAIMABLE: must({\n id: \"LEASE_RECLAIMABLE\",\n statement:\n \"A lease that expires un-renewed MUST make its job claimable again with \" +\n \"no loss of the job.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.2\",\n }),\n\n // ---- Audience and offer scope ----------------------------------------\n AUDIENCE_BOTH_SIDES: must({\n id: \"AUDIENCE_BOTH_SIDES\",\n statement:\n \"A job MUST run on a daemon only if the daemon's offer scope admits the \" +\n \"job's owner AND the job's audience admits the daemon's owner.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §The audience model\",\n }),\n SUBSCRIPTION_SELF_LOCK: must({\n id: \"SUBSCRIPTION_SELF_LOCK\",\n statement:\n \"A subscription-class backend's offer scope MUST be 'self' and MUST NOT \" +\n \"be widened by configuration.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §The audience model\",\n }),\n METERED_DEFAULTS_SELF: must({\n id: \"METERED_DEFAULTS_SELF\",\n statement:\n \"A metered backend's effective offer scope MUST be 'self' unless the \" +\n \"owner has explicitly acknowledged spending money on others' work.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §4\",\n }),\n METERED_REQUIRES_CEILING: must({\n id: \"METERED_REQUIRES_CEILING\",\n statement:\n \"A widened metered backend MUST carry a spend ceiling, and the daemon \" +\n \"MUST refuse community work once it is reached.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §4\",\n }),\n COST_NOT_CONFIGURABLE: must({\n id: \"COST_NOT_CONFIGURABLE\",\n statement:\n \"A built-in provider's cost class MUST NOT be overridable by \" +\n \"configuration.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §2\",\n }),\n REMOTE_IS_NEVER_FREE: must({\n id: \"REMOTE_IS_NEVER_FREE\",\n statement:\n \"A generic HTTP backend whose base URL is not loopback or private MUST \" +\n \"be treated as metered.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §2\",\n }),\n\n NAMED_LOCAL_ALLOWLIST: must({\n id: \"NAMED_LOCAL_ALLOWLIST\",\n statement:\n \"A 'named' job MUST be admitted only by the daemon's own local \" +\n \"(server origin, user id) allowlist — never on the server's assertion \" +\n \"alone.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §B\",\n }),\n\n REFUSAL_NOT_REOFFERED: must({\n id: \"REFUSAL_NOT_REOFFERED\",\n statement:\n \"A server MUST NOT re-offer a job to a runner that released it with \" +\n \"reason 'refused'.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §B (loop resolved in build review)\",\n }),\n\n // ---- Revocation and cancel -------------------------------------------\n REVOCATION_HONORED: must({\n id: \"REVOCATION_HONORED\",\n statement:\n \"A revoked daemon MUST stop claiming and MUST abandon in-flight work by \" +\n \"the next heartbeat at the latest.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n CANCEL_HONORED: must({\n id: \"CANCEL_HONORED\",\n statement:\n \"A job id in a heartbeat response's cancel list MUST abort that job's \" +\n \"in-flight backend call and be reported as 'canceled'.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §C\",\n }),\n\n // ---- Lifecycle, dependencies, delivery -------------------------------\n DEPENDS_ON_GATING: must({\n id: \"DEPENDS_ON_GATING\",\n statement:\n \"A job MUST NOT be claimable until every job in its dependsOn set has \" +\n \"reached the 'ok' state.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §E\",\n }),\n TTL_EXPIRY: must({\n id: \"TTL_EXPIRY\",\n statement:\n \"An unclaimed job MUST become 'expired' once its TTL elapses, and the \" +\n \"TTL clock MUST start when the job becomes claimable, not at enqueue.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §D (TTL clock resolved in build review)\",\n }),\n NO_RUNNER_SIGNAL: must({\n id: \"NO_RUNNER_SIGNAL\",\n statement:\n \"A server MUST surface noRunnerAvailable when no runner with matching \" +\n \"capability has heartbeated within the liveness window, and MUST NOT \" +\n \"raise it for a job still blocked on dependencies.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §D\",\n }),\n RESULT_IDEMPOTENT: must({\n id: \"RESULT_IDEMPOTENT\",\n statement:\n \"Result submission MUST be idempotent by job id; the first terminal \" +\n \"outcome wins and later submissions MUST NOT change it.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.4\",\n }),\n RESULT_PROVENANCE: must({\n id: \"RESULT_PROVENANCE\",\n statement:\n \"A result from a non-'self' job MUST carry its provenance (audience and \" +\n \"runner) to the delivery seam so an app never treats volunteer output \" +\n \"as first-party.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_003 Rev 1 §Return-trip\",\n }),\n\n // ---- The trust surface -------------------------------------------------\n INGRESS_LOGGED_BEFORE_EXECUTION: must({\n id: \"INGRESS_LOGGED_BEFORE_EXECUTION\",\n statement:\n \"Every executed prompt MUST be appended to the local ingress log before \" +\n \"execution begins.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n\n // ---- Execution isolation (byollm_004) ---------------------------------\n NO_SHELL_INTERPOLATION: must({\n id: \"NO_SHELL_INTERPOLATION\",\n statement:\n \"Process-class backends MUST be invoked with a fixed argv array and the \" +\n \"payload delivered on stdin; payload text MUST NOT reach a command line.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n NO_PAYLOAD_ROUTING: must({\n id: \"NO_PAYLOAD_ROUTING\",\n statement:\n \"Model, backend, base URL, and flags MUST come from owner config only; \" +\n \"a payload MUST NOT influence any of them.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n STRIPPED_CHILD_ENV: must({\n id: \"STRIPPED_CHILD_ENV\",\n statement:\n \"Process-class children MUST spawn with an allowlisted environment, a \" +\n \"scratch cwd, no inherited descriptors beyond std streams, and hard \" +\n \"timeout and output-size caps.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n HTTP_BASE_URL_SAFE: must({\n id: \"HTTP_BASE_URL_SAFE\",\n statement:\n \"HTTP-class backends MUST send requests only to the owner-configured \" +\n \"base URL and MUST refuse base URLs resolving to cloud-metadata or \" +\n \"link-local addresses.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 Rev 1 §Backend taxonomy\",\n }),\n OUTPUT_INERT: must({\n id: \"OUTPUT_INERT\",\n statement:\n \"Returned text MUST be treated as inert bytes: never evaluated, never \" +\n \"written to a payload-named path, never interpolated into a shell or \" +\n \"into terminal control sequences when logged.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n COMMUNITY_BUDGETS: must({\n id: \"COMMUNITY_BUDGETS\",\n statement:\n \"Jobs whose owner is not the daemon's owner MUST be subject to the \" +\n \"owner's rate limits, daily cap, and resource budget.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §4\",\n }),\n} as const satisfies Record<string, Must>);\n\n/** The id of any normative MUST. */\nexport type MustId = keyof typeof MUSTS;\n\n/** All MUST ids, for coverage checks. */\nexport const MUST_IDS = Object.freeze(Object.keys(MUSTS) as MustId[]);\n\n/** Every MUST verified a particular way. */\nexport function mustsVerifiedBy(kind: MustVerification): MustId[] {\n return MUST_IDS.filter((id) => MUSTS[id].verifiedBy === kind);\n}\n","import { z } from \"zod\";\nimport { PublicIdentity } from \"./keys.js\";\nimport { OfferScope } from \"./audience.js\";\nimport { BackendClass, BackendIdSchema } from \"./backends.js\";\nimport { ClaimedStub } from \"./job.js\";\nimport { SealedEnvelope } from \"./envelope.js\";\nimport { JobKind } from \"./kinds.js\";\n\n/** Protocol version carried on every request; servers refuse what they can't speak. */\nexport const PROTOCOL_VERSION = \"0\" as const;\n\n/**\n * Every protocol version this build can serve, **oldest first**.\n *\n * One entry today. It is a list rather than a constant because the shape of\n * the check is the point: a server supporting two versions through a\n * migration should not need a different code path from one supporting one.\n */\nexport const SUPPORTED_PROTOCOL_VERSIONS = Object.freeze([\n PROTOCOL_VERSION,\n]) as readonly string[];\n\n/**\n * The oldest version this build will talk to — derived, not declared.\n *\n * Stating it separately would be a second thing to keep in step with the list\n * above, and the failure would be silent: a minimum that no longer matches\n * what is supported produces a refusal naming a version the server would in\n * fact have accepted.\n */\nexport const MIN_PROTOCOL_VERSION: string =\n SUPPORTED_PROTOCOL_VERSIONS[0] ?? PROTOCOL_VERSION;\n\n/** A structured refusal, so a daemon can say something useful to its owner. */\nexport interface VersionRefusal {\n readonly error: \"unsupported-protocol-version\";\n readonly message: string;\n readonly supported: readonly string[];\n readonly minimum: string;\n}\n\n/**\n * Check the protocol version on an incoming request\n * ({@link MUSTS.VERSION_HANDSHAKE_REQUIRED}).\n *\n * Returns a refusal, or `null` to proceed.\n *\n * **A missing version is refused the same way a wrong one is.** That is the\n * half worth stating: before this existed, the version travelled as a\n * `z.literal` inside each endpoint's schema, so a mismatch surfaced as a\n * generic `bad-request` — a daemon and a server discovered they disagreed by\n * failing, with nothing in the response naming the disagreement. An error a\n * user cannot act on is barely better than a hang.\n *\n * The message names the fix, because the person reading it is usually the one\n * who has to apply it.\n */\nexport function checkProtocolVersion(body: unknown): VersionRefusal | null {\n // `hasOwn`, not `in`: `in` walks the prototype chain, and a version check\n // should read what the request actually carried rather than something an\n // object happens to inherit. Not reachable from a JSON body today, which is\n // the reason to fix it now rather than after it is.\n const declared =\n typeof body === \"object\" &&\n body !== null &&\n Object.hasOwn(body, \"protocolVersion\")\n ? (body as { protocolVersion: unknown }).protocolVersion\n : undefined;\n\n if (typeof declared !== \"string\" || declared.length === 0) {\n return {\n error: \"unsupported-protocol-version\",\n message:\n \"this request declared no protocol version. Upgrade the daemon: \" +\n \"`npm i -g byollm@alpha`.\",\n supported: SUPPORTED_PROTOCOL_VERSIONS,\n minimum: MIN_PROTOCOL_VERSION,\n };\n }\n\n if (!SUPPORTED_PROTOCOL_VERSIONS.includes(declared)) {\n return {\n error: \"unsupported-protocol-version\",\n message:\n `this server speaks protocol ${SUPPORTED_PROTOCOL_VERSIONS.join(\", \")} ` +\n `and the daemon asked for ${declared}. ` +\n (declared < MIN_PROTOCOL_VERSION\n ? \"Upgrade the daemon: `npm i -g byollm@alpha`.\"\n : \"This daemon is newer than the server; the server needs upgrading.\"),\n supported: SUPPORTED_PROTOCOL_VERSIONS,\n minimum: MIN_PROTOCOL_VERSION,\n };\n }\n\n return null;\n}\n\n/** The path prefix all endpoints mount under. */\nexport const PROTOCOL_PREFIX = \"/byollm\" as const;\n\n/**\n * The endpoint names, in the order byollm_001 lists them, plus `fetch`.\n *\n * `fetch` is byollm_009 §6's second phase: a claim returns a stub, and the\n * payload is collected separately by the device that took it. Two steps\n * rather than one because a payload can only be sealed once its recipient is\n * known — which is also what makes multi-device free.\n */\nexport const ENDPOINTS = Object.freeze([\n \"pair\",\n \"claim\",\n \"fetch\",\n \"heartbeat\",\n \"result\",\n \"release\",\n] as const);\nexport type Endpoint = (typeof ENDPOINTS)[number];\n\n/**\n * One entry of the capability matrix: a kind this daemon can actually serve,\n * right now, with the backend and model that would serve it.\n *\n * Derived from owner config intersected with detected reality\n * ({@link MUSTS.CAPABILITY_IS_DETECTED}) — a configured-but-unreachable\n * backend must not appear here. Carries `backendClass` so the app can tell\n * whether a result came from a sandboxed spawn or an HTTP call\n * (byollm_001 Rev 1 §A).\n */\nexport const Capability = z\n .object({\n kind: JobKind,\n backendId: BackendIdSchema,\n backendClass: BackendClass,\n model: z.string().min(1),\n offerScope: OfferScope,\n })\n .strict();\nexport type Capability = z.infer<typeof Capability>;\n\n/** The capability matrix a daemon advertises. */\nexport const CapabilityMatrix = z.array(Capability);\nexport type CapabilityMatrix = z.infer<typeof CapabilityMatrix>;\n\n// ---------------------------------------------------------------------------\n// 1. POST /byollm/pair — device-code flow\n// ---------------------------------------------------------------------------\n\n/**\n * Pairing is a device-code exchange, not a pasted secret\n * ({@link MUSTS.PAIR_INTERACTIVE}). The daemon starts a pairing, shows the\n * user a short code and a URL, and polls until the user approves it inside\n * the app's own authenticated session. Nothing listens on the user's machine\n * and nothing works over a copied string alone.\n */\nexport const PairStartRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n action: z.literal(\"start\"),\n daemon: z.object({\n version: z.string().min(1),\n /** Shown in the app's runner list so a user can tell their machines apart. */\n label: z.string().min(1).max(120),\n platform: z.enum([\"darwin\", \"linux\", \"win32\"]),\n }),\n /**\n * This machine's public keys (byollm_009 §5).\n *\n * Pairing is where the two parties learn each other's identities, because\n * it is the one moment a human is already deciding to trust: the approval\n * click. A key exchanged anywhere else would be a key nobody chose.\n */\n device: PublicIdentity,\n capabilities: CapabilityMatrix,\n })\n .strict();\nexport type PairStartRequest = z.infer<typeof PairStartRequest>;\n\nexport const PairStartResponse = z\n .object({\n /** Secret the daemon polls with. Never shown to the user. */\n deviceCode: z.string().min(20),\n /** Short code the user reads and confirms in the browser. */\n userCode: z.string().min(4).max(16),\n /** Where the user approves. Must be on the server's own origin. */\n verificationUrl: z.url(),\n /** Epoch ms after which the code is dead ({@link MUSTS.PAIR_CODE_EXPIRES}). */\n expiresAt: z.number().int().positive(),\n /** How often the daemon may poll. */\n pollIntervalMs: z.number().int().min(500).max(60_000),\n })\n .strict();\nexport type PairStartResponse = z.infer<typeof PairStartResponse>;\n\nexport const PairPollRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n action: z.literal(\"poll\"),\n deviceCode: z.string().min(20),\n })\n .strict();\nexport type PairPollRequest = z.infer<typeof PairPollRequest>;\n\nexport const PairPollResponse = z.discriminatedUnion(\"status\", [\n z.object({ status: z.literal(\"pending\") }).strict(),\n z.object({ status: z.literal(\"denied\") }).strict(),\n z.object({ status: z.literal(\"expired\") }).strict(),\n z\n .object({\n status: z.literal(\"approved\"),\n /** Bearer token for every later call. Scoped to exactly one user. */\n runnerToken: z.string().min(20),\n runnerId: z.string().min(1),\n /** The app's id for the approving user — this daemon's owner forever. */\n owner: z.string().min(1),\n /** Display name for the trust UI, if the app offers one. */\n ownerLabel: z.string().optional(),\n /**\n * The site's public keys, for the daemon to pin (byollm_009 §5).\n *\n * Returned only on approval — a pending or denied poll learns nothing,\n * so an unapproved code cannot be used to enumerate a site's keys.\n */\n site: PublicIdentity,\n })\n .strict(),\n]);\nexport type PairPollResponse = z.infer<typeof PairPollResponse>;\n\nexport const PairRequest = z.discriminatedUnion(\"action\", [\n PairStartRequest,\n PairPollRequest,\n]);\nexport type PairRequest = z.infer<typeof PairRequest>;\n\n// ---------------------------------------------------------------------------\n// 2. POST /byollm/claim\n// ---------------------------------------------------------------------------\n\nexport const ClaimRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n /** Re-sent on every claim so a server never matches against a stale matrix. */\n capabilities: CapabilityMatrix,\n /** Upper bound on jobs to return; the server may return fewer. */\n max: z.number().int().min(1).max(64),\n })\n .strict();\nexport type ClaimRequest = z.infer<typeof ClaimRequest>;\n\nexport const ClaimResponse = z\n .object({\n /**\n * Stubs, not jobs. The payload arrives from `fetch`, sealed to whichever\n * device claimed — see {@link JobStub} for the exhaustive metadata list.\n */\n jobs: z.array(ClaimedStub),\n /** Lease duration granted, so the daemon knows its renewal deadline. */\n leaseMs: z.number().int().positive(),\n })\n .strict();\nexport type ClaimResponse = z.infer<typeof ClaimResponse>;\n\n// ---------------------------------------------------------------------------\n// 3. POST /byollm/heartbeat\n// ---------------------------------------------------------------------------\n\nexport const HeartbeatRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n daemonVersion: z.string().min(1),\n capabilities: CapabilityMatrix,\n /**\n * Leases this daemon believes it holds; the server renews exactly these.\n *\n * Lease ids rather than job ids, so a replayed heartbeat cannot renew a\n * grant the runner no longer holds — see {@link Lease.id}.\n */\n activeLeases: z.array(\n z.object({ jobId: z.string().min(1), leaseId: z.string().min(1) }),\n ),\n /** True while the owner has the daemon paused; the server stops offering work. */\n paused: z.boolean(),\n })\n .strict();\nexport type HeartbeatRequest = z.infer<typeof HeartbeatRequest>;\n\nexport const HeartbeatResponse = z\n .object({\n /** Once true, the daemon stops claiming and abandons in-flight work. */\n revoked: z.boolean(),\n /**\n * Per-job cancel (byollm_001 Rev 1 §C). The daemon aborts these jobs'\n * in-flight backend calls and reports them `canceled`.\n */\n cancel: z.array(z.string().min(1)),\n /** Jobs whose leases were renewed, with their new expiry. */\n leases: z.array(\n z\n .object({\n jobId: z.string().min(1),\n expiresAt: z.number().int().positive(),\n })\n .strict(),\n ),\n /**\n * Jobs the daemon thinks it holds but the server has reassigned or\n * expired. The daemon must stop work on these and not report results.\n */\n lost: z.array(z.string().min(1)),\n /** Server clock, so a daemon with a skewed clock still honors leases. */\n serverTime: z.number().int().positive(),\n })\n .strict();\nexport type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;\n\n// ---------------------------------------------------------------------------\n// 4. POST /byollm/result\n// ---------------------------------------------------------------------------\n\n/**\n * What an intermediary learns about how a job ended — byollm_009 §6.\n *\n * The discriminator and nothing else. A relay has to know a job reached a\n * terminal state, and whether it failed, because that decides whether the job\n * leaves the queue or the app may re-enqueue. It does not have to know what\n * the model said, or what an error said, and this is where that line is drawn.\n *\n * Kept identical to `JobOutcome`'s discriminator rather than coarsened to\n * ok/not-ok: a cancelled job and a failed one are different routing outcomes,\n * and collapsing them would make the relay guess.\n */\nexport const ResultDisposition = z.enum([\"ok\", \"error\", \"canceled\"]);\nexport type ResultDisposition = z.infer<typeof ResultDisposition>;\n\nexport const ResultRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n jobId: z.string().min(1),\n /**\n * The outcome, sealed to the site and signed by the device.\n *\n * The return leg of the payload envelope, and sealed for the same reason:\n * a model's answer is as sensitive as the prompt that produced it, and an\n * intermediary that cannot read one must not be handed the other.\n */\n envelope: SealedEnvelope,\n /**\n * The sealed outcome's discriminator, in the clear.\n *\n * Checked against the envelope once opened. It is a routing hint, not a\n * fact: believing it unverified would let a daemon mark a job `ok` while\n * sealing an error, and only the app would ever find out.\n */\n disposition: ResultDisposition,\n /** Which model actually served it, for the result's provenance. */\n model: z.string().min(1),\n backendClass: BackendClass,\n /** Wall-clock milliseconds the backend call took. */\n durationMs: z.number().int().nonnegative(),\n })\n .strict();\nexport type ResultRequest = z.infer<typeof ResultRequest>;\n\nexport const ResultResponse = z\n .object({\n /**\n * False when the submission lost an idempotency race or the lease was\n * already gone — the daemon should discard, not retry\n * ({@link MUSTS.RESULT_IDEMPOTENT}).\n */\n accepted: z.boolean(),\n /** The job's state after this submission. */\n state: z.string().min(1),\n })\n .strict();\nexport type ResultResponse = z.infer<typeof ResultResponse>;\n\n// ---------------------------------------------------------------------------\n// 5. POST /byollm/release\n// ---------------------------------------------------------------------------\n\nexport const ReleaseRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n /**\n * Which leases to release — the grant, not just the job.\n *\n * A release naming only a job id releases whatever lease exists at the\n * moment it arrives, which for a replayed request is not the lease the\n * daemon meant. See {@link Lease.id}.\n */\n leases: z.array(\n z.object({ jobId: z.string().min(1), leaseId: z.string().min(1) }),\n ),\n /**\n * Why, so the app's runner list can say something true.\n *\n * `refused` is load-bearing, not cosmetic: the server cannot evaluate a\n * daemon's *local* `named` allowlist (§4.2), so it may legitimately offer\n * a job this daemon then declines. The server MUST record the refusal and\n * stop offering that job to that runner, or the pair would spin between\n * claim and release forever.\n */\n reason: z.enum([\"shutdown\", \"pause\", \"revoked\", \"backend-down\", \"refused\"]),\n })\n .strict();\nexport type ReleaseRequest = z.infer<typeof ReleaseRequest>;\n\nexport const ReleaseResponse = z\n .object({\n released: z.array(z.string().min(1)),\n })\n .strict();\nexport type ReleaseResponse = z.infer<typeof ReleaseResponse>;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * Wire error codes.\n *\n * byollm_002 requires that \"server unreachable\", \"revoked\", \"no matching\n * work\" and \"backend down\" never share a message. Distinct codes here are how\n * the daemon can tell three of those apart; the fourth is a transport failure\n * with no response at all.\n */\nexport const WireErrorCode = z.enum([\n \"bad-request\",\n \"unsupported-protocol-version\",\n \"unauthorized\",\n \"revoked\",\n \"not-found\",\n \"rate-limited\",\n \"server-error\",\n]);\nexport type WireErrorCode = z.infer<typeof WireErrorCode>;\n\nexport const WireError = z\n .object({\n error: WireErrorCode,\n message: z.string().min(1),\n /** Seconds; mirrors Retry-After for `rate-limited` and `server-error`. */\n retryAfter: z.number().int().nonnegative().optional(),\n })\n .strict();\nexport type WireError = z.infer<typeof WireError>;\n\n/** HTTP status each error code is served with. */\nexport const ERROR_STATUS: Readonly<Record<WireErrorCode, number>> =\n Object.freeze({\n \"bad-request\": 400,\n \"unsupported-protocol-version\": 400,\n unauthorized: 401,\n revoked: 403,\n \"not-found\": 404,\n \"rate-limited\": 429,\n \"server-error\": 500,\n });\n\n// ---------------------------------------------------------------------------\n// 3. POST /byollm/fetch — collect the payload for a lease you hold\n// ---------------------------------------------------------------------------\n\nexport const FetchRequest = z\n .object({\n protocolVersion: z.string().min(1),\n runnerId: z.string().min(1),\n jobId: z.string().min(1),\n /**\n * The grant this daemon holds.\n *\n * Named, not inferred: a fetch is lease-scoped, and a request that names\n * only the job would be answerable for whatever lease exists when it\n * arrives ({@link Lease.id}).\n */\n leaseId: z.string().min(1),\n })\n .strict();\nexport type FetchRequest = z.infer<typeof FetchRequest>;\n\nexport const FetchResponse = z\n .object({\n /**\n * The work, sealed to the device that claimed it — byollm_009 §6.\n *\n * Not plaintext. The site opens its own at-rest envelope and re-seals to\n * the claiming device's key, signed by the site's identity, so the work\n * is readable only by the machine that took it and only if it came from\n * the site that machine pinned.\n */\n envelope: SealedEnvelope,\n })\n .strict();\nexport type FetchResponse = z.infer<typeof FetchResponse>;\n"],"mappings":"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","names":["z","z","z","z","z","createPrivateKey","createPublicKey","z","z","z","createPrivateKey","createPublicKey","createHash","z","z","createHash","z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/about.ts","../src/audience.ts","../src/backends.ts","../src/kinds.ts","../src/job.ts","../src/grant.ts","../src/keys.ts","../src/envelope.ts","../src/signing.ts","../src/manifest.ts","../src/succession.ts","../src/musts.ts","../src/wire.ts"],"sourcesContent":["// Generated by scripts/generate-about.mjs. Do not edit.\n//\n// The description of record is ABOUT.md and ABOUT-SHORT.md at the root of the\n// byollm repository. Edit those, then run `pnpm run about`. `verify` fails\n// when this file and they disagree, which is the one-source rule with teeth:\n// the paragraph previously lived in hand-kept copies in another repository and\n// a superseded draft nearly shipped.\n\n/** The full description — five sections, plus why it matters. */\nexport const ABOUT = \"# About BYOLLM\\n\\n**What BYOLLM is**\\n\\nBYOLLM lets you use your own AI on websites. You install one small program on\\nyour computer. Then, websites that support BYOLLM can use the AI you already\\nhave — a free model running on your machine, or an AI service you already pay\\nfor — instead of the website paying for AI and passing the cost to you.\\n\\n**Why it matters**\\n\\nFor you:\\n\\n- Your favorite model, everywhere you go.\\n- New models the moment you get them – not when a site gets around to adding\\n them.\\n- Encrypted end-to-end. Your prompts go to your own device; byollm.cloud can't\\n read them.\\n- Sites never learn which model you use, and your subscriptions are never\\n shared.\\n- Pay less. Sites that don't pay for AI can charge you less – or nothing.\\n\\nFor sites and developers:\\n\\n- Zero AI bills. Your users bring their own compute.\\n- No floating money – you don't pay LLM bills up front and hope to collect\\n later, and you never ask people to prepay just to try you.\\n- Free trials that cost you nothing to offer.\\n- Ship the AI features you kept private for fear of the API bill.\\n- One small integration. Your users choose the models.\\n\\n**Your device**\\n\\nThe `byollm` program runs on your computer. It knows which AI services you have\\nset up: free open-source models on your machine, metered services you pay per\\nuse, or your own subscriptions like Claude Pro/Max. When a website you have\\nenabled sends work, your device runs it with the service you chose. Your\\nprompts are encrypted end-to-end to your own device. byollm.cloud passes them\\nalong and cannot read them.\\n\\n**Sites**\\n\\nA website that wants to use BYOLLM says what it needs — \\\"writing help,\\\" \\\"chat,\\\"\\nand so on. When you connect the site, you pick which of your services answers\\neach one. The site never learns which model you use. You can turn a site off at\\nany time, and it stops getting your work.\\n\\n**Teams (optional)**\\n\\nA team lets you share what runs on your devices with people you name — the free\\nopen-source models on your machine, or a metered service with a spending limit\\nyou set. Your subscription accounts (like Claude Pro/Max) are never shared with\\nanyone. That is a rule, not a setting.\\n\\n**byollm.cloud (or your own relay)**\\n\\nMany sites, many devices, many people. byollm.cloud keeps track of who has\\nallowed what and sends each job to the right device. It never sees your\\nprompts. If you would rather run this part yourself, the relay is open source —\\nyou can run your own instead of using byollm.cloud.\";\n\n/**\n * The first paragraph, which stands alone.\n *\n * What the welcome screen shows: somebody deciding whether to trust a site's\n * button needs the whole idea in one breath, not a page.\n */\nexport const ABOUT_SHORT_LEDE = \"BYOLLM – Bring Your Own LLM – lets you use your own AI on websites you authorize. A small program installed on your machine lets you use your own models and subscriptions on any BYOLLM-integrated site, including new models the moment you get access – no site updates required. BYOLLM Cloud connects sites to your devices with end-to-end encryption, so no one, including us, can see your data.\";\n\n/** The rest, for surfaces with room. Shown before \"Learn more →\". */\nexport const ABOUT_SHORT_TAIL = \"Sites can charge you less because you bring your own – see why that matters →. Teams can optionally share the free or metered services on their devices with people they name. Personal subscriptions are never shared.\";\n\n/** Both halves, for a surface that wants the paragraph entire. */\nexport const ABOUT_SHORT = `${ABOUT_SHORT_LEDE}\\n\\n${ABOUT_SHORT_TAIL}`;\n","import { z } from \"zod\";\nimport { type BackendCost } from \"./backends.js\";\n\n/**\n * Who may run a job, declared by the app that enqueued it.\n *\n * - `private` — only the job owner's own devices.\n * - `team` — a device whose owner admits this person.\n *\n * **One vocabulary, ruled 2026-08-24.** These were `self | named | public`\n * while {@link OfferScope} used different words for the same idea, which would\n * have left every seam where the two meet speaking two languages, and every\n * doc explaining \"self versus private\" for ever. They are still independent\n * axes — a job says who may run it, a service says who it will run for — and a\n * job runs only where both agree ({@link MUSTS.AUDIENCE_BOTH_SIDES}).\n *\n * **`public` is gone, ruled 2026-08-26 (byollm_016).** Not deprecated,\n * removed, and removed from the OSS daemon too rather than parked as a\n * community posture. The argument was a measurement rather than a preference:\n * device-side admission had never once been exercised end to end, because\n * every cross-user test ran against a publicly offered service and\n * {@link matchAudience} returned ALLOWED for those *without consulting the\n * device at all*. `public` was the off switch for admission, and an enum with\n * a value that skips verification is a fail-open waiting for the wiring bug\n * that reaches it. There is now no such value.\n */\nexport const Audience = z.enum([\"private\", \"team\"]);\nexport type Audience = z.infer<typeof Audience>;\n\n/**\n * What a device's owner is willing to run for other people, per service.\n *\n * - `private` — the owner's own work only.\n * - `team` — whoever the owner's authority admits. Membership is **central**,\n * not per-person: the device follows what it is told by a signature it can\n * check, rather than holding its own copy of who is in it (byollm_016).\n *\n * Two values, and no third that means \"everyone\". See {@link Audience} for\n * why `public` was removed rather than parked, and note the shape of the\n * remaining enum: **every value left requires the device to verify\n * something.** `private` checks the owner; `team` checks admission. That is\n * the property, not an accident of there being two.\n */\nexport const OfferScope = z.enum([\"private\", \"team\"]);\nexport type OfferScope = z.infer<typeof OfferScope>;\n\n/** All audience values, in widening order. */\nexport const AUDIENCES = Object.freeze(Audience.options);\n/** All offer scopes, in widening order. */\nexport const OFFER_SCOPES = Object.freeze(OfferScope.options);\n\n/**\n * Why a job was refused. Distinct codes because byollm_002 requires that\n * different truths never share a message — \"no matching work\" and \"refused on\n * principle\" are not the same event, and a volunteer debugging their setup\n * needs to know which one happened.\n */\nexport const MatchRefusal = z.enum([\n /** The daemon advertises no capability for this kind. */\n \"no-capability\",\n /** Job is `private` but this daemon belongs to a different user. */\n \"audience-self-other-owner\",\n /**\n * Job is `team` and nothing this device verified admits the job's owner.\n *\n * The id predates the grant and is kept, because ids are public and cited\n * by conformance output. What it means has not moved: this device was not\n * shown anything it could check.\n */\n \"not-locally-allowed\",\n /** Job is `team` but the server's own allowlist excludes this runner. */\n \"not-in-server-allowlist\",\n /** The service offers only `private` and the job belongs to someone else. */\n \"offer-scope-too-narrow\",\n /** The matched backend is subscription-class, which is locked to `private`. */\n \"subscription-self-lock\",\n /** The backend spends the owner's money and they have not agreed to share it. */\n \"metered-no-spend-consent\",\n /** The backend is shared but has spent its ceiling for now. */\n \"metered-ceiling-reached\",\n]);\nexport type MatchRefusal = z.infer<typeof MatchRefusal>;\n\n/** The outcome of an audience match. */\nexport type MatchResult =\n | { readonly ok: true }\n | { readonly ok: false; readonly refusal: MatchRefusal };\n\nconst ALLOWED: MatchResult = Object.freeze({ ok: true as const });\nconst refuse = (refusal: MatchRefusal): MatchResult =>\n Object.freeze({ ok: false as const, refusal });\n\n/** What the owner has agreed to spend on other people's work, if anything. */\nexport interface SpendConsent {\n /** The owner explicitly acknowledged that sharing this backend costs money. */\n readonly acknowledged: boolean;\n /** Their ceiling. Absent means no ceiling was set, which is not consent. */\n readonly ceilingReached?: boolean;\n}\n\n/**\n * The effective offer scope of a backend.\n *\n * Three rules, applied at the one place both the daemon's config loader and\n * its matcher call, so no code path can observe a scope wider than the cost\n * class allows:\n *\n * - `subscription` is locked to `private` regardless of config\n * ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}) — someone else's terms.\n * - `metered` narrows to `private` unless the owner has explicitly acknowledged\n * the spend ({@link MUSTS.METERED_DEFAULTS_SELF}) — their money.\n * - `free` passes through — their electricity.\n *\n * Note the asymmetry: subscription can never be widened, metered can be\n * widened deliberately. Conflating those was byollm_007's bug.\n */\nexport function effectiveOfferScope(\n configured: OfferScope,\n cost: BackendCost,\n spend?: SpendConsent,\n): OfferScope {\n if (cost === \"subscription\") return \"private\";\n if (cost === \"metered\" && spend?.acknowledged !== true) return \"private\";\n return configured;\n}\n\n/** The job-side facts a match needs. */\nexport interface MatchJob {\n /** The app's id for the user who enqueued the job. */\n readonly owner: string;\n /** Who the app says may run it. */\n readonly audience: Audience;\n /**\n * Optional server-side restriction on which runner owners may take a\n * `team` job. Defence in depth only, and direct-mode only — it never\n * reaches a daemon (cloud_008 §0.2), so the device's own admission is the\n * enforcing side ({@link MUSTS.NAMED_LOCAL_ALLOWLIST}).\n */\n readonly audienceAllow?: readonly string[] | undefined;\n}\n\n/** The daemon-side facts a match needs. */\nexport interface MatchDaemon {\n /** The app's id for the user this daemon is paired to. */\n readonly owner: string;\n /** Effective scope of the backend that would run the job. */\n readonly offerScope: OfferScope;\n /** Who pays for that backend's tokens. */\n readonly cost: BackendCost;\n /** What the owner agreed to spend on others, for a `metered` backend. */\n readonly spend?: SpendConsent | undefined;\n /**\n * Has something **this device verified** admitted the job's owner?\n *\n * A predicate rather than a value so the protocol package stays free of\n * both file I/O and signature state. What supplies it has changed twice and\n * will change again — a local allowlist, then a held roster, and now a\n * claim-time signed grant (Amendment J) — and the law it feeds has not\n * changed at all: a `team` service runs a stranger's work only when\n * somebody this device can check said so.\n *\n * The server passes a conservative `() => true`: it cannot know what a\n * remote device verified and must not pretend to. The device is the\n * enforcing side, which is the whole point of asking here.\n *\n * Named for the question, not for where the answer lives. This was called\n * `locallyAllows`, and \"locally\" stopped being true the moment the answer\n * came from a document somebody else signed.\n */\n readonly admits: (owner: string) => boolean;\n}\n\n/**\n * Decide whether a job may run on a daemon.\n *\n * Both sides must agree ({@link MUSTS.AUDIENCE_BOTH_SIDES}):\n * 1. the job's audience must admit the daemon's owner, and\n * 2. the backend's offer scope must admit the job's owner.\n *\n * The full four-way matrix (two audiences × two offer scopes) is asserted by\n * the conformance kit. The function is pure and total so both the daemon\n * and the server can run the identical rule — the daemon refuses, and the\n * server refuses too (byollm_003 §Server-side MUSTs).\n *\n * @example\n * ```ts\n * const result = matchAudience(\n * { owner: \"alice\", audience: \"team\" },\n * {\n * owner: \"bob\",\n * offerScope: \"team\",\n * cost: \"free\",\n * admits: (o) => o === \"alice\",\n * },\n * );\n * // result.ok === true\n * ```\n */\nexport function matchAudience(job: MatchJob, daemon: MatchDaemon): MatchResult {\n const sameOwner = job.owner === daemon.owner;\n\n // --- Side 1: does the job's audience admit this daemon's owner? --------\n if (job.audience === \"private\" && !sameOwner) {\n return refuse(\"audience-self-other-owner\");\n }\n if (\n job.audience === \"team\" &&\n !sameOwner &&\n job.audienceAllow !== undefined &&\n !job.audienceAllow.includes(daemon.owner)\n ) {\n return refuse(\"not-in-server-allowlist\");\n }\n\n // --- Side 2: does the backend's offer scope admit the job's owner? -----\n // The lock is re-applied rather than trusted: a caller that passed a\n // widened scope for a subscription backend gets a refusal, not obedience.\n const scope = effectiveOfferScope(\n daemon.offerScope,\n daemon.cost,\n daemon.spend,\n );\n\n if (sameOwner) {\n // A daemon always runs its own owner's work, at any scope.\n return ALLOWED;\n }\n\n // Order matters for the message, not the outcome: all three refuse, and a\n // volunteer debugging their setup needs to know which truth applies.\n if (daemon.cost === \"subscription\") {\n return refuse(\"subscription-self-lock\");\n }\n if (daemon.cost === \"metered\") {\n if (daemon.spend?.acknowledged !== true) {\n return refuse(\"metered-no-spend-consent\");\n }\n if (daemon.spend.ceilingReached === true) {\n return refuse(\"metered-ceiling-reached\");\n }\n }\n\n switch (scope) {\n case \"private\":\n return refuse(\"offer-scope-too-narrow\");\n case \"team\":\n // The device decides, always. There is deliberately no branch here that\n // returns ALLOWED without asking it — `public` was that branch, and its\n // removal is what makes this switch a verification rather than a\n // lookup. Whatever supplies `admits` may change (byollm_016\n // Amendment J replaces a local list with a signed claim-time grant);\n // that it is *consulted* may not.\n return daemon.admits(job.owner) ? ALLOWED : refuse(\"not-locally-allowed\");\n }\n}\n\n/**\n * Human-readable refusal text for the daemon's log and the trust UI.\n * Each refusal reads as a distinct truth — byollm_002's \"four different\n * truths that must never share a message\" applied to the audience axis.\n */\nexport const REFUSAL_MESSAGES: Readonly<Record<MatchRefusal, string>> =\n Object.freeze({\n \"no-capability\":\n \"no backend on this device is configured and healthy for that job kind\",\n \"audience-self-other-owner\":\n \"the job is private to its owner and this device is paired to someone else\",\n \"not-locally-allowed\":\n \"nothing this device can verify says the job's owner may use it\",\n \"not-in-server-allowlist\":\n \"the app restricted this job to named runners and this device is not one of them\",\n \"offer-scope-too-narrow\":\n \"this service is offered to its owner only (`byollm offer <service> team` to widen)\",\n \"subscription-self-lock\":\n \"subscription-backed models run their owner's work only — this is a protocol rule, not a setting\",\n \"metered-no-spend-consent\":\n \"this backend bills its owner per token, and they have not agreed to spend it on other people's work\",\n \"metered-ceiling-reached\":\n \"this backend is shared but has reached the spend ceiling its owner set\",\n });\n","import { isIP } from \"node:net\";\nimport { z } from \"zod\";\n\n/**\n * How a backend reaches its model — the taxonomy introduced in byollm_001\n * Rev 1 §A, because the two classes have different threat surfaces.\n *\n * - `http`: an OpenAI-compatible HTTP server (Ollama, `mlx_lm.server`,\n * llama.cpp server, vLLM, and every hosted provider that speaks the same\n * wire format). Spawns nothing, so byollm_004 §2's argv, stdin, env and\n * sandbox requirements are not applicable by construction. Its threat\n * surface is SSRF-shaped and bounded by {@link MUSTS.HTTP_BASE_URL_SAFE}.\n * - `process`: spawns a binary (`claude` CLI today, `mlx_lm.lora` for a\n * future `train.*` kind). All of byollm_004 §2 is mandatory here.\n */\nexport const BackendClass = z.enum([\"http\", \"process\"]);\n\n/**\n * Its members, for anything that has to report what it accepts.\n *\n * Derived from the enum for the reason `JOB_KINDS` and `OFFER_SCOPES` are: a\n * second list of the same words is a second thing to keep in step, and this\n * one is read by the promotion gate to compare a deployed hub against a\n * version about to be promoted.\n */\nexport const BACKEND_CLASSES = Object.freeze(BackendClass.options);\nexport type BackendClass = z.infer<typeof BackendClass>;\n\n/**\n * Who pays, and how — byollm_007.\n *\n * This replaced a two-valued `account` field that conflated two unrelated\n * constraints and, in doing so, left a hole: `openai-http` was \"open\", but it\n * accepts an API key, so an owner could point it at a paid endpoint, share it,\n * and donate their credit balance to strangers. The community budgets cap job\n * *count*, not spend.\n *\n * - `free` — local compute. Costs electricity, not money. Shareable.\n * - `metered` — per-token billing against the owner's account. Legal to\n * share and ruinous to share by accident.\n * - `subscription` — a vendor account whose terms forbid third-party work.\n * Sharing is a terms violation, not merely expensive.\n */\nexport const BackendCost = z.enum([\"free\", \"metered\", \"subscription\"]);\nexport type BackendCost = z.infer<typeof BackendCost>;\n\n/** The immutable facts about a backend that the protocol reasons over. */\nexport interface BackendDescriptor {\n /** Stable backend id, as written in `byollm.config.json`. */\n readonly id: string;\n /** Human-readable name for the trust UI. */\n readonly label: string;\n /** Determines which isolation requirements apply. */\n readonly class: BackendClass;\n /**\n * Who pays. Fixed here for every named provider and **not overridable by\n * configuration** ({@link MUSTS.COST_NOT_CONFIGURABLE}) — `openai` is\n * metered because it is, and no setting changes that.\n *\n * `null` only for the generic {@link BACKENDS.\"openai-http\"} entry, whose\n * cost is inferred from its base URL instead\n * ({@link MUSTS.REMOTE_IS_NEVER_FREE}).\n */\n readonly cost: BackendCost | null;\n /**\n * Which adversarial corpus byollm_004 §5 runs against this backend. A\n * backend cannot be registered without one — the coverage check in the\n * adversarial suite enforces it.\n */\n readonly adversarialCorpus: \"process\" | \"http\";\n /**\n * Where this provider lives, when that is knowable. Owner config may\n * override it; a provider with no default requires one to be given.\n */\n readonly defaultBaseUrl?: string;\n}\n\nconst backend = (b: BackendDescriptor): BackendDescriptor => Object.freeze(b);\n\n/**\n * The backend registry.\n *\n * **Providers are entries, not implementations.** Every HTTP-class provider\n * below shares the single `openai-http` transport, because they all speak\n * OpenAI-compatible `/v1/chat/completions`. An entry adds a stable id, a cost\n * class the owner cannot override, and a default base URL. Adding a provider\n * is therefore one line and no new code — which is why the adversarial corpus\n * still covers all of them, and why a PR adding one is reviewable at a glance.\n */\nexport const BACKENDS = Object.freeze({\n // -- free: local compute, costs electricity ------------------------------\n ollama: backend({\n id: \"ollama\",\n label: \"Ollama (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:11434/v1\",\n }),\n mlx: backend({\n id: \"mlx\",\n label: \"MLX (mlx_lm.server, local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8080/v1\",\n }),\n llamacpp: backend({\n id: \"llamacpp\",\n label: \"llama.cpp server (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8080/v1\",\n }),\n vllm: backend({\n id: \"vllm\",\n label: \"vLLM (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8000/v1\",\n }),\n lmstudio: backend({\n id: \"lmstudio\",\n label: \"LM Studio (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:1234/v1\",\n }),\n jan: backend({\n id: \"jan\",\n label: \"Jan (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:1337/v1\",\n }),\n localai: backend({\n id: \"localai\",\n label: \"LocalAI (local)\",\n class: \"http\",\n cost: \"free\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"http://127.0.0.1:8080/v1\",\n }),\n\n // -- metered: the owner's money, per token -------------------------------\n /**\n * Note the pair: `anthropic` and {@link BACKENDS.\"claude-cli\"} reach the\n * same vendor and land in different cost classes. That is not an\n * inconsistency — it is the axis working. One bills a key per token, the\n * other runs under a personal plan whose terms cover one person's work. Who\n * pays and under what terms is the question; which company is not.\n */\n anthropic: backend({\n id: \"anthropic\",\n label: \"Anthropic (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.anthropic.com/v1\",\n }),\n openai: backend({\n id: \"openai\",\n label: \"OpenAI (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.openai.com/v1\",\n }),\n gemini: backend({\n id: \"gemini\",\n label: \"Google Gemini (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n }),\n grok: backend({\n id: \"grok\",\n label: \"xAI Grok (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.x.ai/v1\",\n }),\n groq: backend({\n id: \"groq\",\n label: \"Groq (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.groq.com/openai/v1\",\n }),\n openrouter: backend({\n id: \"openrouter\",\n label: \"OpenRouter (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://openrouter.ai/api/v1\",\n }),\n together: backend({\n id: \"together\",\n label: \"Together AI (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.together.xyz/v1\",\n }),\n deepseek: backend({\n id: \"deepseek\",\n label: \"DeepSeek (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.deepseek.com/v1\",\n }),\n mistral: backend({\n id: \"mistral\",\n label: \"Mistral (your API key)\",\n class: \"http\",\n cost: \"metered\",\n adversarialCorpus: \"http\",\n defaultBaseUrl: \"https://api.mistral.ai/v1\",\n }),\n\n // -- the escape hatch ----------------------------------------------------\n \"openai-http\": backend({\n id: \"openai-http\",\n label: \"Any OpenAI-compatible server\",\n class: \"http\",\n // Unknown until the base URL is known: local means free, remote means\n // metered, and the owner does not get to say otherwise\n // ({@link MUSTS.REMOTE_IS_NEVER_FREE}).\n cost: null,\n adversarialCorpus: \"http\",\n }),\n\n // -- subscription: someone else's terms ----------------------------------\n \"claude-cli\": backend({\n id: \"claude-cli\",\n label: \"Claude CLI (your subscription)\",\n class: \"process\",\n cost: \"subscription\",\n adversarialCorpus: \"process\",\n }),\n /**\n * OpenAI's Codex CLI, on a ChatGPT plan — byollm_016 stage 3.\n *\n * `subscription`, so `SUBSCRIPTION_SELF_LOCK` pins it to its owner's own\n * work whatever the config says. That is load-bearing here in a way it is\n * not for `claude-cli`: Codex is an *agent*, and its default feature set\n * includes a shell tool, browser control and computer use. The daemon\n * disables every one of them, verified against the shipped binary rather\n * than assumed — see `codex-cli.ts` — but the self-lock is the floor under\n * that verification rather than a duplicate of it.\n */\n \"codex-cli\": backend({\n id: \"codex-cli\",\n label: \"Codex CLI (your ChatGPT plan)\",\n class: \"process\",\n cost: \"subscription\",\n adversarialCorpus: \"process\",\n }),\n} as const satisfies Record<string, BackendDescriptor>);\n\n/** The id of a registered backend. */\nexport type BackendId = keyof typeof BACKENDS;\n\n/** All registered backend ids — the adversarial coverage check iterates this. */\nexport const BACKEND_IDS = Object.freeze(Object.keys(BACKENDS) as BackendId[]);\n\nexport const BackendIdSchema = z.enum(\n BACKEND_IDS as [BackendId, ...BackendId[]],\n);\n\n/** Narrow an arbitrary string to a registered backend id. */\nexport function isBackendId(value: string): value is BackendId {\n return Object.hasOwn(BACKENDS, value);\n}\n\n/**\n * Look up a backend descriptor.\n *\n * @throws if the id is not registered — an unregistered backend has no\n * adversarial corpus, so refusing is the safe direction.\n */\nexport function backendDescriptor(id: BackendId): BackendDescriptor {\n return BACKENDS[id];\n}\n\n/**\n * Is this host local enough that compute there is free?\n *\n * Loopback and the private ranges only. This is the rule that makes\n * {@link MUSTS.REMOTE_IS_NEVER_FREE} enforceable rather than a promise: an\n * owner cannot reach a paid API through the generic backend and call it free,\n * because \"free\" is derived from the address, not from what the config claims.\n *\n * **What this cannot see.** The address is all it reads. A proxy on\n * `127.0.0.1` forwarding to a paid API classes as `free` and nothing\n * downstream will contradict it. That is deliberate: standing up a relay is\n * an act by the machine's owner against their own account, and the threat\n * model here is a hostile *job*, not an owner routing around a rule that\n * exists to protect them. What this catches is the accident — a remote paid\n * endpoint offered to a team because nobody thought about the bill. See\n * `docs/security.md` §4a.\n */\nexport function isLocalHost(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/^\\[|\\]$/g, \"\");\n\n // RFC 6761 reserves `localhost` and everything under it for the loopback\n // interface. The only names that are local; every other name is a name.\n if (host === \"localhost\" || host.endsWith(\".localhost\")) return true;\n\n // Everything below is a prefix test on an address, so it only runs on an\n // address — cloud_008 §0.5.\n //\n // These used to run on the raw string. `startsWith(\"10.\")` matched\n // `10.example.com`, `startsWith(\"192.168.\")` matched `192.168.example.com`,\n // and `/^f[cd]/` — meant for `fc00::/7` — matched **any hostname beginning\n // with the letters f-c or f-d**: `fdapi.example.com`, `fchat.ai`,\n // `fc-inference.io`. A paid remote endpoint at such a name resolved to\n // `free`, which is `REMOTE_IS_NEVER_FREE` inverted: no ceiling, no metering,\n // and eligible to be shared. The worst of them needs no attacker and\n // no unusual config — just a vendor whose domain happens to start with two\n // particular letters.\n //\n // `isIP` is `node:net`'s, the same guard `checkBaseUrl` uses. Two questions,\n // one technique: that file asks whether an address is a forbidden\n // destination, this one asks whether it is on the owner's own machine or\n // LAN. Neither reuses the other's *rule* — cloud_007 §2 said it did, which\n // was never true — but nothing hand-parses an address in either.\n const version = isIP(host);\n\n // A DNS name is remote. It might resolve to loopback, and this deliberately\n // does not find out: the alternative is a DNS lookup inside a cost decision,\n // where the answer can change between the check and the request. Metered is\n // the safe side of being wrong — it costs a local user a ceiling they did\n // not need, where the other direction costs a remote user money.\n if (version === 0) return false;\n\n if (version === 6) {\n if (host === \"::1\") return true;\n // Unique local addresses (fc00::/7), now that this can only see an\n // address. An IPv4-mapped form like `::ffff:127.0.0.1` is not matched and\n // classes as metered — the safe side again, and rare enough that guessing\n // at it would add more surface than it removes.\n return /^f[cd]/.test(host);\n }\n\n if (host.startsWith(\"127.\")) return true;\n if (host.startsWith(\"10.\")) return true;\n if (host.startsWith(\"192.168.\")) return true;\n return /^172\\.(1[6-9]|2\\d|3[01])\\./.test(host);\n}\n\n/**\n * Is this model name a hosted one billed by its vendor?\n *\n * Ollama serves cloud models through the same local endpoint as local ones,\n * so the address says \"free\" about a model somebody is being charged for. The\n * only thing that distinguishes them is the name, and the distinguishing part\n * is the **tag** — everything after the last colon.\n *\n * End-anchored on the tag, which is what makes it decidable rather than a\n * guess about substrings:\n *\n * - `glm-5.2:cloud` → cloud\n * - `deepseek-v4-flash:0731-cloud` → cloud\n * - `x:cloudless` → not cloud, the tag ends in \"less\"\n * - `cloudmodel:7b` → not cloud, the tag is \"7b\"\n * - `llama3.2` → not cloud, there is no tag at all\n *\n * An oddball like `:xcloud` classifies as cloud, and that is the **only\n * permitted failure direction**: calling a free model metered narrows what an\n * owner may share and costs nobody money, while the reverse hands somebody\n * else's bill to a stranger.\n */\nexport function isCloudTaggedModel(model: string): boolean {\n return /:[^:]*cloud$/.test(model);\n}\n\n/**\n * The cost class of a configured service.\n *\n * For every named provider this is whatever the registry says, full stop\n * ({@link MUSTS.COST_NOT_CONFIGURABLE}). For the generic `openai-http` entry\n * it is inferred from the base URL, and a base URL that cannot be parsed is\n * treated as `metered` — the expensive side, because guessing \"free\" wrong\n * costs the owner money.\n *\n * The model has the last word in one direction only. A local address with a\n * cloud-tagged model is `metered`: Ollama proxies hosted models through\n * `127.0.0.1`, so the endpoint is local and the bill is not. Read from the\n * **configured value**, never from what the server lists — the owner's config\n * is the thing they chose, and a server's catalogue is not theirs to be\n * classified by.\n */\nexport function resolveCost(\n id: BackendId,\n baseUrl: string | undefined,\n /**\n * **Required, and that is the fix.**\n *\n * This was optional, and the no-re-derivation law was breached through the\n * gap rather than by anybody copying the logic. `byollm offer` passed two of\n * three arguments and `resolveConfig` passed three, so the same service was\n * free to one and metered to the other: `glm-5.2:cloud` on a loopback\n * address looks local until you read the tag. The command wrote a share the\n * daemon then refused, and told its owner to run the command they had just\n * run.\n *\n * A shared rule's signature admits no partial askers. `undefined` is still a\n * legal *value* — a service genuinely without a model — but it has to be\n * passed, so choosing to omit the model is a decision at the call site\n * rather than a default nobody notices.\n */\n model: string | undefined,\n): BackendCost {\n return classifyCost(id, baseUrl, model).cost;\n}\n\n/**\n * Why a service costs what it costs — the same decision, said out loud.\n *\n * Consent has to name the rule that fired. The offer ceremony read\n * \"Any OpenAI-compatible server ... bills your account per token\", which is\n * false about the type — an owner's local qwen is `openai-http` and costs\n * nothing but electricity — and so it gave a reason that its reader could\n * check and find wrong. The thing that bills is the `:cloud` tag on one\n * model, not the transport that carries it.\n *\n * One function decides and one function explains, and the second calls the\n * first, so a message can never describe a classification the code did not\n * make. Splitting them would be the same defect this signature was just\n * hardened against, arriving as prose.\n */\n/**\n * The product's name alone, without the parenthetical that classifies it.\n *\n * Every label in this registry does two jobs: it names a product and says what\n * that product means for the person paying — \"Claude CLI (your subscription)\",\n * \"Ollama (local)\". That is right for a list, where the parenthetical is the\n * only classification on screen.\n *\n * It is wrong inside a sentence that states the classification itself, which\n * then stutters: \"my-claude runs on Claude CLI (your subscription), a\n * subscription whose terms…\". Prose wants the name; the sentence around it is\n * already carrying the meaning.\n *\n * One definition rather than a regex at each call site — and the place to\n * change if the registry ever splits the two facts into two fields, which is\n * the better shape and not worth a migration today.\n */\nexport function backendName(id: BackendId): string {\n return BACKENDS[id].label.replace(/\\s*\\([^)]*\\)$/, \"\");\n}\n\nexport interface CostReason {\n readonly cost: BackendCost;\n /** The rule, in the words a person consenting needs. */\n readonly because: string;\n}\n\nexport function classifyCost(\n id: BackendId,\n baseUrl: string | undefined,\n model: string | undefined,\n): CostReason {\n const declared = BACKENDS[id].cost;\n if (declared !== null) {\n // A named provider's cost is the registry's word and nothing else\n // [COST_NOT_CONFIGURABLE], so here — and only here — the provider's own\n // name is the honest reason. Each class gets its own sentence: a free\n // provider described as billing per token is the same defect as a cloud\n // model described as a generic endpoint.\n const label = BACKENDS[id].label;\n return {\n cost: declared,\n because: {\n subscription: `${label} runs on an account you subscribe to`,\n metered: `${label} bills per token`,\n free: `${label} runs on this machine`,\n }[declared],\n };\n }\n if (model !== undefined && isCloudTaggedModel(model)) {\n return {\n cost: \"metered\",\n because:\n `its model tag ends in \\`:cloud\\`, so the work runs on your ` +\n `provider's cloud account rather than on this machine`,\n };\n }\n if (baseUrl === undefined) {\n return {\n cost: \"metered\",\n because: \"it has no address, so where the work runs cannot be checked\",\n };\n }\n try {\n return isLocalHost(new URL(baseUrl).hostname)\n ? { cost: \"free\", because: \"it runs on this machine\" }\n : {\n cost: \"metered\",\n because: \"its address is not on this machine, so the work leaves it\",\n };\n } catch {\n return {\n cost: \"metered\",\n because: \"its address cannot be read, so where the work runs is unknown\",\n };\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Upper bounds on payload size, enforced at the schema so oversized input is\n * refused at parse time rather than somewhere deeper.\n *\n * All three are enforced — cloud_008 Tier 4, finding 30. `maxTotalChars` was\n * declared here and referenced nowhere, under this docstring's claim that the\n * schema enforces them, so a chat payload of 256 messages at a million\n * characters each parsed cleanly at sixty-four times the stated ceiling. The\n * per-field limits were real and the aggregate one was a number in a frozen\n * object.\n *\n * byollm_004 §4 requires stricter limits for community (`team`) jobs; those\n * are applied on top of these by the daemon's budget check, which knows the\n * job's audience. These are the absolute ceilings for any job.\n */\nexport const PAYLOAD_LIMITS = Object.freeze({\n /** Max characters in any single text field. */\n maxTextChars: 1_000_000,\n /** Max messages in an `llm.chat` conversation. */\n maxMessages: 256,\n /** Max characters across the whole payload. */\n maxTotalChars: 4_000_000,\n});\n\n/**\n * A conversation turn. `role` is a closed enum — it is routing *within the\n * model call*, not routing of the call, so it cannot select a backend.\n */\nexport const ChatMessage = z\n .object({\n role: z.enum([\"system\", \"user\", \"assistant\"]),\n content: z.string().max(PAYLOAD_LIMITS.maxTextChars),\n })\n /**\n * Strict, like everything else on the wire — byollm-review 2026-08-27.\n *\n * This is the shape the law was written about. A site SDK user sends\n * `{role, content, tool_calls: [...]}` expecting tool use; zod's default\n * strips the unknown key, the job runs meaning something other than what\n * was sent, and nothing anywhere says so. \"Unknown fields throw\" exists\n * precisely so a version skew is an error rather than a silent difference\n * in what the model was asked.\n */\n .strict();\nexport type ChatMessage = z.infer<typeof ChatMessage>;\n\n/**\n * Payload for `llm.generate`.\n *\n * @remarks\n * Text only, deliberately. byollm_004 §1 states the payload is \"data handed\n * to a model, never configuration and never a command\", so v0 carries no\n * sampling parameters, no model name, no base URL and no flags — those are\n * owner-side route config. A future `params` field with an explicit closed\n * allowlist and owner-set clamps is reserved; adding a field later is\n * non-breaking, removing one is not.\n */\nexport const GeneratePayload = z\n .object({\n prompt: z.string().min(1).max(PAYLOAD_LIMITS.maxTextChars),\n system: z.string().max(PAYLOAD_LIMITS.maxTextChars).optional(),\n })\n .strict()\n .refine(\n (payload) =>\n payload.prompt.length + (payload.system?.length ?? 0) <=\n PAYLOAD_LIMITS.maxTotalChars,\n {\n message: `payload exceeds ${String(PAYLOAD_LIMITS.maxTotalChars)} characters`,\n },\n );\nexport type GeneratePayload = z.infer<typeof GeneratePayload>;\n\n/** Payload for `llm.chat`. Text only, for the same reason as {@link GeneratePayload}. */\nexport const ChatPayload = z\n .object({\n messages: z.array(ChatMessage).min(1).max(PAYLOAD_LIMITS.maxMessages),\n system: z.string().max(PAYLOAD_LIMITS.maxTextChars).optional(),\n })\n .strict()\n // The aggregate, which is the one that matters here: the per-message and\n // per-count limits multiply, and their product is far above the ceiling\n // this object states.\n .refine(\n (payload) =>\n payload.messages.reduce((sum, m) => sum + m.content.length, 0) +\n (payload.system?.length ?? 0) <=\n PAYLOAD_LIMITS.maxTotalChars,\n {\n message: `payload exceeds ${String(PAYLOAD_LIMITS.maxTotalChars)} characters`,\n },\n );\nexport type ChatPayload = z.infer<typeof ChatPayload>;\n\n/**\n * The job kinds a v1 daemon has handlers for.\n *\n * Kinds are resolved against handlers baked into the daemon\n * ({@link MUSTS.KIND_TYPED_ONLY}); an unknown kind is refused, never guessed.\n * Adding a kind is a protocol change with its own spec and threat review —\n * notably any kind that needs tools, which byollm_004 §2 forbids as a payload\n * flag.\n */\nexport const JobKind = z.enum([\"llm.generate\", \"llm.chat\"]);\nexport type JobKind = z.infer<typeof JobKind>;\n\n/** All v1 job kinds. */\nexport const JOB_KINDS = Object.freeze(JobKind.options);\n\n/** A payload discriminated by its kind. */\nexport const KindedPayload = z.discriminatedUnion(\"kind\", [\n // Strict on the wrappers too. A union member that strips is a door beside\n // the one that is locked: the payloads inside are strict, and an extra key\n // on the envelope vanished just as quietly.\n z\n .object({ kind: z.literal(\"llm.generate\"), payload: GeneratePayload })\n .strict(),\n z.object({ kind: z.literal(\"llm.chat\"), payload: ChatPayload }).strict(),\n]);\nexport type KindedPayload = z.infer<typeof KindedPayload>;\n\n/** The payload type for a given kind. */\nexport type PayloadFor<K extends JobKind> = K extends \"llm.generate\"\n ? GeneratePayload\n : ChatPayload;\n\n/** Narrow an arbitrary string to a known job kind. */\nexport function isJobKind(value: string): value is JobKind {\n return (JOB_KINDS as readonly string[]).includes(value);\n}\n\n/**\n * Total character weight of a payload, used by the daemon's community budget\n * check and by the server's payload-size limits.\n */\nexport function payloadTextLength(kinded: KindedPayload): number {\n if (kinded.kind === \"llm.generate\") {\n return kinded.payload.prompt.length + (kinded.payload.system?.length ?? 0);\n }\n const messages = kinded.payload.messages.reduce(\n (sum, m) => sum + m.content.length,\n 0,\n );\n return messages + (kinded.payload.system?.length ?? 0);\n}\n","import { z } from \"zod\";\nimport { Audience } from \"./audience.js\";\nimport { SignedGrant } from \"./grant.js\";\nimport { BackendClass } from \"./backends.js\";\nimport { ChatPayload, GeneratePayload, JobKind } from \"./kinds.js\";\n\n/**\n * The job lifecycle, made explicit by byollm_001 Rev 1 §D because the most\n * user-visible failure mode — \"nothing is running my job\" — was previously\n * unspecified.\n *\n * ```text\n * queued ──claim──▶ claimed ──start──▶ running ──▶ ok | error | canceled\n * │ │ │\n * │ └──lease expiry─────┘\n * │ ▼\n * │ queued (reclaimable, no loss)\n * └──ttl elapsed──▶ expired\n * ```\n */\nexport const JobState = z.enum([\n \"queued\",\n \"claimed\",\n \"running\",\n \"ok\",\n \"error\",\n \"canceled\",\n \"expired\",\n]);\nexport type JobState = z.infer<typeof JobState>;\n\n/** States from which a job never moves again. */\nexport const TERMINAL_STATES = Object.freeze([\n \"ok\",\n \"error\",\n \"canceled\",\n \"expired\",\n] as const satisfies readonly JobState[]);\n\n/** Is this a state the job can never leave? */\nexport function isTerminal(state: JobState): boolean {\n return (TERMINAL_STATES as readonly JobState[]).includes(state);\n}\n\n/**\n * The legal transitions. Held as data so the store adapters and the\n * conformance kit agree on one definition rather than three implementations.\n */\nconst TRANSITIONS: Readonly<Record<JobState, readonly JobState[]>> =\n Object.freeze({\n queued: [\"claimed\", \"expired\", \"canceled\"],\n // A claimed job returns to `queued` when its lease expires un-renewed\n // ({@link MUSTS.LEASE_RECLAIMABLE}).\n claimed: [\"running\", \"queued\", \"canceled\", \"error\"],\n running: [\"ok\", \"error\", \"canceled\", \"queued\"],\n ok: [],\n error: [],\n canceled: [],\n expired: [],\n });\n\n/** May a job move from `from` to `to`? */\nexport function canTransition(from: JobState, to: JobState): boolean {\n return TRANSITIONS[from].includes(to);\n}\n\n/** A lease: the right to work on a job until `expiresAt`. */\nexport const Lease = z\n .object({\n /**\n * Identifies *this* grant, not just its holder.\n *\n * A runner can hold a job, release it, and claim it again — three leases,\n * one runner id. Without an id for the grant itself, a lease-scoped request\n * names a mutable target ambiguously, and a replayed release from the first\n * grant lands on the third: the job returns to the queue while the daemon\n * is mid-execution, and the work runs twice on the owner's hardware.\n *\n * That was a live hole, found in review after signed requests shipped. The\n * signature scheme's replay argument rests on endpoints being idempotent —\n * and release *is*, per lease, but not across leases, because nothing in\n * the request said which one.\n */\n id: z.string().min(1),\n /** The runner holding the lease. */\n runnerId: z.string().min(1),\n /** Epoch milliseconds after which the claim is void. */\n expiresAt: z.number().int().positive(),\n })\n // Strict, like every other wire shape. An intermediary padding a lease\n // with extra fields would have had them accepted and dropped, which is how\n // version drift hides instead of surfacing.\n .strict();\nexport type Lease = z.infer<typeof Lease>;\n\n/** Payload union as it appears on a job record. */\nexport const JobPayload = z.union([GeneratePayload, ChatPayload]);\nexport type JobPayload = z.infer<typeof JobPayload>;\n\n/**\n * A job as the daemon receives it from `/byollm/claim`.\n *\n * Note what is absent: no model, no backend, no base URL, no flags, no path.\n * Those come from the machine owner's config only\n * ({@link MUSTS.NO_PAYLOAD_ROUTING}). The wire shape is the first place that\n * rule is enforced — there is no field to carry them.\n */\nexport const ClaimedJob = z\n .object({\n id: z.string().min(1),\n kind: JobKind,\n payload: JobPayload,\n audience: Audience,\n /** The app's id for the user who enqueued it. */\n owner: z.string().min(1),\n /**\n * Which site's job — V1-3.\n *\n * The stub has always carried it; the opened job did not, so everything\n * downstream of the payload — the ingress line above all — recorded a job\n * id that belongs to a site without saying which. Two sites can choose\n * the same id, and the meter is the product.\n *\n * Optional so a caller assembling a job by hand is not forced to invent\n * one, and so this reads as what it is: a fact about where the work came\n * from, not a second copy of the routing key.\n */\n site: z.string().min(1).optional(),\n /**\n * Which of the owner's services runs this — resolved, not requested.\n *\n * The daemon picks the backend from this, so it has to be the answer\n * rather than a wish. On a relayed route it is copied off the **grant**,\n * where a control plane put the person's own mapping and signed it; a\n * site never named it and could not.\n *\n * It used to be what the site asked for, which made a job that selected a\n * non-default service liable to be served by the default instead — the\n * substitution `NO_PAYLOAD_ROUTING` forbids. Amendment L removed the\n * asking; what is left is the answering.\n *\n * Optional, because direct mode has no control plane to resolve anything\n * and the owner's own defaults answer under the ambiguity law.\n */\n service: z.string().min(1).optional(),\n lease: Lease,\n })\n .strict();\nexport type ClaimedJob = z.infer<typeof ClaimedJob>;\n\n/**\n * The provenance that travels with every result to the delivery seam.\n *\n * byollm_003 Rev 1: a `team` result is attacker-controlled text.\n * The app must never render volunteer output as its own AI's answer without\n * knowing that is what it is ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).\n */\nexport const ResultProvenance = z\n .object({\n /** The audience the job ran under. */\n audience: Audience,\n /** The runner that produced it. */\n runnerId: z.string().min(1),\n /** The runner owner's id in this app's namespace. */\n runnerOwner: z.string().min(1),\n /** Which backend class produced it — an HTTP call or a sandboxed spawn. */\n backendClass: BackendClass,\n /** The model the runner reports having used. */\n model: z.string().min(1),\n /**\n * False only for `self` jobs. When true the app MUST treat `text` as\n * untrusted third-party content.\n */\n untrusted: z.boolean(),\n })\n .strict();\nexport type ResultProvenance = z.infer<typeof ResultProvenance>;\n\n/**\n * Build provenance for a completed job. `untrusted` is derived, never\n * supplied, so no caller can mark volunteer output as first-party.\n */\nexport function provenanceFor(input: {\n audience: Audience;\n runnerId: string;\n runnerOwner: string;\n backendClass: BackendClass;\n model: string;\n}): ResultProvenance {\n return {\n audience: input.audience,\n runnerId: input.runnerId,\n runnerOwner: input.runnerOwner,\n backendClass: input.backendClass,\n model: input.model,\n untrusted: input.audience !== \"private\",\n };\n}\n\n/**\n * What the daemon did, sealed with the answer — cloud_008 §2.5.\n *\n * These travelled in the clear on `ResultRequest`, which meant two things at\n * once. On the direct plane the site believed unauthenticated fields beside\n * an authenticated envelope — a daemon could seal one answer and *declare* it\n * came from a different model, and only the field it did not sign would be\n * recorded. Through a relay they reached a third party that acts on none of\n * them, and `model` in particular is the kind of detail Amendment A's rule\n * keeps off the wire.\n *\n * Sealed, they are the daemon's signed statement about its own run: the site\n * opens them, nothing in between sees them, and the disposition check that\n * already compares clear-text against ciphertext extends to cover them.\n */\nexport const RunMetadata = z\n .object({\n /** Which model actually served it. */\n model: z.string().min(1),\n backendClass: BackendClass,\n /** Wall-clock milliseconds the backend call took. */\n durationMs: z.number().int().nonnegative(),\n })\n .strict();\nexport type RunMetadata = z.infer<typeof RunMetadata>;\n\n/** Successful outcome. */\nexport const JobResultOk = z\n .object({\n outcome: z.literal(\"ok\"),\n text: z.string(),\n /** Optional reference to a stored artifact; never a local path. */\n artifactUrl: z.url().optional(),\n })\n .strict();\n\n/** Failed outcome. `code` is a stable machine string; `message` is for humans. */\nexport const JobResultError = z\n .object({\n outcome: z.literal(\"error\"),\n code: z.string().min(1),\n message: z.string().min(1),\n /** Whether the app may reasonably re-enqueue. */\n retryable: z.boolean(),\n })\n .strict();\n\n/** Cancelled outcome, reported by the daemon after honoring a cancel. */\nexport const JobResultCanceled = z\n .object({\n outcome: z.literal(\"canceled\"),\n })\n .strict();\n\nexport const JobOutcome = z.discriminatedUnion(\"outcome\", [\n JobResultOk,\n JobResultError,\n JobResultCanceled,\n]);\nexport type JobOutcome = z.infer<typeof JobOutcome>;\n\n/**\n * Why a job can never run — byollm_016 Phase B.\n *\n * Every one of these is **terminal**, and that is the whole point of naming\n * them. A job that cannot be matched used to sit queued until its deadline,\n * which reads exactly like a job that is merely waiting for a device to come\n * online — so an app could not tell \"any moment now\" from \"never\", and neither\n * could the person watching a spinner. Silence must never read as pending.\n *\n * They are decided by whoever knows first: the site's own SDK where it can see\n * the answer without asking, the router where matching happens, and the daemon\n * again on arrival under the both-sides rule. All three reason from the same\n * list rather than three private vocabularies.\n */\nexport const RefusalReason = z.enum([\n /**\n * Two or more services answer this kind and the owner has named no default,\n * so the kind is withheld. Nobody may pick on the owner's behalf — the wrong\n * guess is the metered one.\n *\n * Told apart from its neighbour deliberately, and the line is whether a\n * requester can walk a namespace. There are two kinds; asking about one\n * enumerates nothing they could not learn from what the device advertises,\n * and the difference is actionable — \"the owner has not chosen\" is fixable\n * by the owner, \"the default cannot serve you\" is not. It is also already\n * what a team member sees on the devices page: `awaitingDefault` carries\n * exactly this, by kind, for exactly this reason.\n */\n \"default-ambiguity\",\n /**\n * A default exists and this requester can never use it — byollm_016's\n * defaults-meet-audiences corner.\n *\n * The specimen: an owner's default for `llm.chat` is their Claude\n * subscription, self-locked by `SUBSCRIPTION_SELF_LOCK`. A team member's\n * job resolves to it and can never be served by it. That must be a refusal\n * on the spot, not a wait that expires an hour later looking like nobody\n * was online.\n *\n * Bounded like the value above, and unprobeable for the same reason: the\n * requester named nothing, so there is no name space to walk.\n */\n \"default-unusable\",\n]);\nexport type RefusalReason = z.infer<typeof RefusalReason>;\n\n/**\n * A terminal outcome nobody sealed — byollm_016 Phase B.\n *\n * Every other finished job carries an envelope encrypted by the device that\n * ran it, which is what makes a result unforgeable. These have no device: the\n * job was refused *before* anything could run it, so there is nobody to seal\n * from and no content to seal.\n *\n * **What that costs, stated plainly.** This is the one terminal outcome a\n * router can author. It is worth being exact about the power that grants,\n * because \"the relay can write this\" sounds alarming until you compare it with\n * what a relay could already do: drop the job, never offer it, and let it\n * expire. A router-authored refusal is *denial of service by a shorter route*,\n * which is a power the router has always had and which the trust model has\n * always said it has. What it emphatically is **not** is forgery: this shape\n * carries no envelope and no output, so it can never be mistaken for an answer\n * a device produced. A relay still cannot fabricate a result, because that\n * needs a signature it does not hold.\n *\n * So the rule this shape enforces by construction: a refusal may deny, and may\n * never assert. Anything that claims work was *done* still comes sealed.\n */\nexport const JobRefused = z\n .object({\n outcome: z.literal(\"refused\"),\n reason: RefusalReason,\n /** Plain words for a human reading a log, never parsed. */\n message: z.string().min(1),\n })\n .strict();\nexport type JobRefused = z.infer<typeof JobRefused>;\n\n/**\n * The outward text for each reason, so a message cannot vary by call site.\n *\n * The mirror image of `REFUSAL_MESSAGES` in `audience.ts`, and the contrast is\n * worth holding in one thought. That table is read by the **owner** of the\n * device, where byollm_002's rule applies — four different truths must never\n * share a message, because a person debugging their own machine needs to know\n * which one they hit. This table is read by a **requester**, where the rule\n * inverts: two different truths must share a message exactly, because the\n * difference between them is somebody else's inventory.\n *\n * Same project, opposite requirements, and confusing them is how the oracle\n * comes back. Hence a table rather than prose at the throw site: three\n * refusals written in three places drift into three slightly different\n * sentences, and \"slightly different\" is all an oracle needs.\n */\nexport const REFUSAL_TEXT: Readonly<Record<RefusalReason, string>> =\n Object.freeze({\n \"default-ambiguity\":\n \"this device serves that kind from more than one service and its owner has not chosen which\",\n \"default-unusable\":\n \"this device's default for that kind cannot run work for you\",\n });\n\n/**\n * The plaintext inside a result envelope.\n *\n * The outcome and how it was produced, together, because they are one\n * statement by one signer. A site that opened only the outcome would be\n * trusting the envelope for the answer and the request body for everything\n * about it.\n */\nexport const SealedOutcome = z\n .object({ outcome: JobOutcome, ran: RunMetadata })\n .strict();\nexport type SealedOutcome = z.infer<typeof SealedOutcome>;\n\n/** A completed job as delivered to the app, provenance attached. */\nexport const DeliveredResult = z\n .object({\n jobId: z.string().min(1),\n state: JobState,\n outcome: JobOutcome.optional(),\n provenance: ResultProvenance.optional(),\n /**\n * Present, and always `true`, when this did not come from a runner —\n * {@link MUSTS.FALLBACK_LABELED}.\n *\n * The app's own `onNoRunner` value produced it: a hosted model, a cached\n * answer, an apology. It never travels on the wire, because nothing on\n * the wire produced it; it exists so that a result which did not come\n * from the user's own compute cannot be reported as though it did.\n *\n * A literal rather than a boolean, so `fallback: false` is not a\n * spelling anybody can reach for. The absence of this field means a\n * runner ran the job, and the *server* stamps it — an app cannot supply\n * a substitute that hides what it is.\n */\n fallback: z.literal(true).optional(),\n })\n .strict();\nexport type DeliveredResult = z.infer<typeof DeliveredResult>;\n\n/**\n * How big a payload is, in buckets — byollm_009 §6.\n *\n * A relay routes without reading, and matching a job to a machine needs some\n * notion of size. Buckets rather than byte counts because the exact figure is\n * a stronger fingerprint than the routing decision requires, and because a\n * bucket survives compression and encoding changes that an exact count does\n * not.\n *\n * **Two grains, on purpose — ratified 2026-08-28.** Metering a\n * GB-denominated plan needs real totals, and this is deliberately not where\n * they come from: exact bytes exist only as increment-only *monthly*\n * aggregates, and no per-job byte figure is ever persisted anywhere. The\n * record needs vagueness and the meter needs totals; neither borrows the\n * other's grain, which is why the consent screen's \"roughly how big\" stays\n * exactly true of everything retained about a job. If you are here because\n * you need a number, the aggregate is the one to reach for — adding a byte\n * count to this envelope would trade a promise for a convenience.\n *\n * `unbounded` exists for streamed jobs, which have no size when they start.\n * It is reserved now rather than added later: byollm_009 §8.1 — adding a\n * field to a published envelope is the v2 break all over again.\n */\nexport const SizeClass = z.enum([\"small\", \"medium\", \"large\", \"unbounded\"]);\n\n/** Its members, derived — see {@link BACKEND_CLASSES} for why. */\nexport const SIZE_CLASSES = Object.freeze(SizeClass.options);\nexport type SizeClass = z.infer<typeof SizeClass>;\n\n/**\n * The most one envelope may be, in bytes — ratified 2026-08-28.\n *\n * A **relay-memory safety rail**, not a plan feature: every tier has the same\n * ceiling, and differentiating tiers on it would be selling a safety limit as\n * a benefit. What it bounds is any single job, so no one message can make the\n * relay hold an unbounded amount of somebody else's memory.\n *\n * ## It stores nothing, and that is the design\n *\n * Enforced at ingress by refusing before acceptance, in both directions. A\n * ceiling on what the relay already has in hand needs no schema and no record:\n * the size is known for the length of the check and then it is gone. This\n * matters because the alternative — recording a size to enforce a limit\n * against — is precisely the per-job byte figure the metering ruling exists to\n * not have.\n *\n * ## Measured on the serialised envelope\n *\n * The same quantity the monthly rollup counts, deliberately. The relay stores\n * the serialised envelope and the meter measures what it stored, so a cap on\n * anything else — the ciphertext alone, the decoded length — would mean the\n * limit and the bill disagreed about what a byte is, and a job could be small\n * enough to accept and larger than it was charged as.\n */\nexport const MAX_ENVELOPE_BYTES = 10 * 1024 * 1024;\n\n/**\n * How big an envelope is, by the one measure that counts it.\n *\n * `JSON.stringify` because that is what the store persists and therefore what\n * the meter measures. Length in UTF-16 code units rather than encoded bytes:\n * it is the same number the store's own `HSTRLEN` reports, and the point of\n * this function is that one number answers both questions.\n */\nexport function envelopeBytes(envelope: unknown): number {\n // Annotated, because the lib types are wrong about this one and the lint\n // believes them: `JSON.stringify` is declared to return `string`, and\n // `JSON.stringify(undefined)` returns `undefined` at runtime. Writing the\n // type out makes the case real to the compiler rather than papering it with\n // an optional chain the linter can see is dead — and nothing has zero bytes\n // more honestly than nothing.\n const serialised = JSON.stringify(envelope) as string | undefined;\n return serialised === undefined ? 0 : serialised.length;\n}\n\n/** Where the bucket boundaries sit, in characters of payload text. */\nexport const SIZE_CLASS_LIMITS = Object.freeze({\n small: 4_000,\n medium: 64_000,\n large: Number.POSITIVE_INFINITY,\n});\n\n/**\n * The most a payload in this bucket can be.\n *\n * Used where a decision must be made from a stub, before the payload has been\n * fetched — a budget check, for instance. Charging the bucket's ceiling is the\n * conservative direction: it refuses slightly too eagerly rather than\n * admitting work that turns out larger than the budget allowed.\n *\n * `unbounded` returns `Infinity`, which fails every ceiling. That is correct\n * until byollm_006 defines how a streamed job is budgeted — failing closed on\n * a case nobody has designed beats inventing an allowance for it.\n */\nexport function sizeClassCeiling(sizeClass: SizeClass): number {\n if (sizeClass === \"unbounded\") return Number.POSITIVE_INFINITY;\n return SIZE_CLASS_LIMITS[sizeClass];\n}\n\n/** Bucket a payload by its text length. */\nexport function sizeClassOf(textChars: number): SizeClass {\n if (textChars <= SIZE_CLASS_LIMITS.small) return \"small\";\n if (textChars <= SIZE_CLASS_LIMITS.medium) return \"medium\";\n return \"large\";\n}\n\n/**\n * Everything an upstream may see about a job — byollm_009 §6.\n *\n * **This list is exhaustive and normative.** It is a commitment about the\n * metadata surface, not an accident of what the implementation happens to\n * send: an upstream that requires more has exceeded the protocol, and an\n * endpoint that emits more has leaked past it\n * ({@link MUSTS.STUB_METADATA_EXHAUSTIVE}).\n *\n * What is absent is the point. No payload, no model, no prompt, no result.\n * `kind` is here because capability matching happens upstream; if a later\n * revision moves matching to the daemon, `kind` moves into the ciphertext.\n */\nexport const JobStub = z\n .object({\n id: z.string().min(1),\n kind: JobKind,\n /** The app's id for the user who enqueued it. */\n owner: z.string().min(1),\n /**\n * Which site this job belongs to — byollm_009 Amendment A §A.3.\n *\n * **The site's identity key id**, not an id somebody assigned it. §6 has\n * listed `site` since this spec was frozen; the schema never carried it,\n * which is the drift the amendment closes.\n *\n * A key id rather than an opaque handle for one reason above the others:\n * it makes the stub *self-describing* instead of a pointer into somebody\n * else's table. A daemon holds this key id already, from pinning, so it\n * can check `stub.site` against the payload envelope's `senderKeyId`\n * without a lookup and without trusting the party that routed it. An\n * opaque id can only be believed.\n *\n * It also avoids inventing a second namespace for a thing that has a\n * canonical one — the shape of finding 41 (two owner namespaces compared\n * for equality) and of finding fourteen before it.\n *\n * Rotation is a designed transition rather than a cost: a site publishes a\n * new identity signed by the outgoing one, both are valid through an\n * overlap window, and a daemon re-keys its own map by verifying that\n * signature against the key it already pinned (§A.3.1).\n */\n site: z.string().min(1),\n audience: Audience,\n // `audienceAllow` is **not** here, and its absence is the enforcement —\n // cloud_008 §0.2.\n //\n // It was a list of the people who may run a job, travelling to every\n // routing party on every shared job. byollm_001 Rev 1 §B settled who\n // decides that long before this schema existed: *the daemon's own list\n // decides, not the server's*, and `allowlist.predicateFor(origin)` is the\n // enforcement in both lanes. So this was a second answer to a question the\n // daemon already owned — able only to agree, in which case it was\n // redundant, or to disagree, in which case nothing said which wins.\n //\n // The rule it leaves behind, which decides the next field too: **a class\n // the router acts on may travel; membership never does.** `audience` stays\n // for exactly that reason — the relay narrows on it. A roster does not\n // travel, so `ROSTER_NOT_DISCLOSED` holds here by absence, which is the\n // strongest way for a MUST to hold.\n //\n // The site keeps its own copy on `JobRecord` and still filters candidates\n // with it before offering. That is server-internal, where the party\n // holding the list authored it.\n /**\n * Which of the site's declared purposes this job serves — Amendment L.\n *\n * **A need, never a name.** The site's vocabulary is its own purposes;\n * the person's is their services; and the two never meet. This field says\n * \"writing-assistant\", and a control plane joins it to whatever that\n * person mapped it to. The site learns only whether the slot was\n * satisfiable.\n *\n * It replaced `service`, which let a site name one of the owner's\n * services directly. That field is gone from both routes (Amendment L\n * rider) and its refusal machinery with it — including the collapsed\n * `select-unavailable`, which existed so that \"no such service\" and \"not\n * offered to you\" could not be told apart. There is nothing left to\n * probe: **a vocabulary that never crosses the boundary cannot be\n * enumerated across it**, which is a stronger guarantee than the one the\n * collapse gave.\n *\n * It travels for the reason the absent `audienceAllow` establishes: *a\n * class the router acts on may travel; membership never does.* A purpose\n * is a class, and the control plane acts on it.\n *\n * Optional because direct mode has no control plane to hold a mapping and\n * is kind-only: the owner's own config and defaults answer, under the\n * ambiguity law as shipped. Absent on a relayed route resolves against\n * the site's reserved purpose, which a site that declared its own\n * purposes will not have mapped — so the slot reads as unmapped and the\n * site falls back, loudly enough and without a special case.\n *\n * A **stub** field and never a payload field, which is the line\n * `NO_PAYLOAD_ROUTING` draws: the prompt cannot reach it, so no amount of\n * user text can influence what runs.\n */\n purpose: z.string().min(1).optional(),\n sizeClass: SizeClass,\n /** Reserved for byollm_006. False until streaming exists. */\n streaming: z.boolean(),\n /** Epoch ms after which the work is pointless; bounds ciphertext retention. */\n deadlineAt: z.number().int().positive(),\n })\n .strict();\nexport type JobStub = z.infer<typeof JobStub>;\n\n/**\n * A stub, plus the lease the claiming runner now holds for it — and, on a\n * relayed route, the grant that says it may run at all.\n *\n * The grant lives here rather than on {@link JobStub} because of *when* it is\n * authored. A stub exists from enqueue; a grant is written at claim, against\n * the membership and mapping true at that moment. That timing is the whole of\n * Amendment J: a job queued yesterday for somebody removed this morning gets\n * no grant when it is finally claimed, and a roster held on the device could\n * never have known.\n *\n * Optional, and the absence is meaningful rather than lenient. A device that\n * pinned a control-plane key at pairing **requires** one — a claimed job\n * arriving without it is refused, not admitted by default. A device that\n * pinned none is in direct mode, where there is no control plane to author\n * anything and the owner's own work is the only work that runs.\n */\nexport const ClaimedStub = JobStub.extend({\n lease: Lease,\n grant: SignedGrant.optional(),\n}).strict();\nexport type ClaimedStub = z.infer<typeof ClaimedStub>;\n","import { Buffer } from \"node:buffer\";\nimport { z } from \"zod\";\nimport { signWith, verifyWith, type StoredKeys } from \"./keys.js\";\n\n/**\n * One job, one signature, one answer — byollm_016 Amendment J.\n *\n * A grant is the control plane's signed statement that a particular job may\n * run on a particular device, authored at claim time and verified against the\n * key that device pinned when it paired.\n *\n * ## What it replaced, and why the replacement is smaller\n *\n * Until 2026-08-26 a device held a signed **roster** and answered admission\n * from it. Amendment G's four properties were right and the mechanism was a\n * cache — one that bought nothing. On the cloud route the job path and the\n * roster path share fate: jobs arrive through the relay, so if the relay is\n * unreachable there are no jobs to admit and a locally held roster adds no\n * availability. What it did add was staleness, which is the only reason\n * `ROSTER_MAX_AGE_MS` existed: a bound on how long a removed person keeps\n * running. Authoring at claim collapses that bound to this document's own\n * lifetime — add somebody and their next job runs, remove them and their next\n * claim fails, including jobs already queued.\n *\n * It also collapses four questions into one signature. Consented, member,\n * admitted, and *which service* were four mechanisms answering separately;\n * they are now four fields of one statement, and the device verifies once.\n *\n * ## What it is not\n *\n * Amendment G property 1 outlawed admitting on a per-job assertion, and this\n * is per-job. The distinction is authorship: G outlawed trusting the\n * **relay's or site's unsigned** claim. A grant is signed by the control\n * plane with a key the device pinned at pairing, so the relay can withhold it\n * and cannot forge it — exactly the power a relay has over a job.\n * `RELAY_BLIND` is untouched: the relay delivers, it never authors.\n *\n * ## What the device still checks for itself\n *\n * A grant is necessary and not sufficient. Four checks stay on the device and\n * none of them is delegated:\n *\n * 1. the signature, against the pinned key;\n * 2. replay — {@link SignedGrant.grantId} is single-use;\n * 3. offer-consistency — the named service is one this device actually\n * offers, at a scope that includes this user;\n * 4. **private is absolute** — a `private` service runs for the paired owner\n * and nobody else, so no compromise of a control plane can grant somebody\n * else's job onto it.\n */\n\n/**\n * How long a grant is honoured after it was signed. Ruled 120s (2026-08-26).\n *\n * This bounds **acceptance**, not execution: a job admitted inside the window\n * runs to completion however long it takes. So the number only has to cover\n * the trip from the control plane signing to the device checking — claim,\n * deliver, verify — and every second past that is a second a captured grant\n * stays useful.\n *\n * Two minutes is generous for that trip and mean for the capture. It is also\n * the number ordinary clock drift is measured against, which is why\n * {@link CLOCK_SKEW_WARN_MS} sits well inside it: a device whose clock is off\n * by half the window would refuse real work, and must be told before it does.\n *\n * The verifier's policy, deliberately not a field on the document. An\n * `expiresAt` the signer chose would let whoever signs decide how long their\n * own statement stays good, and the party with the most reason to want a\n * longer window is the party being bounded.\n */\nexport const GRANT_MAX_AGE_MS = 120_000;\n\n/**\n * Clock disagreement past which a device says so, before it starts refusing.\n *\n * Skew eats {@link GRANT_MAX_AGE_MS} directly — a device 60s behind its\n * relay's clock has half a window left, and one 120s behind has none and\n * refuses everything for a reason no refusal message would otherwise name.\n * Thirty seconds is a quarter of the window: far enough out to be a real\n * problem, early enough to be a warning rather than an outage.\n */\nexport const CLOCK_SKEW_WARN_MS = 30_000;\n\n/**\n * Skew past which a freshness refusal names the clock instead of the grant.\n *\n * Five seconds, because below that the clock is not the story and saying so\n * would send somebody to check ntp about an unrelated failure. Above it, \"this\n * grant expired\" and \"your clock is wrong\" are the same event wearing\n * different words, and only one of them can be acted on.\n */\nexport const CLOCK_ATTRIBUTION_MS = 5_000;\n\n/**\n * The domain separator.\n *\n * Every signature in this system says what kind of statement it is before it\n * says anything else. Without it, bytes signed for one purpose verify for\n * another — a grant and a request are both \"bytes this key signed\", and a\n * scheme that could not tell them apart would let one be replayed as the\n * other.\n */\nexport const GRANT_CONTEXT = \"byollm/v1/grant\";\n\nexport const SignedGrant = z\n .object({\n /**\n * This grant's own id — what makes it single-use.\n *\n * **Not the job id, and the difference is load-bearing.** Binding\n * single-use to `jobId` would refuse a legitimate retry: a claim that\n * times out is re-claimed, the control plane authors a second grant for\n * the same job, and a device that recorded the job id as spent would\n * reject its own recovery. A fresh id per authorship replays nothing and\n * retries fine.\n */\n grantId: z.string().min(1),\n /**\n * The job this grant admits, and only this one.\n *\n * A grant lifted from one job and presented for another is the obvious\n * attack, and this field is why it fails.\n */\n jobId: z.string().min(1),\n /**\n * The site the work came from, **as a key id** — byollm-review 2026-08-27.\n *\n * This was `siteId`, holding the site's id in the control plane's\n * namespace, and it was signed by the engine and read by nobody. A signed\n * field nobody checks is not a weak guarantee, it is the appearance of\n * one: the design says \"the grant carries the site\", and nothing anywhere\n * compared it to anything.\n *\n * It could not be compared. Job ids are chosen per site, so a grant\n * authored for (site A, `job_1`) satisfied every device check against a\n * stub naming (site B, `job_1`) — but the device holds sites only by the\n * key ids it pinned, and had no way to relate a control-plane uuid to\n * one. Checking the field would have meant a lookup through the party the\n * grant exists to distrust.\n *\n * So the namespace changes to the one the device already has, and the\n * name changes with it: this is the same value as {@link JobStub.site},\n * compared directly, no lookup and nothing to believe. The control-plane\n * id is not carried alongside — it had no reader, and keeping an\n * unchecked field beside a checked one is how this hole was dug.\n */\n site: z.string().min(1),\n /** Whose job it is — the person the site enqueued for. */\n user: z.string().min(1),\n /**\n * Whose device it is for.\n *\n * Passed to {@link verifyGrant} rather than read out of the document, for\n * the reason every verifier here takes its subject as an argument: a\n * verifier that recovered the owner from the signed bytes would accept a\n * genuine grant belonging to somebody else and pass every check.\n */\n owner: z.string().min(1),\n /** The site purpose this job serves — byollm_016 Amendment L. */\n purpose: z.string().min(1),\n /** The kind of work. */\n kind: z.string().min(1),\n /**\n * The service the control plane resolved this (purpose, kind) to, from\n * the user's own mapping.\n *\n * Selection is the control plane's; **offer-consistency is the\n * device's**. A device verifies it actually offers this service, at a\n * scope that includes {@link user}, before running anything.\n */\n service: z.string().min(1),\n /** When the control plane signed it — epoch ms, the only anchor for age. */\n issuedAt: z.number().int().positive(),\n /** Base64url Ed25519 over {@link grantStatement}. */\n signature: z.string().min(1),\n })\n .strict();\nexport type SignedGrant = z.infer<typeof SignedGrant>;\n\n/** Everything a grant says, before it is signed. */\nexport type GrantClaims = Omit<SignedGrant, \"signature\">;\n\n/**\n * Every field of {@link SignedGrant} except the signature, sorted.\n *\n * **Derived from the schema, never written out by hand.** The unsigned-field\n * attack is that somebody adds a field to the document, forgets to add it to\n * the bytes, and ships a value an intermediary can rewrite without breaking\n * any signature. A hand-maintained list is exactly the shape that fails: it\n * does not grow when the code does, and nothing about adding a field reminds\n * you it exists.\n *\n * Reading the shape closes it structurally rather than by review. A new field\n * is signed the moment it is declared, and grant.test.ts asserts this list\n * still covers the schema so a future zod version that hides `shape` fails\n * loudly instead of silently signing less.\n */\nexport const GRANT_SIGNED_FIELDS: readonly (keyof GrantClaims)[] =\n Object.freeze(\n Object.keys(SignedGrant.shape)\n .filter((key) => key !== \"signature\")\n .sort(),\n ) as readonly (keyof GrantClaims)[];\n\n/**\n * The exact bytes both sides sign and verify.\n *\n * JSON-encoded rather than joined with a separator, because a separator can\n * be imitated. Newline-joining `[\"a\", \"b\\nc\"]` and `[\"a\\nb\", \"c\"]` produces\n * identical bytes, so two different grants would share a signature — and the\n * values here include a site id and a user id, at least one of which comes\n * from somebody else's namespace. JSON escapes the separator it uses, so no\n * arrangement of field values can spell a different document.\n *\n * The context string leads, and the field order is the schema's own sorted\n * keys, so the encoding is canonical without anyone maintaining a list.\n */\nexport function grantStatement(claims: GrantClaims): Uint8Array {\n return Buffer.from(\n JSON.stringify([\n GRANT_CONTEXT,\n ...GRANT_SIGNED_FIELDS.map((field) => claims[field]),\n ]),\n \"utf8\",\n );\n}\n\n/** Sign a grant with the control plane's own key. */\nexport function signGrant(\n keys: Pick<StoredKeys, \"identityPrivate\">,\n claims: GrantClaims,\n): SignedGrant {\n return { ...claims, signature: signWith(keys, grantStatement(claims)) };\n}\n\n/**\n * Why a grant was refused.\n *\n * Split by remedy, because these send somebody to different places: fix your\n * clock, take it up with the relay, or nothing at all — you are being\n * attacked and the refusal worked.\n *\n * There is deliberately no `no-pinned-key` here. A device that pinned no\n * control-plane key never reaches this function: it is in direct mode, and\n * the question \"is this grant good\" does not arise. A value nothing can\n * return is a branch every caller has to handle and no test can reach.\n */\nexport type GrantRefusal =\n /** The signature does not verify against the pinned key. */\n | \"bad-signature\"\n /** Genuine, and for a different device's owner. */\n | \"wrong-owner\"\n /** Genuine, and lifted from a different job. */\n | \"wrong-job\"\n /** Older than {@link GRANT_MAX_AGE_MS}. */\n | \"expired\"\n /**\n * Issued further in the future than clock drift explains.\n *\n * Checked, and not as pedantry: an `issuedAt` ahead of now extends a\n * grant's life past the bound, which is the whole thing being enforced.\n *\n * Tolerant by {@link CLOCK_SKEW_WARN_MS}, because it was tolerant by\n * nothing and that made ordinary drift a total outage — see\n * {@link verifyGrant}.\n */\n | \"from-the-future\";\n\n/**\n * Is this grant one this device may act on, right now?\n *\n * Document-level checks only. Replay, offer-consistency and the private rule\n * need state this function does not have and are the device's to apply — see\n * the class comment for the full list of four.\n */\nexport function verifyGrant(input: {\n grant: SignedGrant;\n owner: string;\n jobId: string;\n controlPlanePublic: string;\n now: number;\n maxAgeMs?: number;\n}): GrantRefusal | null {\n const { grant, now } = input;\n if (grant.owner !== input.owner) return \"wrong-owner\";\n if (grant.jobId !== input.jobId) return \"wrong-job\";\n\n const age = now - grant.issuedAt;\n /**\n * Forward drift is tolerated to the warning threshold — byollm-review\n * 2026-08-27.\n *\n * This was `age < 0`: a device whose clock was **one millisecond** behind\n * its control plane refused every grant it was ever sent. A laptop three\n * seconds behind after a sleep — ordinary NTP drift — ran no relayed work\n * at all, and said \"this grant is dated in the future\" per job while the\n * clock went unmentioned.\n *\n * The asymmetry was the bug. A device *ahead* of the signer had the whole\n * {@link GRANT_MAX_AGE_MS} of slack; a device behind had none, though the\n * two are the same phenomenon with a sign.\n *\n * {@link CLOCK_SKEW_WARN_MS} rather than a new constant, and the choice is\n * load-bearing: it is exactly the drift at which a device is supposed to\n * start warning its owner. Below it, work runs and nobody is troubled;\n * above it, the refusal lands on somebody who has already been told why.\n * The two halves meet at one number instead of leaving a band where a\n * device fails silently for a reason no surface mentioned.\n *\n * It does not widen the window a grant is good for. Expiry is still\n * measured from `issuedAt`, so a post-dated document buys no extra life —\n * it is refused at the far end instead.\n */\n if (age < -CLOCK_SKEW_WARN_MS) return \"from-the-future\";\n if (age > (input.maxAgeMs ?? GRANT_MAX_AGE_MS)) return \"expired\";\n\n // Checked last, so an expired grant reports expiry rather than whichever\n // failure a verifier happened to test first — the same ordering\n // `verifyRequest` uses. \"Your clock is wrong\" and \"this is forged\" send\n // somebody to very different places, and only one of them is actionable.\n return verifyWith(\n input.controlPlanePublic,\n grantStatement(grant),\n grant.signature,\n )\n ? null\n : \"bad-signature\";\n}\n","import {\n createHash,\n createPrivateKey,\n createPublicKey,\n generateKeyPairSync,\n sign,\n verify,\n type KeyObject,\n} from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * Device and site keys — byollm_009 §3.\n *\n * **Two keypairs per party, and the split is load-bearing.** An Ed25519\n * *identity* key signs; an X25519 *encryption* key receives sealed envelopes.\n * The encryption key is signed by the identity key, and **the identity key is\n * what gets pinned**. So \"who sent this\" and \"who can read this\" are answered\n * by different keys — which is what lets an encryption key rotate without\n * re-establishing trust, and what byollm_009 §6's signed-then-sealed envelope\n * depends on.\n *\n * **No new dependency.** byollm_009 §2 says established primitives only, via\n * libsodium. Everything *this* module needs — Ed25519 signing, X25519 key\n * generation — Node provides natively, and using it costs nothing and adds no\n * install weight to a daemon that must land fast on a stranger's laptop.\n *\n * libsodium becomes necessary at envelope v2, where sealing does. That is a\n * real dependency decision and it belongs in the change that needs it: a\n * sealed box is a specific reviewed construction, and rebuilding it out of\n * Node primitives is exactly the \"novel construction\" §2 rules out. Deferring\n * the dependency is not the same as deferring the rule.\n */\n\n/** A public identity, as it travels on the wire. All values base64url. */\nexport const PublicIdentity = z\n .object({\n /** Raw Ed25519 public key. The pinned one. */\n identity: z.string().min(1),\n /** Raw X25519 public key, for sealing to this party. */\n encryption: z.string().min(1),\n /**\n * Ed25519 signature over the encryption key, by the identity key.\n *\n * This is what stops an upstream substituting an encryption key of its\n * own while relaying a genuine identity: the receiver pins the identity\n * and refuses any encryption key not signed by it.\n */\n encryptionSig: z.string().min(1),\n })\n .strict();\nexport type PublicIdentity = z.infer<typeof PublicIdentity>;\n\n/** Private key material, as stored on disk. Never leaves the machine. */\nexport const StoredKeys = z\n .object({\n version: z.literal(1),\n identityPublic: z.string().min(1),\n identityPrivate: z.string().min(1),\n encryptionPublic: z.string().min(1),\n encryptionPrivate: z.string().min(1),\n encryptionSig: z.string().min(1),\n createdAt: z.number().int().positive(),\n })\n .strict();\nexport type StoredKeys = z.infer<typeof StoredKeys>;\n\n/** Domain separator, so a signature over an encryption key cannot be\n * replayed as a signature over anything else. */\n/**\n * What an encryption key's signature covers.\n *\n * Exported because a rotation is a real event this protocol has to be able to\n * *test* — a record whose encryption key moved under an identity that signed\n * the move is the one case pinning must refuse loudly, and building one\n * outside this file otherwise means re-typing this string, which is how two\n * copies of a constant start disagreeing.\n */\nexport const ENCRYPTION_KEY_CONTEXT = \"byollm/v1/encryption-key\";\n\nfunction rawPublic(key: KeyObject): string {\n const jwk = key.export({ format: \"jwk\" });\n const x = jwk.x;\n if (typeof x !== \"string\") throw new Error(\"key has no raw public component\");\n return x;\n}\n\nfunction importPublic(raw: string, crv: \"Ed25519\" | \"X25519\"): KeyObject {\n return createPublicKey({ key: { kty: \"OKP\", crv, x: raw }, format: \"jwk\" });\n}\n\nfunction importPrivate(stored: string): KeyObject {\n return createPrivateKey({\n key: Buffer.from(stored, \"base64\"),\n type: \"pkcs8\",\n format: \"der\",\n });\n}\n\nconst exportPrivate = (key: KeyObject): string =>\n key.export({ type: \"pkcs8\", format: \"der\" }).toString(\"base64\");\n\n/** Generate a fresh pair of keypairs and bind them together. */\nexport function generateKeys(now: number): StoredKeys {\n const identity = generateKeyPairSync(\"ed25519\");\n const encryption = generateKeyPairSync(\"x25519\");\n const encryptionPublic = rawPublic(encryption.publicKey);\n\n return {\n version: 1,\n identityPublic: rawPublic(identity.publicKey),\n identityPrivate: exportPrivate(identity.privateKey),\n encryptionPublic,\n encryptionPrivate: exportPrivate(encryption.privateKey),\n encryptionSig: sign(\n null,\n Buffer.from(`${ENCRYPTION_KEY_CONTEXT}:${encryptionPublic}`),\n identity.privateKey,\n ).toString(\"base64url\"),\n createdAt: now,\n };\n}\n\n/** The public half, for the wire. */\nexport function publicIdentityOf(keys: StoredKeys): PublicIdentity {\n return {\n identity: keys.identityPublic,\n encryption: keys.encryptionPublic,\n encryptionSig: keys.encryptionSig,\n };\n}\n\n/**\n * Check that an encryption key really belongs to the identity presenting it.\n *\n * Called on everything received, including from an upstream we otherwise\n * trust — the point of pinning the identity is that nothing else needs to be\n * trusted, and that only holds if this is checked every time rather than at\n * first sight.\n */\nexport function verifyPublicIdentity(identity: PublicIdentity): boolean {\n try {\n return verify(\n null,\n Buffer.from(`${ENCRYPTION_KEY_CONTEXT}:${identity.encryption}`),\n importPublic(identity.identity, \"Ed25519\"),\n Buffer.from(identity.encryptionSig, \"base64url\"),\n );\n } catch {\n // A malformed key is a failed verification, not a crash. This runs on\n // input from the network.\n return false;\n }\n}\n\n/** Sign arbitrary bytes with an identity key. */\n/**\n * Sign bytes with an identity key.\n *\n * Takes only the private half it uses. A signer that demanded a whole\n * {@link StoredKeys} would make every caller hold an encryption keypair for a\n * job that has no encryption in it — and the control plane, which signs\n * rosters and opens nothing, would be generating and storing secret material\n * it can never need. Every existing caller passes a full `StoredKeys`, which\n * satisfies this.\n */\nexport function signWith(\n keys: Pick<StoredKeys, \"identityPrivate\">,\n data: Uint8Array,\n): string {\n return sign(null, data, importPrivate(keys.identityPrivate)).toString(\n \"base64url\",\n );\n}\n\n/** Verify bytes against a raw Ed25519 public key. */\nexport function verifyWith(\n identityPublic: string,\n data: Uint8Array,\n signature: string,\n): boolean {\n try {\n return verify(\n null,\n data,\n importPublic(identityPublic, \"Ed25519\"),\n Buffer.from(signature, \"base64url\"),\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Crockford base32: no `I`, `L`, `O` or `U`, so a fingerprint read aloud\n * cannot be mis-heard as a different one, and cannot spell anything.\n */\nconst ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n/**\n * A fingerprint a human can compare out loud.\n *\n * 120 bits of SHA-256 over the raw identity key, as six groups of four. Long\n * enough that grinding a colliding key is not worth anyone's afternoon, short\n * enough to read down a phone line — which is the whole point. A fingerprint\n * nobody can be bothered to compare provides no security at all, so\n * legibility is a security property here, not a nicety.\n *\n * Formatted with a `BYOLLM-` prefix so a pasted fingerprint is recognisable\n * out of context, in a support thread or a screenshot.\n */\nexport function fingerprint(identityPublic: string): string {\n const digest = createHash(\"sha256\")\n .update(Buffer.from(identityPublic, \"base64url\"))\n .digest();\n\n let bits = 0;\n let value = 0;\n let out = \"\";\n for (const byte of digest.subarray(0, 15)) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += ALPHABET.charAt((value >>> (bits - 5)) & 31);\n bits -= 5;\n }\n }\n\n const groups = out.match(/.{1,4}/g) ?? [];\n return `BYOLLM-${groups.join(\"-\")}`;\n}\n\n/** The short id used in envelopes and provenance. Stable, and comparable. */\nexport const keyId = (identityPublic: string): string =>\n fingerprint(identityPublic);\n","import { createPrivateKey, createPublicKey, type KeyObject } from \"node:crypto\";\nimport sodium from \"libsodium-wrappers\";\nimport { z } from \"zod\";\nimport { signWith, verifyWith, type StoredKeys } from \"./keys.js\";\n\n/**\n * Sealed, signed envelopes — byollm_009 §6.\n *\n * ## Signed, then sealed\n *\n * An earlier draft specified a bare sealed box. That was wrong in the\n * direction that matters, and the reasoning is kept because someone will\n * propose it again: `crypto_box_seal` is **anonymous-sender by\n * construction** — it derives from an ephemeral keypair and discards the\n * secret — so the recipient can decrypt but learns nothing about who sent it.\n * Both public keys here are public by definition; the upstream distributed\n * them. Any holder of one can therefore produce an envelope that opens\n * cleanly, and a relay holds both.\n *\n * So every envelope is **signed with the sender's Ed25519 identity key, then\n * sealed to the recipient's X25519 encryption key**. The recipient opens it,\n * then verifies against the identity it pinned at consent. An envelope that\n * does not verify is refused, not run.\n *\n * Three details earn their place:\n *\n * - **Both key ids are inside the signature**, so an envelope cannot be\n * lifted from one recipient and replayed to another, nor re-signed by a\n * third party claiming authorship.\n * - **`direction` is inside it**, so a payload envelope can never be replayed\n * as a result envelope.\n * - **Sign-then-encrypt, not encrypt-then-sign.** The signature lives\n * *inside* the ciphertext, so a relay never accumulates a non-repudiable\n * record of who sent what to whom. Signing the outside would hand it\n * exactly the attestation trail `RELAY_BLIND` exists to deny.\n *\n * ## Why libsodium\n *\n * byollm_009 §2: established primitives, no novel constructions. A sealed box\n * is a specific reviewed construction — ephemeral key agreement with a\n * BLAKE2b-derived nonce — and rebuilding it from lower-level pieces is\n * precisely the thing that rule forbids. The keys themselves are Node's,\n * which interoperate: raw X25519 is raw X25519.\n */\n\n/** libsodium is WASM and initialises asynchronously. */\nlet readied: Promise<void> | undefined;\nexport async function cryptoReady(): Promise<void> {\n readied ??= sodium.ready;\n await readied;\n}\n\n/**\n * How long a sealed payload is worth keeping, from creation.\n *\n * Bound into every envelope and recomputed when one is opened, so it lives\n * here rather than in the two places that need it. Two copies of a value the\n * signature depends on is the same bug as two clock readings: it works until\n * they disagree, and then nothing can be opened.\n *\n * Not a job's TTL. That answers how long the *work* is worth doing, belongs\n * to the app and the store, and may legitimately differ per deployment.\n */\nexport const ENVELOPE_MAX_AGE_MS = 24 * 60 * 60_000;\n\n/** Which leg an envelope belongs to. Bound into the signature. */\nexport const EnvelopeDirection = z.enum([\"payload\", \"result\"]);\nexport type EnvelopeDirection = z.infer<typeof EnvelopeDirection>;\n\nexport const SealedEnvelope = z\n .object({\n /** Base64url `crypto_box_seal` output over the signed plaintext. */\n ciphertext: z.string().min(1),\n /** Who this was sealed to — the recipient checks it is them. */\n recipientKeyId: z.string().min(1),\n /** Who signed it — the recipient checks this against its pin. */\n senderKeyId: z.string().min(1),\n direction: EnvelopeDirection,\n /**\n * When this ciphertext stops being worth keeping.\n *\n * Carried *on* the envelope rather than recomputed by the opener. An\n * earlier version derived it from the job's creation time, which meant\n * two systems had to agree on a timestamp to the millisecond — and they\n * did not, once a real database rounded it. A bound value that has to be\n * reconstructed is a bound value that eventually is not.\n *\n * Not trusted as written: it is also inside the signature, so a changed\n * deadline fails to verify.\n */\n deadlineAt: z.number().int().positive(),\n })\n .strict();\nexport type SealedEnvelope = z.infer<typeof SealedEnvelope>;\n\n/** Everything the signature covers besides the plaintext itself. */\nexport interface EnvelopeContext {\n readonly jobId: string;\n readonly senderKeyId: string;\n readonly recipientKeyId: string;\n readonly deadlineAt: number;\n readonly direction: EnvelopeDirection;\n}\n\n/** The bytes signed inside the envelope. */\nfunction signedBody(context: EnvelopeContext, plaintext: string): Buffer {\n return Buffer.from(\n JSON.stringify({\n v: \"byollm/v1/envelope\",\n jobId: context.jobId,\n senderKeyId: context.senderKeyId,\n recipientKeyId: context.recipientKeyId,\n deadlineAt: context.deadlineAt,\n direction: context.direction,\n plaintext,\n }),\n \"utf8\",\n );\n}\n\nconst rawX25519 = (key: KeyObject, part: \"x\" | \"d\"): Uint8Array => {\n const jwk = key.export({ format: \"jwk\" });\n const value = part === \"x\" ? jwk.x : jwk.d;\n if (typeof value !== \"string\") throw new Error(\"not an X25519 key\");\n return new Uint8Array(Buffer.from(value, \"base64url\"));\n};\n\n/** Seal a plaintext to a recipient, signed by the sender's identity. */\nexport async function seal(input: {\n plaintext: string;\n senderKeys: StoredKeys;\n recipientEncryptionPublic: string;\n context: EnvelopeContext;\n}): Promise<SealedEnvelope> {\n await cryptoReady();\n\n const body = signedBody(input.context, input.plaintext);\n const signature = signWith(input.senderKeys, body);\n const inner = JSON.stringify({ body: body.toString(\"base64url\"), signature });\n\n const recipient = new Uint8Array(\n Buffer.from(input.recipientEncryptionPublic, \"base64url\"),\n );\n const ciphertext = sodium.crypto_box_seal(\n new Uint8Array(Buffer.from(inner, \"utf8\")),\n recipient,\n );\n\n return {\n ciphertext: Buffer.from(ciphertext).toString(\"base64url\"),\n recipientKeyId: input.context.recipientKeyId,\n senderKeyId: input.context.senderKeyId,\n direction: input.context.direction,\n deadlineAt: input.context.deadlineAt,\n };\n}\n\n/** Why an envelope was refused. Never distinguished to a remote caller. */\nexport type EnvelopeFailure =\n | \"not-for-us\"\n | \"unopenable\"\n | \"malformed\"\n | \"bad-signature\"\n | \"context-mismatch\";\n\nexport type OpenResult =\n | { readonly ok: true; readonly plaintext: string }\n | { readonly ok: false; readonly reason: EnvelopeFailure };\n\n/**\n * Open an envelope and verify it came from the pinned sender.\n *\n * Every failure returns rather than throws: this runs on input from the\n * network, and a crash here is a denial of service on the delivery path.\n *\n * The context is checked against the signature, not merely read from the\n * envelope. An envelope carries its own claims about who sent it and to\n * whom — believing those would authenticate the attacker's assertion rather\n * than the sender's key.\n */\nexport async function open(input: {\n envelope: SealedEnvelope;\n recipientKeys: StoredKeys;\n senderIdentityPublic: string;\n /** The deadline is taken from the envelope and checked against its signature. */\n expected: Omit<EnvelopeContext, \"deadlineAt\">;\n}): Promise<OpenResult> {\n await cryptoReady();\n const { envelope, expected } = input;\n\n // Cheap structural checks first, before any crypto.\n if (\n envelope.recipientKeyId !== expected.recipientKeyId ||\n envelope.senderKeyId !== expected.senderKeyId ||\n envelope.direction !== expected.direction\n ) {\n return { ok: false, reason: \"not-for-us\" };\n }\n\n let inner: string;\n try {\n const priv = createPrivateKey({\n key: Buffer.from(input.recipientKeys.encryptionPrivate, \"base64\"),\n type: \"pkcs8\",\n format: \"der\",\n });\n const pub = createPublicKey(priv);\n const opened = sodium.crypto_box_seal_open(\n new Uint8Array(Buffer.from(envelope.ciphertext, \"base64url\")),\n rawX25519(pub, \"x\"),\n rawX25519(priv, \"d\"),\n );\n inner = Buffer.from(opened).toString(\"utf8\");\n } catch {\n // Wrong recipient, tampered ciphertext, or garbage. One reason, because\n // the difference is not something the sender is entitled to learn.\n return { ok: false, reason: \"unopenable\" };\n }\n\n let parsed: { body?: unknown; signature?: unknown };\n try {\n parsed = JSON.parse(inner) as { body?: unknown; signature?: unknown };\n } catch {\n return { ok: false, reason: \"malformed\" };\n }\n if (typeof parsed.body !== \"string\" || typeof parsed.signature !== \"string\") {\n return { ok: false, reason: \"malformed\" };\n }\n\n const body = Buffer.from(parsed.body, \"base64url\");\n if (!verifyWith(input.senderIdentityPublic, body, parsed.signature)) {\n // Opened, but not from the key we pinned. This is the injection case: a\n // relay can produce a well-formed sealed box for any public key it holds.\n return { ok: false, reason: \"bad-signature\" };\n }\n\n let claims: Record<string, unknown>;\n try {\n claims = JSON.parse(body.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n return { ok: false, reason: \"malformed\" };\n }\n\n // The signature is valid over *something*; this checks it is valid over\n // what we asked for. Without it a genuinely signed envelope for another\n // job, recipient or leg would verify here.\n if (\n claims[\"jobId\"] !== expected.jobId ||\n claims[\"senderKeyId\"] !== expected.senderKeyId ||\n claims[\"recipientKeyId\"] !== expected.recipientKeyId ||\n claims[\"deadlineAt\"] !== envelope.deadlineAt ||\n claims[\"direction\"] !== expected.direction\n ) {\n return { ok: false, reason: \"context-mismatch\" };\n }\n if (typeof claims[\"plaintext\"] !== \"string\") {\n return { ok: false, reason: \"malformed\" };\n }\n\n return { ok: true, plaintext: claims[\"plaintext\"] };\n}\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { signWith, verifyWith, type StoredKeys } from \"./keys.js\";\n\n/**\n * Request signing — byollm_009 §4.2.\n *\n * Every authenticated call is signed by the calling device's identity key.\n * There is no bearer token on the daemon plane: possession of a file no\n * longer grants access, possession of a *key* does, and the key never leaves\n * the machine.\n *\n * ## Why this is not the server-issued nonce the spec first described\n *\n * byollm_009 §4.2 says \"the upstream issues a nonce; the daemon signs it\".\n * Implementing that costs one of two things: a round trip before every\n * request, or server-side session state — and sessions reintroduce a bearer\n * credential, which is the thing being removed.\n *\n * Signing *the request itself* gets the same property without either, because\n * of something the protocol already guarantees. A captured signature is valid\n * only for the exact request it covers — same endpoint, same runner, same\n * body — and every authenticated endpoint here is idempotent by design:\n * `RESULT_IDEMPOTENT` makes a replayed result a no-op, a replayed claim from\n * the same runner returns what that runner already holds, and heartbeat and\n * release are idempotent in effect. So a replay inside the freshness window\n * gains an attacker nothing they could not obtain by forwarding the original,\n * which a relay can do anyway.\n *\n * That is the whole argument, and it is worth stating because it rests\n * entirely on the endpoints being idempotent. Two ways that can fail, and the\n * second is the one that actually bit:\n *\n * 1. **A future endpoint that is not idempotent cannot use this scheme\n * unchanged** — it would need a server-issued nonce.\n * 2. **Idempotence must hold per *addressed instance*, not per endpoint.** A\n * request that names a mutable target — a lease, a session, a\n * subscription — must name the *instance*, or a replay lands on a\n * different one than the sender meant and the endpoint's idempotence buys\n * nothing. `release` was idempotent per lease and ambiguous across them:\n * it named a job and a runner, both of which survive a\n * claim-release-reclaim cycle, so a replayed release yanked a later grant.\n * Fixed by giving a lease its own id and requiring it.\n *\n * The rule for anything added later: if a signed request can be replayed onto\n * a target that has changed underneath it, the request has to say which\n * target it meant.\n */\n\n/** How far a request's timestamp may be from the server's clock. */\nexport const MAX_CLOCK_SKEW_MS = 120_000;\n\n/** The signed material a request carries. */\nexport const RequestSignature = z\n .object({\n /** Which runner is calling. The server looks up its pinned identity. */\n runnerId: z.string().min(1),\n /** Epoch ms, bounded by {@link MAX_CLOCK_SKEW_MS}. */\n issuedAt: z.number().int().positive(),\n /** Base64url Ed25519 signature over {@link canonicalRequest}. */\n signature: z.string().min(1),\n })\n .strict();\nexport type RequestSignature = z.infer<typeof RequestSignature>;\n\n/**\n * The exact bytes both sides sign and verify.\n *\n * Newline-separated with a version prefix and a domain separator. Every field\n * that decides what the request *does* is in here: leave one out and it\n * becomes something an intermediary can change without breaking the\n * signature.\n *\n * The body is included by hash rather than by value, so signing does not\n * depend on both sides serialising JSON identically — which they would not.\n */\nexport function canonicalRequest(input: {\n endpoint: string;\n runnerId: string;\n issuedAt: number;\n body: string;\n}): Buffer {\n const digest = createHash(\"sha256\").update(input.body, \"utf8\").digest(\"hex\");\n return Buffer.from(\n [\n \"byollm/v1/request\",\n input.endpoint,\n input.runnerId,\n String(input.issuedAt),\n digest,\n ].join(\"\\n\"),\n \"utf8\",\n );\n}\n\n/** Sign an outgoing request with this machine's identity key. */\nexport function signRequest(\n keys: StoredKeys,\n input: { endpoint: string; runnerId: string; issuedAt: number; body: string },\n): RequestSignature {\n return {\n runnerId: input.runnerId,\n issuedAt: input.issuedAt,\n signature: signWith(keys, canonicalRequest(input)),\n };\n}\n\n/**\n * The same scheme, for the party at the other end: a **site** calling a relay.\n *\n * A site talking to a relay is in exactly the daemon's position — an outbound\n * caller with an identity keypair the other side already pins — so it gets the\n * daemon's authentication rather than a second scheme. Bearer tokens for the\n * site plane were the alternative, and they would have reintroduced the\n * credential-in-a-file that §4.2 removed from the daemon plane, on the plane\n * that carries *every* site's traffic.\n *\n * Two things make this safe to build on the same canonical string:\n *\n * 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never\n * `enqueue`. The daemon plane's `result` and the site plane's `results` are\n * one character apart, and a naming collision between planes must not be\n * what stands between a signature and a replay onto the wrong handler. The\n * prefix is applied *inside* these helpers, so the two ends cannot disagree\n * about it — the alternative is two implementations of one bound value,\n * which is this project's most-repeated bug.\n * 2. **The caller slot carries the site id.** `canonicalRequest` names that\n * field `runnerId` because the daemon plane got there first; here it holds\n * the site id, and the verifier looks the key up in the projection's site\n * registry rather than its device registry. The two registries never share\n * an entry, so a device signature cannot authenticate as a site.\n *\n * §4.2's replay argument carries over **only because the site plane's writes\n * are idempotent per addressed instance**, which is a property that had to be\n * built rather than found: `enqueue` reset a job of the same id, so a replayed\n * enqueue inside the freshness window returned a claimed job to the queue and\n * threw away a device's live lease. Identical in shape to the `release` bug\n * above, on the other plane. Anything added to the site plane later must be\n * idempotent by the instance it names, or this scheme does not cover it.\n */\nexport function signSiteRequest(\n keys: StoredKeys,\n input: { endpoint: string; siteId: string; issuedAt: number; body: string },\n): RequestSignature {\n return signRequest(keys, {\n endpoint: siteEndpoint(input.endpoint),\n runnerId: input.siteId,\n issuedAt: input.issuedAt,\n body: input.body,\n });\n}\n\n/** Verify a site's call against the identity the control plane registered. */\nexport function verifySiteRequest(input: {\n identityPublic: string;\n endpoint: string;\n body: string;\n signature: RequestSignature;\n now: number;\n maxSkewMs?: number;\n}): SignatureFailure | null {\n return verifyRequest({\n ...input,\n endpoint: siteEndpoint(input.endpoint),\n });\n}\n\n/** The one place the site plane's domain separator is written. */\nconst siteEndpoint = (endpoint: string): string => `site/${endpoint}`;\n\n/**\n * Why a signed request was refused.\n *\n * **`bad-signature` is never returned verbatim; `stale` is, deliberately.**\n * They are different kinds of refusal and conflating them costs a real user\n * more than it costs an attacker.\n *\n * A bad signature is an authentication failure and the server says only\n * \"unauthorized\" — telling a prober which part they got wrong is free help.\n *\n * A stale timestamp is a **precondition** failure: the signature may be\n * perfectly valid and the caller's clock is simply wrong. Saying so reveals\n * nothing, for two reasons that both have to hold. The server's time is\n * already public — every response carries a `Date` header and the heartbeat\n * response returns `serverTime` outright. And freshness is checked *before*\n * the signature is verified, so a stale answer says nothing about whether the\n * signature was any good.\n *\n * What conflating them costs: a machine whose clock has drifted gets\n * `401 unauthorized` on every request, forever, with nothing anywhere pointing\n * at the clock. That is the shape byollm_013 was filed about — a refusal that\n * is correct, silent, and sends somebody to read our source.\n */\nexport type SignatureFailure = \"stale\" | \"bad-signature\";\n\n/**\n * Verify a signed request against a runner's pinned identity key.\n *\n * Freshness is checked in **both** directions. A clock far ahead is as much a\n * problem as one behind: it would let a captured request stay replayable long\n * after it was made, which is the one thing the window exists to bound.\n */\nexport function verifyRequest(input: {\n identityPublic: string;\n endpoint: string;\n body: string;\n signature: RequestSignature;\n now: number;\n maxSkewMs?: number;\n}): SignatureFailure | null {\n const skew = input.maxSkewMs ?? MAX_CLOCK_SKEW_MS;\n if (Math.abs(input.now - input.signature.issuedAt) > skew) return \"stale\";\n\n const ok = verifyWith(\n input.identityPublic,\n canonicalRequest({\n endpoint: input.endpoint,\n runnerId: input.signature.runnerId,\n issuedAt: input.signature.issuedAt,\n body: input.body,\n }),\n input.signature.signature,\n );\n return ok ? null : \"bad-signature\";\n}\n","import { z } from \"zod\";\nimport { JOB_KINDS, JobKind } from \"./kinds.js\";\n\n/**\n * What a site says it needs — byollm_016 Amendment L.\n *\n * A site declares **purposes**, and each purpose lists the job kinds it uses.\n * A person then maps each purpose to one of their own services, on the consent\n * screen, and that mapping *is* the consent. The control plane joins the two\n * at claim time and signs the result into a grant.\n *\n * ## Why a site declares needs instead of naming services\n *\n * Because it cannot name one. The site's vocabulary is its own purposes; the\n * person's vocabulary is their services; and the two never meet. A site asks\n * for \"writing assistant, llm.chat\" and learns only whether that slot is\n * satisfiable — never which model answered, never whose machine, never even\n * the name of the service. Key-vs-value reaches its strongest form here: the\n * site cannot describe what it wants *or* name it, only ask for what it\n * declared.\n *\n * ## Keys are ids; labels are prose\n *\n * They are separate fields and nothing derives one from the other, which is\n * the amendment's ruling and worth restating where somebody will read it. A\n * key travels on every job and is what mappings are stored against, so it is\n * stable-or-nothing: renaming one deletes a purpose and creates another,\n * unmapping everybody who had chosen for it. A label is changeable whenever\n * the site likes and is the **only** thing a consent screen renders.\n */\n\n/**\n * The purpose a site gets when it declares no purposes of its own.\n *\n * Reserved, and refused by {@link Manifest} rather than by whatever handles\n * registration. A site with a single undifferentiated use has one purpose —\n * everything it does — and that purpose needs an id because mappings are\n * keyed by one. An id taken from the site's own vocabulary would collide the\n * day it declared a real purpose of the same name.\n *\n * **Never rendered.** \"default → your Claude\" tells a person nothing; a\n * consent screen shows the site's own name for this slot, because that is\n * what a single-purpose site's one purpose actually is.\n */\nexport const RESERVED_PURPOSE = \"default\";\n\n/**\n * The characters a string may contain when a person will read it to decide.\n *\n * The purpose **key** got a strict slug regex the day it was written, because\n * it travels. The **label** got a length cap and nothing else — and it is the\n * field the whole consent decision rests on: the module below says it is \"the\n * only thing a consent screen renders\", and it reaches notification emails and\n * daemon logs too.\n *\n * Three classes are refused, each because it makes rendered text mean\n * something other than what was declared:\n *\n * - **Control characters.** A newline lets one label spoof the rows around it\n * on a consent screen or in an email; ANSI escapes corrupt a terminal when a\n * CLI prints the purpose; NUL truncates in whatever reads it next.\n * - **Bidi controls.** `U+202E` and its relatives reorder what follows, so\n * \"Read your files — tnatsissa gnitirW\" renders as something the site did\n * not write. This is the attack that matters here: it changes the sentence a\n * person consents to, invisibly, with every character individually innocent.\n * - **Zero-width characters.** They pad a label past nothing visible, which is\n * how two purposes come to look identical on the screen where telling them\n * apart is the point.\n *\n * Refused at parse rather than escaped at render. There are four renderers\n * already — consent screen, email, CLI, logs — and an escaping rule has to be\n * right in all of them; a parse rule is right once.\n *\n * ## What this costs, said plainly\n *\n * `\\p{Cf}` takes zero-width joiners with it, so a multi-person emoji in a\n * label is refused. That is a real cost and it is the right trade here: the\n * same codepoint that joins an emoji family pads two labels into looking\n * identical, and this is the field where telling them apart is the decision.\n *\n * `\\p{Cn}` — unassigned — is deliberately **not** refused, though it looks\n * like it belongs. Which codepoints are unassigned depends on the Unicode\n * version of whatever engine is parsing, so including it would make a manifest\n * valid on one deployment and refused on another, drifting silently as\n * runtimes update. A rule whose answer depends on the reader is not a rule.\n */\nconst RENDERABLE = /^[^\\p{Cc}\\p{Cf}\\p{Cs}\\p{Co}]+$/u;\n\nconst renderable = (max: number, what: string) =>\n z\n .string()\n .min(1)\n .max(max)\n .regex(\n RENDERABLE,\n `a ${what} is text a person reads — no control characters, ` +\n \"direction overrides or zero-width padding\",\n )\n // A label of spaces passes every rule above and renders as an empty row.\n .refine((value) => value.trim() !== \"\", {\n message: `a ${what} cannot be blank`,\n });\n\n/**\n * A purpose key: a slug, and stable for the life of the purpose.\n *\n * Constrained because it is an **id on a signed document**, not a display\n * string. Something a site can print, a person can recognise in a URL, and\n * nobody has to escape. The label carries everything expressive.\n */\nconst PurposeKey = z\n .string()\n .regex(\n /^[a-z0-9][a-z0-9-]*$/,\n \"a purpose key is a lowercase slug — letters, digits and hyphens\",\n )\n .max(64);\n\nexport const Purpose = z\n .object({\n /**\n * What a person reads on the consent screen. The only rendered field.\n *\n * Declared rather than derived from the key, because a key is a\n * compromise between machines and this is not. \"Writing Assistant\" is\n * what somebody understands; `writing-assistant` is what travels.\n */\n label: renderable(80, \"label\"),\n /** One line of context for the consent screen. Optional. */\n description: renderable(280, \"description\").optional(),\n /**\n * The kinds this purpose uses.\n *\n * A purpose may span kinds, and a mapping is per (purpose, kind) — so a\n * person can send this purpose's chat to one service and its generation\n * to another. Listing a kind here is what makes that slot appear.\n */\n kinds: z\n .array(JobKind)\n .min(1)\n /**\n * Bounded by the vocabulary itself, and unique.\n *\n * This was `.min(1)` and nothing else: one purpose could declare\n * `[\"llm.chat\"]` repeated a million times, every element individually\n * valid, and the consent screen renders one slot per (purpose, kind).\n *\n * The maximum is derived rather than chosen — a purpose cannot need\n * more kinds than exist, so `JOB_KINDS.length` is the honest ceiling\n * and it grows with the protocol instead of becoming a number somebody\n * has to remember to raise.\n */\n .max(JOB_KINDS.length)\n .refine((kinds) => new Set(kinds).size === kinds.length, {\n message: \"a purpose lists each kind once\",\n }),\n })\n .strict();\nexport type Purpose = z.infer<typeof Purpose>;\n\n/**\n * Everything a site needs, by purpose key.\n *\n * At least one purpose: a site that declares none is a site that can enqueue\n * nothing, and accepting it would mean the first refusal a person saw came\n * from a job rather than from registration.\n */\n/**\n * How many purposes one site may declare.\n *\n * There was no bound at all: a site could declare fifty thousand, each one\n * individually valid, and the consent screen renders a slot per (purpose,\n * kind) — so the page that *is* the consent mechanism becomes unusable, and\n * the notification mail that enumerates slots grows with it.\n *\n * Thirty-two is chosen rather than derived, and the number is an argument: a\n * purpose is a thing a person reads and decides about one at a time, and a\n * screen asking more than about thirty separate questions has stopped being a\n * consent screen whatever it renders. Of Tomorrow Press declares five. A site\n * that genuinely needs more has a product question to answer before it has a\n * schema one.\n */\nexport const MAX_PURPOSES = 32;\n\nexport const Manifest = z\n .record(PurposeKey, Purpose)\n .refine((manifest) => Object.keys(manifest).length > 0, {\n message: \"a manifest declares at least one purpose\",\n })\n .refine((manifest) => Object.keys(manifest).length <= MAX_PURPOSES, {\n message:\n `a manifest declares at most ${String(MAX_PURPOSES)} purposes — a ` +\n \"consent screen is a set of questions somebody answers one at a time\",\n })\n .refine((manifest) => !(RESERVED_PURPOSE in manifest), {\n message:\n `\"${RESERVED_PURPOSE}\" is reserved for a site that declares no ` +\n \"purposes of its own — give this one a name from your own vocabulary\",\n });\nexport type Manifest = z.infer<typeof Manifest>;\n\n/**\n * The manifest a site with no declared purposes is treated as having.\n *\n * The sugar in Amendment L, made explicit rather than special-cased\n * downstream: everything after this point sees a manifest with one purpose,\n * so no consent screen, mapping table or resolver needs a branch for the\n * flat-list case.\n *\n * The label is the caller's — a site's own name — because it is the one thing\n * that can make \"everything this site does\" read as a sentence about a\n * particular site rather than about software in general.\n */\nexport function singlePurposeManifest(input: {\n readonly label: string;\n readonly kinds: readonly JobKind[];\n}): Manifest {\n return {\n [RESERVED_PURPOSE]: { label: input.label, kinds: [...input.kinds] },\n };\n}\n","import { z } from \"zod\";\nimport {\n PublicIdentity,\n type StoredKeys,\n keyId,\n signWith,\n verifyPublicIdentity,\n verifyWith,\n} from \"./keys.js\";\n\n/**\n * Rotation — byollm_009 Amendment C.\n *\n * A site holding identity key **K1** wants to be known by **K2**. It publishes\n * a *succession*: K2, plus a signature by K1 over a statement naming both key\n * ids. That signature is the entire mechanism, and the reason rotation can be\n * automatic without becoming a hole is that **the relay cannot mint one** — it\n * never holds K1. It is the same trust step a daemon already performs at\n * pairing, applied to the site's own succession.\n *\n * ## Why the statement names both keys\n *\n * A signature over K2 alone could be lifted from this site's record and\n * replayed into another site's, moving *that* site to K2 — a key the attacker\n * holds. Naming the predecessor binds the succession to one chain, and it is\n * the reason `verifyLink` takes the id it expects to be succeeding from\n * rather than reading it out of the statement it is checking.\n */\n\n/** The domain separator. Distinct from every other thing an identity signs. */\nexport const SUCCESSION_CONTEXT = \"byollm/v1/site-succession\";\n\n/**\n * How long a retired key may still sign work — Amendment C, ruling 2.\n *\n * A protocol constant and not the site's to choose. Per-site overlap\n * arithmetic is exactly the kind of number that has to mean one thing\n * everywhere, and a site that could choose it could choose *forever*, which is\n * a two-key site permanently and a second key nobody ever notices retiring.\n *\n * Seven days: long enough that a daemon which polls daily and a laptop shut\n * for a long weekend both see the new record before the old key stops working,\n * short enough that \"which key is live\" is never an interesting question.\n */\nexport const RETIREMENT_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * The longest chain a daemon will walk — Amendment C, ruling 1.\n *\n * **A denial-of-service guard, not policy.** The bound exists so a projection\n * cannot make a daemon verify ten thousand signatures, not to express an\n * opinion about how often a site may rotate. A site that legitimately exceeds\n * it has a re-pair ahead of it, which is why it is generous: at one rotation a\n * quarter this is sixteen years.\n */\nexport const MAX_SUCCESSION_CHAIN = 64;\n\n/** One step of a chain: a key, and the signature by it over its successor. */\nexport const Succession = z\n .object({\n /**\n * The predecessor's public identity — K1, in full.\n *\n * The whole identity rather than the key id, because a daemon meeting a\n * chain it has not seen before has to *verify* each link, and a key id is\n * a fingerprint: enough to compare, never enough to check a signature.\n */\n identity: PublicIdentity,\n /** K1's signature over the statement naming K1 and its successor. */\n signature: z.string().min(1),\n })\n .strict();\nexport type Succession = z.infer<typeof Succession>;\n\n/** The exact bytes signed. One definition; both sides call it. */\nexport function successionStatement(\n fromKeyId: string,\n toKeyId: string,\n): Uint8Array {\n return Buffer.from(`${SUCCESSION_CONTEXT}:${fromKeyId}:${toKeyId}`);\n}\n\n/**\n * Sign a succession from the keys being retired to the identity taking over.\n *\n * Takes `StoredKeys` for the predecessor because only the holder of K1's\n * private half can produce this, which is the property the whole design rests\n * on. A site calls this once, at rotation, on the machine holding its keys.\n */\nexport function signSuccession(\n previous: StoredKeys,\n next: PublicIdentity,\n): Succession {\n return {\n identity: {\n identity: previous.identityPublic,\n encryption: previous.encryptionPublic,\n encryptionSig: previous.encryptionSig,\n },\n signature: signWith(\n previous,\n successionStatement(keyId(previous.identityPublic), keyId(next.identity)),\n ),\n };\n}\n\n/**\n * Check one link: did `link.identity` sign over succeeding to `toKeyId`?\n *\n * `toKeyId` is passed in rather than read from anywhere in `link`, and that is\n * the load-bearing detail. A verifier that recovered the successor from the\n * signed statement would accept a statement about *any* successor, which is\n * the replay this design names in C.1 — the signature is genuine, the\n * successor it names is not the one being installed.\n */\nexport function verifyLink(link: Succession, toKeyId: string): boolean {\n // The predecessor's own identity is checked first, for the reason\n // `verifyPublicIdentity` exists: an encryption key not signed by the\n // identity presenting it is an upstream substitution, and a chain is a\n // place that key would otherwise arrive unexamined.\n if (!verifyPublicIdentity(link.identity)) return false;\n return verifyWith(\n link.identity.identity,\n successionStatement(keyId(link.identity.identity), toKeyId),\n link.signature,\n );\n}\n\n/** Why a chain was refused, in the words a log line uses. */\nexport type SuccessionFailure =\n \"no-chain\" | \"too-long\" | \"unknown-origin\" | \"broken-link\";\n\nexport interface SuccessionWalk {\n /** The ids the chain passes through, oldest first, ending at the current. */\n readonly path: string[];\n /** The approved id the chain reached, when it reached one. */\n readonly from?: string;\n readonly failure?: SuccessionFailure;\n}\n\n/**\n * Walk a chain from the key being presented back to a key already approved.\n *\n * `chain` is ordered oldest last, as the projection carries it — so walking it\n * means starting at the current key and stepping backwards, each link proving\n * that its holder signed for the id in front of it.\n *\n * Returns the approved id it reached, or why it did not. **Deliberately\n * returns rather than throws**: a chain that does not verify is ordinary\n * hostile input, and the caller's job is to keep its existing pin and say so.\n *\n * `approved` is asked as a predicate rather than taken as a set because the\n * daemon's notion of \"already approved\" includes tombstoned ids — a site that\n * left the allowlist and came back is still a site this machine has vouched\n * for, and rotation must not become a way to launder that distinction away.\n */\nexport function walkSuccession(input: {\n current: string;\n chain: readonly Succession[];\n approved: (keyId: string) => boolean;\n}): SuccessionWalk {\n const { current, chain, approved } = input;\n if (chain.length === 0) return { path: [current], failure: \"no-chain\" };\n if (chain.length > MAX_SUCCESSION_CHAIN)\n return { path: [current], failure: \"too-long\" };\n\n // Newest first: the last entry is the key that signed for `current`.\n const steps = [...chain].reverse();\n const path = [current];\n let succeeding = current;\n\n for (const link of steps) {\n if (!verifyLink(link, succeeding)) return { path, failure: \"broken-link\" };\n const previous = keyId(link.identity.identity);\n path.unshift(previous);\n if (approved(previous)) return { path, from: previous };\n succeeding = previous;\n }\n\n // Every link verified and none of them is a key this machine ever approved.\n // Not an attack and not an error: a stranger with a history, offered for\n // local approval like any other stranger.\n return { path, failure: \"unknown-origin\" };\n}\n","/**\n * The normative MUSTs of protocol v0, as data.\n *\n * byollm_001 requires that \"every MUST above has a conformance test id\n * referenced inline\". Keeping the MUSTs as a frozen registry rather than\n * prose is what makes that requirement *checkable*: the conformance kit\n * imports {@link MUSTS} and fails if any id has no test asserting it, so a\n * new MUST cannot be added without a test and a test cannot silently drift\n * away from the statement it claims to prove.\n *\n * Ids are stable and public — third-party servers cite them in their\n * certification output.\n */\n\n/** Which side of the wire is obliged to enforce a given MUST. */\nexport type MustEnforcer = \"daemon\" | \"server\" | \"both\";\n\n/**\n * How a MUST is actually verified — which is not the same question as who\n * enforces it, and is the one that decides what \"byollm-compatible\" means.\n *\n * The conformance kit's credibility rests on an implicit claim that every\n * MUST is checkable. Ten of them were not, and the kit reported that honestly\n * while nothing acted on it. Making the kind explicit turns \"uncovered\" from\n * a number needing a paragraph of explanation into a number that should be\n * zero.\n *\n * - `conformance` — the kit asserts it against *any* implementation. This is\n * the strong kind: a third party runs the suite and learns something.\n * - `adversarial` — proved by the reference daemon's own suites in this repo\n * (the hostile-payload corpus, or its unit tests). Real verification, and\n * it runs in CI — but it proves things about *our* daemon, not about\n * someone else's, so the kit cannot carry it.\n * - `construction` — true by the shape of the code, where a test could only\n * sample. A reviewer verifies it; a suite cannot.\n * ## When a MUST binds both sides — cloud_008 Tier 3\n *\n * `AUDIENCE_BOTH_SIDES` says the server and the daemon each enforce. The kit\n * passed **entirely** with the server's half deleted: every check drove a real\n * daemon, and a daemon refuses locally, so \"the job did not run\" looked\n * identical whichever side refused it. A full-honest-stack test proves only\n * the conjunction.\n *\n * So a `both`-enforced MUST needs **one check per party, each with the honest\n * counterpart removed** — C032 claims over the raw protocol precisely so no\n * daemon admission logic runs. Where a check strips one side, its comment\n * says which; where a MUST is enforced by both and only one side is checked,\n * that is a gap rather than coverage.\n *\n * - `operator` — a claim about how someone runs a deployment, verifiable only\n * by audit or by reading source. The honest category, and the one that\n * exists so a property nobody can check from outside is *labelled* as such\n * rather than laundered by association with the checkable ones.\n */\nexport type MustVerification =\n \"conformance\" | \"adversarial\" | \"construction\" | \"operator\";\n\n/**\n * How a MUST is verified — one kind, or several.\n *\n * Several is not hedging. `SITES_LOCALLY_APPROVED` is the case that forced it:\n * the fence is **construction** — a daemon cannot serve a site that is not in\n * its map, and admission refuses before a payload is fetched — while the\n * property that a *removed and re-offered* id is still refused needs a hostile\n * sequence of heartbeats no honest client would send, which is\n * **adversarial**. Recording one and dropping the other would either overstate\n * what a type check proves or understate what the suites do.\n *\n * The alternative was a second field for the second kind, which is two answers\n * to one question — the shape this project keeps deleting.\n */\nexport type MustVerifiedBy =\n MustVerification | readonly [MustVerification, ...MustVerification[]];\n\n/** The kinds a MUST claims, always as a list. */\nexport function kindsOf(must: {\n readonly verifiedBy: MustVerifiedBy;\n}): readonly MustVerification[] {\n return typeof must.verifiedBy === \"string\"\n ? [must.verifiedBy]\n : must.verifiedBy;\n}\n\n/** A single normative requirement of the protocol. */\nexport interface Must {\n /** Stable public id, cited by conformance output. */\n readonly id: string;\n /** The requirement, in MUST language. */\n readonly statement: string;\n /** Which implementation is obliged to enforce it. */\n readonly enforcedBy: MustEnforcer;\n /**\n * How this is verified. `conformance` is the only kind the kit can assert;\n * see {@link MustVerification} for why the others exist.\n */\n readonly verifiedBy: MustVerifiedBy;\n /** Spec section this was adjudicated in. */\n readonly source: string;\n}\n\nconst must = (m: Must): Must => Object.freeze(m);\n\n/**\n * Every normative MUST in protocol v0.\n *\n * @remarks\n * Grouped by concern for readability; the conformance kit treats this as a\n * flat set. Adding an entry here without a corresponding conformance test is\n * a CI failure, by design.\n */\nexport const MUSTS = Object.freeze({\n // ---- Pairing and identity -------------------------------------------\n PAIR_ONE_USER: must({\n id: \"PAIR_ONE_USER\",\n statement:\n \"A runner token MUST be bound to exactly one user; a daemon MUST refuse \" +\n \"work not attributable to its paired user.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n PAIR_INTERACTIVE: must({\n id: \"PAIR_INTERACTIVE\",\n statement:\n \"Pairing MUST be interactive (device-code approval in the app's own \" +\n \"session); a long-lived pasted secret MUST NOT be accepted as pairing.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.1\",\n }),\n PAIR_CODE_EXPIRES: must({\n id: \"PAIR_CODE_EXPIRES\",\n statement:\n \"An unapproved device code MUST expire and MUST NOT be redeemable after \" +\n \"expiry.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.1\",\n }),\n\n // ---- Typed job kinds --------------------------------------------------\n VERSION_HANDSHAKE_REQUIRED: must({\n id: \"VERSION_HANDSHAKE_REQUIRED\",\n statement:\n \"Every protocol request MUST declare a protocol version, and a server \" +\n \"MUST refuse an absent or unsupported one with a structured error \" +\n \"naming what it supports — never a generic parse failure.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §4\",\n }),\n SITE_KEY_BY_STUB: must({\n id: \"SITE_KEY_BY_STUB\",\n statement:\n \"A daemon MUST verify a job's payload against the pinned key of the \" +\n \"site the stub names, and MUST refuse a job naming a site it has not \" +\n \"pinned. It MUST NOT fall back to another pinned key, and MUST refuse \" +\n \"an envelope whose declared sender disagrees with the stub's site.\",\n enforcedBy: \"daemon\",\n // Adversarial, and the reason is the finding that produced it: the\n // honest paths pass with every site check deleted, because `open`\n // refuses a signature from the wrong key anyway. What distinguishes an\n // enforced rule from a coincidence here is a hostile pairing of stub and\n // envelope, which no conformance client would ever send.\n verifiedBy: \"adversarial\",\n source: \"byollm_009 §A.3\",\n }),\n SITES_LOCALLY_APPROVED: must({\n id: \"SITES_LOCALLY_APPROVED\",\n statement:\n \"A daemon MUST NOT run work for a site on an upstream's word alone. \" +\n \"An upstream may propose a site set; work for any site in it MUST \" +\n \"additionally carry a grant signed by the control-plane key this \" +\n \"daemon pinned at pairing. A key that has changed for an id this \" +\n \"daemon already pinned MUST be refused for the life of the pairing, \" +\n \"including after that id has left the set and returned. A **verified \" +\n \"succession** is not a changed key: a new key id carrying a signature, \" +\n \"by a key this daemon has already pinned, over a statement naming both \" +\n \"key ids MUST be accepted — provided the control plane projects the \" +\n \"same successor — and MUST be announced rather than applied silently. \" +\n \"The first job from a site this daemon has never served MUST be \" +\n \"announced at the machine.\",\n enforcedBy: \"daemon\",\n // Two kinds, and the second is the one that matters — V1-1.\n //\n // `construction`: the daemon cannot serve a site that is not in its\n // pinned map, and admission refuses before a payload is fetched — and\n // since byollm_016 Amendment K, being in the map is no longer sufficient\n // either: a signed grant is, and the relay proposing the set cannot\n // produce one.\n //\n // `adversarial`: the property that survives is about a *sequence* —\n // remove the id, re-offer it under a different key — which no honest\n // upstream sends and which the fence above does not see. That was the\n // bypass: the pin was deleted with the id, so the comparison had nothing\n // to compare against and the substitution arrived as a stranger.\n // **Not `conformance`, and that is a live gap rather than a judgement.**\n // Amendment C's succession clause is a rule about two implementations\n // agreeing, which is what a conformance check is for — but rotating a\n // site's key is not something `ConformanceTarget` can express, and adding\n // an optional hook that most targets omit would produce a check reporting\n // success for a reason unrelated to the property it claims. That is this\n // project's most-repeated bug, and it is not worth reintroducing for a\n // stronger-sounding word in a table. The rotation path is verified by\n // `site-rotation.test.ts` (both directions, against the shipped runner)\n // and `relay/test/rotation.test.ts` (both planes, against the reference\n // relay); the missing piece is a second *independent* implementation to\n // check them against, and there is not one yet.\n verifiedBy: [\"construction\", \"adversarial\"],\n source: \"byollm_009 §B.2, Amendment C\",\n }),\n KEYS_EXCHANGED_AT_CONSENT: must({\n id: \"KEYS_EXCHANGED_AT_CONSENT\",\n statement:\n \"Pairing MUST exchange both parties' public identities; each side MUST \" +\n \"verify that the encryption key is signed by the identity presenting \" +\n \"it, and MUST pin the identity. Keys MUST NOT be delivered before \" +\n \"approval.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §5\",\n }),\n REQUESTS_SIGNED_NOT_BEARER: must({\n id: \"REQUESTS_SIGNED_NOT_BEARER\",\n statement:\n \"Every authenticated request MUST be signed by the calling device's \" +\n \"pinned identity key, over the endpoint, the runner id, a timestamp \" +\n \"and the exact request body. A server MUST NOT accept a bearer \" +\n \"credential in place of a signature.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §4.2\",\n }),\n LEASE_SCOPED_BY_GRANT: must({\n id: \"LEASE_SCOPED_BY_GRANT\",\n statement:\n \"A lease-scoped request MUST name the lease it acts on, and a server \" +\n \"MUST apply it only to that lease. Naming the job and the runner is \" +\n \"not sufficient: both survive a claim-release-reclaim cycle.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §4.2\",\n }),\n STUB_METADATA_EXHAUSTIVE: must({\n id: \"STUB_METADATA_EXHAUSTIVE\",\n statement:\n \"A claim MUST answer with stubs carrying exactly the enumerated \" +\n \"fields and no payload. An endpoint MUST NOT emit a stub carrying \" +\n \"others, and an upstream MUST NOT require any.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §6\",\n }),\n ENVELOPE_SEALED_AND_SIGNED: must({\n id: \"ENVELOPE_SEALED_AND_SIGNED\",\n statement:\n \"A stored payload MUST be sealed, and MUST be signed by the sender's \" +\n \"identity key. An endpoint MUST refuse an envelope whose signature \" +\n \"does not verify against the identity it pinned.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §6\",\n }),\n KIND_TYPED_ONLY: must({\n id: \"KIND_TYPED_ONLY\",\n statement:\n \"Job kinds MUST resolve against handlers baked into the daemon. A daemon \" +\n \"MUST refuse an unknown kind rather than guess.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Jobs are typed data\",\n }),\n KIND_NO_CODE: must({\n id: \"KIND_NO_CODE\",\n statement:\n \"A server MUST NOT be able to convey code, a shell string, or a path to \" +\n \"execute; payloads are data handed to a model only.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Jobs are typed data; byollm_004 §1\",\n }),\n\n // ---- Capability and claiming -----------------------------------------\n CLAIM_REQUIRES_CAPABILITY: must({\n id: \"CLAIM_REQUIRES_CAPABILITY\",\n statement:\n \"A daemon MUST NOT be given a job whose kind is absent from its \" +\n \"advertised capability matrix.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n CAPABILITY_IS_DETECTED: must({\n id: \"CAPABILITY_IS_DETECTED\",\n statement:\n \"An advertised capability matrix MUST be the intersection of owner \" +\n \"config and detected, healthy reality — never config alone.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_002 §Routing\",\n }),\n CLAIM_ATOMIC: must({\n id: \"CLAIM_ATOMIC\",\n statement:\n \"Claiming MUST be atomic: a job MUST NOT be handed to two runners \" +\n \"concurrently.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.2\",\n }),\n\n // ---- Leases -----------------------------------------------------------\n LEASE_HONORED: must({\n id: \"LEASE_HONORED\",\n statement:\n \"A daemon MUST stop work on a job whose lease it has failed to renew, \" +\n \"and MUST NOT report a result for an expired lease it no longer holds.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n LEASE_RECLAIMABLE: must({\n id: \"LEASE_RECLAIMABLE\",\n statement:\n \"A lease that expires un-renewed MUST make its job claimable again with \" +\n \"no loss of the job.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.2\",\n }),\n\n // ---- Audience and offer scope ----------------------------------------\n AUDIENCE_BOTH_SIDES: must({\n id: \"AUDIENCE_BOTH_SIDES\",\n statement:\n \"A job MUST run on a daemon only if the daemon's offer scope admits the \" +\n \"job's owner AND the job's audience admits the daemon's owner.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §The audience model\",\n }),\n SUBSCRIPTION_SELF_LOCK: must({\n id: \"SUBSCRIPTION_SELF_LOCK\",\n statement:\n \"A subscription-class backend's offer scope MUST be 'private' and MUST NOT \" +\n \"be widened by configuration.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §The audience model\",\n }),\n METERED_DEFAULTS_SELF: must({\n id: \"METERED_DEFAULTS_SELF\",\n statement:\n \"A metered backend's effective offer scope MUST be 'private' unless the \" +\n \"owner has explicitly acknowledged spending money on others' work.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §4\",\n }),\n METERED_REQUIRES_CEILING: must({\n id: \"METERED_REQUIRES_CEILING\",\n statement:\n \"A widened metered backend MUST carry a spend ceiling, and the daemon \" +\n \"MUST refuse community work once it is reached.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §4\",\n }),\n COST_NOT_CONFIGURABLE: must({\n id: \"COST_NOT_CONFIGURABLE\",\n statement:\n \"A built-in provider's cost class MUST NOT be overridable by \" +\n \"configuration.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §2\",\n }),\n REMOTE_IS_NEVER_FREE: must({\n id: \"REMOTE_IS_NEVER_FREE\",\n statement:\n \"A generic HTTP backend whose base URL is not loopback or private MUST \" +\n \"be treated as metered.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_007 §2\",\n }),\n\n NAMED_LOCAL_ALLOWLIST: must({\n id: \"NAMED_LOCAL_ALLOWLIST\",\n statement:\n \"A 'team' job MUST be admitted only by something the device itself \" +\n \"verified, keyed by (server origin, user id) — never on the routing \" +\n \"party's assertion alone.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §B\",\n }),\n\n REFUSAL_NOT_REOFFERED: must({\n id: \"REFUSAL_NOT_REOFFERED\",\n statement:\n \"A server MUST NOT re-offer a job to a runner that released it with \" +\n \"reason 'refused'.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §B (loop resolved in build review)\",\n }),\n\n // ---- Revocation and cancel -------------------------------------------\n REVOCATION_HONORED: must({\n id: \"REVOCATION_HONORED\",\n statement:\n \"A revoked daemon MUST stop claiming and MUST abandon in-flight work by \" +\n \"the next heartbeat at the latest.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n CANCEL_HONORED: must({\n id: \"CANCEL_HONORED\",\n statement:\n \"A job id in a heartbeat response's cancel list MUST abort that job's \" +\n \"in-flight backend call and be reported as 'canceled'.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §C\",\n }),\n\n // ---- Lifecycle, dependencies, delivery -------------------------------\n DEPENDS_ON_GATING: must({\n id: \"DEPENDS_ON_GATING\",\n statement:\n \"A job MUST NOT be claimable until every job in its dependsOn set has \" +\n \"reached the 'ok' state.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §E\",\n }),\n TTL_EXPIRY: must({\n id: \"TTL_EXPIRY\",\n statement:\n \"An unclaimed job MUST become 'expired' once its TTL elapses, and the \" +\n \"TTL clock MUST start when the job becomes claimable, not at enqueue.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §D (TTL clock resolved in build review)\",\n }),\n NO_RUNNER_SIGNAL: must({\n id: \"NO_RUNNER_SIGNAL\",\n statement:\n \"A server MUST surface noRunnerAvailable when no runner with matching \" +\n \"capability has heartbeated within the liveness window, and MUST NOT \" +\n \"raise it for a job still blocked on dependencies.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 Rev 1 §D\",\n }),\n RESULT_IDEMPOTENT: must({\n id: \"RESULT_IDEMPOTENT\",\n statement:\n \"Result submission MUST be idempotent by job id; the first terminal \" +\n \"outcome wins and later submissions MUST NOT change it.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §Endpoints.4\",\n }),\n PROVENANCE_NAMES_DEVICE: must({\n id: \"PROVENANCE_NAMES_DEVICE\",\n statement:\n \"A result MUST carry the claiming device's key id and its relationship \" +\n \"to the requester, to the delivery seam, so an app never treats \" +\n \"volunteer output as first-party. The key id MUST be the device the \" +\n \"upstream granted the lease to, and a result whose signature does not \" +\n \"verify against that device MUST be refused rather than recorded.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §11\",\n }),\n\n // ---- The trust surface -------------------------------------------------\n INGRESS_LOGGED_BEFORE_EXECUTION: must({\n id: \"INGRESS_LOGGED_BEFORE_EXECUTION\",\n statement:\n \"Every executed prompt MUST be appended to the local ingress log before \" +\n \"execution begins.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"conformance\",\n source: \"byollm_001 §MUSTs\",\n }),\n\n // ---- Execution isolation (byollm_004) ---------------------------------\n NO_SHELL_INTERPOLATION: must({\n id: \"NO_SHELL_INTERPOLATION\",\n statement:\n \"Process-class backends MUST be invoked with a fixed argv array and the \" +\n \"payload delivered on stdin; payload text MUST NOT reach a command line.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n /**\n * Amended for byollm_016 Phase B, and the amendment is deliberately narrow.\n *\n * A site may now name a **service** on the stub. The temptation is to read\n * that as a crack in this law, so the statement below says exactly where the\n * line is: a name selects from a menu the owner published, and resolves to a\n * model, backend, base URL and flags **only** through that owner's own\n * config. The site supplies a key; the owner supplies every value it maps\n * to. A name the owner does not advertise is refused rather than\n * substituted, because substitution is how \"you may pick from my list\" turns\n * into \"you may ask for anything and get something\".\n *\n * Two properties keep it from drifting into \"sites demand models\":\n *\n * 1. **Nothing the site sends is ever a value.** No model string, no URL,\n * no flag crosses the wire — only a key that means nothing off this\n * owner's machine.\n * 2. **It is a stub field, never a payload field.** The prompt cannot\n * reach it. That is unchanged and is the sentence the second clause\n * below still enforces verbatim.\n */\n NO_PAYLOAD_ROUTING: must({\n id: \"NO_PAYLOAD_ROUTING\",\n statement:\n \"Model, backend, base URL, and flags MUST come from owner config only; \" +\n \"a payload MUST NOT influence any of them. A stub MAY name a service \" +\n \"the owner advertises, which selects among that owner's own config \" +\n \"entries and MUST NOT introduce any value the owner did not write; an \" +\n \"unadvertised name MUST be refused, never substituted.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2, amended byollm_016 §Phase B\",\n }),\n STRIPPED_CHILD_ENV: must({\n id: \"STRIPPED_CHILD_ENV\",\n statement:\n \"Process-class children MUST spawn with an allowlisted environment, a \" +\n \"scratch cwd, no inherited descriptors beyond std streams, and hard \" +\n \"timeout and output-size caps.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n HTTP_BASE_URL_SAFE: must({\n id: \"HTTP_BASE_URL_SAFE\",\n statement:\n \"HTTP-class backends MUST send requests only to the owner-configured \" +\n \"base URL and MUST refuse base URLs resolving to cloud-metadata or \" +\n \"link-local addresses.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 Rev 1 §Backend taxonomy\",\n }),\n OUTPUT_INERT: must({\n id: \"OUTPUT_INERT\",\n statement:\n \"Returned text MUST be treated as inert bytes: never evaluated, never \" +\n \"written to a payload-named path, never interpolated into a shell or \" +\n \"into terminal control sequences when logged.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §2\",\n }),\n COMMUNITY_BUDGETS: must({\n id: \"COMMUNITY_BUDGETS\",\n statement:\n \"Jobs whose owner is not the daemon's owner MUST be subject to the \" +\n \"owner's rate limits, daily cap, and resource budget.\",\n enforcedBy: \"daemon\",\n verifiedBy: \"adversarial\",\n source: \"byollm_004 §4\",\n }),\n REVOCATION_IMMEDIATE: must({\n id: \"REVOCATION_IMMEDIATE\",\n statement:\n \"Revocation MUST take effect at the upstream at once — a revoked \" +\n \"runner MUST NOT be granted further work from the moment the record \" +\n \"changes — and MUST reach the daemon by its next heartbeat.\",\n // Both, and stated as one sentence with two obligations rather than\n // folded into REVOCATION_HONORED. That one binds the *daemon*: a revoked\n // daemon stops claiming and abandons in-flight work. This binds the\n // *upstream*. byollm_009 §5 is explicit that the pair is the point — \"a\n // revocation enforced at one end survives a compromise of that end\" — and\n // one entry covering both would make a compromised daemon look compliant.\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §11\",\n }),\n CONSENT_BEFORE_ROUTE: must({\n id: \"CONSENT_BEFORE_ROUTE\",\n statement:\n \"An upstream MUST NOT route a job to a device without a record binding \" +\n \"that user, that site and that scope. There MUST be no discovery path \" +\n \"by which a device receives work it was never granted.\",\n enforcedBy: \"server\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §11\",\n }),\n ROSTER_NOT_DISCLOSED: must({\n id: \"ROSTER_NOT_DISCLOSED\",\n statement:\n \"A site MUST NOT learn the membership of a group whose compute it \" +\n \"uses, and MUST NOT publish membership to a routing party. No wire \" +\n \"message may carry a list of who may run a job.\",\n // Checkable since cloud_008 §0.2 took `audienceAllow` off the stub: the\n // property now holds by *absence*, and absence is exactly what a strict\n // schema and a serialised stub can be asked about. Before that it was a\n // sentence — and one this project cited in code comments, tests and two\n // specs as though it were enforced data, which is why it is worth\n // stating precisely rather than generously.\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §11\",\n }),\n EFFECTIVE_OFFER_ONLY: must({\n id: \"EFFECTIVE_OFFER_ONLY\",\n statement:\n \"A daemon MUST declare effective offers only. An upstream MUST NOT \" +\n \"receive raw config, allowlists, or capacity the owner has not shared, \" +\n \"and MUST act on the declared offer rather than on what was asked for.\",\n enforcedBy: \"both\",\n verifiedBy: \"conformance\",\n source: \"byollm_009 §11\",\n }),\n FALLBACK_LABELED: must({\n id: \"FALLBACK_LABELED\",\n statement:\n \"Work served by anything other than the user's own compute MUST be \" +\n \"labelled as such wherever it is reported, and MUST NOT be silently \" +\n \"substituted.\",\n // `construction` today, and deliberately not `conformance`. Nothing on\n // the wire yet distinguishes a fallback from any other community job —\n // the ledger that would give it a surface is unbuilt — so a check would\n // have to assert something it cannot observe. Promoted the day that\n // surface exists. Marking it `conformance` now would put \"verified\"\n // beside a property no third party can see, which is the one thing the\n // kinds exist to prevent.\n enforcedBy: \"both\",\n verifiedBy: \"construction\",\n source: \"byollm_009 §11\",\n }),\n RELAY_BLIND: must({\n id: \"RELAY_BLIND\",\n statement:\n \"A relay MUST NOT hold any key capable of decrypting a payload, a \" +\n \"result, or a delta frame.\",\n // Operator: a third party can read the relay's types and see there is\n // nowhere to put such a key, but the kit certifies a *server* and cannot\n // reach inside somebody's deployment to prove what it holds.\n enforcedBy: \"server\",\n verifiedBy: \"operator\",\n source: \"byollm_009 §11\",\n }),\n SHARED_COMPUTE_DISCLOSED: must({\n id: \"SHARED_COMPUTE_DISCLOSED\",\n statement:\n \"Before a user's work first runs on compute they do not own, they MUST \" +\n \"be told in plain language that the machine's owner can see it.\",\n // Operator, and cloud_008 §0.3 is why the classification now comes with a\n // standing answer rather than a standing question. The screen is not\n // wire-observable, but the *string the server composes* is, and it is\n // now unit-tested with the two false sentences forbidden by name. The\n // kind stays `operator` because a third-party site can still render\n // whatever it likes; what changed is that the part inside our own\n // boundary stopped depending on somebody remembering to audit it.\n enforcedBy: \"server\",\n verifiedBy: \"operator\",\n source: \"byollm_009 §11\",\n }),\n} as const satisfies Record<string, Must>);\n\n/**\n * Ids that were retired, and what took over.\n *\n * Ids are public — third-party certification output cites them — so one\n * cannot simply disappear. `RESULT_PROVENANCE` was not renamed: since\n * Amendment A a result's attribution is by *proof of possession* rather than\n * by a carried label, and `PROVENANCE_NAMES_DEVICE` says so. The old\n * statement is true of the new one and weaker, which is what \"subsumed\"\n * means here.\n */\nexport const RETIRED_MUSTS = Object.freeze({\n RESULT_PROVENANCE: {\n supersededBy: \"PROVENANCE_NAMES_DEVICE\",\n note:\n \"Strengthened, not renamed: attribution is now by proof of possession \" +\n \"— the result's signature must verify against the device the upstream \" +\n \"granted the lease to — rather than by a provenance label travelling \" +\n \"beside it. byollm_009 §11 states the stronger form.\",\n },\n} as const satisfies Record<string, { supersededBy: string; note: string }>);\n\n/** The id of any normative MUST. */\nexport type MustId = keyof typeof MUSTS;\n\n/** All MUST ids, for coverage checks. */\nexport const MUST_IDS = Object.freeze(Object.keys(MUSTS) as MustId[]);\n\n/** Every MUST verified a particular way. */\nexport function mustsVerifiedBy(kind: MustVerification): MustId[] {\n return MUST_IDS.filter((id) => kindsOf(MUSTS[id]).includes(kind));\n}\n","import { z } from \"zod\";\nimport { PublicIdentity } from \"./keys.js\";\nimport { MAX_SUCCESSION_CHAIN, Succession } from \"./succession.js\";\nimport { OfferScope } from \"./audience.js\";\nimport { BackendClass, BackendIdSchema } from \"./backends.js\";\nimport { ClaimedStub } from \"./job.js\";\nimport { SealedEnvelope } from \"./envelope.js\";\nimport { JobKind } from \"./kinds.js\";\n\n/**\n * Protocol version carried on every request; servers refuse what they can't\n * speak.\n *\n * **`1` because byollm_016 changed the vocabulary** — byollm-review\n * 2026-08-27. `OfferScope` lost `public`, `self|named` became\n * `private|team`, `JobStub` lost `service` and gained `purpose`, and the\n * grant's site field changed namespace. The version stayed `0` through all of\n * it.\n *\n * The consequence was the failure the handshake exists to prevent, arriving\n * around it: a pre-rip daemon declares `0`, passes the version check, and\n * then fails whole-body schema validation with \"request failed schema\n * validation\" — no field named, no vocabulary named, no upgrade command. Once\n * every ten seconds, forever, while its owner watches a device go stale for\n * no stated reason. The check below was written because \"a mismatch surfaced\n * as a generic bad-request\" and \"an error a user cannot act on is barely\n * better than a hang\"; the number not moving is how that came back.\n *\n * A registry is a schema and an enum value is the contract — this project's\n * own words, from the release that silenced a fleet by adding a backend id.\n * The same sentence applies to removing an offer scope.\n */\nexport const PROTOCOL_VERSION = \"1\" as const;\n\n/**\n * Every protocol version this build can serve, **oldest first**.\n *\n * One entry today. It is a list rather than a constant because the shape of\n * the check is the point: a server supporting two versions through a\n * migration should not need a different code path from one supporting one.\n */\n/**\n * `0` is deliberately **not** here, though the list exists for exactly that.\n *\n * Supporting two versions through a migration is the shape this was built\n * for, and it is the wrong tool when the vocabularies are incompatible: a `0`\n * daemon sends `offer: \"public\"` and a `service` on its stubs, so accepting\n * its version only moves the refusal one layer down — to the schema error\n * that names nothing, which is the bug. Refusing the version is the whole\n * point, because that refusal says what to do.\n */\nexport const SUPPORTED_PROTOCOL_VERSIONS = Object.freeze([\n PROTOCOL_VERSION,\n]) as readonly string[];\n\n/**\n * The oldest version this build will talk to — derived, not declared.\n *\n * Stating it separately would be a second thing to keep in step with the list\n * above, and the failure would be silent: a minimum that no longer matches\n * what is supported produces a refusal naming a version the server would in\n * fact have accepted.\n */\nexport const MIN_PROTOCOL_VERSION: string =\n SUPPORTED_PROTOCOL_VERSIONS[0] ?? PROTOCOL_VERSION;\n\n/** A structured refusal, so a daemon can say something useful to its owner. */\nexport interface VersionRefusal {\n readonly error: \"unsupported-protocol-version\";\n readonly message: string;\n readonly supported: readonly string[];\n readonly minimum: string;\n}\n\n/**\n * The version a request declares, wherever it carries it.\n *\n * A POST declares it in its body, which is where every request schema has\n * always put it. A GET has no body, and the relay has one — the site plane's\n * `pending` read — so it declares it in the query string instead.\n *\n * **Two carriers, one rule.** That asymmetry is HTTP's rather than ours, and\n * the alternative was worse in both directions: a header for everything would\n * change every existing daemon's request, and skipping GETs would leave an\n * endpoint outside the handshake — which is precisely the shape B.4 found,\n * where a whole plane was outside it.\n */\nexport function declaredVersion(input: {\n body?: unknown;\n query?: URLSearchParams;\n}): unknown {\n const { body, query } = input;\n if (\n typeof body === \"object\" &&\n body !== null &&\n Object.hasOwn(body, \"protocolVersion\")\n ) {\n return (body as { protocolVersion: unknown }).protocolVersion;\n }\n return query?.get(\"protocolVersion\") ?? undefined;\n}\n\n/**\n * Check the protocol version on an incoming request\n * ({@link MUSTS.VERSION_HANDSHAKE_REQUIRED}).\n *\n * Returns a refusal, or `null` to proceed.\n *\n * **A missing version is refused the same way a wrong one is.** That is the\n * half worth stating: before this existed, the version travelled as a\n * `z.literal` inside each endpoint's schema, so a mismatch surfaced as a\n * generic `bad-request` — a daemon and a server discovered they disagreed by\n * failing, with nothing in the response naming the disagreement. An error a\n * user cannot act on is barely better than a hang.\n *\n * The message names the fix, because the person reading it is usually the one\n * who has to apply it.\n */\nexport function checkProtocolVersion(body: unknown): VersionRefusal | null {\n // `hasOwn`, not `in`: `in` walks the prototype chain, and a version check\n // should read what the request actually carried rather than something an\n // object happens to inherit. Not reachable from a JSON body today, which is\n // the reason to fix it now rather than after it is.\n const declared =\n typeof body === \"object\" &&\n body !== null &&\n Object.hasOwn(body, \"protocolVersion\")\n ? (body as { protocolVersion: unknown }).protocolVersion\n : undefined;\n\n if (typeof declared !== \"string\" || declared.length === 0) {\n return {\n error: \"unsupported-protocol-version\",\n message:\n \"this request declared no protocol version. Upgrade the daemon: \" +\n `\\`${UPGRADE_COMMAND}\\`.`,\n supported: SUPPORTED_PROTOCOL_VERSIONS,\n minimum: MIN_PROTOCOL_VERSION,\n };\n }\n\n if (!SUPPORTED_PROTOCOL_VERSIONS.includes(declared)) {\n return {\n error: \"unsupported-protocol-version\",\n message:\n `this server speaks protocol ${SUPPORTED_PROTOCOL_VERSIONS.join(\", \")} ` +\n `and the daemon asked for ${declared}. ` +\n (declared < MIN_PROTOCOL_VERSION\n ? `Upgrade the daemon: \\`${UPGRADE_COMMAND}\\`.`\n : \"This daemon is newer than the server; the server needs upgrading.\"),\n supported: SUPPORTED_PROTOCOL_VERSIONS,\n minimum: MIN_PROTOCOL_VERSION,\n };\n }\n\n return null;\n}\n\n/**\n * How to upgrade a daemon, in one place.\n *\n * `@latest`, which is correct in both eras and therefore never has to be\n * revisited: during a prerelease it resolves to the current alpha, and after\n * one it resolves to the current stable.\n *\n * `@alpha` was considered and rejected. The argument for it was that `latest`\n * is moved by hand — it needs a human with 2FA, deliberately — so it can lag\n * the `alpha` tag. In practice that lag has been minutes, and the cost on the\n * other side is permanent: the day this stops being a prerelease, `@alpha`\n * starts meaning \"the unstable one\", and every user who followed this message\n * is pinned to prereleases with nothing to tell them.\n *\n * Note this is deliberately *not* the rule `scripts/check-site.mjs` enforces\n * on the docs, which requires `npx byollm@alpha`. That rule is about somebody\n * choosing to install a prerelease knowingly, with the warning in front of\n * them. This is an upgrade instruction handed to somebody who already has the\n * daemon and needs a newer one — a different question with a different answer.\n */\nexport const UPGRADE_COMMAND = \"npm i -g byollm@latest\" as const;\n\n/** The path prefix all endpoints mount under. */\nexport const PROTOCOL_PREFIX = \"/byollm\" as const;\n\n/**\n * The endpoint names, in the order byollm_001 lists them, plus `fetch`.\n *\n * `fetch` is byollm_009 §6's second phase: a claim returns a stub, and the\n * payload is collected separately by the device that took it. Two steps\n * rather than one because a payload can only be sealed once its recipient is\n * known — which is also what makes multi-device free.\n */\nexport const ENDPOINTS = Object.freeze([\n \"pair\",\n \"claim\",\n \"fetch\",\n \"heartbeat\",\n \"result\",\n \"release\",\n] as const);\nexport type Endpoint = (typeof ENDPOINTS)[number];\n\n/**\n * One entry of the capability matrix: a kind this daemon can actually serve,\n * right now, with the backend and model that would serve it.\n *\n * Derived from owner config intersected with detected reality\n * ({@link MUSTS.CAPABILITY_IS_DETECTED}) — a configured-but-unreachable\n * backend must not appear here. Carries `backendClass` so the app can tell\n * whether a result came from a sandboxed spawn or an HTTP call\n * (byollm_001 Rev 1 §A).\n */\nexport const Capability = z\n .object({\n kind: JobKind,\n /**\n * The owner's name for the service answering this kind — byollm_016.\n *\n * A device advertises *which* of its services serves a kind, not merely\n * that something does. **A site never sees this**, and never did after\n * Amendment L: it is what a control plane resolves a person's mapping\n * against, so that the service a grant names is one this device actually\n * offers rather than one somebody invented.\n *\n * `isDefault` used to sit beside it, saying which row an unselected job\n * took. Nothing selects any more — a job names a purpose and a person's\n * mapping names a service — so there is no unselected job for a default\n * to catch, and the field went with the machinery it served.\n */\n service: z.string().min(1),\n\n backendId: BackendIdSchema,\n backendClass: BackendClass,\n model: z.string().min(1),\n /**\n * Models this device's CLI knows about — byollm_017 ruling 3.\n *\n * **Suggestions, not a vocabulary.** Free text is always allowed: the\n * promise is that a model released this morning works this morning, and a\n * frozen list anywhere a person picks from breaks that on the first day\n * it matters. What makes free text safe is ruling 2 — a model is probed\n * before it is stored, so \"found is not works\" is answered by the device\n * rather than by a list.\n *\n * Announced with the capability rather than kept in the dashboard,\n * because the answer is \"what does THIS device's CLI know\" and only the\n * device can say. A list held cloud-side would be one more thing to\n * update on release day, and wrong for anybody who had not upgraded.\n *\n * Optional, and empty is legal. A backend with nothing to suggest — a\n * local server serving one model — is not a backend in an error state,\n * and a reader must not render an absent list as \"no models available\".\n */\n knownModels: z.array(z.string().min(1)).optional(),\n offerScope: OfferScope,\n })\n .strict();\nexport type Capability = z.infer<typeof Capability>;\n\n/** The capability matrix a daemon advertises. */\nexport const CapabilityMatrix = z.array(Capability);\nexport type CapabilityMatrix = z.infer<typeof CapabilityMatrix>;\n\n/**\n * A kind this device could serve and deliberately does not — byollm_016.\n *\n * Two services answer one kind and the owner has not said which wins, so the\n * kind is not advertised. That is correct and, unsaid, invisible: the owner\n * adds a second service, jobs stop matching, and no surface explains it.\n *\n * It travels because the surfaces that must say so are not all on the device.\n * The owner's card names the claimants; a teammate's card says only that the\n * owner has a choice to make. Claimant **offer scopes** ride along so the hub\n * can compute that difference without the device deciding who is asking —\n * carry for computation, filter for display, the same shape the effective\n * offer already uses.\n */\nexport const WithheldKind = z\n .object({\n kind: JobKind,\n claimants: z\n .array(\n z.object({ service: z.string().min(1), offer: OfferScope }).strict(),\n )\n .min(2),\n })\n .strict();\nexport type WithheldKind = z.infer<typeof WithheldKind>;\n\n// ---------------------------------------------------------------------------\n// 1. POST /byollm/pair — device-code flow\n// ---------------------------------------------------------------------------\n\n/**\n * One grant, named by both halves — V1-3.\n *\n * A job id is chosen per site, so the lease id is the unique thing an upstream\n * and a daemon can both point at. Anywhere a request says \"this piece of work,\n * held by me\", it says it with both.\n *\n * Declared once because it was written out twice — `activeLeases` and\n * `ReleaseRequest.leases` — and both needed the same `.strict()` added. Two\n * copies of a shape are two places to forget it.\n */\nexport const GrantRef = z\n .object({ jobId: z.string().min(1), leaseId: z.string().min(1) })\n .strict();\nexport type GrantRef = z.infer<typeof GrantRef>;\n\n/**\n * Pairing is a device-code exchange, not a pasted secret\n * ({@link MUSTS.PAIR_INTERACTIVE}). The daemon starts a pairing, shows the\n * user a short code and a URL, and polls until the user approves it inside\n * the app's own authenticated session. Nothing listens on the user's machine\n * and nothing works over a copied string alone.\n */\nexport const PairStartRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n action: z.literal(\"start\"),\n daemon: z\n .object({\n version: z.string().min(1),\n /** Shown in the app's runner list so a user can tell their machines apart. */\n label: z.string().min(1).max(120),\n platform: z.enum([\"darwin\", \"linux\", \"win32\"]),\n })\n // Its own `.strict()`: a parent's does not reach a nested object, so\n // this one stripped unknown keys while the shape around it threw.\n .strict(),\n /**\n * This machine's public keys (byollm_009 §5).\n *\n * Pairing is where the two parties learn each other's identities, because\n * it is the one moment a human is already deciding to trust: the approval\n * click. A key exchanged anywhere else would be a key nobody chose.\n */\n device: PublicIdentity,\n capabilities: CapabilityMatrix,\n })\n .strict();\nexport type PairStartRequest = z.infer<typeof PairStartRequest>;\n\nexport const PairStartResponse = z\n .object({\n /** Secret the daemon polls with. Never shown to the user. */\n deviceCode: z.string().min(20),\n /** Short code the user reads and confirms in the browser. */\n userCode: z.string().min(4).max(16),\n /** Where the user approves. Must be on the server's own origin. */\n verificationUrl: z.url(),\n /** Epoch ms after which the code is dead ({@link MUSTS.PAIR_CODE_EXPIRES}). */\n expiresAt: z.number().int().positive(),\n /** How often the daemon may poll. */\n pollIntervalMs: z.number().int().min(500).max(60_000),\n })\n .strict();\nexport type PairStartResponse = z.infer<typeof PairStartResponse>;\n\nexport const PairPollRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n action: z.literal(\"poll\"),\n deviceCode: z.string().min(20),\n })\n .strict();\nexport type PairPollRequest = z.infer<typeof PairPollRequest>;\n\nexport const PairPollResponse = z.discriminatedUnion(\"status\", [\n z.object({ status: z.literal(\"pending\") }).strict(),\n z.object({ status: z.literal(\"denied\") }).strict(),\n z.object({ status: z.literal(\"expired\") }).strict(),\n z\n .object({\n status: z.literal(\"approved\"),\n // `runnerToken` is gone — cloud_008 §2.4, finding 37.\n //\n // It was minted here, hashed into `RunnerRecord.tokenHash`, written to\n // the daemon's pairings file, and then **never sent, never looked up\n // and never compared**. `getRunnerByTokenHash` existed on both stores\n // and was called by nothing but a test asserting it returns null.\n //\n // Not merely dead wire, which is what `audienceAllow` and\n // `HeartbeatResponse.leases` were. This was a *secret*: minted,\n // transmitted, and written to two disks at rest, for nothing. A\n // credential with no purpose is a liability rather than clutter,\n // because the only thing it can ever do is leak.\n //\n // `REQUESTS_SIGNED_NOT_BEARER` was already the rule and was already\n // enforced — every authenticated call is signed by the device's pinned\n // identity key. This removes the thing the MUST is named after.\n runnerId: z.string().min(1),\n /** The app's id for the approving user — this daemon's owner forever. */\n owner: z.string().min(1),\n /** Display name for the trust UI, if the app offers one. */\n ownerLabel: z.string().optional(),\n /**\n * The sites this pairing covers, for the daemon to pin (byollm_009 §5),\n * keyed by each site's identity key id — cloud_009 §5.\n *\n * Returned only on approval: a pending or denied poll learns nothing,\n * so an unapproved code cannot be used to enumerate a site's keys.\n *\n * **One pairing per upstream, not one per site.** A user who connects a\n * site on a web dashboard has no reason to go back to a laptop and run\n * a command, so which sites a pairing covers is a projection of consent\n * — refreshed on the heartbeat — rather than something frozen at\n * pairing. A direct site answers with exactly one entry, which is the\n * same shape and not a special case.\n *\n * Keyed by the id `stub.site` carries (Amendment A §A.3), so the\n * runner's lookup is a map read rather than a join across two\n * namespaces.\n */\n sites: z.record(z.string().min(1), PublicIdentity),\n /**\n * The control plane's grant-signing key, pinned here — Amendment J.\n *\n * **Pairing is when, and that is the whole question.** Pairing is\n * already the ceremony where an owner proves out of band that this\n * device is theirs, so a key learned here rides trust that has already\n * happened. The rejected alternative is trust-on-first-grant, and it is\n * rejected because it hands the decision back to the relay: a daemon\n * that learns whose signature to trust from the first grant to arrive\n * has its admission authority chosen by whoever controls delivery.\n *\n * Optional on the wire, and only on the wire: a direct-mode server has\n * no control plane and signs nothing, and a daemon that receives no key\n * serves its owner alone. It is not optional for a relay with a control\n * plane — one that omitted it would be asking devices to accept grants\n * from nobody in particular, and would find every job refused.\n *\n * Rotation is Amendment C's, with no path where a grant teaches a\n * daemon a new key.\n */\n controlPlanePublic: z.string().min(1).optional(),\n })\n .strict(),\n]);\nexport type PairPollResponse = z.infer<typeof PairPollResponse>;\n\nexport const PairRequest = z.discriminatedUnion(\"action\", [\n PairStartRequest,\n PairPollRequest,\n]);\nexport type PairRequest = z.infer<typeof PairRequest>;\n\n// ---------------------------------------------------------------------------\n// 2. POST /byollm/claim\n// ---------------------------------------------------------------------------\n\nexport const ClaimRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n /** Re-sent on every claim so a server never matches against a stale matrix. */\n capabilities: CapabilityMatrix,\n /** Upper bound on jobs to return; the server may return fewer. */\n max: z.number().int().min(1).max(64),\n })\n .strict();\nexport type ClaimRequest = z.infer<typeof ClaimRequest>;\n\nexport const ClaimResponse = z\n .object({\n /**\n * Stubs, not jobs. The payload arrives from `fetch`, sealed to whichever\n * device claimed — see {@link JobStub} for the exhaustive metadata list.\n */\n jobs: z.array(ClaimedStub),\n /** Lease duration granted, so the daemon knows its renewal deadline. */\n leaseMs: z.number().int().positive(),\n })\n .strict();\nexport type ClaimResponse = z.infer<typeof ClaimResponse>;\n\n// ---------------------------------------------------------------------------\n// 3. POST /byollm/heartbeat\n// ---------------------------------------------------------------------------\n\nexport const HeartbeatRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n daemonVersion: z.string().min(1),\n capabilities: CapabilityMatrix,\n /**\n * Kinds this device is withholding, and why it can be said.\n *\n * Optional so a daemon that has nothing withheld sends nothing, and so an\n * older daemon against a newer hub is simply a device with no withheld\n * kinds rather than a parse failure.\n */\n withheld: z.array(WithheldKind).default([]),\n /**\n * Leases this daemon believes it holds; the server renews exactly these.\n *\n * Lease ids rather than job ids, so a replayed heartbeat cannot renew a\n * grant the runner no longer holds — see {@link Lease.id}.\n */\n activeLeases: z.array(GrantRef),\n /** True while the owner has the daemon paused; the server stops offering work. */\n paused: z.boolean(),\n })\n .strict();\nexport type HeartbeatRequest = z.infer<typeof HeartbeatRequest>;\n\nexport const HeartbeatResponse = z\n .object({\n /**\n * The sites this daemon may serve, right now — cloud_008 finding 59.\n *\n * Revocation used to be a boolean, and it was device-wide: the daemon\n * plane refused every call when the (owner, hub-site) consent was gone,\n * heartbeat answered `revoked: true` with `lost: all`, and the daemon\n * dropped its whole pairing by origin. Under a hub that is one site's\n * revocation ending a machine's relationship with every other site it\n * served — the amplification finding 48 warned about, arriving through\n * the one field nobody thought of as tenancy.\n *\n * So the answer is the set. A site that leaves it is revoked *for that\n * site*: the daemon drops that pin and keeps the rest. An empty set is\n * what \"revoked\" used to mean, and the daemon can see that for itself\n * rather than being told a second time — two fields for one fact is how\n * they drift.\n */\n sites: z.record(z.string().min(1), PublicIdentity),\n /**\n * How a site's current key traces back to one this daemon already holds —\n * byollm_009 Amendment C.\n *\n * Keyed by the same id as `sites`, and **additive on purpose**: `sites`\n * remains the one statement of which key is current, and this says only\n * how that key got there. Two fields for one fact is how they drift; this\n * is two facts, and the second is evidence about the first.\n *\n * Optional because a site that has never rotated has no chain, which is\n * every site today. A daemon that receives one for an id it already holds\n * ignores it: the pin it has is the pin it approved.\n *\n * §12 carries what this adds to the metadata surface — a site's rotation\n * history is public by construction, because a daemon that cannot read it\n * cannot verify it.\n */\n successions: z\n .record(\n z.string().min(1),\n z\n .object({\n /** Oldest last, as the projection carries it. */\n succeeds: z.array(Succession).max(MAX_SUCCESSION_CHAIN),\n /**\n * Until when the superseded key may still sign work — epoch ms.\n *\n * The daemon holds its own clock against this, for the reason it\n * holds its own allowlist: a projection that could extend the\n * window indefinitely would be a two-key site forever, decided by\n * the party this design does not trust.\n */\n retiringUntil: z.number().int().positive().optional(),\n })\n .strict(),\n )\n .optional(),\n /**\n * Per-job cancel (byollm_001 Rev 1 §C). The daemon aborts these jobs'\n * in-flight backend calls and reports them `canceled`.\n *\n * **The grant, not the id** — V1-3. Job ids are chosen per site, so two\n * sites may pick the same one, and a bare id told a daemon holding both\n * to abort whichever it happened to have filed under that name. The lease\n * is the unique grant and the daemon already keys its work by it; this is\n * the same shape `activeLeases` sends in the other direction.\n */\n cancel: z.array(\n z\n .object({ jobId: z.string().min(1), leaseId: z.string().min(1) })\n .strict(),\n ),\n // `leases` is deliberately absent — cloud_008 §1.4b, finding 16.\n //\n // It carried \"these leases were renewed, and here is the new expiry\", and\n // **no daemon ever read it.** A mutation returning an empty list while\n // renewing correctly survived every test, which is what made it visible.\n //\n // It is neither a class nor membership, so Amendment A's rule does not\n // decide it — the older test does: nothing reads it, so it is dead wire.\n // §6's exhaustiveness is a commitment about what an upstream can see, and\n // it applies to every message rather than only to the stub.\n //\n // `lost` is the actionable signal and always was: a daemon stops work on\n // a lease it no longer holds. \"Renewed\" was the same question answered a\n // second time, and a second answer can only agree or contradict.\n //\n // Renewal itself is untouched — the upstream still extends the grants a\n // heartbeat names, which is what §0.6 fixed. What ended is telling the\n // daemon about it in a field it ignored. If an upstream ever needs to\n // push lease decisions, that is a new field with a reader, added on\n // purpose.\n /**\n * Jobs the daemon thinks it holds but the server has reassigned or\n * expired. The daemon must stop work on these and not report results.\n *\n * Named by grant rather than by id, for V1-3's reason: a bare id is\n * ambiguous across sites, and \"the lease you no longer hold\" is exactly\n * what this field means anyway.\n */\n lost: z.array(\n z\n .object({ jobId: z.string().min(1), leaseId: z.string().min(1) })\n .strict(),\n ),\n /** Server clock, so a daemon with a skewed clock still honors leases. */\n serverTime: z.number().int().positive(),\n /**\n * Sites whose disclosure the user must read again before work moves —\n * cloud_008 finding 48, named rather than counted.\n *\n * A **subset of `sites`**, deliberately: a paused site keeps its pin, so\n * re-consenting never costs a re-pair. The daemon can say which site is\n * waiting and the user can go and read it, which is the difference\n * between a machine that is quietly idle and one that says why.\n *\n * Not `revoked`, which is a human ending a relationship, and not\n * `paused`, which on the request side already means \"this daemon's\n * operator stopped it\" — one word with two subjects on two halves of one\n * exchange is a confusion nobody untangles from a log.\n */\n awaitingConsent: z.array(z.string().min(1)),\n })\n .strict();\nexport type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;\n\n// ---------------------------------------------------------------------------\n// 4. POST /byollm/result\n// ---------------------------------------------------------------------------\n\n/**\n * What an intermediary learns about how a job ended — byollm_009 §6.\n *\n * The discriminator and nothing else. A relay has to know a job reached a\n * terminal state, and whether it failed, because that decides whether the job\n * leaves the queue or the app may re-enqueue. It does not have to know what\n * the model said, or what an error said, and this is where that line is drawn.\n *\n * Kept identical to `JobOutcome`'s discriminator rather than coarsened to\n * ok/not-ok: a cancelled job and a failed one are different routing outcomes,\n * and collapsing them would make the relay guess.\n */\nexport const ResultDisposition = z.enum([\"ok\", \"error\", \"canceled\"]);\nexport type ResultDisposition = z.infer<typeof ResultDisposition>;\n\nexport const ResultRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n jobId: z.string().min(1),\n /**\n * The grant this result was produced under — cloud_008 §1.4a.\n *\n * `fetch` has always named its lease, with the reasoning written beside\n * it: a request that names only the job would be answerable for whatever\n * lease exists when it arrives. **The operation that writes the result did\n * not**, on either plane, and checked only the runner id — which survives\n * a claim-release-reclaim cycle, so a device whose grant had been swept\n * and reissued could still land a result for a job it no longer held.\n *\n * Found by tracing a mutation that survived in §0.6: the lease lapsed, the\n * sweep requeued, the daemon re-claimed under a new grant, and the\n * original run finished and posted anyway. The relay marked the job done\n * with a result the site cannot open — it verifies the envelope against\n * the *current* holder's device, so the crypto contains the substitution —\n * and then refused the real holder's result as a replay. A lost job, in\n * silence.\n *\n * `LEASE_HONORED` is a statement about a lease *instance*. That was\n * learned once already, when a replayed release yanked a later grant, and\n * it applies here for the same reason.\n */\n leaseId: z.string().min(1),\n /**\n * The outcome, sealed to the site and signed by the device.\n *\n * The return leg of the payload envelope, and sealed for the same reason:\n * a model's answer is as sensitive as the prompt that produced it, and an\n * intermediary that cannot read one must not be handed the other.\n */\n envelope: SealedEnvelope,\n /**\n * The sealed outcome's discriminator, in the clear.\n *\n * Checked against the envelope once opened. It is a routing hint, not a\n * fact: believing it unverified would let a daemon mark a job `ok` while\n * sealing an error, and only the app would ever find out.\n */\n disposition: ResultDisposition,\n // `model`, `backendClass` and `durationMs` are **inside the envelope** —\n // cloud_008 §2.5. See {@link RunMetadata}.\n //\n // They were here, in the clear, and that was two problems wearing one\n // coat. On the direct plane the site recorded unauthenticated fields\n // beside an authenticated answer: a daemon could seal one result and\n // declare a different model, and only the unsigned half would reach the\n // app. Through a relay they reached a third party that acts on none of\n // them — `model` in particular being the sort of detail Amendment A's\n // rule keeps off the wire.\n //\n // `disposition` stays, and the difference is the test: a relay *routes*\n // on it, so it is a class a routing party consumes. Nobody between the\n // two ends consumes these.\n })\n .strict();\nexport type ResultRequest = z.infer<typeof ResultRequest>;\n\nexport const ResultResponse = z\n .object({\n /**\n * False when this submission wrote nothing — the daemon should discard,\n * not retry ({@link MUSTS.RESULT_IDEMPOTENT}).\n */\n accepted: z.boolean(),\n /**\n * True when this device had already recorded this job's result.\n *\n * The difference between \"already recorded\" and \"you no longer hold this\"\n * — cloud_008 §3.6. A daemon whose acknowledgment was lost is in the first\n * case and needs to hear it: its answer is safely on disk. Reporting a\n * stale lease instead invents a worry about a result that is already\n * stored, and sends its owner looking for a routing problem.\n *\n * Set only for the device that finished the job. A different device gets\n * the same refusal it would get for a job that is *not* terminal, so a job\n * id cannot be used as a terminality probe.\n */\n duplicate: z.boolean().optional(),\n /** The job's state after this submission. */\n state: z.string().min(1),\n })\n .strict();\nexport type ResultResponse = z.infer<typeof ResultResponse>;\n\n// ---------------------------------------------------------------------------\n// 5. POST /byollm/release\n// ---------------------------------------------------------------------------\n\nexport const ReleaseRequest = z\n .object({\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n /**\n * Which leases to release — the grant, not just the job.\n *\n * A release naming only a job id releases whatever lease exists at the\n * moment it arrives, which for a replayed request is not the lease the\n * daemon meant. See {@link Lease.id}.\n */\n leases: z.array(GrantRef),\n /**\n * Why, so the app's runner list can say something true.\n *\n * `refused` is load-bearing, not cosmetic: the server cannot evaluate\n * what a device will admit (§4.2), so it may legitimately offer\n * a job this daemon then declines. The server MUST record the refusal and\n * stop offering that job to that runner, or the pair would spin between\n * claim and release forever.\n */\n reason: z.enum([\"shutdown\", \"pause\", \"revoked\", \"backend-down\", \"refused\"]),\n })\n .strict();\nexport type ReleaseRequest = z.infer<typeof ReleaseRequest>;\n\nexport const ReleaseResponse = z\n .object({\n released: z.array(z.string().min(1)),\n })\n .strict();\nexport type ReleaseResponse = z.infer<typeof ReleaseResponse>;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * Wire error codes.\n *\n * byollm_002 requires that \"server unreachable\", \"revoked\", \"no matching\n * work\" and \"backend down\" never share a message. Distinct codes here are how\n * the daemon can tell three of those apart; the fourth is a transport failure\n * with no response at all.\n */\nexport const WireErrorCode = z.enum([\n \"bad-request\",\n \"unsupported-protocol-version\",\n // \"We do not know who you are.\" Exactly 401, and only that — cloud_008\n // §1.4d.\n \"unauthorized\",\n /**\n * \"We know exactly who you are, and the answer is no.\" Exactly 403.\n *\n * Five refusals across both planes served 403 with `unauthorized`, whose\n * table entry is 401: a revoked device, a site claiming another site's\n * stub, a job you do not hold, a device belonging to another owner, a\n * relay that does not route for you. Every one of them is an *identified*\n * caller being refused.\n *\n * Collapsing the two loses a distinction that matters everywhere it is\n * read: a revoked daemon would look like an unsigned one in every log and\n * every client branch, and \"check your keys\" is the wrong advice for both\n * of them in opposite directions.\n */\n \"forbidden\",\n \"revoked\",\n \"not-found\",\n // Claimed, but the site has not sealed the payload yet — cloud_008 §1.4.\n //\n // A daemon must retry rather than abandon: the job is legitimately still\n // its own until the lease or the awaiting-payload clock says otherwise.\n // That is why it cannot be `not-found` or `server-error`, and why it was\n // the protocol gap that produced a bare 409 in the first place.\n \"not-ready\",\n /**\n * The job is over, and this call is about a job — V1-6, and the code the\n * site plane has been serving without one (V1-13).\n *\n * Distinct from `not-found`, which says \"no such job\", and from\n * `not-ready`, which says \"not yet, keep asking\". This one says \"yes, and\n * it finished\" — so a daemon must stop rather than retry, and a replayed\n * request must not be able to reopen it.\n */\n \"too-late\",\n // The caller's clock is too far from ours to judge a signature's freshness.\n //\n // Split out from `unauthorized` because the remedy is completely different\n // and only the server can tell them apart: a bad signature means the key is\n // wrong, this means the machine's time is wrong. A daemon reporting it as a\n // generic rejection sends its owner looking at their network.\n \"clock-skew\",\n \"rate-limited\",\n \"server-error\",\n]);\nexport type WireErrorCode = z.infer<typeof WireErrorCode>;\n\nexport const WireError = z\n .object({\n error: WireErrorCode,\n message: z.string().min(1),\n /**\n * What this server speaks, on `unsupported-protocol-version` — §B.4.\n *\n * The refusal has carried these since the version handshake existed and\n * the enumeration did not model them, so the one error that exists to be\n * *acted on* was the one that failed to parse as a wire error. Found by\n * the relay's own suite the day the relay started sending it: a refusal\n * outside the enumerated shape is a refusal a client cannot branch on,\n * which is the whole reason §1.4 enumerates them.\n *\n * Modelled the way `clock-skew`'s two fields already are — code-specific\n * extras, refused on any other code by the refinement below.\n */\n supported: z.array(z.string().min(1)).optional(),\n minimum: z.string().min(1).optional(),\n /** Seconds; mirrors Retry-After for `rate-limited` and `server-error`. */\n retryAfter: z.number().int().nonnegative().optional(),\n /**\n * The server's clock, and the window it allows. `clock-skew` only.\n *\n * So the far side can say *how far off* rather than *that something is\n * wrong* — the difference between \"adjust your clock by four minutes\" and\n * \"something is wrong with your connection\". Not a disclosure: the\n * heartbeat response returns the same value, and so does every `Date`\n * header.\n */\n serverTime: z.number().int().positive().optional(),\n maxSkewMs: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((error, ctx) => {\n // Present exactly when they mean something. Optional fields that *may*\n // appear anywhere are a third state — the same shape `audience` is being\n // held to in §1.2 — and a daemon reading `serverTime` off an\n // `unauthorized` would be reading a number nobody promised.\n const skew = error.error === \"clock-skew\";\n const carried =\n error.serverTime !== undefined || error.maxSkewMs !== undefined;\n if (skew && !carried) {\n ctx.addIssue({\n code: \"custom\",\n message: \"clock-skew must carry serverTime and maxSkewMs\",\n });\n }\n if (!skew && carried) {\n ctx.addIssue({\n code: \"custom\",\n message: `${error.error} must not carry serverTime or maxSkewMs`,\n });\n }\n // The same rule for the version fields — §B.4. A code-specific extra on\n // the wrong code is how an enumeration stops meaning anything: every\n // reader has to guess whether the field applies.\n const version = error.error === \"unsupported-protocol-version\";\n const versionFields =\n error.supported !== undefined || error.minimum !== undefined;\n if (version && !versionFields) {\n ctx.addIssue({\n code: \"custom\",\n message:\n \"unsupported-protocol-version must carry supported and minimum\",\n });\n }\n if (!version && versionFields) {\n ctx.addIssue({\n code: \"custom\",\n message: `${error.error} must not carry supported or minimum`,\n });\n }\n });\nexport type WireError = z.infer<typeof WireError>;\n\n/** HTTP status each error code is served with. */\nexport const ERROR_STATUS: Readonly<Record<WireErrorCode, number>> =\n Object.freeze({\n \"bad-request\": 400,\n \"unsupported-protocol-version\": 400,\n unauthorized: 401,\n forbidden: 403,\n revoked: 403,\n \"not-found\": 404,\n // 409, not 404: the job exists and is yours, it is simply not ready.\n \"not-ready\": 409,\n // The same 409 as `not-ready` and the opposite instruction: that one says\n // keep asking, this one says stop. The status is the class of the\n // problem — a request that does not fit the resource's state — and the\n // code is what a caller acts on.\n \"too-late\": 409,\n // 401 alongside `unauthorized`, because that is what it is — the\n // signature could not be judged. The code is what carries the remedy.\n \"clock-skew\": 401,\n \"rate-limited\": 429,\n \"server-error\": 500,\n });\n\n// ---------------------------------------------------------------------------\n// 3. POST /byollm/fetch — collect the payload for a lease you hold\n// ---------------------------------------------------------------------------\n\nexport const FetchRequest = z\n .object({\n // `literal`, like every other request — V1-17. This one said\n // `string().min(1)`, so a daemon speaking a version this server does not\n // know got past the handshake on the one endpoint that hands over a\n // sealed payload. The version check exists so that a mismatch is a named\n // refusal rather than a schema failure three fields later; here it was\n // neither.\n protocolVersion: z.literal(PROTOCOL_VERSION),\n runnerId: z.string().min(1),\n jobId: z.string().min(1),\n /**\n * The grant this daemon holds.\n *\n * Named, not inferred: a fetch is lease-scoped, and a request that names\n * only the job would be answerable for whatever lease exists when it\n * arrives ({@link Lease.id}).\n */\n leaseId: z.string().min(1),\n })\n .strict();\nexport type FetchRequest = z.infer<typeof FetchRequest>;\n\nexport const FetchResponse = z\n .object({\n /**\n * The work, sealed to the device that claimed it — byollm_009 §6.\n *\n * Not plaintext. The site opens its own at-rest envelope and re-seals to\n * the claiming device's key, signed by the site's identity, so the work\n * is readable only by the machine that took it and only if it came from\n * the site that machine pinned.\n */\n envelope: SealedEnvelope,\n })\n .strict();\nexport type FetchResponse = z.infer<typeof FetchResponse>;\n"],"mappings":"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","names":["z","z","z","z","Buffer","z","z","z","Buffer","z","createPrivateKey","createPublicKey","z","z","createPrivateKey","createPublicKey","createHash","z","z","createHash","z","z","z","z","must","z","z"]}
|