@byollm/protocol 0.1.0-alpha.5 → 0.1.0-alpha.51

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/dist/index.d.ts CHANGED
@@ -104,11 +104,23 @@ declare const BACKENDS: Readonly<{
104
104
  readonly mistral: BackendDescriptor;
105
105
  readonly "openai-http": BackendDescriptor;
106
106
  readonly "claude-cli": BackendDescriptor;
107
+ /**
108
+ * OpenAI's Codex CLI, on a ChatGPT plan — byollm_016 stage 3.
109
+ *
110
+ * `subscription`, so `SUBSCRIPTION_SELF_LOCK` pins it to its owner's own
111
+ * work whatever the config says. That is load-bearing here in a way it is
112
+ * not for `claude-cli`: Codex is an *agent*, and its default feature set
113
+ * includes a shell tool, browser control and computer use. The daemon
114
+ * disables every one of them, verified against the shipped binary rather
115
+ * than assumed — see `codex-cli.ts` — but the self-lock is the floor under
116
+ * that verification rather than a duplicate of it.
117
+ */
118
+ readonly "codex-cli": BackendDescriptor;
107
119
  }>;
108
120
  /** The id of a registered backend. */
109
121
  type BackendId = keyof typeof BACKENDS;
110
122
  /** All registered backend ids — the adversarial coverage check iterates this. */
111
- declare const BACKEND_IDS: readonly ("ollama" | "mlx" | "llamacpp" | "vllm" | "lmstudio" | "jan" | "localai" | "anthropic" | "openai" | "gemini" | "grok" | "groq" | "openrouter" | "together" | "deepseek" | "mistral" | "openai-http" | "claude-cli")[];
123
+ declare const BACKEND_IDS: readonly ("ollama" | "mlx" | "llamacpp" | "vllm" | "lmstudio" | "jan" | "localai" | "anthropic" | "openai" | "gemini" | "grok" | "groq" | "openrouter" | "together" | "deepseek" | "mistral" | "openai-http" | "claude-cli" | "codex-cli")[];
112
124
  declare const BackendIdSchema: z.ZodEnum<{
113
125
  ollama: "ollama";
114
126
  mlx: "mlx";
@@ -128,6 +140,7 @@ declare const BackendIdSchema: z.ZodEnum<{
128
140
  mistral: "mistral";
129
141
  "openai-http": "openai-http";
130
142
  "claude-cli": "claude-cli";
143
+ "codex-cli": "codex-cli";
131
144
  }>;
132
145
  /** Narrow an arbitrary string to a registered backend id. */
133
146
  declare function isBackendId(value: string): value is BackendId;
@@ -157,45 +170,98 @@ declare function backendDescriptor(id: BackendId): BackendDescriptor;
157
170
  */
158
171
  declare function isLocalHost(hostname: string): boolean;
159
172
  /**
160
- * The cost class of a configured backend instance.
173
+ * Is this model name a hosted one billed by its vendor?
174
+ *
175
+ * Ollama serves cloud models through the same local endpoint as local ones,
176
+ * so the address says "free" about a model somebody is being charged for. The
177
+ * only thing that distinguishes them is the name, and the distinguishing part
178
+ * is the **tag** — everything after the last colon.
179
+ *
180
+ * End-anchored on the tag, which is what makes it decidable rather than a
181
+ * guess about substrings:
182
+ *
183
+ * - `glm-5.2:cloud` → cloud
184
+ * - `deepseek-v4-flash:0731-cloud` → cloud
185
+ * - `x:cloudless` → not cloud, the tag ends in "less"
186
+ * - `cloudmodel:7b` → not cloud, the tag is "7b"
187
+ * - `llama3.2` → not cloud, there is no tag at all
188
+ *
189
+ * An oddball like `:xcloud` classifies as cloud, and that is the **only
190
+ * permitted failure direction**: calling a free model metered narrows what an
191
+ * owner may share and costs nobody money, while the reverse hands somebody
192
+ * else's bill to a stranger.
193
+ */
194
+ declare function isCloudTaggedModel(model: string): boolean;
195
+ /**
196
+ * The cost class of a configured service.
161
197
  *
162
198
  * For every named provider this is whatever the registry says, full stop
163
199
  * ({@link MUSTS.COST_NOT_CONFIGURABLE}). For the generic `openai-http` entry
164
200
  * it is inferred from the base URL, and a base URL that cannot be parsed is
165
201
  * treated as `metered` — the expensive side, because guessing "free" wrong
166
202
  * costs the owner money.
203
+ *
204
+ * The model has the last word in one direction only. A local address with a
205
+ * cloud-tagged model is `metered`: Ollama proxies hosted models through
206
+ * `127.0.0.1`, so the endpoint is local and the bill is not. Read from the
207
+ * **configured value**, never from what the server lists — the owner's config
208
+ * is the thing they chose, and a server's catalogue is not theirs to be
209
+ * classified by.
167
210
  */
168
- declare function resolveCost(id: BackendId, baseUrl: string | undefined): BackendCost;
211
+ declare function resolveCost(id: BackendId, baseUrl: string | undefined, model?: string): BackendCost;
169
212
 
170
213
  /**
171
214
  * Who may run a job, declared by the app that enqueued it.
172
215
  *
173
- * - `self` — only the job owner's own daemon.
174
- * - `named` — a daemon whose owner has explicitly allowed this (server, user)
175
- * pair in their *local* allowlist (byollm_001 Rev 1 §B).
176
- * - `public` — any daemon offering `public` compute.
216
+ * - `private` — only the job owner's own devices.
217
+ * - `team` — a device whose owner has this person on their roster.
218
+ * - `public` any device offering `public` compute.
219
+ *
220
+ * **One vocabulary, ruled 2026-08-24.** These were `self | named | public`
221
+ * while {@link OfferScope} became `private | team | public`, which would have
222
+ * left every seam where the two meet speaking two languages for one idea, and
223
+ * every doc explaining "self versus private" for ever. They are still
224
+ * independent axes — a job says who may run it, a service says who it will run
225
+ * for — and a job runs only where both agree
226
+ * ({@link MUSTS.AUDIENCE_BOTH_SIDES}). Sharing the words costs nothing and
227
+ * saves a mapping layer nobody would have enjoyed maintaining.
177
228
  */
178
229
  declare const Audience: z.ZodEnum<{
179
- self: "self";
180
- named: "named";
230
+ private: "private";
231
+ team: "team";
181
232
  public: "public";
182
233
  }>;
183
234
  type Audience = z.infer<typeof Audience>;
184
235
  /**
185
- * What a daemon backend is willing to run, declared by the machine's owner.
186
- * Same three values as {@link Audience}, but the two are independent axes —
187
- * a job runs only where both agree ({@link MUSTS.AUDIENCE_BOTH_SIDES}).
236
+ * What a device's owner is willing to run for other people, per service.
237
+ *
238
+ * - `private` the owner's own work only.
239
+ * - `team` — the owner's roster. Membership is **central**, not per-person:
240
+ * the device follows the roster rather than holding its own copy of who is
241
+ * in it (byollm_016, 2026-08-24).
242
+ * - `public` — any job, from anyone. This is the community posture the daemon
243
+ * has always had (`byollm offer <service> public`), bounded by community
244
+ * budgets. byollm_016 parks it as a **hosted-product** surface — the cloud
245
+ * dashboard does not offer it — which is not the same as removing it from
246
+ * the daemon, and the CLI still accepts it.
247
+ *
248
+ * Deliberately *not* the same words as {@link Audience}, which they used to
249
+ * share. They are independent axes — a job says who may run it, a service says
250
+ * who it will run for — and identical spellings made that read as one concept
251
+ * with two homes. A job runs only where both agree
252
+ * ({@link MUSTS.AUDIENCE_BOTH_SIDES}); {@link matches} is the one place the
253
+ * correspondence lives.
188
254
  */
189
255
  declare const OfferScope: z.ZodEnum<{
190
- self: "self";
191
- named: "named";
256
+ private: "private";
257
+ team: "team";
192
258
  public: "public";
193
259
  }>;
194
260
  type OfferScope = z.infer<typeof OfferScope>;
195
261
  /** All audience values, in widening order. */
196
- declare const AUDIENCES: readonly ("self" | "named" | "public")[];
262
+ declare const AUDIENCES: readonly ("private" | "team" | "public")[];
197
263
  /** All offer scopes, in widening order. */
198
- declare const OFFER_SCOPES: readonly ("self" | "named" | "public")[];
264
+ declare const OFFER_SCOPES: readonly ("private" | "team" | "public")[];
199
265
  /**
200
266
  * Why a job was refused. Distinct codes because byollm_002 requires that
201
267
  * different truths never share a message — "no matching work" and "refused on
@@ -236,7 +302,7 @@ interface SpendConsent {
236
302
  *
237
303
  * - `subscription` is locked to `self` regardless of config
238
304
  * ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}) — someone else's terms.
239
- * - `metered` narrows to `self` unless the owner has explicitly acknowledged
305
+ * - `metered` narrows to `private` unless the owner has explicitly acknowledged
240
306
  * the spend ({@link MUSTS.METERED_DEFAULTS_SELF}) — their money.
241
307
  * - `free` passes through — their electricity.
242
308
  *
@@ -291,10 +357,10 @@ interface MatchDaemon {
291
357
  * @example
292
358
  * ```ts
293
359
  * const result = matchAudience(
294
- * { owner: "alice", audience: "named" },
360
+ * { owner: "alice", audience: "team" },
295
361
  * {
296
362
  * owner: "bob",
297
- * offerScope: "named",
363
+ * offerScope: "team",
298
364
  * cost: "free",
299
365
  * locallyAllows: (o) => o === "alice",
300
366
  * },
@@ -314,6 +380,13 @@ declare const REFUSAL_MESSAGES: Readonly<Record<MatchRefusal, string>>;
314
380
  * Upper bounds on payload size, enforced at the schema so oversized input is
315
381
  * refused at parse time rather than somewhere deeper.
316
382
  *
383
+ * All three are enforced — cloud_008 Tier 4, finding 30. `maxTotalChars` was
384
+ * declared here and referenced nowhere, under this docstring's claim that the
385
+ * schema enforces them, so a chat payload of 256 messages at a million
386
+ * characters each parsed cleanly at sixty-four times the stated ceiling. The
387
+ * per-field limits were real and the aggregate one was a number in a frozen
388
+ * object.
389
+ *
317
390
  * byollm_004 §4 requires stricter limits for community (`named`/`public`)
318
391
  * jobs; those are applied on top of these by the daemon's budget check, which
319
392
  * knows the job's audience. These are the absolute ceilings for any job.
@@ -498,12 +571,13 @@ declare const ClaimedJob: z.ZodObject<{
498
571
  system: z.ZodOptional<z.ZodString>;
499
572
  }, z.core.$strict>]>;
500
573
  audience: z.ZodEnum<{
501
- self: "self";
502
- named: "named";
574
+ private: "private";
575
+ team: "team";
503
576
  public: "public";
504
577
  }>;
505
578
  owner: z.ZodString;
506
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
579
+ site: z.ZodOptional<z.ZodString>;
580
+ service: z.ZodOptional<z.ZodString>;
507
581
  lease: z.ZodObject<{
508
582
  id: z.ZodString;
509
583
  runnerId: z.ZodString;
@@ -516,12 +590,12 @@ type ClaimedJob = z.infer<typeof ClaimedJob>;
516
590
  *
517
591
  * byollm_003 Rev 1: a `named`/`public` result is attacker-controlled text.
518
592
  * The app must never render volunteer output as its own AI's answer without
519
- * knowing that is what it is ({@link MUSTS.RESULT_PROVENANCE}).
593
+ * knowing that is what it is ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).
520
594
  */
521
595
  declare const ResultProvenance: z.ZodObject<{
522
596
  audience: z.ZodEnum<{
523
- self: "self";
524
- named: "named";
597
+ private: "private";
598
+ team: "team";
525
599
  public: "public";
526
600
  }>;
527
601
  runnerId: z.ZodString;
@@ -545,6 +619,30 @@ declare function provenanceFor(input: {
545
619
  backendClass: BackendClass;
546
620
  model: string;
547
621
  }): ResultProvenance;
622
+ /**
623
+ * What the daemon did, sealed with the answer — cloud_008 §2.5.
624
+ *
625
+ * These travelled in the clear on `ResultRequest`, which meant two things at
626
+ * once. On the direct plane the site believed unauthenticated fields beside
627
+ * an authenticated envelope — a daemon could seal one answer and *declare* it
628
+ * came from a different model, and only the field it did not sign would be
629
+ * recorded. Through a relay they reached a third party that acts on none of
630
+ * them, and `model` in particular is the kind of detail Amendment A's rule
631
+ * keeps off the wire.
632
+ *
633
+ * Sealed, they are the daemon's signed statement about its own run: the site
634
+ * opens them, nothing in between sees them, and the disposition check that
635
+ * already compares clear-text against ciphertext extends to cover them.
636
+ */
637
+ declare const RunMetadata: z.ZodObject<{
638
+ model: z.ZodString;
639
+ backendClass: z.ZodEnum<{
640
+ http: "http";
641
+ process: "process";
642
+ }>;
643
+ durationMs: z.ZodNumber;
644
+ }, z.core.$strict>;
645
+ type RunMetadata = z.infer<typeof RunMetadata>;
548
646
  /** Successful outcome. */
549
647
  declare const JobResultOk: z.ZodObject<{
550
648
  outcome: z.ZodLiteral<"ok">;
@@ -575,6 +673,89 @@ declare const JobOutcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
575
673
  outcome: z.ZodLiteral<"canceled">;
576
674
  }, z.core.$strict>], "outcome">;
577
675
  type JobOutcome = z.infer<typeof JobOutcome>;
676
+ /**
677
+ * Why a job can never run — byollm_016 Phase B.
678
+ *
679
+ * Every one of these is **terminal**, and that is the whole point of naming
680
+ * them. A job that cannot be matched used to sit queued until its deadline,
681
+ * which reads exactly like a job that is merely waiting for a device to come
682
+ * online — so an app could not tell "any moment now" from "never", and neither
683
+ * could the person watching a spinner. Silence must never read as pending.
684
+ *
685
+ * They are decided by whoever knows first: the site's own SDK where it can see
686
+ * the answer without asking, the router where matching happens, and the daemon
687
+ * again on arrival under the both-sides rule. All three reason from the same
688
+ * list rather than three private vocabularies.
689
+ */
690
+ declare const RefusalReason: z.ZodEnum<{
691
+ "select-unavailable": "select-unavailable";
692
+ "default-ambiguity": "default-ambiguity";
693
+ "default-unusable": "default-unusable";
694
+ }>;
695
+ type RefusalReason = z.infer<typeof RefusalReason>;
696
+ /**
697
+ * A terminal outcome nobody sealed — byollm_016 Phase B.
698
+ *
699
+ * Every other finished job carries an envelope encrypted by the device that
700
+ * ran it, which is what makes a result unforgeable. These have no device: the
701
+ * job was refused *before* anything could run it, so there is nobody to seal
702
+ * from and no content to seal.
703
+ *
704
+ * **What that costs, stated plainly.** This is the one terminal outcome a
705
+ * router can author. It is worth being exact about the power that grants,
706
+ * because "the relay can write this" sounds alarming until you compare it with
707
+ * what a relay could already do: drop the job, never offer it, and let it
708
+ * expire. A router-authored refusal is *denial of service by a shorter route*,
709
+ * which is a power the router has always had and which the trust model has
710
+ * always said it has. What it emphatically is **not** is forgery: this shape
711
+ * carries no envelope and no output, so it can never be mistaken for an answer
712
+ * a device produced. A relay still cannot fabricate a result, because that
713
+ * needs a signature it does not hold.
714
+ *
715
+ * So the rule this shape enforces by construction: a refusal may deny, and may
716
+ * never assert. Anything that claims work was *done* still comes sealed.
717
+ */
718
+ declare const JobRefused: z.ZodObject<{
719
+ outcome: z.ZodLiteral<"refused">;
720
+ reason: z.ZodEnum<{
721
+ "select-unavailable": "select-unavailable";
722
+ "default-ambiguity": "default-ambiguity";
723
+ "default-unusable": "default-unusable";
724
+ }>;
725
+ message: z.ZodString;
726
+ }, z.core.$strict>;
727
+ type JobRefused = z.infer<typeof JobRefused>;
728
+ /**
729
+ * The plaintext inside a result envelope.
730
+ *
731
+ * The outcome and how it was produced, together, because they are one
732
+ * statement by one signer. A site that opened only the outcome would be
733
+ * trusting the envelope for the answer and the request body for everything
734
+ * about it.
735
+ */
736
+ declare const SealedOutcome: z.ZodObject<{
737
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
738
+ outcome: z.ZodLiteral<"ok">;
739
+ text: z.ZodString;
740
+ artifactUrl: z.ZodOptional<z.ZodURL>;
741
+ }, z.core.$strict>, z.ZodObject<{
742
+ outcome: z.ZodLiteral<"error">;
743
+ code: z.ZodString;
744
+ message: z.ZodString;
745
+ retryable: z.ZodBoolean;
746
+ }, z.core.$strict>, z.ZodObject<{
747
+ outcome: z.ZodLiteral<"canceled">;
748
+ }, z.core.$strict>], "outcome">;
749
+ ran: z.ZodObject<{
750
+ model: z.ZodString;
751
+ backendClass: z.ZodEnum<{
752
+ http: "http";
753
+ process: "process";
754
+ }>;
755
+ durationMs: z.ZodNumber;
756
+ }, z.core.$strict>;
757
+ }, z.core.$strict>;
758
+ type SealedOutcome = z.infer<typeof SealedOutcome>;
578
759
  /** A completed job as delivered to the app, provenance attached. */
579
760
  declare const DeliveredResult: z.ZodObject<{
580
761
  jobId: z.ZodString;
@@ -601,8 +782,8 @@ declare const DeliveredResult: z.ZodObject<{
601
782
  }, z.core.$strict>], "outcome">>;
602
783
  provenance: z.ZodOptional<z.ZodObject<{
603
784
  audience: z.ZodEnum<{
604
- self: "self";
605
- named: "named";
785
+ private: "private";
786
+ team: "team";
606
787
  public: "public";
607
788
  }>;
608
789
  runnerId: z.ZodString;
@@ -614,6 +795,7 @@ declare const DeliveredResult: z.ZodObject<{
614
795
  model: z.ZodString;
615
796
  untrusted: z.ZodBoolean;
616
797
  }, z.core.$strict>>;
798
+ fallback: z.ZodOptional<z.ZodLiteral<true>>;
617
799
  }, z.core.$strict>;
618
800
  type DeliveredResult = z.infer<typeof DeliveredResult>;
619
801
  /**
@@ -677,12 +859,13 @@ declare const JobStub: z.ZodObject<{
677
859
  "llm.chat": "llm.chat";
678
860
  }>;
679
861
  owner: z.ZodString;
862
+ site: z.ZodString;
680
863
  audience: z.ZodEnum<{
681
- self: "self";
682
- named: "named";
864
+ private: "private";
865
+ team: "team";
683
866
  public: "public";
684
867
  }>;
685
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
868
+ service: z.ZodOptional<z.ZodString>;
686
869
  sizeClass: z.ZodEnum<{
687
870
  small: "small";
688
871
  medium: "medium";
@@ -701,12 +884,13 @@ declare const ClaimedStub: z.ZodObject<{
701
884
  "llm.chat": "llm.chat";
702
885
  }>;
703
886
  owner: z.ZodString;
887
+ site: z.ZodString;
704
888
  audience: z.ZodEnum<{
705
- self: "self";
706
- named: "named";
889
+ private: "private";
890
+ team: "team";
707
891
  public: "public";
708
892
  }>;
709
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
893
+ service: z.ZodOptional<z.ZodString>;
710
894
  sizeClass: z.ZodEnum<{
711
895
  small: "small";
712
896
  medium: "medium";
@@ -763,6 +947,18 @@ declare const StoredKeys: z.ZodObject<{
763
947
  createdAt: z.ZodNumber;
764
948
  }, z.core.$strict>;
765
949
  type StoredKeys = z.infer<typeof StoredKeys>;
950
+ /** Domain separator, so a signature over an encryption key cannot be
951
+ * replayed as a signature over anything else. */
952
+ /**
953
+ * What an encryption key's signature covers.
954
+ *
955
+ * Exported because a rotation is a real event this protocol has to be able to
956
+ * *test* — a record whose encryption key moved under an identity that signed
957
+ * the move is the one case pinning must refuse loudly, and building one
958
+ * outside this file otherwise means re-typing this string, which is how two
959
+ * copies of a constant start disagreeing.
960
+ */
961
+ declare const ENCRYPTION_KEY_CONTEXT = "byollm/v1/encryption-key";
766
962
  /** Generate a fresh pair of keypairs and bind them together. */
767
963
  declare function generateKeys(now: number): StoredKeys;
768
964
  /** The public half, for the wire. */
@@ -946,7 +1142,77 @@ declare function signRequest(keys: StoredKeys, input: {
946
1142
  issuedAt: number;
947
1143
  body: string;
948
1144
  }): RequestSignature;
949
- /** Why a signed request was refused. Never returned to the caller verbatim. */
1145
+ /**
1146
+ * The same scheme, for the party at the other end: a **site** calling a relay.
1147
+ *
1148
+ * A site talking to a relay is in exactly the daemon's position — an outbound
1149
+ * caller with an identity keypair the other side already pins — so it gets the
1150
+ * daemon's authentication rather than a second scheme. Bearer tokens for the
1151
+ * site plane were the alternative, and they would have reintroduced the
1152
+ * credential-in-a-file that §4.2 removed from the daemon plane, on the plane
1153
+ * that carries *every* site's traffic.
1154
+ *
1155
+ * Two things make this safe to build on the same canonical string:
1156
+ *
1157
+ * 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never
1158
+ * `enqueue`. The daemon plane's `result` and the site plane's `results` are
1159
+ * one character apart, and a naming collision between planes must not be
1160
+ * what stands between a signature and a replay onto the wrong handler. The
1161
+ * prefix is applied *inside* these helpers, so the two ends cannot disagree
1162
+ * about it — the alternative is two implementations of one bound value,
1163
+ * which is this project's most-repeated bug.
1164
+ * 2. **The caller slot carries the site id.** `canonicalRequest` names that
1165
+ * field `runnerId` because the daemon plane got there first; here it holds
1166
+ * the site id, and the verifier looks the key up in the projection's site
1167
+ * registry rather than its device registry. The two registries never share
1168
+ * an entry, so a device signature cannot authenticate as a site.
1169
+ *
1170
+ * §4.2's replay argument carries over **only because the site plane's writes
1171
+ * are idempotent per addressed instance**, which is a property that had to be
1172
+ * built rather than found: `enqueue` reset a job of the same id, so a replayed
1173
+ * enqueue inside the freshness window returned a claimed job to the queue and
1174
+ * threw away a device's live lease. Identical in shape to the `release` bug
1175
+ * above, on the other plane. Anything added to the site plane later must be
1176
+ * idempotent by the instance it names, or this scheme does not cover it.
1177
+ */
1178
+ declare function signSiteRequest(keys: StoredKeys, input: {
1179
+ endpoint: string;
1180
+ siteId: string;
1181
+ issuedAt: number;
1182
+ body: string;
1183
+ }): RequestSignature;
1184
+ /** Verify a site's call against the identity the control plane registered. */
1185
+ declare function verifySiteRequest(input: {
1186
+ identityPublic: string;
1187
+ endpoint: string;
1188
+ body: string;
1189
+ signature: RequestSignature;
1190
+ now: number;
1191
+ maxSkewMs?: number;
1192
+ }): SignatureFailure | null;
1193
+ /**
1194
+ * Why a signed request was refused.
1195
+ *
1196
+ * **`bad-signature` is never returned verbatim; `stale` is, deliberately.**
1197
+ * They are different kinds of refusal and conflating them costs a real user
1198
+ * more than it costs an attacker.
1199
+ *
1200
+ * A bad signature is an authentication failure and the server says only
1201
+ * "unauthorized" — telling a prober which part they got wrong is free help.
1202
+ *
1203
+ * A stale timestamp is a **precondition** failure: the signature may be
1204
+ * perfectly valid and the caller's clock is simply wrong. Saying so reveals
1205
+ * nothing, for two reasons that both have to hold. The server's time is
1206
+ * already public — every response carries a `Date` header and the heartbeat
1207
+ * response returns `serverTime` outright. And freshness is checked *before*
1208
+ * the signature is verified, so a stale answer says nothing about whether the
1209
+ * signature was any good.
1210
+ *
1211
+ * What conflating them costs: a machine whose clock has drifted gets
1212
+ * `401 unauthorized` on every request, forever, with nothing anywhere pointing
1213
+ * at the clock. That is the shape byollm_013 was filed about — a refusal that
1214
+ * is correct, silent, and sends somebody to read our source.
1215
+ */
950
1216
  type SignatureFailure = "stale" | "bad-signature";
951
1217
  /**
952
1218
  * Verify a signed request against a runner's pinned identity key.
@@ -964,6 +1230,110 @@ declare function verifyRequest(input: {
964
1230
  maxSkewMs?: number;
965
1231
  }): SignatureFailure | null;
966
1232
 
1233
+ /**
1234
+ * Rotation — byollm_009 Amendment C.
1235
+ *
1236
+ * A site holding identity key **K1** wants to be known by **K2**. It publishes
1237
+ * a *succession*: K2, plus a signature by K1 over a statement naming both key
1238
+ * ids. That signature is the entire mechanism, and the reason rotation can be
1239
+ * automatic without becoming a hole is that **the relay cannot mint one** — it
1240
+ * never holds K1. It is the same trust step a daemon already performs at
1241
+ * pairing, applied to the site's own succession.
1242
+ *
1243
+ * ## Why the statement names both keys
1244
+ *
1245
+ * A signature over K2 alone could be lifted from this site's record and
1246
+ * replayed into another site's, moving *that* site to K2 — a key the attacker
1247
+ * holds. Naming the predecessor binds the succession to one chain, and it is
1248
+ * the reason `verifyLink` takes the id it expects to be succeeding from
1249
+ * rather than reading it out of the statement it is checking.
1250
+ */
1251
+ /** The domain separator. Distinct from every other thing an identity signs. */
1252
+ declare const SUCCESSION_CONTEXT = "byollm/v1/site-succession";
1253
+ /**
1254
+ * How long a retired key may still sign work — Amendment C, ruling 2.
1255
+ *
1256
+ * A protocol constant and not the site's to choose. Per-site overlap
1257
+ * arithmetic is exactly the kind of number that has to mean one thing
1258
+ * everywhere, and a site that could choose it could choose *forever*, which is
1259
+ * a two-key site permanently and a second key nobody ever notices retiring.
1260
+ *
1261
+ * Seven days: long enough that a daemon which polls daily and a laptop shut
1262
+ * for a long weekend both see the new record before the old key stops working,
1263
+ * short enough that "which key is live" is never an interesting question.
1264
+ */
1265
+ declare const RETIREMENT_WINDOW_MS: number;
1266
+ /**
1267
+ * The longest chain a daemon will walk — Amendment C, ruling 1.
1268
+ *
1269
+ * **A denial-of-service guard, not policy.** The bound exists so a projection
1270
+ * cannot make a daemon verify ten thousand signatures, not to express an
1271
+ * opinion about how often a site may rotate. A site that legitimately exceeds
1272
+ * it has a re-pair ahead of it, which is why it is generous: at one rotation a
1273
+ * quarter this is sixteen years.
1274
+ */
1275
+ declare const MAX_SUCCESSION_CHAIN = 64;
1276
+ /** One step of a chain: a key, and the signature by it over its successor. */
1277
+ declare const Succession: z.ZodObject<{
1278
+ identity: z.ZodObject<{
1279
+ identity: z.ZodString;
1280
+ encryption: z.ZodString;
1281
+ encryptionSig: z.ZodString;
1282
+ }, z.core.$strict>;
1283
+ signature: z.ZodString;
1284
+ }, z.core.$strict>;
1285
+ type Succession = z.infer<typeof Succession>;
1286
+ /** The exact bytes signed. One definition; both sides call it. */
1287
+ declare function successionStatement(fromKeyId: string, toKeyId: string): Uint8Array;
1288
+ /**
1289
+ * Sign a succession from the keys being retired to the identity taking over.
1290
+ *
1291
+ * Takes `StoredKeys` for the predecessor because only the holder of K1's
1292
+ * private half can produce this, which is the property the whole design rests
1293
+ * on. A site calls this once, at rotation, on the machine holding its keys.
1294
+ */
1295
+ declare function signSuccession(previous: StoredKeys, next: PublicIdentity): Succession;
1296
+ /**
1297
+ * Check one link: did `link.identity` sign over succeeding to `toKeyId`?
1298
+ *
1299
+ * `toKeyId` is passed in rather than read from anywhere in `link`, and that is
1300
+ * the load-bearing detail. A verifier that recovered the successor from the
1301
+ * signed statement would accept a statement about *any* successor, which is
1302
+ * the replay this design names in C.1 — the signature is genuine, the
1303
+ * successor it names is not the one being installed.
1304
+ */
1305
+ declare function verifyLink(link: Succession, toKeyId: string): boolean;
1306
+ /** Why a chain was refused, in the words a log line uses. */
1307
+ type SuccessionFailure = "no-chain" | "too-long" | "unknown-origin" | "broken-link";
1308
+ interface SuccessionWalk {
1309
+ /** The ids the chain passes through, oldest first, ending at the current. */
1310
+ readonly path: string[];
1311
+ /** The approved id the chain reached, when it reached one. */
1312
+ readonly from?: string;
1313
+ readonly failure?: SuccessionFailure;
1314
+ }
1315
+ /**
1316
+ * Walk a chain from the key being presented back to a key already approved.
1317
+ *
1318
+ * `chain` is ordered oldest last, as the projection carries it — so walking it
1319
+ * means starting at the current key and stepping backwards, each link proving
1320
+ * that its holder signed for the id in front of it.
1321
+ *
1322
+ * Returns the approved id it reached, or why it did not. **Deliberately
1323
+ * returns rather than throws**: a chain that does not verify is ordinary
1324
+ * hostile input, and the caller's job is to keep its existing pin and say so.
1325
+ *
1326
+ * `approved` is asked as a predicate rather than taken as a set because the
1327
+ * daemon's notion of "already approved" includes tombstoned ids — a site that
1328
+ * left the allowlist and came back is still a site this machine has vouched
1329
+ * for, and rotation must not become a way to launder that distinction away.
1330
+ */
1331
+ declare function walkSuccession(input: {
1332
+ current: string;
1333
+ chain: readonly Succession[];
1334
+ approved: (keyId: string) => boolean;
1335
+ }): SuccessionWalk;
1336
+
967
1337
  /**
968
1338
  * The normative MUSTs of protocol v0, as data.
969
1339
  *
@@ -997,12 +1367,45 @@ type MustEnforcer = "daemon" | "server" | "both";
997
1367
  * someone else's, so the kit cannot carry it.
998
1368
  * - `construction` — true by the shape of the code, where a test could only
999
1369
  * sample. A reviewer verifies it; a suite cannot.
1370
+ * ## When a MUST binds both sides — cloud_008 Tier 3
1371
+ *
1372
+ * `AUDIENCE_BOTH_SIDES` says the server and the daemon each enforce. The kit
1373
+ * passed **entirely** with the server's half deleted: every check drove a real
1374
+ * daemon, and a daemon refuses locally, so "the job did not run" looked
1375
+ * identical whichever side refused it. A full-honest-stack test proves only
1376
+ * the conjunction.
1377
+ *
1378
+ * So a `both`-enforced MUST needs **one check per party, each with the honest
1379
+ * counterpart removed** — C032 claims over the raw protocol precisely so no
1380
+ * daemon admission logic runs. Where a check strips one side, its comment
1381
+ * says which; where a MUST is enforced by both and only one side is checked,
1382
+ * that is a gap rather than coverage.
1383
+ *
1000
1384
  * - `operator` — a claim about how someone runs a deployment, verifiable only
1001
1385
  * by audit or by reading source. The honest category, and the one that
1002
1386
  * exists so a property nobody can check from outside is *labelled* as such
1003
1387
  * rather than laundered by association with the checkable ones.
1004
1388
  */
1005
1389
  type MustVerification = "conformance" | "adversarial" | "construction" | "operator";
1390
+ /**
1391
+ * How a MUST is verified — one kind, or several.
1392
+ *
1393
+ * Several is not hedging. `SITES_LOCALLY_APPROVED` is the case that forced it:
1394
+ * the fence is **construction** — a daemon cannot serve a site that is not in
1395
+ * its map, and admission refuses before a payload is fetched — while the
1396
+ * property that a *removed and re-offered* id is still refused needs a hostile
1397
+ * sequence of heartbeats no honest client would send, which is
1398
+ * **adversarial**. Recording one and dropping the other would either overstate
1399
+ * what a type check proves or understate what the suites do.
1400
+ *
1401
+ * The alternative was a second field for the second kind, which is two answers
1402
+ * to one question — the shape this project keeps deleting.
1403
+ */
1404
+ type MustVerifiedBy = MustVerification | readonly [MustVerification, ...MustVerification[]];
1405
+ /** The kinds a MUST claims, always as a list. */
1406
+ declare function kindsOf(must: {
1407
+ readonly verifiedBy: MustVerifiedBy;
1408
+ }): readonly MustVerification[];
1006
1409
  /** A single normative requirement of the protocol. */
1007
1410
  interface Must {
1008
1411
  /** Stable public id, cited by conformance output. */
@@ -1015,7 +1418,7 @@ interface Must {
1015
1418
  * How this is verified. `conformance` is the only kind the kit can assert;
1016
1419
  * see {@link MustVerification} for why the others exist.
1017
1420
  */
1018
- readonly verifiedBy: MustVerification;
1421
+ readonly verifiedBy: MustVerifiedBy;
1019
1422
  /** Spec section this was adjudicated in. */
1020
1423
  readonly source: string;
1021
1424
  }
@@ -1032,6 +1435,8 @@ declare const MUSTS: Readonly<{
1032
1435
  readonly PAIR_INTERACTIVE: Must;
1033
1436
  readonly PAIR_CODE_EXPIRES: Must;
1034
1437
  readonly VERSION_HANDSHAKE_REQUIRED: Must;
1438
+ readonly SITE_KEY_BY_STUB: Must;
1439
+ readonly SITES_LOCALLY_APPROVED: Must;
1035
1440
  readonly KEYS_EXCHANGED_AT_CONSENT: Must;
1036
1441
  readonly REQUESTS_SIGNED_NOT_BEARER: Must;
1037
1442
  readonly LEASE_SCOPED_BY_GRANT: Must;
@@ -1058,19 +1463,47 @@ declare const MUSTS: Readonly<{
1058
1463
  readonly TTL_EXPIRY: Must;
1059
1464
  readonly NO_RUNNER_SIGNAL: Must;
1060
1465
  readonly RESULT_IDEMPOTENT: Must;
1061
- readonly RESULT_PROVENANCE: Must;
1466
+ readonly PROVENANCE_NAMES_DEVICE: Must;
1062
1467
  readonly INGRESS_LOGGED_BEFORE_EXECUTION: Must;
1063
1468
  readonly NO_SHELL_INTERPOLATION: Must;
1469
+ /**
1470
+ * Amended for byollm_016 Phase B, and the amendment is deliberately narrow.
1471
+ *
1472
+ * A site may now name a **service** on the stub. The temptation is to read
1473
+ * that as a crack in this law, so the statement below says exactly where the
1474
+ * line is: a name selects from a menu the owner published, and resolves to a
1475
+ * model, backend, base URL and flags **only** through that owner's own
1476
+ * config. The site supplies a key; the owner supplies every value it maps
1477
+ * to. A name the owner does not advertise is refused rather than
1478
+ * substituted, because substitution is how "you may pick from my list" turns
1479
+ * into "you may ask for anything and get something".
1480
+ *
1481
+ * Two properties keep it from drifting into "sites demand models":
1482
+ *
1483
+ * 1. **Nothing the site sends is ever a value.** No model string, no URL,
1484
+ * no flag crosses the wire — only a key that means nothing off this
1485
+ * owner's machine.
1486
+ * 2. **It is a stub field, never a payload field.** The prompt cannot
1487
+ * reach it. That is unchanged and is the sentence the second clause
1488
+ * below still enforces verbatim.
1489
+ */
1064
1490
  readonly NO_PAYLOAD_ROUTING: Must;
1065
1491
  readonly STRIPPED_CHILD_ENV: Must;
1066
1492
  readonly HTTP_BASE_URL_SAFE: Must;
1067
1493
  readonly OUTPUT_INERT: Must;
1068
1494
  readonly COMMUNITY_BUDGETS: Must;
1495
+ readonly REVOCATION_IMMEDIATE: Must;
1496
+ readonly CONSENT_BEFORE_ROUTE: Must;
1497
+ readonly ROSTER_NOT_DISCLOSED: Must;
1498
+ readonly EFFECTIVE_OFFER_ONLY: Must;
1499
+ readonly FALLBACK_LABELED: Must;
1500
+ readonly RELAY_BLIND: Must;
1501
+ readonly SHARED_COMPUTE_DISCLOSED: Must;
1069
1502
  }>;
1070
1503
  /** The id of any normative MUST. */
1071
1504
  type MustId = keyof typeof MUSTS;
1072
1505
  /** All MUST ids, for coverage checks. */
1073
- declare const MUST_IDS: readonly ("PAIR_ONE_USER" | "PAIR_INTERACTIVE" | "PAIR_CODE_EXPIRES" | "VERSION_HANDSHAKE_REQUIRED" | "KEYS_EXCHANGED_AT_CONSENT" | "REQUESTS_SIGNED_NOT_BEARER" | "LEASE_SCOPED_BY_GRANT" | "STUB_METADATA_EXHAUSTIVE" | "ENVELOPE_SEALED_AND_SIGNED" | "KIND_TYPED_ONLY" | "KIND_NO_CODE" | "CLAIM_REQUIRES_CAPABILITY" | "CAPABILITY_IS_DETECTED" | "CLAIM_ATOMIC" | "LEASE_HONORED" | "LEASE_RECLAIMABLE" | "AUDIENCE_BOTH_SIDES" | "SUBSCRIPTION_SELF_LOCK" | "METERED_DEFAULTS_SELF" | "METERED_REQUIRES_CEILING" | "COST_NOT_CONFIGURABLE" | "REMOTE_IS_NEVER_FREE" | "NAMED_LOCAL_ALLOWLIST" | "REFUSAL_NOT_REOFFERED" | "REVOCATION_HONORED" | "CANCEL_HONORED" | "DEPENDS_ON_GATING" | "TTL_EXPIRY" | "NO_RUNNER_SIGNAL" | "RESULT_IDEMPOTENT" | "RESULT_PROVENANCE" | "INGRESS_LOGGED_BEFORE_EXECUTION" | "NO_SHELL_INTERPOLATION" | "NO_PAYLOAD_ROUTING" | "STRIPPED_CHILD_ENV" | "HTTP_BASE_URL_SAFE" | "OUTPUT_INERT" | "COMMUNITY_BUDGETS")[];
1506
+ declare const MUST_IDS: readonly ("PAIR_ONE_USER" | "PAIR_INTERACTIVE" | "PAIR_CODE_EXPIRES" | "VERSION_HANDSHAKE_REQUIRED" | "SITE_KEY_BY_STUB" | "SITES_LOCALLY_APPROVED" | "KEYS_EXCHANGED_AT_CONSENT" | "REQUESTS_SIGNED_NOT_BEARER" | "LEASE_SCOPED_BY_GRANT" | "STUB_METADATA_EXHAUSTIVE" | "ENVELOPE_SEALED_AND_SIGNED" | "KIND_TYPED_ONLY" | "KIND_NO_CODE" | "CLAIM_REQUIRES_CAPABILITY" | "CAPABILITY_IS_DETECTED" | "CLAIM_ATOMIC" | "LEASE_HONORED" | "LEASE_RECLAIMABLE" | "AUDIENCE_BOTH_SIDES" | "SUBSCRIPTION_SELF_LOCK" | "METERED_DEFAULTS_SELF" | "METERED_REQUIRES_CEILING" | "COST_NOT_CONFIGURABLE" | "REMOTE_IS_NEVER_FREE" | "NAMED_LOCAL_ALLOWLIST" | "REFUSAL_NOT_REOFFERED" | "REVOCATION_HONORED" | "CANCEL_HONORED" | "DEPENDS_ON_GATING" | "TTL_EXPIRY" | "NO_RUNNER_SIGNAL" | "RESULT_IDEMPOTENT" | "PROVENANCE_NAMES_DEVICE" | "INGRESS_LOGGED_BEFORE_EXECUTION" | "NO_SHELL_INTERPOLATION" | "NO_PAYLOAD_ROUTING" | "STRIPPED_CHILD_ENV" | "HTTP_BASE_URL_SAFE" | "OUTPUT_INERT" | "COMMUNITY_BUDGETS" | "REVOCATION_IMMEDIATE" | "CONSENT_BEFORE_ROUTE" | "ROSTER_NOT_DISCLOSED" | "EFFECTIVE_OFFER_ONLY" | "FALLBACK_LABELED" | "RELAY_BLIND" | "SHARED_COMPUTE_DISCLOSED")[];
1074
1507
  /** Every MUST verified a particular way. */
1075
1508
  declare function mustsVerifiedBy(kind: MustVerification): MustId[];
1076
1509
 
@@ -1100,6 +1533,23 @@ interface VersionRefusal {
1100
1533
  readonly supported: readonly string[];
1101
1534
  readonly minimum: string;
1102
1535
  }
1536
+ /**
1537
+ * The version a request declares, wherever it carries it.
1538
+ *
1539
+ * A POST declares it in its body, which is where every request schema has
1540
+ * always put it. A GET has no body, and the relay has one — the site plane's
1541
+ * `pending` read — so it declares it in the query string instead.
1542
+ *
1543
+ * **Two carriers, one rule.** That asymmetry is HTTP's rather than ours, and
1544
+ * the alternative was worse in both directions: a header for everything would
1545
+ * change every existing daemon's request, and skipping GETs would leave an
1546
+ * endpoint outside the handshake — which is precisely the shape B.4 found,
1547
+ * where a whole plane was outside it.
1548
+ */
1549
+ declare function declaredVersion(input: {
1550
+ body?: unknown;
1551
+ query?: URLSearchParams;
1552
+ }): unknown;
1103
1553
  /**
1104
1554
  * Check the protocol version on an incoming request
1105
1555
  * ({@link MUSTS.VERSION_HANDSHAKE_REQUIRED}).
@@ -1144,6 +1594,8 @@ declare const Capability: z.ZodObject<{
1144
1594
  "llm.generate": "llm.generate";
1145
1595
  "llm.chat": "llm.chat";
1146
1596
  }>;
1597
+ service: z.ZodString;
1598
+ isDefault: z.ZodBoolean;
1147
1599
  backendId: z.ZodEnum<{
1148
1600
  ollama: "ollama";
1149
1601
  mlx: "mlx";
@@ -1163,6 +1615,7 @@ declare const Capability: z.ZodObject<{
1163
1615
  mistral: "mistral";
1164
1616
  "openai-http": "openai-http";
1165
1617
  "claude-cli": "claude-cli";
1618
+ "codex-cli": "codex-cli";
1166
1619
  }>;
1167
1620
  backendClass: z.ZodEnum<{
1168
1621
  http: "http";
@@ -1170,8 +1623,8 @@ declare const Capability: z.ZodObject<{
1170
1623
  }>;
1171
1624
  model: z.ZodString;
1172
1625
  offerScope: z.ZodEnum<{
1173
- self: "self";
1174
- named: "named";
1626
+ private: "private";
1627
+ team: "team";
1175
1628
  public: "public";
1176
1629
  }>;
1177
1630
  }, z.core.$strict>;
@@ -1182,6 +1635,8 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1182
1635
  "llm.generate": "llm.generate";
1183
1636
  "llm.chat": "llm.chat";
1184
1637
  }>;
1638
+ service: z.ZodString;
1639
+ isDefault: z.ZodBoolean;
1185
1640
  backendId: z.ZodEnum<{
1186
1641
  ollama: "ollama";
1187
1642
  mlx: "mlx";
@@ -1201,6 +1656,7 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1201
1656
  mistral: "mistral";
1202
1657
  "openai-http": "openai-http";
1203
1658
  "claude-cli": "claude-cli";
1659
+ "codex-cli": "codex-cli";
1204
1660
  }>;
1205
1661
  backendClass: z.ZodEnum<{
1206
1662
  http: "http";
@@ -1208,12 +1664,41 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1208
1664
  }>;
1209
1665
  model: z.ZodString;
1210
1666
  offerScope: z.ZodEnum<{
1211
- self: "self";
1212
- named: "named";
1667
+ private: "private";
1668
+ team: "team";
1213
1669
  public: "public";
1214
1670
  }>;
1215
1671
  }, z.core.$strict>>;
1216
1672
  type CapabilityMatrix = z.infer<typeof CapabilityMatrix>;
1673
+ /**
1674
+ * A kind this device could serve and deliberately does not — byollm_016.
1675
+ *
1676
+ * Two services answer one kind and the owner has not said which wins, so the
1677
+ * kind is not advertised. That is correct and, unsaid, invisible: the owner
1678
+ * adds a second service, jobs stop matching, and no surface explains it.
1679
+ *
1680
+ * It travels because the surfaces that must say so are not all on the device.
1681
+ * The owner's card names the claimants; a teammate's card says only that the
1682
+ * owner has a choice to make. Claimant **offer scopes** ride along so the hub
1683
+ * can compute that difference without the device deciding who is asking —
1684
+ * carry for computation, filter for display, the same shape the effective
1685
+ * offer already uses.
1686
+ */
1687
+ declare const WithheldKind: z.ZodObject<{
1688
+ kind: z.ZodEnum<{
1689
+ "llm.generate": "llm.generate";
1690
+ "llm.chat": "llm.chat";
1691
+ }>;
1692
+ claimants: z.ZodArray<z.ZodObject<{
1693
+ service: z.ZodString;
1694
+ offer: z.ZodEnum<{
1695
+ private: "private";
1696
+ team: "team";
1697
+ public: "public";
1698
+ }>;
1699
+ }, z.core.$strict>>;
1700
+ }, z.core.$strict>;
1701
+ type WithheldKind = z.infer<typeof WithheldKind>;
1217
1702
  /**
1218
1703
  * Pairing is a device-code exchange, not a pasted secret
1219
1704
  * ({@link MUSTS.PAIR_INTERACTIVE}). The daemon starts a pairing, shows the
@@ -1243,6 +1728,8 @@ declare const PairStartRequest: z.ZodObject<{
1243
1728
  "llm.generate": "llm.generate";
1244
1729
  "llm.chat": "llm.chat";
1245
1730
  }>;
1731
+ service: z.ZodString;
1732
+ isDefault: z.ZodBoolean;
1246
1733
  backendId: z.ZodEnum<{
1247
1734
  ollama: "ollama";
1248
1735
  mlx: "mlx";
@@ -1262,6 +1749,7 @@ declare const PairStartRequest: z.ZodObject<{
1262
1749
  mistral: "mistral";
1263
1750
  "openai-http": "openai-http";
1264
1751
  "claude-cli": "claude-cli";
1752
+ "codex-cli": "codex-cli";
1265
1753
  }>;
1266
1754
  backendClass: z.ZodEnum<{
1267
1755
  http: "http";
@@ -1269,8 +1757,8 @@ declare const PairStartRequest: z.ZodObject<{
1269
1757
  }>;
1270
1758
  model: z.ZodString;
1271
1759
  offerScope: z.ZodEnum<{
1272
- self: "self";
1273
- named: "named";
1760
+ private: "private";
1761
+ team: "team";
1274
1762
  public: "public";
1275
1763
  }>;
1276
1764
  }, z.core.$strict>>;
@@ -1298,15 +1786,14 @@ declare const PairPollResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
1298
1786
  status: z.ZodLiteral<"expired">;
1299
1787
  }, z.core.$strict>, z.ZodObject<{
1300
1788
  status: z.ZodLiteral<"approved">;
1301
- runnerToken: z.ZodString;
1302
1789
  runnerId: z.ZodString;
1303
1790
  owner: z.ZodString;
1304
1791
  ownerLabel: z.ZodOptional<z.ZodString>;
1305
- site: z.ZodObject<{
1792
+ sites: z.ZodRecord<z.ZodString, z.ZodObject<{
1306
1793
  identity: z.ZodString;
1307
1794
  encryption: z.ZodString;
1308
1795
  encryptionSig: z.ZodString;
1309
- }, z.core.$strict>;
1796
+ }, z.core.$strict>>;
1310
1797
  }, z.core.$strict>], "status">;
1311
1798
  type PairPollResponse = z.infer<typeof PairPollResponse>;
1312
1799
  declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -1331,6 +1818,8 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1331
1818
  "llm.generate": "llm.generate";
1332
1819
  "llm.chat": "llm.chat";
1333
1820
  }>;
1821
+ service: z.ZodString;
1822
+ isDefault: z.ZodBoolean;
1334
1823
  backendId: z.ZodEnum<{
1335
1824
  ollama: "ollama";
1336
1825
  mlx: "mlx";
@@ -1350,6 +1839,7 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1350
1839
  mistral: "mistral";
1351
1840
  "openai-http": "openai-http";
1352
1841
  "claude-cli": "claude-cli";
1842
+ "codex-cli": "codex-cli";
1353
1843
  }>;
1354
1844
  backendClass: z.ZodEnum<{
1355
1845
  http: "http";
@@ -1357,8 +1847,8 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1357
1847
  }>;
1358
1848
  model: z.ZodString;
1359
1849
  offerScope: z.ZodEnum<{
1360
- self: "self";
1361
- named: "named";
1850
+ private: "private";
1851
+ team: "team";
1362
1852
  public: "public";
1363
1853
  }>;
1364
1854
  }, z.core.$strict>>;
@@ -1376,6 +1866,8 @@ declare const ClaimRequest: z.ZodObject<{
1376
1866
  "llm.generate": "llm.generate";
1377
1867
  "llm.chat": "llm.chat";
1378
1868
  }>;
1869
+ service: z.ZodString;
1870
+ isDefault: z.ZodBoolean;
1379
1871
  backendId: z.ZodEnum<{
1380
1872
  ollama: "ollama";
1381
1873
  mlx: "mlx";
@@ -1395,6 +1887,7 @@ declare const ClaimRequest: z.ZodObject<{
1395
1887
  mistral: "mistral";
1396
1888
  "openai-http": "openai-http";
1397
1889
  "claude-cli": "claude-cli";
1890
+ "codex-cli": "codex-cli";
1398
1891
  }>;
1399
1892
  backendClass: z.ZodEnum<{
1400
1893
  http: "http";
@@ -1402,8 +1895,8 @@ declare const ClaimRequest: z.ZodObject<{
1402
1895
  }>;
1403
1896
  model: z.ZodString;
1404
1897
  offerScope: z.ZodEnum<{
1405
- self: "self";
1406
- named: "named";
1898
+ private: "private";
1899
+ team: "team";
1407
1900
  public: "public";
1408
1901
  }>;
1409
1902
  }, z.core.$strict>>;
@@ -1418,12 +1911,13 @@ declare const ClaimResponse: z.ZodObject<{
1418
1911
  "llm.chat": "llm.chat";
1419
1912
  }>;
1420
1913
  owner: z.ZodString;
1914
+ site: z.ZodString;
1421
1915
  audience: z.ZodEnum<{
1422
- self: "self";
1423
- named: "named";
1916
+ private: "private";
1917
+ team: "team";
1424
1918
  public: "public";
1425
1919
  }>;
1426
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
1920
+ service: z.ZodOptional<z.ZodString>;
1427
1921
  sizeClass: z.ZodEnum<{
1428
1922
  small: "small";
1429
1923
  medium: "medium";
@@ -1450,6 +1944,8 @@ declare const HeartbeatRequest: z.ZodObject<{
1450
1944
  "llm.generate": "llm.generate";
1451
1945
  "llm.chat": "llm.chat";
1452
1946
  }>;
1947
+ service: z.ZodString;
1948
+ isDefault: z.ZodBoolean;
1453
1949
  backendId: z.ZodEnum<{
1454
1950
  ollama: "ollama";
1455
1951
  mlx: "mlx";
@@ -1469,6 +1965,7 @@ declare const HeartbeatRequest: z.ZodObject<{
1469
1965
  mistral: "mistral";
1470
1966
  "openai-http": "openai-http";
1471
1967
  "claude-cli": "claude-cli";
1968
+ "codex-cli": "codex-cli";
1472
1969
  }>;
1473
1970
  backendClass: z.ZodEnum<{
1474
1971
  http: "http";
@@ -1476,11 +1973,25 @@ declare const HeartbeatRequest: z.ZodObject<{
1476
1973
  }>;
1477
1974
  model: z.ZodString;
1478
1975
  offerScope: z.ZodEnum<{
1479
- self: "self";
1480
- named: "named";
1976
+ private: "private";
1977
+ team: "team";
1481
1978
  public: "public";
1482
1979
  }>;
1483
1980
  }, z.core.$strict>>;
1981
+ withheld: z.ZodDefault<z.ZodArray<z.ZodObject<{
1982
+ kind: z.ZodEnum<{
1983
+ "llm.generate": "llm.generate";
1984
+ "llm.chat": "llm.chat";
1985
+ }>;
1986
+ claimants: z.ZodArray<z.ZodObject<{
1987
+ service: z.ZodString;
1988
+ offer: z.ZodEnum<{
1989
+ private: "private";
1990
+ team: "team";
1991
+ public: "public";
1992
+ }>;
1993
+ }, z.core.$strict>>;
1994
+ }, z.core.$strict>>>;
1484
1995
  activeLeases: z.ZodArray<z.ZodObject<{
1485
1996
  jobId: z.ZodString;
1486
1997
  leaseId: z.ZodString;
@@ -1489,14 +2000,32 @@ declare const HeartbeatRequest: z.ZodObject<{
1489
2000
  }, z.core.$strict>;
1490
2001
  type HeartbeatRequest = z.infer<typeof HeartbeatRequest>;
1491
2002
  declare const HeartbeatResponse: z.ZodObject<{
1492
- revoked: z.ZodBoolean;
1493
- cancel: z.ZodArray<z.ZodString>;
1494
- leases: z.ZodArray<z.ZodObject<{
2003
+ sites: z.ZodRecord<z.ZodString, z.ZodObject<{
2004
+ identity: z.ZodString;
2005
+ encryption: z.ZodString;
2006
+ encryptionSig: z.ZodString;
2007
+ }, z.core.$strict>>;
2008
+ successions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
2009
+ succeeds: z.ZodArray<z.ZodObject<{
2010
+ identity: z.ZodObject<{
2011
+ identity: z.ZodString;
2012
+ encryption: z.ZodString;
2013
+ encryptionSig: z.ZodString;
2014
+ }, z.core.$strict>;
2015
+ signature: z.ZodString;
2016
+ }, z.core.$strict>>;
2017
+ retiringUntil: z.ZodOptional<z.ZodNumber>;
2018
+ }, z.core.$strict>>>;
2019
+ cancel: z.ZodArray<z.ZodObject<{
1495
2020
  jobId: z.ZodString;
1496
- expiresAt: z.ZodNumber;
2021
+ leaseId: z.ZodString;
2022
+ }, z.core.$strict>>;
2023
+ lost: z.ZodArray<z.ZodObject<{
2024
+ jobId: z.ZodString;
2025
+ leaseId: z.ZodString;
1497
2026
  }, z.core.$strict>>;
1498
- lost: z.ZodArray<z.ZodString>;
1499
2027
  serverTime: z.ZodNumber;
2028
+ awaitingConsent: z.ZodArray<z.ZodString>;
1500
2029
  }, z.core.$strict>;
1501
2030
  type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;
1502
2031
  /**
@@ -1521,6 +2050,7 @@ declare const ResultRequest: z.ZodObject<{
1521
2050
  protocolVersion: z.ZodLiteral<"0">;
1522
2051
  runnerId: z.ZodString;
1523
2052
  jobId: z.ZodString;
2053
+ leaseId: z.ZodString;
1524
2054
  envelope: z.ZodObject<{
1525
2055
  ciphertext: z.ZodString;
1526
2056
  recipientKeyId: z.ZodString;
@@ -1536,16 +2066,11 @@ declare const ResultRequest: z.ZodObject<{
1536
2066
  error: "error";
1537
2067
  canceled: "canceled";
1538
2068
  }>;
1539
- model: z.ZodString;
1540
- backendClass: z.ZodEnum<{
1541
- http: "http";
1542
- process: "process";
1543
- }>;
1544
- durationMs: z.ZodNumber;
1545
2069
  }, z.core.$strict>;
1546
2070
  type ResultRequest = z.infer<typeof ResultRequest>;
1547
2071
  declare const ResultResponse: z.ZodObject<{
1548
2072
  accepted: z.ZodBoolean;
2073
+ duplicate: z.ZodOptional<z.ZodBoolean>;
1549
2074
  state: z.ZodString;
1550
2075
  }, z.core.$strict>;
1551
2076
  type ResultResponse = z.infer<typeof ResultResponse>;
@@ -1557,11 +2082,11 @@ declare const ReleaseRequest: z.ZodObject<{
1557
2082
  leaseId: z.ZodString;
1558
2083
  }, z.core.$strip>>;
1559
2084
  reason: z.ZodEnum<{
1560
- revoked: "revoked";
2085
+ refused: "refused";
1561
2086
  shutdown: "shutdown";
1562
2087
  pause: "pause";
2088
+ revoked: "revoked";
1563
2089
  "backend-down": "backend-down";
1564
- refused: "refused";
1565
2090
  }>;
1566
2091
  }, z.core.$strict>;
1567
2092
  type ReleaseRequest = z.infer<typeof ReleaseRequest>;
@@ -1582,7 +2107,11 @@ declare const WireErrorCode: z.ZodEnum<{
1582
2107
  revoked: "revoked";
1583
2108
  "bad-request": "bad-request";
1584
2109
  unauthorized: "unauthorized";
2110
+ forbidden: "forbidden";
1585
2111
  "not-found": "not-found";
2112
+ "not-ready": "not-ready";
2113
+ "too-late": "too-late";
2114
+ "clock-skew": "clock-skew";
1586
2115
  "rate-limited": "rate-limited";
1587
2116
  "server-error": "server-error";
1588
2117
  }>;
@@ -1593,18 +2122,26 @@ declare const WireError: z.ZodObject<{
1593
2122
  revoked: "revoked";
1594
2123
  "bad-request": "bad-request";
1595
2124
  unauthorized: "unauthorized";
2125
+ forbidden: "forbidden";
1596
2126
  "not-found": "not-found";
2127
+ "not-ready": "not-ready";
2128
+ "too-late": "too-late";
2129
+ "clock-skew": "clock-skew";
1597
2130
  "rate-limited": "rate-limited";
1598
2131
  "server-error": "server-error";
1599
2132
  }>;
1600
2133
  message: z.ZodString;
2134
+ supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
2135
+ minimum: z.ZodOptional<z.ZodString>;
1601
2136
  retryAfter: z.ZodOptional<z.ZodNumber>;
2137
+ serverTime: z.ZodOptional<z.ZodNumber>;
2138
+ maxSkewMs: z.ZodOptional<z.ZodNumber>;
1602
2139
  }, z.core.$strict>;
1603
2140
  type WireError = z.infer<typeof WireError>;
1604
2141
  /** HTTP status each error code is served with. */
1605
2142
  declare const ERROR_STATUS: Readonly<Record<WireErrorCode, number>>;
1606
2143
  declare const FetchRequest: z.ZodObject<{
1607
- protocolVersion: z.ZodString;
2144
+ protocolVersion: z.ZodLiteral<"0">;
1608
2145
  runnerId: z.ZodString;
1609
2146
  jobId: z.ZodString;
1610
2147
  leaseId: z.ZodString;
@@ -1624,4 +2161,4 @@ declare const FetchResponse: z.ZodObject<{
1624
2161
  }, z.core.$strict>;
1625
2162
  type FetchResponse = z.infer<typeof FetchResponse>;
1626
2163
 
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 };
2164
+ export { AUDIENCES, Audience, BACKENDS, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, FetchRequest, FetchResponse, GeneratePayload, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, REFUSAL_MESSAGES, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SizeClass, type SpendConsent, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, canTransition, canonicalRequest, checkProtocolVersion, cryptoReady, declaredVersion, effectiveOfferScope, fingerprint, generateKeys, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signRequest, signSiteRequest, signSuccession, signWith, sizeClassCeiling, sizeClassOf, successionStatement, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession };