@byollm/protocol 0.1.0-alpha.7 → 0.1.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  > [!WARNING]
2
- > **Alpha (`0.1.0-alpha.7`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.8`) — under active development. Don't use this yet.**
3
3
  >
4
4
  > Install it deliberately: `npm install @byollm/protocol@alpha`.
5
5
  >
package/dist/index.d.ts CHANGED
@@ -946,6 +946,54 @@ declare function signRequest(keys: StoredKeys, input: {
946
946
  issuedAt: number;
947
947
  body: string;
948
948
  }): RequestSignature;
949
+ /**
950
+ * The same scheme, for the party at the other end: a **site** calling a relay.
951
+ *
952
+ * A site talking to a relay is in exactly the daemon's position — an outbound
953
+ * caller with an identity keypair the other side already pins — so it gets the
954
+ * daemon's authentication rather than a second scheme. Bearer tokens for the
955
+ * site plane were the alternative, and they would have reintroduced the
956
+ * credential-in-a-file that §4.2 removed from the daemon plane, on the plane
957
+ * that carries *every* site's traffic.
958
+ *
959
+ * Two things make this safe to build on the same canonical string:
960
+ *
961
+ * 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never
962
+ * `enqueue`. The daemon plane's `result` and the site plane's `results` are
963
+ * one character apart, and a naming collision between planes must not be
964
+ * what stands between a signature and a replay onto the wrong handler. The
965
+ * prefix is applied *inside* these helpers, so the two ends cannot disagree
966
+ * about it — the alternative is two implementations of one bound value,
967
+ * which is this project's most-repeated bug.
968
+ * 2. **The caller slot carries the site id.** `canonicalRequest` names that
969
+ * field `runnerId` because the daemon plane got there first; here it holds
970
+ * the site id, and the verifier looks the key up in the projection's site
971
+ * registry rather than its device registry. The two registries never share
972
+ * an entry, so a device signature cannot authenticate as a site.
973
+ *
974
+ * §4.2's replay argument carries over **only because the site plane's writes
975
+ * are idempotent per addressed instance**, which is a property that had to be
976
+ * built rather than found: `enqueue` reset a job of the same id, so a replayed
977
+ * enqueue inside the freshness window returned a claimed job to the queue and
978
+ * threw away a device's live lease. Identical in shape to the `release` bug
979
+ * above, on the other plane. Anything added to the site plane later must be
980
+ * idempotent by the instance it names, or this scheme does not cover it.
981
+ */
982
+ declare function signSiteRequest(keys: StoredKeys, input: {
983
+ endpoint: string;
984
+ siteId: string;
985
+ issuedAt: number;
986
+ body: string;
987
+ }): RequestSignature;
988
+ /** Verify a site's call against the identity the control plane registered. */
989
+ declare function verifySiteRequest(input: {
990
+ identityPublic: string;
991
+ endpoint: string;
992
+ body: string;
993
+ signature: RequestSignature;
994
+ now: number;
995
+ maxSkewMs?: number;
996
+ }): SignatureFailure | null;
949
997
  /** Why a signed request was refused. Never returned to the caller verbatim. */
950
998
  type SignatureFailure = "stale" | "bad-signature";
951
999
  /**
@@ -1624,4 +1672,4 @@ declare const FetchResponse: z.ZodObject<{
1624
1672
  }, z.core.$strict>;
1625
1673
  type FetchResponse = z.infer<typeof FetchResponse>;
1626
1674
 
1627
- export { AUDIENCES, Audience, BACKENDS, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, FetchRequest, FetchResponse, GeneratePayload, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, REFUSAL_MESSAGES, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, SIZE_CLASS_LIMITS, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, type SignatureFailure, SizeClass, type SpendConsent, StoredKeys, TERMINAL_STATES, type VersionRefusal, WireError, WireErrorCode, backendDescriptor, canTransition, canonicalRequest, checkProtocolVersion, cryptoReady, effectiveOfferScope, fingerprint, generateKeys, isBackendId, isJobKind, isLocalHost, isTerminal, keyId, matchAudience, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signRequest, signWith, sizeClassCeiling, sizeClassOf, verifyPublicIdentity, verifyRequest, verifyWith };
1675
+ export { AUDIENCES, Audience, BACKENDS, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, FetchRequest, FetchResponse, GeneratePayload, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, REFUSAL_MESSAGES, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, SIZE_CLASS_LIMITS, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, type SignatureFailure, SizeClass, type SpendConsent, StoredKeys, TERMINAL_STATES, type VersionRefusal, WireError, WireErrorCode, backendDescriptor, canTransition, canonicalRequest, checkProtocolVersion, cryptoReady, effectiveOfferScope, fingerprint, generateKeys, isBackendId, isJobKind, isLocalHost, isTerminal, keyId, matchAudience, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signRequest, signSiteRequest, signWith, sizeClassCeiling, sizeClassOf, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith };
package/dist/index.js CHANGED
@@ -748,6 +748,21 @@ function signRequest(keys, input) {
748
748
  signature: signWith(keys, canonicalRequest(input))
749
749
  };
750
750
  }
751
+ function signSiteRequest(keys, input) {
752
+ return signRequest(keys, {
753
+ endpoint: siteEndpoint(input.endpoint),
754
+ runnerId: input.siteId,
755
+ issuedAt: input.issuedAt,
756
+ body: input.body
757
+ });
758
+ }
759
+ function verifySiteRequest(input) {
760
+ return verifyRequest({
761
+ ...input,
762
+ endpoint: siteEndpoint(input.endpoint)
763
+ });
764
+ }
765
+ var siteEndpoint = (endpoint) => `site/${endpoint}`;
751
766
  function verifyRequest(input) {
752
767
  const skew = input.maxSkewMs ?? MAX_CLOCK_SKEW_MS;
753
768
  if (Math.abs(input.now - input.signature.issuedAt) > skew) return "stale";
@@ -1414,11 +1429,13 @@ export {
1414
1429
  resolveCost,
1415
1430
  seal,
1416
1431
  signRequest,
1432
+ signSiteRequest,
1417
1433
  signWith,
1418
1434
  sizeClassCeiling,
1419
1435
  sizeClassOf,
1420
1436
  verifyPublicIdentity,
1421
1437
  verifyRequest,
1438
+ verifySiteRequest,
1422
1439
  verifyWith
1423
1440
  };
1424
1441
  //# sourceMappingURL=index.js.map
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/** 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":";AAAA,SAAS,KAAAA,UAAS;;;ACAlB,SAAS,SAAS;AAcX,IAAM,eAAe,EAAE,KAAK,CAAC,QAAQ,SAAS,CAAC;AAkB/C,IAAM,cAAc,EAAE,KAAK,CAAC,QAAQ,WAAW,cAAc,CAAC;AAkCrE,IAAM,UAAU,CAAC,MAA4C,OAAO,OAAO,CAAC;AAYrE,IAAM,WAAW,OAAO,OAAO;AAAA;AAAA,EAEpC,QAAQ,QAAQ;AAAA,IACd,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,KAAK,QAAQ;AAAA,IACX,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,UAAU,QAAQ;AAAA,IAChB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,MAAM,QAAQ;AAAA,IACZ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,UAAU,QAAQ;AAAA,IAChB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,KAAK,QAAQ;AAAA,IACX,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,SAAS,QAAQ;AAAA,IACf,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUD,WAAW,QAAQ;AAAA,IACjB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,QAAQ,QAAQ;AAAA,IACd,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,QAAQ,QAAQ;AAAA,IACd,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,MAAM,QAAQ;AAAA,IACZ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,MAAM,QAAQ;AAAA,IACZ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,YAAY,QAAQ;AAAA,IAClB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,UAAU,QAAQ;AAAA,IAChB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,UAAU,QAAQ;AAAA,IAChB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,SAAS,QAAQ;AAAA,IACf,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,EAClB,CAAC;AAAA;AAAA,EAGD,eAAe,QAAQ;AAAA,IACrB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA;AAAA;AAAA;AAAA,IAIP,MAAM;AAAA,IACN,mBAAmB;AAAA,EACrB,CAAC;AAAA;AAAA,EAGD,cAAc,QAAQ;AAAA,IACpB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,mBAAmB;AAAA,EACrB,CAAC;AACH,CAAsD;AAM/C,IAAM,cAAc,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAgB;AAEtE,IAAM,kBAAkB,EAAE;AAAA,EAC/B;AACF;AAGO,SAAS,YAAY,OAAmC;AAC7D,SAAO,OAAO,OAAO,UAAU,KAAK;AACtC;AAQO,SAAS,kBAAkB,IAAkC;AAClE,SAAO,SAAS,EAAE;AACpB;AAmBO,SAAS,YAAY,UAA2B;AACrD,QAAM,OAAO,SAAS,YAAY,EAAE,QAAQ,YAAY,EAAE;AAC1D,MAAI,SAAS,eAAe,KAAK,SAAS,YAAY,EAAG,QAAO;AAChE,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,KAAK,WAAW,MAAM,EAAG,QAAO;AACpC,MAAI,KAAK,WAAW,KAAK,EAAG,QAAO;AACnC,MAAI,KAAK,WAAW,UAAU,EAAG,QAAO;AACxC,MAAI,6BAA6B,KAAK,IAAI,EAAG,QAAO;AAEpD,MAAI,SAAS,KAAK,IAAI,EAAG,QAAO;AAChC,SAAO;AACT;AAWO,SAAS,YACd,IACA,SACa;AACb,QAAM,WAAW,SAAS,EAAE,EAAE;AAC9B,MAAI,aAAa,KAAM,QAAO;AAC9B,MAAI,YAAY,OAAW,QAAO;AAClC,MAAI;AACF,WAAO,YAAY,IAAI,IAAI,OAAO,EAAE,QAAQ,IAAI,SAAS;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADjTO,IAAM,WAAWC,GAAE,KAAK,CAAC,QAAQ,SAAS,QAAQ,CAAC;AAQnD,IAAM,aAAaA,GAAE,KAAK,CAAC,QAAQ,SAAS,QAAQ,CAAC;AAIrD,IAAM,YAAY,OAAO,OAAO,SAAS,OAAO;AAEhD,IAAM,eAAe,OAAO,OAAO,WAAW,OAAO;AAQrD,IAAM,eAAeA,GAAE,KAAK;AAAA;AAAA,EAEjC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF,CAAC;AAQD,IAAM,UAAuB,OAAO,OAAO,EAAE,IAAI,KAAc,CAAC;AAChE,IAAM,SAAS,CAAC,YACd,OAAO,OAAO,EAAE,IAAI,OAAgB,QAAQ,CAAC;AA0BxC,SAAS,oBACd,YACA,MACA,OACY;AACZ,MAAI,SAAS,eAAgB,QAAO;AACpC,MAAI,SAAS,aAAa,OAAO,iBAAiB,KAAM,QAAO;AAC/D,SAAO;AACT;AA8DO,SAAS,cAAc,KAAe,QAAkC;AAC7E,QAAM,YAAY,IAAI,UAAU,OAAO;AAGvC,MAAI,IAAI,aAAa,UAAU,CAAC,WAAW;AACzC,WAAO,OAAO,2BAA2B;AAAA,EAC3C;AACA,MACE,IAAI,aAAa,WACjB,CAAC,aACD,IAAI,kBAAkB,UACtB,CAAC,IAAI,cAAc,SAAS,OAAO,KAAK,GACxC;AACA,WAAO,OAAO,yBAAyB;AAAA,EACzC;AAKA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAEA,MAAI,WAAW;AAEb,WAAO;AAAA,EACT;AAIA,MAAI,OAAO,SAAS,gBAAgB;AAClC,WAAO,OAAO,wBAAwB;AAAA,EACxC;AACA,MAAI,OAAO,SAAS,WAAW;AAC7B,QAAI,OAAO,OAAO,iBAAiB,MAAM;AACvC,aAAO,OAAO,0BAA0B;AAAA,IAC1C;AACA,QAAI,OAAO,MAAM,mBAAmB,MAAM;AACxC,aAAO,OAAO,yBAAyB;AAAA,IACzC;AAAA,EACF;AAEA,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,OAAO,wBAAwB;AAAA,IACxC,KAAK;AAEH,aAAO,OAAO,cAAc,IAAI,KAAK,IACjC,UACA,OAAO,qBAAqB;AAAA,IAClC,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAOO,IAAM,mBACX,OAAO,OAAO;AAAA,EACZ,iBACE;AAAA,EACF,6BACE;AAAA,EACF,uBACE;AAAA,EACF,2BACE;AAAA,EACF,0BACE;AAAA,EACF,0BACE;AAAA,EACF,4BACE;AAAA,EACF,2BACE;AACJ,CAAC;;;AE5OH,SAAS,KAAAC,UAAS;AAUX,IAAM,iBAAiB,OAAO,OAAO;AAAA;AAAA,EAE1C,cAAc;AAAA;AAAA,EAEd,aAAa;AAAA;AAAA,EAEb,eAAe;AACjB,CAAC;AAMM,IAAM,cAAcA,GAAE,OAAO;AAAA,EAClC,MAAMA,GAAE,KAAK,CAAC,UAAU,QAAQ,WAAW,CAAC;AAAA,EAC5C,SAASA,GAAE,OAAO,EAAE,IAAI,eAAe,YAAY;AACrD,CAAC;AAcM,IAAM,kBAAkBA,GAC5B,OAAO;AAAA,EACN,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,eAAe,YAAY;AAAA,EACzD,QAAQA,GAAE,OAAO,EAAE,IAAI,eAAe,YAAY,EAAE,SAAS;AAC/D,CAAC,EACA,OAAO;AAIH,IAAM,cAAcA,GACxB,OAAO;AAAA,EACN,UAAUA,GAAE,MAAM,WAAW,EAAE,IAAI,CAAC,EAAE,IAAI,eAAe,WAAW;AAAA,EACpE,QAAQA,GAAE,OAAO,EAAE,IAAI,eAAe,YAAY,EAAE,SAAS;AAC/D,CAAC,EACA,OAAO;AAYH,IAAM,UAAUA,GAAE,KAAK,CAAC,gBAAgB,UAAU,CAAC;AAInD,IAAM,YAAY,OAAO,OAAO,QAAQ,OAAO;AAG/C,IAAM,gBAAgBA,GAAE,mBAAmB,QAAQ;AAAA,EACxDA,GAAE,OAAO,EAAE,MAAMA,GAAE,QAAQ,cAAc,GAAG,SAAS,gBAAgB,CAAC;AAAA,EACtEA,GAAE,OAAO,EAAE,MAAMA,GAAE,QAAQ,UAAU,GAAG,SAAS,YAAY,CAAC;AAChE,CAAC;AASM,SAAS,UAAU,OAAiC;AACzD,SAAQ,UAAgC,SAAS,KAAK;AACxD;AAMO,SAAS,kBAAkB,QAA+B;AAC/D,MAAI,OAAO,SAAS,gBAAgB;AAClC,WAAO,OAAO,QAAQ,OAAO,UAAU,OAAO,QAAQ,QAAQ,UAAU;AAAA,EAC1E;AACA,QAAM,WAAW,OAAO,QAAQ,SAAS;AAAA,IACvC,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,YAAY,OAAO,QAAQ,QAAQ,UAAU;AACtD;;;ACtGA,SAAS,KAAAC,UAAS;AAmBX,IAAM,WAAWC,GAAE,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAIM,IAAM,kBAAkB,OAAO,OAAO;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAwC;AAGjC,SAAS,WAAW,OAA0B;AACnD,SAAQ,gBAAwC,SAAS,KAAK;AAChE;AAMA,IAAM,cACJ,OAAO,OAAO;AAAA,EACZ,QAAQ,CAAC,WAAW,WAAW,UAAU;AAAA;AAAA;AAAA,EAGzC,SAAS,CAAC,WAAW,UAAU,YAAY,OAAO;AAAA,EAClD,SAAS,CAAC,MAAM,SAAS,YAAY,QAAQ;AAAA,EAC7C,IAAI,CAAC;AAAA,EACL,OAAO,CAAC;AAAA,EACR,UAAU,CAAC;AAAA,EACX,SAAS,CAAC;AACZ,CAAC;AAGI,SAAS,cAAc,MAAgB,IAAuB;AACnE,SAAO,YAAY,IAAI,EAAE,SAAS,EAAE;AACtC;AAGO,IAAM,QAAQA,GAAE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe5B,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEpB,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE1B,WAAWA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACvC,CAAC;AAIM,IAAM,aAAaA,GAAE,MAAM,CAAC,iBAAiB,WAAW,CAAC;AAWzD,IAAM,aAAaA,GACvB,OAAO;AAAA,EACN,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA;AAAA,EAEV,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEvB,eAAeA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AAAA,EACnD,OAAO;AACT,CAAC,EACA,OAAO;AAUH,IAAM,mBAAmBA,GAC7B,OAAO;AAAA;AAAA,EAEN,UAAU;AAAA;AAAA,EAEV,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE1B,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE7B,cAAc;AAAA;AAAA,EAEd,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKvB,WAAWA,GAAE,QAAQ;AACvB,CAAC,EACA,OAAO;AAOH,SAAS,cAAc,OAMT;AACnB,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,aAAa,MAAM;AAAA,IACnB,cAAc,MAAM;AAAA,IACpB,OAAO,MAAM;AAAA,IACb,WAAW,MAAM,aAAa;AAAA,EAChC;AACF;AAGO,IAAM,cAAcA,GACxB,OAAO;AAAA,EACN,SAASA,GAAE,QAAQ,IAAI;AAAA,EACvB,MAAMA,GAAE,OAAO;AAAA;AAAA,EAEf,aAAaA,GAAE,IAAI,EAAE,SAAS;AAChC,CAAC,EACA,OAAO;AAGH,IAAM,iBAAiBA,GAC3B,OAAO;AAAA,EACN,SAASA,GAAE,QAAQ,OAAO;AAAA,EAC1B,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEzB,WAAWA,GAAE,QAAQ;AACvB,CAAC,EACA,OAAO;AAGH,IAAM,oBAAoBA,GAC9B,OAAO;AAAA,EACN,SAASA,GAAE,QAAQ,UAAU;AAC/B,CAAC,EACA,OAAO;AAEH,IAAM,aAAaA,GAAE,mBAAmB,WAAW;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAIM,IAAM,kBAAkBA,GAC5B,OAAO;AAAA,EACN,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,OAAO;AAAA,EACP,SAAS,WAAW,SAAS;AAAA,EAC7B,YAAY,iBAAiB,SAAS;AACxC,CAAC,EACA,OAAO;AAgBH,IAAM,YAAYA,GAAE,KAAK,CAAC,SAAS,UAAU,SAAS,WAAW,CAAC;AAIlE,IAAM,oBAAoB,OAAO,OAAO;AAAA,EAC7C,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO,OAAO;AAChB,CAAC;AAcM,SAAS,iBAAiB,WAA8B;AAC7D,MAAI,cAAc,YAAa,QAAO,OAAO;AAC7C,SAAO,kBAAkB,SAAS;AACpC;AAGO,SAAS,YAAY,WAA8B;AACxD,MAAI,aAAa,kBAAkB,MAAO,QAAO;AACjD,MAAI,aAAa,kBAAkB,OAAQ,QAAO;AAClD,SAAO;AACT;AAeO,IAAM,UAAUA,GACpB,OAAO;AAAA,EACN,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAM;AAAA;AAAA,EAEN,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,UAAU;AAAA,EACV,eAAeA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AAAA,EACnD,WAAW;AAAA;AAAA,EAEX,WAAWA,GAAE,QAAQ;AAAA;AAAA,EAErB,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACxC,CAAC,EACA,OAAO;AAIH,IAAM,cAAc,QAAQ,OAAO,EAAE,OAAO,MAAM,CAAC,EAAE,OAAO;;;ACjSnE,SAAS,oBAAAC,mBAAkB,mBAAAC,wBAAuC;AAClE,OAAO,YAAY;AACnB,SAAS,KAAAC,UAAS;;;ACFlB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,KAAAC,UAAS;AA0BX,IAAM,iBAAiBA,GAC3B,OAAO;AAAA;AAAA,EAEN,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE1B,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5B,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AACjC,CAAC,EACA,OAAO;AAIH,IAAM,aAAaA,GACvB,OAAO;AAAA,EACN,SAASA,GAAE,QAAQ,CAAC;AAAA,EACpB,gBAAgBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAChC,iBAAiBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACjC,kBAAkBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAClC,mBAAmBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACnC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,WAAWA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACvC,CAAC,EACA,OAAO;AAKV,IAAM,yBAAyB;AAE/B,SAAS,UAAU,KAAwB;AACzC,QAAM,MAAM,IAAI,OAAO,EAAE,QAAQ,MAAM,CAAC;AACxC,QAAM,IAAI,IAAI;AACd,MAAI,OAAO,MAAM,SAAU,OAAM,IAAI,MAAM,iCAAiC;AAC5E,SAAO;AACT;AAEA,SAAS,aAAa,KAAa,KAAsC;AACvE,SAAO,gBAAgB,EAAE,KAAK,EAAE,KAAK,OAAO,KAAK,GAAG,IAAI,GAAG,QAAQ,MAAM,CAAC;AAC5E;AAEA,SAAS,cAAc,QAA2B;AAChD,SAAO,iBAAiB;AAAA,IACtB,KAAK,OAAO,KAAK,QAAQ,QAAQ;AAAA,IACjC,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AACH;AAEA,IAAM,gBAAgB,CAAC,QACrB,IAAI,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC,EAAE,SAAS,QAAQ;AAGzD,SAAS,aAAa,KAAyB;AACpD,QAAM,WAAW,oBAAoB,SAAS;AAC9C,QAAM,aAAa,oBAAoB,QAAQ;AAC/C,QAAM,mBAAmB,UAAU,WAAW,SAAS;AAEvD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,gBAAgB,UAAU,SAAS,SAAS;AAAA,IAC5C,iBAAiB,cAAc,SAAS,UAAU;AAAA,IAClD;AAAA,IACA,mBAAmB,cAAc,WAAW,UAAU;AAAA,IACtD,eAAe;AAAA,MACb;AAAA,MACA,OAAO,KAAK,GAAG,sBAAsB,IAAI,gBAAgB,EAAE;AAAA,MAC3D,SAAS;AAAA,IACX,EAAE,SAAS,WAAW;AAAA,IACtB,WAAW;AAAA,EACb;AACF;AAGO,SAAS,iBAAiB,MAAkC;AACjE,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,YAAY,KAAK;AAAA,IACjB,eAAe,KAAK;AAAA,EACtB;AACF;AAUO,SAAS,qBAAqB,UAAmC;AACtE,MAAI;AACF,WAAO;AAAA,MACL;AAAA,MACA,OAAO,KAAK,GAAG,sBAAsB,IAAI,SAAS,UAAU,EAAE;AAAA,MAC9D,aAAa,SAAS,UAAU,SAAS;AAAA,MACzC,OAAO,KAAK,SAAS,eAAe,WAAW;AAAA,IACjD;AAAA,EACF,QAAQ;AAGN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,SAAS,MAAkB,MAA0B;AACnE,SAAO,KAAK,MAAM,MAAM,cAAc,KAAK,eAAe,CAAC,EAAE;AAAA,IAC3D;AAAA,EACF;AACF;AAGO,SAAS,WACd,gBACA,MACA,WACS;AACT,MAAI;AACF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,aAAa,gBAAgB,SAAS;AAAA,MACtC,OAAO,KAAK,WAAW,WAAW;AAAA,IACpC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,IAAM,WAAW;AAcV,SAAS,YAAY,gBAAgC;AAC1D,QAAM,SAAS,WAAW,QAAQ,EAC/B,OAAO,OAAO,KAAK,gBAAgB,WAAW,CAAC,EAC/C,OAAO;AAEV,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO,SAAS,GAAG,EAAE,GAAG;AACzC,YAAS,SAAS,IAAK;AACvB,YAAQ;AACR,WAAO,QAAQ,GAAG;AAChB,aAAO,SAAS,OAAQ,UAAW,OAAO,IAAM,EAAE;AAClD,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,MAAM,SAAS,KAAK,CAAC;AACxC,SAAO,UAAU,OAAO,KAAK,GAAG,CAAC;AACnC;AAGO,IAAM,QAAQ,CAAC,mBACpB,YAAY,cAAc;;;ADtK5B,IAAI;AACJ,eAAsB,cAA6B;AACjD,cAAY,OAAO;AACnB,QAAM;AACR;AAaO,IAAM,sBAAsB,KAAK,KAAK;AAGtC,IAAM,oBAAoBC,GAAE,KAAK,CAAC,WAAW,QAAQ,CAAC;AAGtD,IAAM,iBAAiBA,GAC3B,OAAO;AAAA;AAAA,EAEN,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE5B,gBAAgBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEhC,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaX,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACxC,CAAC,EACA,OAAO;AAaV,SAAS,WAAW,SAA0B,WAA2B;AACvE,SAAO,OAAO;AAAA,IACZ,KAAK,UAAU;AAAA,MACb,GAAG;AAAA,MACH,OAAO,QAAQ;AAAA,MACf,aAAa,QAAQ;AAAA,MACrB,gBAAgB,QAAQ;AAAA,MACxB,YAAY,QAAQ;AAAA,MACpB,WAAW,QAAQ;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,IACD;AAAA,EACF;AACF;AAEA,IAAM,YAAY,CAAC,KAAgB,SAAgC;AACjE,QAAM,MAAM,IAAI,OAAO,EAAE,QAAQ,MAAM,CAAC;AACxC,QAAM,QAAQ,SAAS,MAAM,IAAI,IAAI,IAAI;AACzC,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,mBAAmB;AAClE,SAAO,IAAI,WAAW,OAAO,KAAK,OAAO,WAAW,CAAC;AACvD;AAGA,eAAsB,KAAK,OAKC;AAC1B,QAAM,YAAY;AAElB,QAAM,OAAO,WAAW,MAAM,SAAS,MAAM,SAAS;AACtD,QAAM,YAAY,SAAS,MAAM,YAAY,IAAI;AACjD,QAAM,QAAQ,KAAK,UAAU,EAAE,MAAM,KAAK,SAAS,WAAW,GAAG,UAAU,CAAC;AAE5E,QAAM,YAAY,IAAI;AAAA,IACpB,OAAO,KAAK,MAAM,2BAA2B,WAAW;AAAA,EAC1D;AACA,QAAM,aAAa,OAAO;AAAA,IACxB,IAAI,WAAW,OAAO,KAAK,OAAO,MAAM,CAAC;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,YAAY,OAAO,KAAK,UAAU,EAAE,SAAS,WAAW;AAAA,IACxD,gBAAgB,MAAM,QAAQ;AAAA,IAC9B,aAAa,MAAM,QAAQ;AAAA,IAC3B,WAAW,MAAM,QAAQ;AAAA,IACzB,YAAY,MAAM,QAAQ;AAAA,EAC5B;AACF;AAyBA,eAAsB,KAAK,OAMH;AACtB,QAAM,YAAY;AAClB,QAAM,EAAE,UAAU,SAAS,IAAI;AAG/B,MACE,SAAS,mBAAmB,SAAS,kBACrC,SAAS,gBAAgB,SAAS,eAClC,SAAS,cAAc,SAAS,WAChC;AACA,WAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AAAA,EAC3C;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,OAAOC,kBAAiB;AAAA,MAC5B,KAAK,OAAO,KAAK,MAAM,cAAc,mBAAmB,QAAQ;AAAA,MAChE,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AACD,UAAM,MAAMC,iBAAgB,IAAI;AAChC,UAAM,SAAS,OAAO;AAAA,MACpB,IAAI,WAAW,OAAO,KAAK,SAAS,YAAY,WAAW,CAAC;AAAA,MAC5D,UAAU,KAAK,GAAG;AAAA,MAClB,UAAU,MAAM,GAAG;AAAA,IACrB;AACA,YAAQ,OAAO,KAAK,MAAM,EAAE,SAAS,MAAM;AAAA,EAC7C,QAAQ;AAGN,WAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AAAA,EAC3C;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;AAAA,EAC3B,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAAA,EAC1C;AACA,MAAI,OAAO,OAAO,SAAS,YAAY,OAAO,OAAO,cAAc,UAAU;AAC3E,WAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAAA,EAC1C;AAEA,QAAM,OAAO,OAAO,KAAK,OAAO,MAAM,WAAW;AACjD,MAAI,CAAC,WAAW,MAAM,sBAAsB,MAAM,OAAO,SAAS,GAAG;AAGnE,WAAO,EAAE,IAAI,OAAO,QAAQ,gBAAgB;AAAA,EAC9C;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAAA,EAC1C;AAKA,MACE,OAAO,OAAO,MAAM,SAAS,SAC7B,OAAO,aAAa,MAAM,SAAS,eACnC,OAAO,gBAAgB,MAAM,SAAS,kBACtC,OAAO,YAAY,MAAM,SAAS,cAClC,OAAO,WAAW,MAAM,SAAS,WACjC;AACA,WAAO,EAAE,IAAI,OAAO,QAAQ,mBAAmB;AAAA,EACjD;AACA,MAAI,OAAO,OAAO,WAAW,MAAM,UAAU;AAC3C,WAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAAA,EAC1C;AAEA,SAAO,EAAE,IAAI,MAAM,WAAW,OAAO,WAAW,EAAE;AACpD;;;AEpQA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,KAAAC,UAAS;AAiDX,IAAM,oBAAoB;AAG1B,IAAM,mBAAmBC,GAC7B,OAAO;AAAA;AAAA,EAEN,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE1B,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA;AAAA,EAEpC,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAC7B,CAAC,EACA,OAAO;AAcH,SAAS,iBAAiB,OAKtB;AACT,QAAM,SAASC,YAAW,QAAQ,EAAE,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,KAAK;AAC3E,SAAO,OAAO;AAAA,IACZ;AAAA,MACE;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO,MAAM,QAAQ;AAAA,MACrB;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,IACX;AAAA,EACF;AACF;AAGO,SAAS,YACd,MACA,OACkB;AAClB,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,WAAW,SAAS,MAAM,iBAAiB,KAAK,CAAC;AAAA,EACnD;AACF;AAYO,SAAS,cAAc,OAOF;AAC1B,QAAM,OAAO,MAAM,aAAa;AAChC,MAAI,KAAK,IAAI,MAAM,MAAM,MAAM,UAAU,QAAQ,IAAI,KAAM,QAAO;AAElE,QAAM,KAAK;AAAA,IACT,MAAM;AAAA,IACN,iBAAiB;AAAA,MACf,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM,UAAU;AAAA,MAC1B,UAAU,MAAM,UAAU;AAAA,MAC1B,MAAM,MAAM;AAAA,IACd,CAAC;AAAA,IACD,MAAM,UAAU;AAAA,EAClB;AACA,SAAO,KAAK,OAAO;AACrB;;;AC/EA,IAAM,OAAO,CAAC,MAAkB,OAAO,OAAO,CAAC;AAUxC,IAAM,QAAQ,OAAO,OAAO;AAAA;AAAA,EAEjC,eAAe,KAAK;AAAA,IAClB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,kBAAkB,KAAK;AAAA,IACrB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,mBAAmB,KAAK;AAAA,IACtB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,4BAA4B,KAAK;AAAA,IAC/B,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,2BAA2B,KAAK;AAAA,IAC9B,IAAI;AAAA,IACJ,WACE;AAAA,IAIF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,4BAA4B,KAAK;AAAA,IAC/B,IAAI;AAAA,IACJ,WACE;AAAA,IAIF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,uBAAuB,KAAK;AAAA,IAC1B,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,0BAA0B,KAAK;AAAA,IAC7B,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,4BAA4B,KAAK;AAAA,IAC/B,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,iBAAiB,KAAK;AAAA,IACpB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,cAAc,KAAK;AAAA,IACjB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,2BAA2B,KAAK;AAAA,IAC9B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,wBAAwB,KAAK;AAAA,IAC3B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,cAAc,KAAK;AAAA,IACjB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,eAAe,KAAK;AAAA,IAClB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,mBAAmB,KAAK;AAAA,IACtB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,qBAAqB,KAAK;AAAA,IACxB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,wBAAwB,KAAK;AAAA,IAC3B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,uBAAuB,KAAK;AAAA,IAC1B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,0BAA0B,KAAK;AAAA,IAC7B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,uBAAuB,KAAK;AAAA,IAC1B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,sBAAsB,KAAK;AAAA,IACzB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EAED,uBAAuB,KAAK;AAAA,IAC1B,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EAED,uBAAuB,KAAK;AAAA,IAC1B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,oBAAoB,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,gBAAgB,KAAK;AAAA,IACnB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,mBAAmB,KAAK;AAAA,IACtB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,YAAY,KAAK;AAAA,IACf,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,kBAAkB,KAAK;AAAA,IACrB,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,mBAAmB,KAAK;AAAA,IACtB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,mBAAmB,KAAK;AAAA,IACtB,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,iCAAiC,KAAK;AAAA,IACpC,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA;AAAA,EAGD,wBAAwB,KAAK;AAAA,IAC3B,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,oBAAoB,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,oBAAoB,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,oBAAoB,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,cAAc,KAAK;AAAA,IACjB,IAAI;AAAA,IACJ,WACE;AAAA,IAGF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,mBAAmB,KAAK;AAAA,IACtB,IAAI;AAAA,IACJ,WACE;AAAA,IAEF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AACH,CAAyC;AAMlC,IAAM,WAAW,OAAO,OAAO,OAAO,KAAK,KAAK,CAAa;AAG7D,SAAS,gBAAgB,MAAkC;AAChE,SAAO,SAAS,OAAO,CAAC,OAAO,MAAM,EAAE,EAAE,eAAe,IAAI;AAC9D;;;ACzcA,SAAS,KAAAC,UAAS;AASX,IAAM,mBAAmB;AASzB,IAAM,8BAA8B,OAAO,OAAO;AAAA,EACvD;AACF,CAAC;AAUM,IAAM,uBACX,4BAA4B,CAAC,KAAK;AA0B7B,SAAS,qBAAqB,MAAsC;AAKzE,QAAM,WACJ,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,OAAO,MAAM,iBAAiB,IAChC,KAAsC,kBACvC;AAEN,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SACE;AAAA,MAEF,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,CAAC,4BAA4B,SAAS,QAAQ,GAAG;AACnD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SACE,+BAA+B,4BAA4B,KAAK,IAAI,CAAC,6BACzC,QAAQ,QACnC,WAAW,uBACR,iDACA;AAAA,MACN,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AACT;AAGO,IAAM,kBAAkB;AAUxB,IAAM,YAAY,OAAO,OAAO;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAU;AAaH,IAAM,aAAaC,GACvB,OAAO;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,cAAc;AAAA,EACd,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,YAAY;AACd,CAAC,EACA,OAAO;AAIH,IAAM,mBAAmBA,GAAE,MAAM,UAAU;AAc3C,IAAM,mBAAmBA,GAC7B,OAAO;AAAA,EACN,iBAAiBA,GAAE,QAAQ,gBAAgB;AAAA,EAC3C,QAAQA,GAAE,QAAQ,OAAO;AAAA,EACzB,QAAQA,GAAE,OAAO;AAAA,IACf,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,IAEzB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IAChC,UAAUA,GAAE,KAAK,CAAC,UAAU,SAAS,OAAO,CAAC;AAAA,EAC/C,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQD,QAAQ;AAAA,EACR,cAAc;AAChB,CAAC,EACA,OAAO;AAGH,IAAM,oBAAoBA,GAC9B,OAAO;AAAA;AAAA,EAEN,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA;AAAA,EAE7B,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA;AAAA,EAElC,iBAAiBA,GAAE,IAAI;AAAA;AAAA,EAEvB,WAAWA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA;AAAA,EAErC,gBAAgBA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,IAAI,GAAM;AACtD,CAAC,EACA,OAAO;AAGH,IAAM,kBAAkBA,GAC5B,OAAO;AAAA,EACN,iBAAiBA,GAAE,QAAQ,gBAAgB;AAAA,EAC3C,QAAQA,GAAE,QAAQ,MAAM;AAAA,EACxB,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE;AAC/B,CAAC,EACA,OAAO;AAGH,IAAM,mBAAmBA,GAAE,mBAAmB,UAAU;AAAA,EAC7DA,GAAE,OAAO,EAAE,QAAQA,GAAE,QAAQ,SAAS,EAAE,CAAC,EAAE,OAAO;AAAA,EAClDA,GAAE,OAAO,EAAE,QAAQA,GAAE,QAAQ,QAAQ,EAAE,CAAC,EAAE,OAAO;AAAA,EACjDA,GAAE,OAAO,EAAE,QAAQA,GAAE,QAAQ,SAAS,EAAE,CAAC,EAAE,OAAO;AAAA,EAClDA,GACG,OAAO;AAAA,IACN,QAAQA,GAAE,QAAQ,UAAU;AAAA;AAAA,IAE5B,aAAaA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,IAC9B,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,IAE1B,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,IAEvB,YAAYA,GAAE,OAAO,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOhC,MAAM;AAAA,EACR,CAAC,EACA,OAAO;AACZ,CAAC;AAGM,IAAM,cAAcA,GAAE,mBAAmB,UAAU;AAAA,EACxD;AAAA,EACA;AACF,CAAC;AAOM,IAAM,eAAeA,GACzB,OAAO;AAAA,EACN,iBAAiBA,GAAE,QAAQ,gBAAgB;AAAA,EAC3C,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAE1B,cAAc;AAAA;AAAA,EAEd,KAAKA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AACrC,CAAC,EACA,OAAO;AAGH,IAAM,gBAAgBA,GAC1B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKN,MAAMA,GAAE,MAAM,WAAW;AAAA;AAAA,EAEzB,SAASA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACrC,CAAC,EACA,OAAO;AAOH,IAAM,mBAAmBA,GAC7B,OAAO;AAAA,EACN,iBAAiBA,GAAE,QAAQ,gBAAgB;AAAA,EAC3C,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,cAAcA,GAAE;AAAA,IACdA,GAAE,OAAO,EAAE,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,GAAG,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAAA,EACnE;AAAA;AAAA,EAEA,QAAQA,GAAE,QAAQ;AACpB,CAAC,EACA,OAAO;AAGH,IAAM,oBAAoBA,GAC9B,OAAO;AAAA;AAAA,EAEN,SAASA,GAAE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnB,QAAQA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAAA;AAAA,EAEjC,QAAQA,GAAE;AAAA,IACRA,GACG,OAAO;AAAA,MACN,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MACvB,WAAWA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,IACvC,CAAC,EACA,OAAO;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,MAAMA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAAA;AAAA,EAE/B,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACxC,CAAC,EACA,OAAO;AAmBH,IAAM,oBAAoBA,GAAE,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC;AAG5D,IAAM,gBAAgBA,GAC1B,OAAO;AAAA,EACN,iBAAiBA,GAAE,QAAQ,gBAAgB;AAAA,EAC3C,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQV,aAAa;AAAA;AAAA,EAEb,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,cAAc;AAAA;AAAA,EAEd,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAC3C,CAAC,EACA,OAAO;AAGH,IAAM,iBAAiBA,GAC3B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMN,UAAUA,GAAE,QAAQ;AAAA;AAAA,EAEpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC,EACA,OAAO;AAOH,IAAM,iBAAiBA,GAC3B,OAAO;AAAA,EACN,iBAAiBA,GAAE,QAAQ,gBAAgB;AAAA,EAC3C,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1B,QAAQA,GAAE;AAAA,IACRA,GAAE,OAAO,EAAE,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,GAAG,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QAAQA,GAAE,KAAK,CAAC,YAAY,SAAS,WAAW,gBAAgB,SAAS,CAAC;AAC5E,CAAC,EACA,OAAO;AAGH,IAAM,kBAAkBA,GAC5B,OAAO;AAAA,EACN,UAAUA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC,EACA,OAAO;AAeH,IAAM,gBAAgBA,GAAE,KAAK;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,YAAYA,GACtB,OAAO;AAAA,EACN,OAAO;AAAA,EACP,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEzB,YAAYA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AACtD,CAAC,EACA,OAAO;AAIH,IAAM,eACX,OAAO,OAAO;AAAA,EACZ,eAAe;AAAA,EACf,gCAAgC;AAAA,EAChC,cAAc;AAAA,EACd,SAAS;AAAA,EACT,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAClB,CAAC;AAMI,IAAM,eAAeA,GACzB,OAAO;AAAA,EACN,iBAAiBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACjC,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvB,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAC3B,CAAC,EACA,OAAO;AAGH,IAAM,gBAAgBA,GAC1B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASN,UAAU;AACZ,CAAC,EACA,OAAO;","names":["z","z","z","z","z","createPrivateKey","createPublicKey","z","z","z","createPrivateKey","createPublicKey","createHash","z","z","createHash","z","z"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byollm/protocol",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "The BYOLLM wire contract — types, zod schemas, and the audience rules both the daemon and server enforce. ALPHA: under active development.",
5
5
  "license": "MIT",
6
6
  "type": "module",