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

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,12 @@ 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>;
507
580
  lease: z.ZodObject<{
508
581
  id: z.ZodString;
509
582
  runnerId: z.ZodString;
@@ -516,12 +589,12 @@ type ClaimedJob = z.infer<typeof ClaimedJob>;
516
589
  *
517
590
  * byollm_003 Rev 1: a `named`/`public` result is attacker-controlled text.
518
591
  * 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}).
592
+ * knowing that is what it is ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).
520
593
  */
521
594
  declare const ResultProvenance: z.ZodObject<{
522
595
  audience: z.ZodEnum<{
523
- self: "self";
524
- named: "named";
596
+ private: "private";
597
+ team: "team";
525
598
  public: "public";
526
599
  }>;
527
600
  runnerId: z.ZodString;
@@ -545,6 +618,30 @@ declare function provenanceFor(input: {
545
618
  backendClass: BackendClass;
546
619
  model: string;
547
620
  }): ResultProvenance;
621
+ /**
622
+ * What the daemon did, sealed with the answer — cloud_008 §2.5.
623
+ *
624
+ * These travelled in the clear on `ResultRequest`, which meant two things at
625
+ * once. On the direct plane the site believed unauthenticated fields beside
626
+ * an authenticated envelope — a daemon could seal one answer and *declare* it
627
+ * came from a different model, and only the field it did not sign would be
628
+ * recorded. Through a relay they reached a third party that acts on none of
629
+ * them, and `model` in particular is the kind of detail Amendment A's rule
630
+ * keeps off the wire.
631
+ *
632
+ * Sealed, they are the daemon's signed statement about its own run: the site
633
+ * opens them, nothing in between sees them, and the disposition check that
634
+ * already compares clear-text against ciphertext extends to cover them.
635
+ */
636
+ declare const RunMetadata: z.ZodObject<{
637
+ model: z.ZodString;
638
+ backendClass: z.ZodEnum<{
639
+ http: "http";
640
+ process: "process";
641
+ }>;
642
+ durationMs: z.ZodNumber;
643
+ }, z.core.$strict>;
644
+ type RunMetadata = z.infer<typeof RunMetadata>;
548
645
  /** Successful outcome. */
549
646
  declare const JobResultOk: z.ZodObject<{
550
647
  outcome: z.ZodLiteral<"ok">;
@@ -575,6 +672,89 @@ declare const JobOutcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
575
672
  outcome: z.ZodLiteral<"canceled">;
576
673
  }, z.core.$strict>], "outcome">;
577
674
  type JobOutcome = z.infer<typeof JobOutcome>;
675
+ /**
676
+ * Why a job can never run — byollm_016 Phase B.
677
+ *
678
+ * Every one of these is **terminal**, and that is the whole point of naming
679
+ * them. A job that cannot be matched used to sit queued until its deadline,
680
+ * which reads exactly like a job that is merely waiting for a device to come
681
+ * online — so an app could not tell "any moment now" from "never", and neither
682
+ * could the person watching a spinner. Silence must never read as pending.
683
+ *
684
+ * They are decided by whoever knows first: the site's own SDK where it can see
685
+ * the answer without asking, the router where matching happens, and the daemon
686
+ * again on arrival under the both-sides rule. All three reason from the same
687
+ * list rather than three private vocabularies.
688
+ */
689
+ declare const RefusalReason: z.ZodEnum<{
690
+ "select-unavailable": "select-unavailable";
691
+ "default-ambiguity": "default-ambiguity";
692
+ "default-unusable": "default-unusable";
693
+ }>;
694
+ type RefusalReason = z.infer<typeof RefusalReason>;
695
+ /**
696
+ * A terminal outcome nobody sealed — byollm_016 Phase B.
697
+ *
698
+ * Every other finished job carries an envelope encrypted by the device that
699
+ * ran it, which is what makes a result unforgeable. These have no device: the
700
+ * job was refused *before* anything could run it, so there is nobody to seal
701
+ * from and no content to seal.
702
+ *
703
+ * **What that costs, stated plainly.** This is the one terminal outcome a
704
+ * router can author. It is worth being exact about the power that grants,
705
+ * because "the relay can write this" sounds alarming until you compare it with
706
+ * what a relay could already do: drop the job, never offer it, and let it
707
+ * expire. A router-authored refusal is *denial of service by a shorter route*,
708
+ * which is a power the router has always had and which the trust model has
709
+ * always said it has. What it emphatically is **not** is forgery: this shape
710
+ * carries no envelope and no output, so it can never be mistaken for an answer
711
+ * a device produced. A relay still cannot fabricate a result, because that
712
+ * needs a signature it does not hold.
713
+ *
714
+ * So the rule this shape enforces by construction: a refusal may deny, and may
715
+ * never assert. Anything that claims work was *done* still comes sealed.
716
+ */
717
+ declare const JobRefused: z.ZodObject<{
718
+ outcome: z.ZodLiteral<"refused">;
719
+ reason: z.ZodEnum<{
720
+ "select-unavailable": "select-unavailable";
721
+ "default-ambiguity": "default-ambiguity";
722
+ "default-unusable": "default-unusable";
723
+ }>;
724
+ message: z.ZodString;
725
+ }, z.core.$strict>;
726
+ type JobRefused = z.infer<typeof JobRefused>;
727
+ /**
728
+ * The plaintext inside a result envelope.
729
+ *
730
+ * The outcome and how it was produced, together, because they are one
731
+ * statement by one signer. A site that opened only the outcome would be
732
+ * trusting the envelope for the answer and the request body for everything
733
+ * about it.
734
+ */
735
+ declare const SealedOutcome: z.ZodObject<{
736
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
737
+ outcome: z.ZodLiteral<"ok">;
738
+ text: z.ZodString;
739
+ artifactUrl: z.ZodOptional<z.ZodURL>;
740
+ }, z.core.$strict>, z.ZodObject<{
741
+ outcome: z.ZodLiteral<"error">;
742
+ code: z.ZodString;
743
+ message: z.ZodString;
744
+ retryable: z.ZodBoolean;
745
+ }, z.core.$strict>, z.ZodObject<{
746
+ outcome: z.ZodLiteral<"canceled">;
747
+ }, z.core.$strict>], "outcome">;
748
+ ran: z.ZodObject<{
749
+ model: z.ZodString;
750
+ backendClass: z.ZodEnum<{
751
+ http: "http";
752
+ process: "process";
753
+ }>;
754
+ durationMs: z.ZodNumber;
755
+ }, z.core.$strict>;
756
+ }, z.core.$strict>;
757
+ type SealedOutcome = z.infer<typeof SealedOutcome>;
578
758
  /** A completed job as delivered to the app, provenance attached. */
579
759
  declare const DeliveredResult: z.ZodObject<{
580
760
  jobId: z.ZodString;
@@ -601,8 +781,8 @@ declare const DeliveredResult: z.ZodObject<{
601
781
  }, z.core.$strict>], "outcome">>;
602
782
  provenance: z.ZodOptional<z.ZodObject<{
603
783
  audience: z.ZodEnum<{
604
- self: "self";
605
- named: "named";
784
+ private: "private";
785
+ team: "team";
606
786
  public: "public";
607
787
  }>;
608
788
  runnerId: z.ZodString;
@@ -614,6 +794,7 @@ declare const DeliveredResult: z.ZodObject<{
614
794
  model: z.ZodString;
615
795
  untrusted: z.ZodBoolean;
616
796
  }, z.core.$strict>>;
797
+ fallback: z.ZodOptional<z.ZodLiteral<true>>;
617
798
  }, z.core.$strict>;
618
799
  type DeliveredResult = z.infer<typeof DeliveredResult>;
619
800
  /**
@@ -677,12 +858,13 @@ declare const JobStub: z.ZodObject<{
677
858
  "llm.chat": "llm.chat";
678
859
  }>;
679
860
  owner: z.ZodString;
861
+ site: z.ZodString;
680
862
  audience: z.ZodEnum<{
681
- self: "self";
682
- named: "named";
863
+ private: "private";
864
+ team: "team";
683
865
  public: "public";
684
866
  }>;
685
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
867
+ service: z.ZodOptional<z.ZodString>;
686
868
  sizeClass: z.ZodEnum<{
687
869
  small: "small";
688
870
  medium: "medium";
@@ -701,12 +883,13 @@ declare const ClaimedStub: z.ZodObject<{
701
883
  "llm.chat": "llm.chat";
702
884
  }>;
703
885
  owner: z.ZodString;
886
+ site: z.ZodString;
704
887
  audience: z.ZodEnum<{
705
- self: "self";
706
- named: "named";
888
+ private: "private";
889
+ team: "team";
707
890
  public: "public";
708
891
  }>;
709
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
892
+ service: z.ZodOptional<z.ZodString>;
710
893
  sizeClass: z.ZodEnum<{
711
894
  small: "small";
712
895
  medium: "medium";
@@ -763,6 +946,18 @@ declare const StoredKeys: z.ZodObject<{
763
946
  createdAt: z.ZodNumber;
764
947
  }, z.core.$strict>;
765
948
  type StoredKeys = z.infer<typeof StoredKeys>;
949
+ /** Domain separator, so a signature over an encryption key cannot be
950
+ * replayed as a signature over anything else. */
951
+ /**
952
+ * What an encryption key's signature covers.
953
+ *
954
+ * Exported because a rotation is a real event this protocol has to be able to
955
+ * *test* — a record whose encryption key moved under an identity that signed
956
+ * the move is the one case pinning must refuse loudly, and building one
957
+ * outside this file otherwise means re-typing this string, which is how two
958
+ * copies of a constant start disagreeing.
959
+ */
960
+ declare const ENCRYPTION_KEY_CONTEXT = "byollm/v1/encryption-key";
766
961
  /** Generate a fresh pair of keypairs and bind them together. */
767
962
  declare function generateKeys(now: number): StoredKeys;
768
963
  /** The public half, for the wire. */
@@ -946,7 +1141,77 @@ declare function signRequest(keys: StoredKeys, input: {
946
1141
  issuedAt: number;
947
1142
  body: string;
948
1143
  }): RequestSignature;
949
- /** Why a signed request was refused. Never returned to the caller verbatim. */
1144
+ /**
1145
+ * The same scheme, for the party at the other end: a **site** calling a relay.
1146
+ *
1147
+ * A site talking to a relay is in exactly the daemon's position — an outbound
1148
+ * caller with an identity keypair the other side already pins — so it gets the
1149
+ * daemon's authentication rather than a second scheme. Bearer tokens for the
1150
+ * site plane were the alternative, and they would have reintroduced the
1151
+ * credential-in-a-file that §4.2 removed from the daemon plane, on the plane
1152
+ * that carries *every* site's traffic.
1153
+ *
1154
+ * Two things make this safe to build on the same canonical string:
1155
+ *
1156
+ * 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never
1157
+ * `enqueue`. The daemon plane's `result` and the site plane's `results` are
1158
+ * one character apart, and a naming collision between planes must not be
1159
+ * what stands between a signature and a replay onto the wrong handler. The
1160
+ * prefix is applied *inside* these helpers, so the two ends cannot disagree
1161
+ * about it — the alternative is two implementations of one bound value,
1162
+ * which is this project's most-repeated bug.
1163
+ * 2. **The caller slot carries the site id.** `canonicalRequest` names that
1164
+ * field `runnerId` because the daemon plane got there first; here it holds
1165
+ * the site id, and the verifier looks the key up in the projection's site
1166
+ * registry rather than its device registry. The two registries never share
1167
+ * an entry, so a device signature cannot authenticate as a site.
1168
+ *
1169
+ * §4.2's replay argument carries over **only because the site plane's writes
1170
+ * are idempotent per addressed instance**, which is a property that had to be
1171
+ * built rather than found: `enqueue` reset a job of the same id, so a replayed
1172
+ * enqueue inside the freshness window returned a claimed job to the queue and
1173
+ * threw away a device's live lease. Identical in shape to the `release` bug
1174
+ * above, on the other plane. Anything added to the site plane later must be
1175
+ * idempotent by the instance it names, or this scheme does not cover it.
1176
+ */
1177
+ declare function signSiteRequest(keys: StoredKeys, input: {
1178
+ endpoint: string;
1179
+ siteId: string;
1180
+ issuedAt: number;
1181
+ body: string;
1182
+ }): RequestSignature;
1183
+ /** Verify a site's call against the identity the control plane registered. */
1184
+ declare function verifySiteRequest(input: {
1185
+ identityPublic: string;
1186
+ endpoint: string;
1187
+ body: string;
1188
+ signature: RequestSignature;
1189
+ now: number;
1190
+ maxSkewMs?: number;
1191
+ }): SignatureFailure | null;
1192
+ /**
1193
+ * Why a signed request was refused.
1194
+ *
1195
+ * **`bad-signature` is never returned verbatim; `stale` is, deliberately.**
1196
+ * They are different kinds of refusal and conflating them costs a real user
1197
+ * more than it costs an attacker.
1198
+ *
1199
+ * A bad signature is an authentication failure and the server says only
1200
+ * "unauthorized" — telling a prober which part they got wrong is free help.
1201
+ *
1202
+ * A stale timestamp is a **precondition** failure: the signature may be
1203
+ * perfectly valid and the caller's clock is simply wrong. Saying so reveals
1204
+ * nothing, for two reasons that both have to hold. The server's time is
1205
+ * already public — every response carries a `Date` header and the heartbeat
1206
+ * response returns `serverTime` outright. And freshness is checked *before*
1207
+ * the signature is verified, so a stale answer says nothing about whether the
1208
+ * signature was any good.
1209
+ *
1210
+ * What conflating them costs: a machine whose clock has drifted gets
1211
+ * `401 unauthorized` on every request, forever, with nothing anywhere pointing
1212
+ * at the clock. That is the shape byollm_013 was filed about — a refusal that
1213
+ * is correct, silent, and sends somebody to read our source.
1214
+ */
950
1215
  type SignatureFailure = "stale" | "bad-signature";
951
1216
  /**
952
1217
  * Verify a signed request against a runner's pinned identity key.
@@ -964,6 +1229,110 @@ declare function verifyRequest(input: {
964
1229
  maxSkewMs?: number;
965
1230
  }): SignatureFailure | null;
966
1231
 
1232
+ /**
1233
+ * Rotation — byollm_009 Amendment C.
1234
+ *
1235
+ * A site holding identity key **K1** wants to be known by **K2**. It publishes
1236
+ * a *succession*: K2, plus a signature by K1 over a statement naming both key
1237
+ * ids. That signature is the entire mechanism, and the reason rotation can be
1238
+ * automatic without becoming a hole is that **the relay cannot mint one** — it
1239
+ * never holds K1. It is the same trust step a daemon already performs at
1240
+ * pairing, applied to the site's own succession.
1241
+ *
1242
+ * ## Why the statement names both keys
1243
+ *
1244
+ * A signature over K2 alone could be lifted from this site's record and
1245
+ * replayed into another site's, moving *that* site to K2 — a key the attacker
1246
+ * holds. Naming the predecessor binds the succession to one chain, and it is
1247
+ * the reason `verifyLink` takes the id it expects to be succeeding from
1248
+ * rather than reading it out of the statement it is checking.
1249
+ */
1250
+ /** The domain separator. Distinct from every other thing an identity signs. */
1251
+ declare const SUCCESSION_CONTEXT = "byollm/v1/site-succession";
1252
+ /**
1253
+ * How long a retired key may still sign work — Amendment C, ruling 2.
1254
+ *
1255
+ * A protocol constant and not the site's to choose. Per-site overlap
1256
+ * arithmetic is exactly the kind of number that has to mean one thing
1257
+ * everywhere, and a site that could choose it could choose *forever*, which is
1258
+ * a two-key site permanently and a second key nobody ever notices retiring.
1259
+ *
1260
+ * Seven days: long enough that a daemon which polls daily and a laptop shut
1261
+ * for a long weekend both see the new record before the old key stops working,
1262
+ * short enough that "which key is live" is never an interesting question.
1263
+ */
1264
+ declare const RETIREMENT_WINDOW_MS: number;
1265
+ /**
1266
+ * The longest chain a daemon will walk — Amendment C, ruling 1.
1267
+ *
1268
+ * **A denial-of-service guard, not policy.** The bound exists so a projection
1269
+ * cannot make a daemon verify ten thousand signatures, not to express an
1270
+ * opinion about how often a site may rotate. A site that legitimately exceeds
1271
+ * it has a re-pair ahead of it, which is why it is generous: at one rotation a
1272
+ * quarter this is sixteen years.
1273
+ */
1274
+ declare const MAX_SUCCESSION_CHAIN = 64;
1275
+ /** One step of a chain: a key, and the signature by it over its successor. */
1276
+ declare const Succession: z.ZodObject<{
1277
+ identity: z.ZodObject<{
1278
+ identity: z.ZodString;
1279
+ encryption: z.ZodString;
1280
+ encryptionSig: z.ZodString;
1281
+ }, z.core.$strict>;
1282
+ signature: z.ZodString;
1283
+ }, z.core.$strict>;
1284
+ type Succession = z.infer<typeof Succession>;
1285
+ /** The exact bytes signed. One definition; both sides call it. */
1286
+ declare function successionStatement(fromKeyId: string, toKeyId: string): Uint8Array;
1287
+ /**
1288
+ * Sign a succession from the keys being retired to the identity taking over.
1289
+ *
1290
+ * Takes `StoredKeys` for the predecessor because only the holder of K1's
1291
+ * private half can produce this, which is the property the whole design rests
1292
+ * on. A site calls this once, at rotation, on the machine holding its keys.
1293
+ */
1294
+ declare function signSuccession(previous: StoredKeys, next: PublicIdentity): Succession;
1295
+ /**
1296
+ * Check one link: did `link.identity` sign over succeeding to `toKeyId`?
1297
+ *
1298
+ * `toKeyId` is passed in rather than read from anywhere in `link`, and that is
1299
+ * the load-bearing detail. A verifier that recovered the successor from the
1300
+ * signed statement would accept a statement about *any* successor, which is
1301
+ * the replay this design names in C.1 — the signature is genuine, the
1302
+ * successor it names is not the one being installed.
1303
+ */
1304
+ declare function verifyLink(link: Succession, toKeyId: string): boolean;
1305
+ /** Why a chain was refused, in the words a log line uses. */
1306
+ type SuccessionFailure = "no-chain" | "too-long" | "unknown-origin" | "broken-link";
1307
+ interface SuccessionWalk {
1308
+ /** The ids the chain passes through, oldest first, ending at the current. */
1309
+ readonly path: string[];
1310
+ /** The approved id the chain reached, when it reached one. */
1311
+ readonly from?: string;
1312
+ readonly failure?: SuccessionFailure;
1313
+ }
1314
+ /**
1315
+ * Walk a chain from the key being presented back to a key already approved.
1316
+ *
1317
+ * `chain` is ordered oldest last, as the projection carries it — so walking it
1318
+ * means starting at the current key and stepping backwards, each link proving
1319
+ * that its holder signed for the id in front of it.
1320
+ *
1321
+ * Returns the approved id it reached, or why it did not. **Deliberately
1322
+ * returns rather than throws**: a chain that does not verify is ordinary
1323
+ * hostile input, and the caller's job is to keep its existing pin and say so.
1324
+ *
1325
+ * `approved` is asked as a predicate rather than taken as a set because the
1326
+ * daemon's notion of "already approved" includes tombstoned ids — a site that
1327
+ * left the allowlist and came back is still a site this machine has vouched
1328
+ * for, and rotation must not become a way to launder that distinction away.
1329
+ */
1330
+ declare function walkSuccession(input: {
1331
+ current: string;
1332
+ chain: readonly Succession[];
1333
+ approved: (keyId: string) => boolean;
1334
+ }): SuccessionWalk;
1335
+
967
1336
  /**
968
1337
  * The normative MUSTs of protocol v0, as data.
969
1338
  *
@@ -997,12 +1366,45 @@ type MustEnforcer = "daemon" | "server" | "both";
997
1366
  * someone else's, so the kit cannot carry it.
998
1367
  * - `construction` — true by the shape of the code, where a test could only
999
1368
  * sample. A reviewer verifies it; a suite cannot.
1369
+ * ## When a MUST binds both sides — cloud_008 Tier 3
1370
+ *
1371
+ * `AUDIENCE_BOTH_SIDES` says the server and the daemon each enforce. The kit
1372
+ * passed **entirely** with the server's half deleted: every check drove a real
1373
+ * daemon, and a daemon refuses locally, so "the job did not run" looked
1374
+ * identical whichever side refused it. A full-honest-stack test proves only
1375
+ * the conjunction.
1376
+ *
1377
+ * So a `both`-enforced MUST needs **one check per party, each with the honest
1378
+ * counterpart removed** — C032 claims over the raw protocol precisely so no
1379
+ * daemon admission logic runs. Where a check strips one side, its comment
1380
+ * says which; where a MUST is enforced by both and only one side is checked,
1381
+ * that is a gap rather than coverage.
1382
+ *
1000
1383
  * - `operator` — a claim about how someone runs a deployment, verifiable only
1001
1384
  * by audit or by reading source. The honest category, and the one that
1002
1385
  * exists so a property nobody can check from outside is *labelled* as such
1003
1386
  * rather than laundered by association with the checkable ones.
1004
1387
  */
1005
1388
  type MustVerification = "conformance" | "adversarial" | "construction" | "operator";
1389
+ /**
1390
+ * How a MUST is verified — one kind, or several.
1391
+ *
1392
+ * Several is not hedging. `SITES_LOCALLY_APPROVED` is the case that forced it:
1393
+ * the fence is **construction** — a daemon cannot serve a site that is not in
1394
+ * its map, and admission refuses before a payload is fetched — while the
1395
+ * property that a *removed and re-offered* id is still refused needs a hostile
1396
+ * sequence of heartbeats no honest client would send, which is
1397
+ * **adversarial**. Recording one and dropping the other would either overstate
1398
+ * what a type check proves or understate what the suites do.
1399
+ *
1400
+ * The alternative was a second field for the second kind, which is two answers
1401
+ * to one question — the shape this project keeps deleting.
1402
+ */
1403
+ type MustVerifiedBy = MustVerification | readonly [MustVerification, ...MustVerification[]];
1404
+ /** The kinds a MUST claims, always as a list. */
1405
+ declare function kindsOf(must: {
1406
+ readonly verifiedBy: MustVerifiedBy;
1407
+ }): readonly MustVerification[];
1006
1408
  /** A single normative requirement of the protocol. */
1007
1409
  interface Must {
1008
1410
  /** Stable public id, cited by conformance output. */
@@ -1015,7 +1417,7 @@ interface Must {
1015
1417
  * How this is verified. `conformance` is the only kind the kit can assert;
1016
1418
  * see {@link MustVerification} for why the others exist.
1017
1419
  */
1018
- readonly verifiedBy: MustVerification;
1420
+ readonly verifiedBy: MustVerifiedBy;
1019
1421
  /** Spec section this was adjudicated in. */
1020
1422
  readonly source: string;
1021
1423
  }
@@ -1032,6 +1434,8 @@ declare const MUSTS: Readonly<{
1032
1434
  readonly PAIR_INTERACTIVE: Must;
1033
1435
  readonly PAIR_CODE_EXPIRES: Must;
1034
1436
  readonly VERSION_HANDSHAKE_REQUIRED: Must;
1437
+ readonly SITE_KEY_BY_STUB: Must;
1438
+ readonly SITES_LOCALLY_APPROVED: Must;
1035
1439
  readonly KEYS_EXCHANGED_AT_CONSENT: Must;
1036
1440
  readonly REQUESTS_SIGNED_NOT_BEARER: Must;
1037
1441
  readonly LEASE_SCOPED_BY_GRANT: Must;
@@ -1058,19 +1462,47 @@ declare const MUSTS: Readonly<{
1058
1462
  readonly TTL_EXPIRY: Must;
1059
1463
  readonly NO_RUNNER_SIGNAL: Must;
1060
1464
  readonly RESULT_IDEMPOTENT: Must;
1061
- readonly RESULT_PROVENANCE: Must;
1465
+ readonly PROVENANCE_NAMES_DEVICE: Must;
1062
1466
  readonly INGRESS_LOGGED_BEFORE_EXECUTION: Must;
1063
1467
  readonly NO_SHELL_INTERPOLATION: Must;
1468
+ /**
1469
+ * Amended for byollm_016 Phase B, and the amendment is deliberately narrow.
1470
+ *
1471
+ * A site may now name a **service** on the stub. The temptation is to read
1472
+ * that as a crack in this law, so the statement below says exactly where the
1473
+ * line is: a name selects from a menu the owner published, and resolves to a
1474
+ * model, backend, base URL and flags **only** through that owner's own
1475
+ * config. The site supplies a key; the owner supplies every value it maps
1476
+ * to. A name the owner does not advertise is refused rather than
1477
+ * substituted, because substitution is how "you may pick from my list" turns
1478
+ * into "you may ask for anything and get something".
1479
+ *
1480
+ * Two properties keep it from drifting into "sites demand models":
1481
+ *
1482
+ * 1. **Nothing the site sends is ever a value.** No model string, no URL,
1483
+ * no flag crosses the wire — only a key that means nothing off this
1484
+ * owner's machine.
1485
+ * 2. **It is a stub field, never a payload field.** The prompt cannot
1486
+ * reach it. That is unchanged and is the sentence the second clause
1487
+ * below still enforces verbatim.
1488
+ */
1064
1489
  readonly NO_PAYLOAD_ROUTING: Must;
1065
1490
  readonly STRIPPED_CHILD_ENV: Must;
1066
1491
  readonly HTTP_BASE_URL_SAFE: Must;
1067
1492
  readonly OUTPUT_INERT: Must;
1068
1493
  readonly COMMUNITY_BUDGETS: Must;
1494
+ readonly REVOCATION_IMMEDIATE: Must;
1495
+ readonly CONSENT_BEFORE_ROUTE: Must;
1496
+ readonly ROSTER_NOT_DISCLOSED: Must;
1497
+ readonly EFFECTIVE_OFFER_ONLY: Must;
1498
+ readonly FALLBACK_LABELED: Must;
1499
+ readonly RELAY_BLIND: Must;
1500
+ readonly SHARED_COMPUTE_DISCLOSED: Must;
1069
1501
  }>;
1070
1502
  /** The id of any normative MUST. */
1071
1503
  type MustId = keyof typeof MUSTS;
1072
1504
  /** 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")[];
1505
+ 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
1506
  /** Every MUST verified a particular way. */
1075
1507
  declare function mustsVerifiedBy(kind: MustVerification): MustId[];
1076
1508
 
@@ -1100,6 +1532,23 @@ interface VersionRefusal {
1100
1532
  readonly supported: readonly string[];
1101
1533
  readonly minimum: string;
1102
1534
  }
1535
+ /**
1536
+ * The version a request declares, wherever it carries it.
1537
+ *
1538
+ * A POST declares it in its body, which is where every request schema has
1539
+ * always put it. A GET has no body, and the relay has one — the site plane's
1540
+ * `pending` read — so it declares it in the query string instead.
1541
+ *
1542
+ * **Two carriers, one rule.** That asymmetry is HTTP's rather than ours, and
1543
+ * the alternative was worse in both directions: a header for everything would
1544
+ * change every existing daemon's request, and skipping GETs would leave an
1545
+ * endpoint outside the handshake — which is precisely the shape B.4 found,
1546
+ * where a whole plane was outside it.
1547
+ */
1548
+ declare function declaredVersion(input: {
1549
+ body?: unknown;
1550
+ query?: URLSearchParams;
1551
+ }): unknown;
1103
1552
  /**
1104
1553
  * Check the protocol version on an incoming request
1105
1554
  * ({@link MUSTS.VERSION_HANDSHAKE_REQUIRED}).
@@ -1144,6 +1593,8 @@ declare const Capability: z.ZodObject<{
1144
1593
  "llm.generate": "llm.generate";
1145
1594
  "llm.chat": "llm.chat";
1146
1595
  }>;
1596
+ service: z.ZodString;
1597
+ isDefault: z.ZodBoolean;
1147
1598
  backendId: z.ZodEnum<{
1148
1599
  ollama: "ollama";
1149
1600
  mlx: "mlx";
@@ -1163,6 +1614,7 @@ declare const Capability: z.ZodObject<{
1163
1614
  mistral: "mistral";
1164
1615
  "openai-http": "openai-http";
1165
1616
  "claude-cli": "claude-cli";
1617
+ "codex-cli": "codex-cli";
1166
1618
  }>;
1167
1619
  backendClass: z.ZodEnum<{
1168
1620
  http: "http";
@@ -1170,8 +1622,8 @@ declare const Capability: z.ZodObject<{
1170
1622
  }>;
1171
1623
  model: z.ZodString;
1172
1624
  offerScope: z.ZodEnum<{
1173
- self: "self";
1174
- named: "named";
1625
+ private: "private";
1626
+ team: "team";
1175
1627
  public: "public";
1176
1628
  }>;
1177
1629
  }, z.core.$strict>;
@@ -1182,6 +1634,8 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1182
1634
  "llm.generate": "llm.generate";
1183
1635
  "llm.chat": "llm.chat";
1184
1636
  }>;
1637
+ service: z.ZodString;
1638
+ isDefault: z.ZodBoolean;
1185
1639
  backendId: z.ZodEnum<{
1186
1640
  ollama: "ollama";
1187
1641
  mlx: "mlx";
@@ -1201,6 +1655,7 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1201
1655
  mistral: "mistral";
1202
1656
  "openai-http": "openai-http";
1203
1657
  "claude-cli": "claude-cli";
1658
+ "codex-cli": "codex-cli";
1204
1659
  }>;
1205
1660
  backendClass: z.ZodEnum<{
1206
1661
  http: "http";
@@ -1208,12 +1663,41 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1208
1663
  }>;
1209
1664
  model: z.ZodString;
1210
1665
  offerScope: z.ZodEnum<{
1211
- self: "self";
1212
- named: "named";
1666
+ private: "private";
1667
+ team: "team";
1213
1668
  public: "public";
1214
1669
  }>;
1215
1670
  }, z.core.$strict>>;
1216
1671
  type CapabilityMatrix = z.infer<typeof CapabilityMatrix>;
1672
+ /**
1673
+ * A kind this device could serve and deliberately does not — byollm_016.
1674
+ *
1675
+ * Two services answer one kind and the owner has not said which wins, so the
1676
+ * kind is not advertised. That is correct and, unsaid, invisible: the owner
1677
+ * adds a second service, jobs stop matching, and no surface explains it.
1678
+ *
1679
+ * It travels because the surfaces that must say so are not all on the device.
1680
+ * The owner's card names the claimants; a teammate's card says only that the
1681
+ * owner has a choice to make. Claimant **offer scopes** ride along so the hub
1682
+ * can compute that difference without the device deciding who is asking —
1683
+ * carry for computation, filter for display, the same shape the effective
1684
+ * offer already uses.
1685
+ */
1686
+ declare const WithheldKind: z.ZodObject<{
1687
+ kind: z.ZodEnum<{
1688
+ "llm.generate": "llm.generate";
1689
+ "llm.chat": "llm.chat";
1690
+ }>;
1691
+ claimants: z.ZodArray<z.ZodObject<{
1692
+ service: z.ZodString;
1693
+ offer: z.ZodEnum<{
1694
+ private: "private";
1695
+ team: "team";
1696
+ public: "public";
1697
+ }>;
1698
+ }, z.core.$strict>>;
1699
+ }, z.core.$strict>;
1700
+ type WithheldKind = z.infer<typeof WithheldKind>;
1217
1701
  /**
1218
1702
  * Pairing is a device-code exchange, not a pasted secret
1219
1703
  * ({@link MUSTS.PAIR_INTERACTIVE}). The daemon starts a pairing, shows the
@@ -1243,6 +1727,8 @@ declare const PairStartRequest: z.ZodObject<{
1243
1727
  "llm.generate": "llm.generate";
1244
1728
  "llm.chat": "llm.chat";
1245
1729
  }>;
1730
+ service: z.ZodString;
1731
+ isDefault: z.ZodBoolean;
1246
1732
  backendId: z.ZodEnum<{
1247
1733
  ollama: "ollama";
1248
1734
  mlx: "mlx";
@@ -1262,6 +1748,7 @@ declare const PairStartRequest: z.ZodObject<{
1262
1748
  mistral: "mistral";
1263
1749
  "openai-http": "openai-http";
1264
1750
  "claude-cli": "claude-cli";
1751
+ "codex-cli": "codex-cli";
1265
1752
  }>;
1266
1753
  backendClass: z.ZodEnum<{
1267
1754
  http: "http";
@@ -1269,8 +1756,8 @@ declare const PairStartRequest: z.ZodObject<{
1269
1756
  }>;
1270
1757
  model: z.ZodString;
1271
1758
  offerScope: z.ZodEnum<{
1272
- self: "self";
1273
- named: "named";
1759
+ private: "private";
1760
+ team: "team";
1274
1761
  public: "public";
1275
1762
  }>;
1276
1763
  }, z.core.$strict>>;
@@ -1298,15 +1785,14 @@ declare const PairPollResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
1298
1785
  status: z.ZodLiteral<"expired">;
1299
1786
  }, z.core.$strict>, z.ZodObject<{
1300
1787
  status: z.ZodLiteral<"approved">;
1301
- runnerToken: z.ZodString;
1302
1788
  runnerId: z.ZodString;
1303
1789
  owner: z.ZodString;
1304
1790
  ownerLabel: z.ZodOptional<z.ZodString>;
1305
- site: z.ZodObject<{
1791
+ sites: z.ZodRecord<z.ZodString, z.ZodObject<{
1306
1792
  identity: z.ZodString;
1307
1793
  encryption: z.ZodString;
1308
1794
  encryptionSig: z.ZodString;
1309
- }, z.core.$strict>;
1795
+ }, z.core.$strict>>;
1310
1796
  }, z.core.$strict>], "status">;
1311
1797
  type PairPollResponse = z.infer<typeof PairPollResponse>;
1312
1798
  declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -1331,6 +1817,8 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1331
1817
  "llm.generate": "llm.generate";
1332
1818
  "llm.chat": "llm.chat";
1333
1819
  }>;
1820
+ service: z.ZodString;
1821
+ isDefault: z.ZodBoolean;
1334
1822
  backendId: z.ZodEnum<{
1335
1823
  ollama: "ollama";
1336
1824
  mlx: "mlx";
@@ -1350,6 +1838,7 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1350
1838
  mistral: "mistral";
1351
1839
  "openai-http": "openai-http";
1352
1840
  "claude-cli": "claude-cli";
1841
+ "codex-cli": "codex-cli";
1353
1842
  }>;
1354
1843
  backendClass: z.ZodEnum<{
1355
1844
  http: "http";
@@ -1357,8 +1846,8 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1357
1846
  }>;
1358
1847
  model: z.ZodString;
1359
1848
  offerScope: z.ZodEnum<{
1360
- self: "self";
1361
- named: "named";
1849
+ private: "private";
1850
+ team: "team";
1362
1851
  public: "public";
1363
1852
  }>;
1364
1853
  }, z.core.$strict>>;
@@ -1376,6 +1865,8 @@ declare const ClaimRequest: z.ZodObject<{
1376
1865
  "llm.generate": "llm.generate";
1377
1866
  "llm.chat": "llm.chat";
1378
1867
  }>;
1868
+ service: z.ZodString;
1869
+ isDefault: z.ZodBoolean;
1379
1870
  backendId: z.ZodEnum<{
1380
1871
  ollama: "ollama";
1381
1872
  mlx: "mlx";
@@ -1395,6 +1886,7 @@ declare const ClaimRequest: z.ZodObject<{
1395
1886
  mistral: "mistral";
1396
1887
  "openai-http": "openai-http";
1397
1888
  "claude-cli": "claude-cli";
1889
+ "codex-cli": "codex-cli";
1398
1890
  }>;
1399
1891
  backendClass: z.ZodEnum<{
1400
1892
  http: "http";
@@ -1402,8 +1894,8 @@ declare const ClaimRequest: z.ZodObject<{
1402
1894
  }>;
1403
1895
  model: z.ZodString;
1404
1896
  offerScope: z.ZodEnum<{
1405
- self: "self";
1406
- named: "named";
1897
+ private: "private";
1898
+ team: "team";
1407
1899
  public: "public";
1408
1900
  }>;
1409
1901
  }, z.core.$strict>>;
@@ -1418,12 +1910,13 @@ declare const ClaimResponse: z.ZodObject<{
1418
1910
  "llm.chat": "llm.chat";
1419
1911
  }>;
1420
1912
  owner: z.ZodString;
1913
+ site: z.ZodString;
1421
1914
  audience: z.ZodEnum<{
1422
- self: "self";
1423
- named: "named";
1915
+ private: "private";
1916
+ team: "team";
1424
1917
  public: "public";
1425
1918
  }>;
1426
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
1919
+ service: z.ZodOptional<z.ZodString>;
1427
1920
  sizeClass: z.ZodEnum<{
1428
1921
  small: "small";
1429
1922
  medium: "medium";
@@ -1450,6 +1943,8 @@ declare const HeartbeatRequest: z.ZodObject<{
1450
1943
  "llm.generate": "llm.generate";
1451
1944
  "llm.chat": "llm.chat";
1452
1945
  }>;
1946
+ service: z.ZodString;
1947
+ isDefault: z.ZodBoolean;
1453
1948
  backendId: z.ZodEnum<{
1454
1949
  ollama: "ollama";
1455
1950
  mlx: "mlx";
@@ -1469,6 +1964,7 @@ declare const HeartbeatRequest: z.ZodObject<{
1469
1964
  mistral: "mistral";
1470
1965
  "openai-http": "openai-http";
1471
1966
  "claude-cli": "claude-cli";
1967
+ "codex-cli": "codex-cli";
1472
1968
  }>;
1473
1969
  backendClass: z.ZodEnum<{
1474
1970
  http: "http";
@@ -1476,11 +1972,25 @@ declare const HeartbeatRequest: z.ZodObject<{
1476
1972
  }>;
1477
1973
  model: z.ZodString;
1478
1974
  offerScope: z.ZodEnum<{
1479
- self: "self";
1480
- named: "named";
1975
+ private: "private";
1976
+ team: "team";
1481
1977
  public: "public";
1482
1978
  }>;
1483
1979
  }, z.core.$strict>>;
1980
+ withheld: z.ZodDefault<z.ZodArray<z.ZodObject<{
1981
+ kind: z.ZodEnum<{
1982
+ "llm.generate": "llm.generate";
1983
+ "llm.chat": "llm.chat";
1984
+ }>;
1985
+ claimants: z.ZodArray<z.ZodObject<{
1986
+ service: z.ZodString;
1987
+ offer: z.ZodEnum<{
1988
+ private: "private";
1989
+ team: "team";
1990
+ public: "public";
1991
+ }>;
1992
+ }, z.core.$strict>>;
1993
+ }, z.core.$strict>>>;
1484
1994
  activeLeases: z.ZodArray<z.ZodObject<{
1485
1995
  jobId: z.ZodString;
1486
1996
  leaseId: z.ZodString;
@@ -1489,14 +1999,32 @@ declare const HeartbeatRequest: z.ZodObject<{
1489
1999
  }, z.core.$strict>;
1490
2000
  type HeartbeatRequest = z.infer<typeof HeartbeatRequest>;
1491
2001
  declare const HeartbeatResponse: z.ZodObject<{
1492
- revoked: z.ZodBoolean;
1493
- cancel: z.ZodArray<z.ZodString>;
1494
- leases: z.ZodArray<z.ZodObject<{
2002
+ sites: z.ZodRecord<z.ZodString, z.ZodObject<{
2003
+ identity: z.ZodString;
2004
+ encryption: z.ZodString;
2005
+ encryptionSig: z.ZodString;
2006
+ }, z.core.$strict>>;
2007
+ successions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
2008
+ succeeds: z.ZodArray<z.ZodObject<{
2009
+ identity: z.ZodObject<{
2010
+ identity: z.ZodString;
2011
+ encryption: z.ZodString;
2012
+ encryptionSig: z.ZodString;
2013
+ }, z.core.$strict>;
2014
+ signature: z.ZodString;
2015
+ }, z.core.$strict>>;
2016
+ retiringUntil: z.ZodOptional<z.ZodNumber>;
2017
+ }, z.core.$strict>>>;
2018
+ cancel: z.ZodArray<z.ZodObject<{
1495
2019
  jobId: z.ZodString;
1496
- expiresAt: z.ZodNumber;
2020
+ leaseId: z.ZodString;
2021
+ }, z.core.$strict>>;
2022
+ lost: z.ZodArray<z.ZodObject<{
2023
+ jobId: z.ZodString;
2024
+ leaseId: z.ZodString;
1497
2025
  }, z.core.$strict>>;
1498
- lost: z.ZodArray<z.ZodString>;
1499
2026
  serverTime: z.ZodNumber;
2027
+ awaitingConsent: z.ZodArray<z.ZodString>;
1500
2028
  }, z.core.$strict>;
1501
2029
  type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;
1502
2030
  /**
@@ -1521,6 +2049,7 @@ declare const ResultRequest: z.ZodObject<{
1521
2049
  protocolVersion: z.ZodLiteral<"0">;
1522
2050
  runnerId: z.ZodString;
1523
2051
  jobId: z.ZodString;
2052
+ leaseId: z.ZodString;
1524
2053
  envelope: z.ZodObject<{
1525
2054
  ciphertext: z.ZodString;
1526
2055
  recipientKeyId: z.ZodString;
@@ -1536,16 +2065,11 @@ declare const ResultRequest: z.ZodObject<{
1536
2065
  error: "error";
1537
2066
  canceled: "canceled";
1538
2067
  }>;
1539
- model: z.ZodString;
1540
- backendClass: z.ZodEnum<{
1541
- http: "http";
1542
- process: "process";
1543
- }>;
1544
- durationMs: z.ZodNumber;
1545
2068
  }, z.core.$strict>;
1546
2069
  type ResultRequest = z.infer<typeof ResultRequest>;
1547
2070
  declare const ResultResponse: z.ZodObject<{
1548
2071
  accepted: z.ZodBoolean;
2072
+ duplicate: z.ZodOptional<z.ZodBoolean>;
1549
2073
  state: z.ZodString;
1550
2074
  }, z.core.$strict>;
1551
2075
  type ResultResponse = z.infer<typeof ResultResponse>;
@@ -1557,11 +2081,11 @@ declare const ReleaseRequest: z.ZodObject<{
1557
2081
  leaseId: z.ZodString;
1558
2082
  }, z.core.$strip>>;
1559
2083
  reason: z.ZodEnum<{
1560
- revoked: "revoked";
2084
+ refused: "refused";
1561
2085
  shutdown: "shutdown";
1562
2086
  pause: "pause";
2087
+ revoked: "revoked";
1563
2088
  "backend-down": "backend-down";
1564
- refused: "refused";
1565
2089
  }>;
1566
2090
  }, z.core.$strict>;
1567
2091
  type ReleaseRequest = z.infer<typeof ReleaseRequest>;
@@ -1582,7 +2106,11 @@ declare const WireErrorCode: z.ZodEnum<{
1582
2106
  revoked: "revoked";
1583
2107
  "bad-request": "bad-request";
1584
2108
  unauthorized: "unauthorized";
2109
+ forbidden: "forbidden";
1585
2110
  "not-found": "not-found";
2111
+ "not-ready": "not-ready";
2112
+ "too-late": "too-late";
2113
+ "clock-skew": "clock-skew";
1586
2114
  "rate-limited": "rate-limited";
1587
2115
  "server-error": "server-error";
1588
2116
  }>;
@@ -1593,18 +2121,26 @@ declare const WireError: z.ZodObject<{
1593
2121
  revoked: "revoked";
1594
2122
  "bad-request": "bad-request";
1595
2123
  unauthorized: "unauthorized";
2124
+ forbidden: "forbidden";
1596
2125
  "not-found": "not-found";
2126
+ "not-ready": "not-ready";
2127
+ "too-late": "too-late";
2128
+ "clock-skew": "clock-skew";
1597
2129
  "rate-limited": "rate-limited";
1598
2130
  "server-error": "server-error";
1599
2131
  }>;
1600
2132
  message: z.ZodString;
2133
+ supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
2134
+ minimum: z.ZodOptional<z.ZodString>;
1601
2135
  retryAfter: z.ZodOptional<z.ZodNumber>;
2136
+ serverTime: z.ZodOptional<z.ZodNumber>;
2137
+ maxSkewMs: z.ZodOptional<z.ZodNumber>;
1602
2138
  }, z.core.$strict>;
1603
2139
  type WireError = z.infer<typeof WireError>;
1604
2140
  /** HTTP status each error code is served with. */
1605
2141
  declare const ERROR_STATUS: Readonly<Record<WireErrorCode, number>>;
1606
2142
  declare const FetchRequest: z.ZodObject<{
1607
- protocolVersion: z.ZodString;
2143
+ protocolVersion: z.ZodLiteral<"0">;
1608
2144
  runnerId: z.ZodString;
1609
2145
  jobId: z.ZodString;
1610
2146
  leaseId: z.ZodString;
@@ -1624,4 +2160,4 @@ declare const FetchResponse: z.ZodObject<{
1624
2160
  }, z.core.$strict>;
1625
2161
  type FetchResponse = z.infer<typeof FetchResponse>;
1626
2162
 
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 };
2163
+ 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 };