@byollm/protocol 0.1.0-alpha.6 → 0.1.0-alpha.60

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
@@ -22,9 +22,9 @@ type BackendClass = z.infer<typeof BackendClass>;
22
22
  *
23
23
  * This replaced a two-valued `account` field that conflated two unrelated
24
24
  * constraints and, in doing so, left a hole: `openai-http` was "open", but it
25
- * accepts an API key, so an owner could point it at a paid endpoint, offer it
26
- * `public`, and donate their credit balance to strangers. The community
27
- * budgets cap job *count*, not spend.
25
+ * accepts an API key, so an owner could point it at a paid endpoint, share it,
26
+ * and donate their credit balance to strangers. The community budgets cap job
27
+ * *count*, not spend.
28
28
  *
29
29
  * - `free` — local compute. Costs electricity, not money. Shareable.
30
30
  * - `metered` — per-token billing against the owner's account. Legal to
@@ -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;
@@ -152,50 +165,158 @@ declare function backendDescriptor(id: BackendId): BackendDescriptor;
152
165
  * an act by the machine's owner against their own account, and the threat
153
166
  * model here is a hostile *job*, not an owner routing around a rule that
154
167
  * exists to protect them. What this catches is the accident — a remote paid
155
- * endpoint offered `public` because nobody thought about the bill. See
168
+ * endpoint offered to a team because nobody thought about the bill. See
156
169
  * `docs/security.md` §4a.
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,
212
+ /**
213
+ * **Required, and that is the fix.**
214
+ *
215
+ * This was optional, and the no-re-derivation law was breached through the
216
+ * gap rather than by anybody copying the logic. `byollm offer` passed two of
217
+ * three arguments and `resolveConfig` passed three, so the same service was
218
+ * free to one and metered to the other: `glm-5.2:cloud` on a loopback
219
+ * address looks local until you read the tag. The command wrote a share the
220
+ * daemon then refused, and told its owner to run the command they had just
221
+ * run.
222
+ *
223
+ * A shared rule's signature admits no partial askers. `undefined` is still a
224
+ * legal *value* — a service genuinely without a model — but it has to be
225
+ * passed, so choosing to omit the model is a decision at the call site
226
+ * rather than a default nobody notices.
227
+ */
228
+ model: string | undefined): BackendCost;
229
+ /**
230
+ * Why a service costs what it costs — the same decision, said out loud.
231
+ *
232
+ * Consent has to name the rule that fired. The offer ceremony read
233
+ * "Any OpenAI-compatible server ... bills your account per token", which is
234
+ * false about the type — an owner's local qwen is `openai-http` and costs
235
+ * nothing but electricity — and so it gave a reason that its reader could
236
+ * check and find wrong. The thing that bills is the `:cloud` tag on one
237
+ * model, not the transport that carries it.
238
+ *
239
+ * One function decides and one function explains, and the second calls the
240
+ * first, so a message can never describe a classification the code did not
241
+ * make. Splitting them would be the same defect this signature was just
242
+ * hardened against, arriving as prose.
243
+ */
244
+ /**
245
+ * The product's name alone, without the parenthetical that classifies it.
246
+ *
247
+ * Every label in this registry does two jobs: it names a product and says what
248
+ * that product means for the person paying — "Claude CLI (your subscription)",
249
+ * "Ollama (local)". That is right for a list, where the parenthetical is the
250
+ * only classification on screen.
251
+ *
252
+ * It is wrong inside a sentence that states the classification itself, which
253
+ * then stutters: "my-claude runs on Claude CLI (your subscription), a
254
+ * subscription whose terms…". Prose wants the name; the sentence around it is
255
+ * already carrying the meaning.
256
+ *
257
+ * One definition rather than a regex at each call site — and the place to
258
+ * change if the registry ever splits the two facts into two fields, which is
259
+ * the better shape and not worth a migration today.
260
+ */
261
+ declare function backendName(id: BackendId): string;
262
+ interface CostReason {
263
+ readonly cost: BackendCost;
264
+ /** The rule, in the words a person consenting needs. */
265
+ readonly because: string;
266
+ }
267
+ declare function classifyCost(id: BackendId, baseUrl: string | undefined, model: string | undefined): CostReason;
169
268
 
170
269
  /**
171
270
  * Who may run a job, declared by the app that enqueued it.
172
271
  *
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.
272
+ * - `private` — only the job owner's own devices.
273
+ * - `team` — a device whose owner admits this person.
274
+ *
275
+ * **One vocabulary, ruled 2026-08-24.** These were `self | named | public`
276
+ * while {@link OfferScope} used different words for the same idea, which would
277
+ * have left every seam where the two meet speaking two languages, and every
278
+ * doc explaining "self versus private" for ever. They are still independent
279
+ * axes — a job says who may run it, a service says who it will run for — and a
280
+ * job runs only where both agree ({@link MUSTS.AUDIENCE_BOTH_SIDES}).
281
+ *
282
+ * **`public` is gone, ruled 2026-08-26 (byollm_016).** Not deprecated,
283
+ * removed, and removed from the OSS daemon too rather than parked as a
284
+ * community posture. The argument was a measurement rather than a preference:
285
+ * device-side admission had never once been exercised end to end, because
286
+ * every cross-user test ran against a publicly offered service and
287
+ * {@link matchAudience} returned ALLOWED for those *without consulting the
288
+ * device at all*. `public` was the off switch for admission, and an enum with
289
+ * a value that skips verification is a fail-open waiting for the wiring bug
290
+ * that reaches it. There is now no such value.
177
291
  */
178
292
  declare const Audience: z.ZodEnum<{
179
- self: "self";
180
- named: "named";
181
- public: "public";
293
+ private: "private";
294
+ team: "team";
182
295
  }>;
183
296
  type Audience = z.infer<typeof Audience>;
184
297
  /**
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}).
298
+ * What a device's owner is willing to run for other people, per service.
299
+ *
300
+ * - `private` the owner's own work only.
301
+ * - `team` — whoever the owner's authority admits. Membership is **central**,
302
+ * not per-person: the device follows what it is told by a signature it can
303
+ * check, rather than holding its own copy of who is in it (byollm_016).
304
+ *
305
+ * Two values, and no third that means "everyone". See {@link Audience} for
306
+ * why `public` was removed rather than parked, and note the shape of the
307
+ * remaining enum: **every value left requires the device to verify
308
+ * something.** `private` checks the owner; `team` checks admission. That is
309
+ * the property, not an accident of there being two.
188
310
  */
189
311
  declare const OfferScope: z.ZodEnum<{
190
- self: "self";
191
- named: "named";
192
- public: "public";
312
+ private: "private";
313
+ team: "team";
193
314
  }>;
194
315
  type OfferScope = z.infer<typeof OfferScope>;
195
316
  /** All audience values, in widening order. */
196
- declare const AUDIENCES: readonly ("self" | "named" | "public")[];
317
+ declare const AUDIENCES: readonly ("private" | "team")[];
197
318
  /** All offer scopes, in widening order. */
198
- declare const OFFER_SCOPES: readonly ("self" | "named" | "public")[];
319
+ declare const OFFER_SCOPES: readonly ("private" | "team")[];
199
320
  /**
200
321
  * Why a job was refused. Distinct codes because byollm_002 requires that
201
322
  * different truths never share a message — "no matching work" and "refused on
@@ -234,9 +355,9 @@ interface SpendConsent {
234
355
  * its matcher call, so no code path can observe a scope wider than the cost
235
356
  * class allows:
236
357
  *
237
- * - `subscription` is locked to `self` regardless of config
358
+ * - `subscription` is locked to `private` regardless of config
238
359
  * ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}) — someone else's terms.
239
- * - `metered` narrows to `self` unless the owner has explicitly acknowledged
360
+ * - `metered` narrows to `private` unless the owner has explicitly acknowledged
240
361
  * the spend ({@link MUSTS.METERED_DEFAULTS_SELF}) — their money.
241
362
  * - `free` passes through — their electricity.
242
363
  *
@@ -252,7 +373,8 @@ interface MatchJob {
252
373
  readonly audience: Audience;
253
374
  /**
254
375
  * Optional server-side restriction on which runner owners may take a
255
- * `named` job. Defence in depth only the daemon's local allowlist is the
376
+ * `team` job. Defence in depth only, and direct-mode only it never
377
+ * reaches a daemon (cloud_008 §0.2), so the device's own admission is the
256
378
  * enforcing side ({@link MUSTS.NAMED_LOCAL_ALLOWLIST}).
257
379
  */
258
380
  readonly audienceAllow?: readonly string[] | undefined;
@@ -268,13 +390,24 @@ interface MatchDaemon {
268
390
  /** What the owner agreed to spend on others, for a `metered` backend. */
269
391
  readonly spend?: SpendConsent | undefined;
270
392
  /**
271
- * Does this daemon's *local* allowlist admit the given owner for the server
272
- * origin the job came from? Supplied as a predicate so the protocol package
273
- * stays free of file I/O; the daemon passes its allowlist, the server
274
- * passes a conservative `() => true` because it cannot know a remote
275
- * daemon's local list and must not pretend to.
393
+ * Has something **this device verified** admitted the job's owner?
394
+ *
395
+ * A predicate rather than a value so the protocol package stays free of
396
+ * both file I/O and signature state. What supplies it has changed twice and
397
+ * will change again — a local allowlist, then a held roster, and now a
398
+ * claim-time signed grant (Amendment J) — and the law it feeds has not
399
+ * changed at all: a `team` service runs a stranger's work only when
400
+ * somebody this device can check said so.
401
+ *
402
+ * The server passes a conservative `() => true`: it cannot know what a
403
+ * remote device verified and must not pretend to. The device is the
404
+ * enforcing side, which is the whole point of asking here.
405
+ *
406
+ * Named for the question, not for where the answer lives. This was called
407
+ * `locallyAllows`, and "locally" stopped being true the moment the answer
408
+ * came from a document somebody else signed.
276
409
  */
277
- readonly locallyAllows: (owner: string) => boolean;
410
+ readonly admits: (owner: string) => boolean;
278
411
  }
279
412
  /**
280
413
  * Decide whether a job may run on a daemon.
@@ -283,20 +416,20 @@ interface MatchDaemon {
283
416
  * 1. the job's audience must admit the daemon's owner, and
284
417
  * 2. the backend's offer scope must admit the job's owner.
285
418
  *
286
- * The full nine-way matrix (three audiences × three offer scopes) is asserted
287
- * by the conformance kit. The function is pure and total so both the daemon
419
+ * The full four-way matrix (two audiences × two offer scopes) is asserted by
420
+ * the conformance kit. The function is pure and total so both the daemon
288
421
  * and the server can run the identical rule — the daemon refuses, and the
289
422
  * server refuses too (byollm_003 §Server-side MUSTs).
290
423
  *
291
424
  * @example
292
425
  * ```ts
293
426
  * const result = matchAudience(
294
- * { owner: "alice", audience: "named" },
427
+ * { owner: "alice", audience: "team" },
295
428
  * {
296
429
  * owner: "bob",
297
- * offerScope: "named",
430
+ * offerScope: "team",
298
431
  * cost: "free",
299
- * locallyAllows: (o) => o === "alice",
432
+ * admits: (o) => o === "alice",
300
433
  * },
301
434
  * );
302
435
  * // result.ok === true
@@ -314,9 +447,16 @@ declare const REFUSAL_MESSAGES: Readonly<Record<MatchRefusal, string>>;
314
447
  * Upper bounds on payload size, enforced at the schema so oversized input is
315
448
  * refused at parse time rather than somewhere deeper.
316
449
  *
317
- * byollm_004 §4 requires stricter limits for community (`named`/`public`)
318
- * jobs; those are applied on top of these by the daemon's budget check, which
319
- * knows the job's audience. These are the absolute ceilings for any job.
450
+ * All three are enforced cloud_008 Tier 4, finding 30. `maxTotalChars` was
451
+ * declared here and referenced nowhere, under this docstring's claim that the
452
+ * schema enforces them, so a chat payload of 256 messages at a million
453
+ * characters each parsed cleanly at sixty-four times the stated ceiling. The
454
+ * per-field limits were real and the aggregate one was a number in a frozen
455
+ * object.
456
+ *
457
+ * byollm_004 §4 requires stricter limits for community (`team`) jobs; those
458
+ * are applied on top of these by the daemon's budget check, which knows the
459
+ * job's audience. These are the absolute ceilings for any job.
320
460
  */
321
461
  declare const PAYLOAD_LIMITS: Readonly<{
322
462
  /** Max characters in any single text field. */
@@ -337,7 +477,7 @@ declare const ChatMessage: z.ZodObject<{
337
477
  assistant: "assistant";
338
478
  }>;
339
479
  content: z.ZodString;
340
- }, z.core.$strip>;
480
+ }, z.core.$strict>;
341
481
  type ChatMessage = z.infer<typeof ChatMessage>;
342
482
  /**
343
483
  * Payload for `llm.generate`.
@@ -364,7 +504,7 @@ declare const ChatPayload: z.ZodObject<{
364
504
  assistant: "assistant";
365
505
  }>;
366
506
  content: z.ZodString;
367
- }, z.core.$strip>>;
507
+ }, z.core.$strict>>;
368
508
  system: z.ZodOptional<z.ZodString>;
369
509
  }, z.core.$strict>;
370
510
  type ChatPayload = z.infer<typeof ChatPayload>;
@@ -391,7 +531,7 @@ declare const KindedPayload: z.ZodDiscriminatedUnion<[z.ZodObject<{
391
531
  prompt: z.ZodString;
392
532
  system: z.ZodOptional<z.ZodString>;
393
533
  }, z.core.$strict>;
394
- }, z.core.$strip>, z.ZodObject<{
534
+ }, z.core.$strict>, z.ZodObject<{
395
535
  kind: z.ZodLiteral<"llm.chat">;
396
536
  payload: z.ZodObject<{
397
537
  messages: z.ZodArray<z.ZodObject<{
@@ -401,10 +541,10 @@ declare const KindedPayload: z.ZodDiscriminatedUnion<[z.ZodObject<{
401
541
  assistant: "assistant";
402
542
  }>;
403
543
  content: z.ZodString;
404
- }, z.core.$strip>>;
544
+ }, z.core.$strict>>;
405
545
  system: z.ZodOptional<z.ZodString>;
406
546
  }, z.core.$strict>;
407
- }, z.core.$strip>], "kind">;
547
+ }, z.core.$strict>], "kind">;
408
548
  type KindedPayload = z.infer<typeof KindedPayload>;
409
549
  /** The payload type for a given kind. */
410
550
  type PayloadFor<K extends JobKind> = K extends "llm.generate" ? GeneratePayload : ChatPayload;
@@ -433,11 +573,11 @@ declare function payloadTextLength(kinded: KindedPayload): number;
433
573
  declare const JobState: z.ZodEnum<{
434
574
  ok: "ok";
435
575
  error: "error";
576
+ expired: "expired";
436
577
  queued: "queued";
437
578
  claimed: "claimed";
438
579
  running: "running";
439
580
  canceled: "canceled";
440
- expired: "expired";
441
581
  }>;
442
582
  type JobState = z.infer<typeof JobState>;
443
583
  /** States from which a job never moves again. */
@@ -451,7 +591,7 @@ declare const Lease: z.ZodObject<{
451
591
  id: z.ZodString;
452
592
  runnerId: z.ZodString;
453
593
  expiresAt: z.ZodNumber;
454
- }, z.core.$strip>;
594
+ }, z.core.$strict>;
455
595
  type Lease = z.infer<typeof Lease>;
456
596
  /** Payload union as it appears on a job record. */
457
597
  declare const JobPayload: z.ZodUnion<readonly [z.ZodObject<{
@@ -465,7 +605,7 @@ declare const JobPayload: z.ZodUnion<readonly [z.ZodObject<{
465
605
  assistant: "assistant";
466
606
  }>;
467
607
  content: z.ZodString;
468
- }, z.core.$strip>>;
608
+ }, z.core.$strict>>;
469
609
  system: z.ZodOptional<z.ZodString>;
470
610
  }, z.core.$strict>]>;
471
611
  type JobPayload = z.infer<typeof JobPayload>;
@@ -494,35 +634,34 @@ declare const ClaimedJob: z.ZodObject<{
494
634
  assistant: "assistant";
495
635
  }>;
496
636
  content: z.ZodString;
497
- }, z.core.$strip>>;
637
+ }, z.core.$strict>>;
498
638
  system: z.ZodOptional<z.ZodString>;
499
639
  }, z.core.$strict>]>;
500
640
  audience: z.ZodEnum<{
501
- self: "self";
502
- named: "named";
503
- public: "public";
641
+ private: "private";
642
+ team: "team";
504
643
  }>;
505
644
  owner: z.ZodString;
506
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
645
+ site: z.ZodOptional<z.ZodString>;
646
+ service: z.ZodOptional<z.ZodString>;
507
647
  lease: z.ZodObject<{
508
648
  id: z.ZodString;
509
649
  runnerId: z.ZodString;
510
650
  expiresAt: z.ZodNumber;
511
- }, z.core.$strip>;
651
+ }, z.core.$strict>;
512
652
  }, z.core.$strict>;
513
653
  type ClaimedJob = z.infer<typeof ClaimedJob>;
514
654
  /**
515
655
  * The provenance that travels with every result to the delivery seam.
516
656
  *
517
- * byollm_003 Rev 1: a `named`/`public` result is attacker-controlled text.
657
+ * byollm_003 Rev 1: a `team` result is attacker-controlled text.
518
658
  * 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}).
659
+ * knowing that is what it is ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).
520
660
  */
521
661
  declare const ResultProvenance: z.ZodObject<{
522
662
  audience: z.ZodEnum<{
523
- self: "self";
524
- named: "named";
525
- public: "public";
663
+ private: "private";
664
+ team: "team";
526
665
  }>;
527
666
  runnerId: z.ZodString;
528
667
  runnerOwner: z.ZodString;
@@ -545,6 +684,30 @@ declare function provenanceFor(input: {
545
684
  backendClass: BackendClass;
546
685
  model: string;
547
686
  }): ResultProvenance;
687
+ /**
688
+ * What the daemon did, sealed with the answer — cloud_008 §2.5.
689
+ *
690
+ * These travelled in the clear on `ResultRequest`, which meant two things at
691
+ * once. On the direct plane the site believed unauthenticated fields beside
692
+ * an authenticated envelope — a daemon could seal one answer and *declare* it
693
+ * came from a different model, and only the field it did not sign would be
694
+ * recorded. Through a relay they reached a third party that acts on none of
695
+ * them, and `model` in particular is the kind of detail Amendment A's rule
696
+ * keeps off the wire.
697
+ *
698
+ * Sealed, they are the daemon's signed statement about its own run: the site
699
+ * opens them, nothing in between sees them, and the disposition check that
700
+ * already compares clear-text against ciphertext extends to cover them.
701
+ */
702
+ declare const RunMetadata: z.ZodObject<{
703
+ model: z.ZodString;
704
+ backendClass: z.ZodEnum<{
705
+ http: "http";
706
+ process: "process";
707
+ }>;
708
+ durationMs: z.ZodNumber;
709
+ }, z.core.$strict>;
710
+ type RunMetadata = z.infer<typeof RunMetadata>;
548
711
  /** Successful outcome. */
549
712
  declare const JobResultOk: z.ZodObject<{
550
713
  outcome: z.ZodLiteral<"ok">;
@@ -575,17 +738,98 @@ declare const JobOutcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
575
738
  outcome: z.ZodLiteral<"canceled">;
576
739
  }, z.core.$strict>], "outcome">;
577
740
  type JobOutcome = z.infer<typeof JobOutcome>;
741
+ /**
742
+ * Why a job can never run — byollm_016 Phase B.
743
+ *
744
+ * Every one of these is **terminal**, and that is the whole point of naming
745
+ * them. A job that cannot be matched used to sit queued until its deadline,
746
+ * which reads exactly like a job that is merely waiting for a device to come
747
+ * online — so an app could not tell "any moment now" from "never", and neither
748
+ * could the person watching a spinner. Silence must never read as pending.
749
+ *
750
+ * They are decided by whoever knows first: the site's own SDK where it can see
751
+ * the answer without asking, the router where matching happens, and the daemon
752
+ * again on arrival under the both-sides rule. All three reason from the same
753
+ * list rather than three private vocabularies.
754
+ */
755
+ declare const RefusalReason: z.ZodEnum<{
756
+ "default-ambiguity": "default-ambiguity";
757
+ "default-unusable": "default-unusable";
758
+ }>;
759
+ type RefusalReason = z.infer<typeof RefusalReason>;
760
+ /**
761
+ * A terminal outcome nobody sealed — byollm_016 Phase B.
762
+ *
763
+ * Every other finished job carries an envelope encrypted by the device that
764
+ * ran it, which is what makes a result unforgeable. These have no device: the
765
+ * job was refused *before* anything could run it, so there is nobody to seal
766
+ * from and no content to seal.
767
+ *
768
+ * **What that costs, stated plainly.** This is the one terminal outcome a
769
+ * router can author. It is worth being exact about the power that grants,
770
+ * because "the relay can write this" sounds alarming until you compare it with
771
+ * what a relay could already do: drop the job, never offer it, and let it
772
+ * expire. A router-authored refusal is *denial of service by a shorter route*,
773
+ * which is a power the router has always had and which the trust model has
774
+ * always said it has. What it emphatically is **not** is forgery: this shape
775
+ * carries no envelope and no output, so it can never be mistaken for an answer
776
+ * a device produced. A relay still cannot fabricate a result, because that
777
+ * needs a signature it does not hold.
778
+ *
779
+ * So the rule this shape enforces by construction: a refusal may deny, and may
780
+ * never assert. Anything that claims work was *done* still comes sealed.
781
+ */
782
+ declare const JobRefused: z.ZodObject<{
783
+ outcome: z.ZodLiteral<"refused">;
784
+ reason: z.ZodEnum<{
785
+ "default-ambiguity": "default-ambiguity";
786
+ "default-unusable": "default-unusable";
787
+ }>;
788
+ message: z.ZodString;
789
+ }, z.core.$strict>;
790
+ type JobRefused = z.infer<typeof JobRefused>;
791
+ /**
792
+ * The plaintext inside a result envelope.
793
+ *
794
+ * The outcome and how it was produced, together, because they are one
795
+ * statement by one signer. A site that opened only the outcome would be
796
+ * trusting the envelope for the answer and the request body for everything
797
+ * about it.
798
+ */
799
+ declare const SealedOutcome: z.ZodObject<{
800
+ outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
801
+ outcome: z.ZodLiteral<"ok">;
802
+ text: z.ZodString;
803
+ artifactUrl: z.ZodOptional<z.ZodURL>;
804
+ }, z.core.$strict>, z.ZodObject<{
805
+ outcome: z.ZodLiteral<"error">;
806
+ code: z.ZodString;
807
+ message: z.ZodString;
808
+ retryable: z.ZodBoolean;
809
+ }, z.core.$strict>, z.ZodObject<{
810
+ outcome: z.ZodLiteral<"canceled">;
811
+ }, z.core.$strict>], "outcome">;
812
+ ran: z.ZodObject<{
813
+ model: z.ZodString;
814
+ backendClass: z.ZodEnum<{
815
+ http: "http";
816
+ process: "process";
817
+ }>;
818
+ durationMs: z.ZodNumber;
819
+ }, z.core.$strict>;
820
+ }, z.core.$strict>;
821
+ type SealedOutcome = z.infer<typeof SealedOutcome>;
578
822
  /** A completed job as delivered to the app, provenance attached. */
579
823
  declare const DeliveredResult: z.ZodObject<{
580
824
  jobId: z.ZodString;
581
825
  state: z.ZodEnum<{
582
826
  ok: "ok";
583
827
  error: "error";
828
+ expired: "expired";
584
829
  queued: "queued";
585
830
  claimed: "claimed";
586
831
  running: "running";
587
832
  canceled: "canceled";
588
- expired: "expired";
589
833
  }>;
590
834
  outcome: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
591
835
  outcome: z.ZodLiteral<"ok">;
@@ -601,9 +845,8 @@ declare const DeliveredResult: z.ZodObject<{
601
845
  }, z.core.$strict>], "outcome">>;
602
846
  provenance: z.ZodOptional<z.ZodObject<{
603
847
  audience: z.ZodEnum<{
604
- self: "self";
605
- named: "named";
606
- public: "public";
848
+ private: "private";
849
+ team: "team";
607
850
  }>;
608
851
  runnerId: z.ZodString;
609
852
  runnerOwner: z.ZodString;
@@ -614,6 +857,7 @@ declare const DeliveredResult: z.ZodObject<{
614
857
  model: z.ZodString;
615
858
  untrusted: z.ZodBoolean;
616
859
  }, z.core.$strict>>;
860
+ fallback: z.ZodOptional<z.ZodLiteral<true>>;
617
861
  }, z.core.$strict>;
618
862
  type DeliveredResult = z.infer<typeof DeliveredResult>;
619
863
  /**
@@ -677,12 +921,12 @@ declare const JobStub: z.ZodObject<{
677
921
  "llm.chat": "llm.chat";
678
922
  }>;
679
923
  owner: z.ZodString;
924
+ site: z.ZodString;
680
925
  audience: z.ZodEnum<{
681
- self: "self";
682
- named: "named";
683
- public: "public";
926
+ private: "private";
927
+ team: "team";
684
928
  }>;
685
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
929
+ purpose: z.ZodOptional<z.ZodString>;
686
930
  sizeClass: z.ZodEnum<{
687
931
  small: "small";
688
932
  medium: "medium";
@@ -693,7 +937,23 @@ declare const JobStub: z.ZodObject<{
693
937
  deadlineAt: z.ZodNumber;
694
938
  }, z.core.$strict>;
695
939
  type JobStub = z.infer<typeof JobStub>;
696
- /** A stub, plus the lease the claiming runner now holds for it. */
940
+ /**
941
+ * A stub, plus the lease the claiming runner now holds for it — and, on a
942
+ * relayed route, the grant that says it may run at all.
943
+ *
944
+ * The grant lives here rather than on {@link JobStub} because of *when* it is
945
+ * authored. A stub exists from enqueue; a grant is written at claim, against
946
+ * the membership and mapping true at that moment. That timing is the whole of
947
+ * Amendment J: a job queued yesterday for somebody removed this morning gets
948
+ * no grant when it is finally claimed, and a roster held on the device could
949
+ * never have known.
950
+ *
951
+ * Optional, and the absence is meaningful rather than lenient. A device that
952
+ * pinned a control-plane key at pairing **requires** one — a claimed job
953
+ * arriving without it is refused, not admitted by default. A device that
954
+ * pinned none is in direct mode, where there is no control plane to author
955
+ * anything and the owner's own work is the only work that runs.
956
+ */
697
957
  declare const ClaimedStub: z.ZodObject<{
698
958
  id: z.ZodString;
699
959
  kind: z.ZodEnum<{
@@ -701,12 +961,12 @@ declare const ClaimedStub: z.ZodObject<{
701
961
  "llm.chat": "llm.chat";
702
962
  }>;
703
963
  owner: z.ZodString;
964
+ site: z.ZodString;
704
965
  audience: z.ZodEnum<{
705
- self: "self";
706
- named: "named";
707
- public: "public";
966
+ private: "private";
967
+ team: "team";
708
968
  }>;
709
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
969
+ purpose: z.ZodOptional<z.ZodString>;
710
970
  sizeClass: z.ZodEnum<{
711
971
  small: "small";
712
972
  medium: "medium";
@@ -719,7 +979,19 @@ declare const ClaimedStub: z.ZodObject<{
719
979
  id: z.ZodString;
720
980
  runnerId: z.ZodString;
721
981
  expiresAt: z.ZodNumber;
722
- }, z.core.$strip>;
982
+ }, z.core.$strict>;
983
+ grant: z.ZodOptional<z.ZodObject<{
984
+ grantId: z.ZodString;
985
+ jobId: z.ZodString;
986
+ site: z.ZodString;
987
+ user: z.ZodString;
988
+ owner: z.ZodString;
989
+ purpose: z.ZodString;
990
+ kind: z.ZodString;
991
+ service: z.ZodString;
992
+ issuedAt: z.ZodNumber;
993
+ signature: z.ZodString;
994
+ }, z.core.$strict>>;
723
995
  }, z.core.$strict>;
724
996
  type ClaimedStub = z.infer<typeof ClaimedStub>;
725
997
 
@@ -763,6 +1035,18 @@ declare const StoredKeys: z.ZodObject<{
763
1035
  createdAt: z.ZodNumber;
764
1036
  }, z.core.$strict>;
765
1037
  type StoredKeys = z.infer<typeof StoredKeys>;
1038
+ /** Domain separator, so a signature over an encryption key cannot be
1039
+ * replayed as a signature over anything else. */
1040
+ /**
1041
+ * What an encryption key's signature covers.
1042
+ *
1043
+ * Exported because a rotation is a real event this protocol has to be able to
1044
+ * *test* — a record whose encryption key moved under an identity that signed
1045
+ * the move is the one case pinning must refuse loudly, and building one
1046
+ * outside this file otherwise means re-typing this string, which is how two
1047
+ * copies of a constant start disagreeing.
1048
+ */
1049
+ declare const ENCRYPTION_KEY_CONTEXT = "byollm/v1/encryption-key";
766
1050
  /** Generate a fresh pair of keypairs and bind them together. */
767
1051
  declare function generateKeys(now: number): StoredKeys;
768
1052
  /** The public half, for the wire. */
@@ -777,7 +1061,17 @@ declare function publicIdentityOf(keys: StoredKeys): PublicIdentity;
777
1061
  */
778
1062
  declare function verifyPublicIdentity(identity: PublicIdentity): boolean;
779
1063
  /** Sign arbitrary bytes with an identity key. */
780
- declare function signWith(keys: StoredKeys, data: Uint8Array): string;
1064
+ /**
1065
+ * Sign bytes with an identity key.
1066
+ *
1067
+ * Takes only the private half it uses. A signer that demanded a whole
1068
+ * {@link StoredKeys} would make every caller hold an encryption keypair for a
1069
+ * job that has no encryption in it — and the control plane, which signs
1070
+ * rosters and opens nothing, would be generating and storing secret material
1071
+ * it can never need. Every existing caller passes a full `StoredKeys`, which
1072
+ * satisfies this.
1073
+ */
1074
+ declare function signWith(keys: Pick<StoredKeys, "identityPrivate">, data: Uint8Array): string;
781
1075
  /** Verify bytes against a raw Ed25519 public key. */
782
1076
  declare function verifyWith(identityPublic: string, data: Uint8Array, signature: string): boolean;
783
1077
  /**
@@ -946,7 +1240,77 @@ declare function signRequest(keys: StoredKeys, input: {
946
1240
  issuedAt: number;
947
1241
  body: string;
948
1242
  }): RequestSignature;
949
- /** Why a signed request was refused. Never returned to the caller verbatim. */
1243
+ /**
1244
+ * The same scheme, for the party at the other end: a **site** calling a relay.
1245
+ *
1246
+ * A site talking to a relay is in exactly the daemon's position — an outbound
1247
+ * caller with an identity keypair the other side already pins — so it gets the
1248
+ * daemon's authentication rather than a second scheme. Bearer tokens for the
1249
+ * site plane were the alternative, and they would have reintroduced the
1250
+ * credential-in-a-file that §4.2 removed from the daemon plane, on the plane
1251
+ * that carries *every* site's traffic.
1252
+ *
1253
+ * Two things make this safe to build on the same canonical string:
1254
+ *
1255
+ * 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never
1256
+ * `enqueue`. The daemon plane's `result` and the site plane's `results` are
1257
+ * one character apart, and a naming collision between planes must not be
1258
+ * what stands between a signature and a replay onto the wrong handler. The
1259
+ * prefix is applied *inside* these helpers, so the two ends cannot disagree
1260
+ * about it — the alternative is two implementations of one bound value,
1261
+ * which is this project's most-repeated bug.
1262
+ * 2. **The caller slot carries the site id.** `canonicalRequest` names that
1263
+ * field `runnerId` because the daemon plane got there first; here it holds
1264
+ * the site id, and the verifier looks the key up in the projection's site
1265
+ * registry rather than its device registry. The two registries never share
1266
+ * an entry, so a device signature cannot authenticate as a site.
1267
+ *
1268
+ * §4.2's replay argument carries over **only because the site plane's writes
1269
+ * are idempotent per addressed instance**, which is a property that had to be
1270
+ * built rather than found: `enqueue` reset a job of the same id, so a replayed
1271
+ * enqueue inside the freshness window returned a claimed job to the queue and
1272
+ * threw away a device's live lease. Identical in shape to the `release` bug
1273
+ * above, on the other plane. Anything added to the site plane later must be
1274
+ * idempotent by the instance it names, or this scheme does not cover it.
1275
+ */
1276
+ declare function signSiteRequest(keys: StoredKeys, input: {
1277
+ endpoint: string;
1278
+ siteId: string;
1279
+ issuedAt: number;
1280
+ body: string;
1281
+ }): RequestSignature;
1282
+ /** Verify a site's call against the identity the control plane registered. */
1283
+ declare function verifySiteRequest(input: {
1284
+ identityPublic: string;
1285
+ endpoint: string;
1286
+ body: string;
1287
+ signature: RequestSignature;
1288
+ now: number;
1289
+ maxSkewMs?: number;
1290
+ }): SignatureFailure | null;
1291
+ /**
1292
+ * Why a signed request was refused.
1293
+ *
1294
+ * **`bad-signature` is never returned verbatim; `stale` is, deliberately.**
1295
+ * They are different kinds of refusal and conflating them costs a real user
1296
+ * more than it costs an attacker.
1297
+ *
1298
+ * A bad signature is an authentication failure and the server says only
1299
+ * "unauthorized" — telling a prober which part they got wrong is free help.
1300
+ *
1301
+ * A stale timestamp is a **precondition** failure: the signature may be
1302
+ * perfectly valid and the caller's clock is simply wrong. Saying so reveals
1303
+ * nothing, for two reasons that both have to hold. The server's time is
1304
+ * already public — every response carries a `Date` header and the heartbeat
1305
+ * response returns `serverTime` outright. And freshness is checked *before*
1306
+ * the signature is verified, so a stale answer says nothing about whether the
1307
+ * signature was any good.
1308
+ *
1309
+ * What conflating them costs: a machine whose clock has drifted gets
1310
+ * `401 unauthorized` on every request, forever, with nothing anywhere pointing
1311
+ * at the clock. That is the shape byollm_013 was filed about — a refusal that
1312
+ * is correct, silent, and sends somebody to read our source.
1313
+ */
950
1314
  type SignatureFailure = "stale" | "bad-signature";
951
1315
  /**
952
1316
  * Verify a signed request against a runner's pinned identity key.
@@ -964,6 +1328,399 @@ declare function verifyRequest(input: {
964
1328
  maxSkewMs?: number;
965
1329
  }): SignatureFailure | null;
966
1330
 
1331
+ /**
1332
+ * What a site says it needs — byollm_016 Amendment L.
1333
+ *
1334
+ * A site declares **purposes**, and each purpose lists the job kinds it uses.
1335
+ * A person then maps each purpose to one of their own services, on the consent
1336
+ * screen, and that mapping *is* the consent. The control plane joins the two
1337
+ * at claim time and signs the result into a grant.
1338
+ *
1339
+ * ## Why a site declares needs instead of naming services
1340
+ *
1341
+ * Because it cannot name one. The site's vocabulary is its own purposes; the
1342
+ * person's vocabulary is their services; and the two never meet. A site asks
1343
+ * for "writing assistant, llm.chat" and learns only whether that slot is
1344
+ * satisfiable — never which model answered, never whose machine, never even
1345
+ * the name of the service. Key-vs-value reaches its strongest form here: the
1346
+ * site cannot describe what it wants *or* name it, only ask for what it
1347
+ * declared.
1348
+ *
1349
+ * ## Keys are ids; labels are prose
1350
+ *
1351
+ * They are separate fields and nothing derives one from the other, which is
1352
+ * the amendment's ruling and worth restating where somebody will read it. A
1353
+ * key travels on every job and is what mappings are stored against, so it is
1354
+ * stable-or-nothing: renaming one deletes a purpose and creates another,
1355
+ * unmapping everybody who had chosen for it. A label is changeable whenever
1356
+ * the site likes and is the **only** thing a consent screen renders.
1357
+ */
1358
+ /**
1359
+ * The purpose a site gets when it declares no purposes of its own.
1360
+ *
1361
+ * Reserved, and refused by {@link Manifest} rather than by whatever handles
1362
+ * registration. A site with a single undifferentiated use has one purpose —
1363
+ * everything it does — and that purpose needs an id because mappings are
1364
+ * keyed by one. An id taken from the site's own vocabulary would collide the
1365
+ * day it declared a real purpose of the same name.
1366
+ *
1367
+ * **Never rendered.** "default → your Claude" tells a person nothing; a
1368
+ * consent screen shows the site's own name for this slot, because that is
1369
+ * what a single-purpose site's one purpose actually is.
1370
+ */
1371
+ declare const RESERVED_PURPOSE = "default";
1372
+ declare const Purpose: z.ZodObject<{
1373
+ label: z.ZodString;
1374
+ description: z.ZodOptional<z.ZodString>;
1375
+ kinds: z.ZodArray<z.ZodEnum<{
1376
+ "llm.generate": "llm.generate";
1377
+ "llm.chat": "llm.chat";
1378
+ }>>;
1379
+ }, z.core.$strict>;
1380
+ type Purpose = z.infer<typeof Purpose>;
1381
+ /**
1382
+ * Everything a site needs, by purpose key.
1383
+ *
1384
+ * At least one purpose: a site that declares none is a site that can enqueue
1385
+ * nothing, and accepting it would mean the first refusal a person saw came
1386
+ * from a job rather than from registration.
1387
+ */
1388
+ /**
1389
+ * How many purposes one site may declare.
1390
+ *
1391
+ * There was no bound at all: a site could declare fifty thousand, each one
1392
+ * individually valid, and the consent screen renders a slot per (purpose,
1393
+ * kind) — so the page that *is* the consent mechanism becomes unusable, and
1394
+ * the notification mail that enumerates slots grows with it.
1395
+ *
1396
+ * Thirty-two is chosen rather than derived, and the number is an argument: a
1397
+ * purpose is a thing a person reads and decides about one at a time, and a
1398
+ * screen asking more than about thirty separate questions has stopped being a
1399
+ * consent screen whatever it renders. Of Tomorrow Press declares five. A site
1400
+ * that genuinely needs more has a product question to answer before it has a
1401
+ * schema one.
1402
+ */
1403
+ declare const MAX_PURPOSES = 32;
1404
+ declare const Manifest: z.ZodRecord<z.ZodString, z.ZodObject<{
1405
+ label: z.ZodString;
1406
+ description: z.ZodOptional<z.ZodString>;
1407
+ kinds: z.ZodArray<z.ZodEnum<{
1408
+ "llm.generate": "llm.generate";
1409
+ "llm.chat": "llm.chat";
1410
+ }>>;
1411
+ }, z.core.$strict>>;
1412
+ type Manifest = z.infer<typeof Manifest>;
1413
+ /**
1414
+ * The manifest a site with no declared purposes is treated as having.
1415
+ *
1416
+ * The sugar in Amendment L, made explicit rather than special-cased
1417
+ * downstream: everything after this point sees a manifest with one purpose,
1418
+ * so no consent screen, mapping table or resolver needs a branch for the
1419
+ * flat-list case.
1420
+ *
1421
+ * The label is the caller's — a site's own name — because it is the one thing
1422
+ * that can make "everything this site does" read as a sentence about a
1423
+ * particular site rather than about software in general.
1424
+ */
1425
+ declare function singlePurposeManifest(input: {
1426
+ readonly label: string;
1427
+ readonly kinds: readonly JobKind[];
1428
+ }): Manifest;
1429
+
1430
+ /**
1431
+ * One job, one signature, one answer — byollm_016 Amendment J.
1432
+ *
1433
+ * A grant is the control plane's signed statement that a particular job may
1434
+ * run on a particular device, authored at claim time and verified against the
1435
+ * key that device pinned when it paired.
1436
+ *
1437
+ * ## What it replaced, and why the replacement is smaller
1438
+ *
1439
+ * Until 2026-08-26 a device held a signed **roster** and answered admission
1440
+ * from it. Amendment G's four properties were right and the mechanism was a
1441
+ * cache — one that bought nothing. On the cloud route the job path and the
1442
+ * roster path share fate: jobs arrive through the relay, so if the relay is
1443
+ * unreachable there are no jobs to admit and a locally held roster adds no
1444
+ * availability. What it did add was staleness, which is the only reason
1445
+ * `ROSTER_MAX_AGE_MS` existed: a bound on how long a removed person keeps
1446
+ * running. Authoring at claim collapses that bound to this document's own
1447
+ * lifetime — add somebody and their next job runs, remove them and their next
1448
+ * claim fails, including jobs already queued.
1449
+ *
1450
+ * It also collapses four questions into one signature. Consented, member,
1451
+ * admitted, and *which service* were four mechanisms answering separately;
1452
+ * they are now four fields of one statement, and the device verifies once.
1453
+ *
1454
+ * ## What it is not
1455
+ *
1456
+ * Amendment G property 1 outlawed admitting on a per-job assertion, and this
1457
+ * is per-job. The distinction is authorship: G outlawed trusting the
1458
+ * **relay's or site's unsigned** claim. A grant is signed by the control
1459
+ * plane with a key the device pinned at pairing, so the relay can withhold it
1460
+ * and cannot forge it — exactly the power a relay has over a job.
1461
+ * `RELAY_BLIND` is untouched: the relay delivers, it never authors.
1462
+ *
1463
+ * ## What the device still checks for itself
1464
+ *
1465
+ * A grant is necessary and not sufficient. Four checks stay on the device and
1466
+ * none of them is delegated:
1467
+ *
1468
+ * 1. the signature, against the pinned key;
1469
+ * 2. replay — {@link SignedGrant.grantId} is single-use;
1470
+ * 3. offer-consistency — the named service is one this device actually
1471
+ * offers, at a scope that includes this user;
1472
+ * 4. **private is absolute** — a `private` service runs for the paired owner
1473
+ * and nobody else, so no compromise of a control plane can grant somebody
1474
+ * else's job onto it.
1475
+ */
1476
+ /**
1477
+ * How long a grant is honoured after it was signed. Ruled 120s (2026-08-26).
1478
+ *
1479
+ * This bounds **acceptance**, not execution: a job admitted inside the window
1480
+ * runs to completion however long it takes. So the number only has to cover
1481
+ * the trip from the control plane signing to the device checking — claim,
1482
+ * deliver, verify — and every second past that is a second a captured grant
1483
+ * stays useful.
1484
+ *
1485
+ * Two minutes is generous for that trip and mean for the capture. It is also
1486
+ * the number ordinary clock drift is measured against, which is why
1487
+ * {@link CLOCK_SKEW_WARN_MS} sits well inside it: a device whose clock is off
1488
+ * by half the window would refuse real work, and must be told before it does.
1489
+ *
1490
+ * The verifier's policy, deliberately not a field on the document. An
1491
+ * `expiresAt` the signer chose would let whoever signs decide how long their
1492
+ * own statement stays good, and the party with the most reason to want a
1493
+ * longer window is the party being bounded.
1494
+ */
1495
+ declare const GRANT_MAX_AGE_MS = 120000;
1496
+ /**
1497
+ * Clock disagreement past which a device says so, before it starts refusing.
1498
+ *
1499
+ * Skew eats {@link GRANT_MAX_AGE_MS} directly — a device 60s behind its
1500
+ * relay's clock has half a window left, and one 120s behind has none and
1501
+ * refuses everything for a reason no refusal message would otherwise name.
1502
+ * Thirty seconds is a quarter of the window: far enough out to be a real
1503
+ * problem, early enough to be a warning rather than an outage.
1504
+ */
1505
+ declare const CLOCK_SKEW_WARN_MS = 30000;
1506
+ /**
1507
+ * Skew past which a freshness refusal names the clock instead of the grant.
1508
+ *
1509
+ * Five seconds, because below that the clock is not the story and saying so
1510
+ * would send somebody to check ntp about an unrelated failure. Above it, "this
1511
+ * grant expired" and "your clock is wrong" are the same event wearing
1512
+ * different words, and only one of them can be acted on.
1513
+ */
1514
+ declare const CLOCK_ATTRIBUTION_MS = 5000;
1515
+ /**
1516
+ * The domain separator.
1517
+ *
1518
+ * Every signature in this system says what kind of statement it is before it
1519
+ * says anything else. Without it, bytes signed for one purpose verify for
1520
+ * another — a grant and a request are both "bytes this key signed", and a
1521
+ * scheme that could not tell them apart would let one be replayed as the
1522
+ * other.
1523
+ */
1524
+ declare const GRANT_CONTEXT = "byollm/v1/grant";
1525
+ declare const SignedGrant: z.ZodObject<{
1526
+ grantId: z.ZodString;
1527
+ jobId: z.ZodString;
1528
+ site: z.ZodString;
1529
+ user: z.ZodString;
1530
+ owner: z.ZodString;
1531
+ purpose: z.ZodString;
1532
+ kind: z.ZodString;
1533
+ service: z.ZodString;
1534
+ issuedAt: z.ZodNumber;
1535
+ signature: z.ZodString;
1536
+ }, z.core.$strict>;
1537
+ type SignedGrant = z.infer<typeof SignedGrant>;
1538
+ /** Everything a grant says, before it is signed. */
1539
+ type GrantClaims = Omit<SignedGrant, "signature">;
1540
+ /**
1541
+ * Every field of {@link SignedGrant} except the signature, sorted.
1542
+ *
1543
+ * **Derived from the schema, never written out by hand.** The unsigned-field
1544
+ * attack is that somebody adds a field to the document, forgets to add it to
1545
+ * the bytes, and ships a value an intermediary can rewrite without breaking
1546
+ * any signature. A hand-maintained list is exactly the shape that fails: it
1547
+ * does not grow when the code does, and nothing about adding a field reminds
1548
+ * you it exists.
1549
+ *
1550
+ * Reading the shape closes it structurally rather than by review. A new field
1551
+ * is signed the moment it is declared, and grant.test.ts asserts this list
1552
+ * still covers the schema so a future zod version that hides `shape` fails
1553
+ * loudly instead of silently signing less.
1554
+ */
1555
+ declare const GRANT_SIGNED_FIELDS: readonly (keyof GrantClaims)[];
1556
+ /**
1557
+ * The exact bytes both sides sign and verify.
1558
+ *
1559
+ * JSON-encoded rather than joined with a separator, because a separator can
1560
+ * be imitated. Newline-joining `["a", "b\nc"]` and `["a\nb", "c"]` produces
1561
+ * identical bytes, so two different grants would share a signature — and the
1562
+ * values here include a site id and a user id, at least one of which comes
1563
+ * from somebody else's namespace. JSON escapes the separator it uses, so no
1564
+ * arrangement of field values can spell a different document.
1565
+ *
1566
+ * The context string leads, and the field order is the schema's own sorted
1567
+ * keys, so the encoding is canonical without anyone maintaining a list.
1568
+ */
1569
+ declare function grantStatement(claims: GrantClaims): Uint8Array;
1570
+ /** Sign a grant with the control plane's own key. */
1571
+ declare function signGrant(keys: Pick<StoredKeys, "identityPrivate">, claims: GrantClaims): SignedGrant;
1572
+ /**
1573
+ * Why a grant was refused.
1574
+ *
1575
+ * Split by remedy, because these send somebody to different places: fix your
1576
+ * clock, take it up with the relay, or nothing at all — you are being
1577
+ * attacked and the refusal worked.
1578
+ *
1579
+ * There is deliberately no `no-pinned-key` here. A device that pinned no
1580
+ * control-plane key never reaches this function: it is in direct mode, and
1581
+ * the question "is this grant good" does not arise. A value nothing can
1582
+ * return is a branch every caller has to handle and no test can reach.
1583
+ */
1584
+ type GrantRefusal =
1585
+ /** The signature does not verify against the pinned key. */
1586
+ "bad-signature"
1587
+ /** Genuine, and for a different device's owner. */
1588
+ | "wrong-owner"
1589
+ /** Genuine, and lifted from a different job. */
1590
+ | "wrong-job"
1591
+ /** Older than {@link GRANT_MAX_AGE_MS}. */
1592
+ | "expired"
1593
+ /**
1594
+ * Issued further in the future than clock drift explains.
1595
+ *
1596
+ * Checked, and not as pedantry: an `issuedAt` ahead of now extends a
1597
+ * grant's life past the bound, which is the whole thing being enforced.
1598
+ *
1599
+ * Tolerant by {@link CLOCK_SKEW_WARN_MS}, because it was tolerant by
1600
+ * nothing and that made ordinary drift a total outage — see
1601
+ * {@link verifyGrant}.
1602
+ */
1603
+ | "from-the-future";
1604
+ /**
1605
+ * Is this grant one this device may act on, right now?
1606
+ *
1607
+ * Document-level checks only. Replay, offer-consistency and the private rule
1608
+ * need state this function does not have and are the device's to apply — see
1609
+ * the class comment for the full list of four.
1610
+ */
1611
+ declare function verifyGrant(input: {
1612
+ grant: SignedGrant;
1613
+ owner: string;
1614
+ jobId: string;
1615
+ controlPlanePublic: string;
1616
+ now: number;
1617
+ maxAgeMs?: number;
1618
+ }): GrantRefusal | null;
1619
+
1620
+ /**
1621
+ * Rotation — byollm_009 Amendment C.
1622
+ *
1623
+ * A site holding identity key **K1** wants to be known by **K2**. It publishes
1624
+ * a *succession*: K2, plus a signature by K1 over a statement naming both key
1625
+ * ids. That signature is the entire mechanism, and the reason rotation can be
1626
+ * automatic without becoming a hole is that **the relay cannot mint one** — it
1627
+ * never holds K1. It is the same trust step a daemon already performs at
1628
+ * pairing, applied to the site's own succession.
1629
+ *
1630
+ * ## Why the statement names both keys
1631
+ *
1632
+ * A signature over K2 alone could be lifted from this site's record and
1633
+ * replayed into another site's, moving *that* site to K2 — a key the attacker
1634
+ * holds. Naming the predecessor binds the succession to one chain, and it is
1635
+ * the reason `verifyLink` takes the id it expects to be succeeding from
1636
+ * rather than reading it out of the statement it is checking.
1637
+ */
1638
+ /** The domain separator. Distinct from every other thing an identity signs. */
1639
+ declare const SUCCESSION_CONTEXT = "byollm/v1/site-succession";
1640
+ /**
1641
+ * How long a retired key may still sign work — Amendment C, ruling 2.
1642
+ *
1643
+ * A protocol constant and not the site's to choose. Per-site overlap
1644
+ * arithmetic is exactly the kind of number that has to mean one thing
1645
+ * everywhere, and a site that could choose it could choose *forever*, which is
1646
+ * a two-key site permanently and a second key nobody ever notices retiring.
1647
+ *
1648
+ * Seven days: long enough that a daemon which polls daily and a laptop shut
1649
+ * for a long weekend both see the new record before the old key stops working,
1650
+ * short enough that "which key is live" is never an interesting question.
1651
+ */
1652
+ declare const RETIREMENT_WINDOW_MS: number;
1653
+ /**
1654
+ * The longest chain a daemon will walk — Amendment C, ruling 1.
1655
+ *
1656
+ * **A denial-of-service guard, not policy.** The bound exists so a projection
1657
+ * cannot make a daemon verify ten thousand signatures, not to express an
1658
+ * opinion about how often a site may rotate. A site that legitimately exceeds
1659
+ * it has a re-pair ahead of it, which is why it is generous: at one rotation a
1660
+ * quarter this is sixteen years.
1661
+ */
1662
+ declare const MAX_SUCCESSION_CHAIN = 64;
1663
+ /** One step of a chain: a key, and the signature by it over its successor. */
1664
+ declare const Succession: z.ZodObject<{
1665
+ identity: z.ZodObject<{
1666
+ identity: z.ZodString;
1667
+ encryption: z.ZodString;
1668
+ encryptionSig: z.ZodString;
1669
+ }, z.core.$strict>;
1670
+ signature: z.ZodString;
1671
+ }, z.core.$strict>;
1672
+ type Succession = z.infer<typeof Succession>;
1673
+ /** The exact bytes signed. One definition; both sides call it. */
1674
+ declare function successionStatement(fromKeyId: string, toKeyId: string): Uint8Array;
1675
+ /**
1676
+ * Sign a succession from the keys being retired to the identity taking over.
1677
+ *
1678
+ * Takes `StoredKeys` for the predecessor because only the holder of K1's
1679
+ * private half can produce this, which is the property the whole design rests
1680
+ * on. A site calls this once, at rotation, on the machine holding its keys.
1681
+ */
1682
+ declare function signSuccession(previous: StoredKeys, next: PublicIdentity): Succession;
1683
+ /**
1684
+ * Check one link: did `link.identity` sign over succeeding to `toKeyId`?
1685
+ *
1686
+ * `toKeyId` is passed in rather than read from anywhere in `link`, and that is
1687
+ * the load-bearing detail. A verifier that recovered the successor from the
1688
+ * signed statement would accept a statement about *any* successor, which is
1689
+ * the replay this design names in C.1 — the signature is genuine, the
1690
+ * successor it names is not the one being installed.
1691
+ */
1692
+ declare function verifyLink(link: Succession, toKeyId: string): boolean;
1693
+ /** Why a chain was refused, in the words a log line uses. */
1694
+ type SuccessionFailure = "no-chain" | "too-long" | "unknown-origin" | "broken-link";
1695
+ interface SuccessionWalk {
1696
+ /** The ids the chain passes through, oldest first, ending at the current. */
1697
+ readonly path: string[];
1698
+ /** The approved id the chain reached, when it reached one. */
1699
+ readonly from?: string;
1700
+ readonly failure?: SuccessionFailure;
1701
+ }
1702
+ /**
1703
+ * Walk a chain from the key being presented back to a key already approved.
1704
+ *
1705
+ * `chain` is ordered oldest last, as the projection carries it — so walking it
1706
+ * means starting at the current key and stepping backwards, each link proving
1707
+ * that its holder signed for the id in front of it.
1708
+ *
1709
+ * Returns the approved id it reached, or why it did not. **Deliberately
1710
+ * returns rather than throws**: a chain that does not verify is ordinary
1711
+ * hostile input, and the caller's job is to keep its existing pin and say so.
1712
+ *
1713
+ * `approved` is asked as a predicate rather than taken as a set because the
1714
+ * daemon's notion of "already approved" includes tombstoned ids — a site that
1715
+ * left the allowlist and came back is still a site this machine has vouched
1716
+ * for, and rotation must not become a way to launder that distinction away.
1717
+ */
1718
+ declare function walkSuccession(input: {
1719
+ current: string;
1720
+ chain: readonly Succession[];
1721
+ approved: (keyId: string) => boolean;
1722
+ }): SuccessionWalk;
1723
+
967
1724
  /**
968
1725
  * The normative MUSTs of protocol v0, as data.
969
1726
  *
@@ -997,12 +1754,45 @@ type MustEnforcer = "daemon" | "server" | "both";
997
1754
  * someone else's, so the kit cannot carry it.
998
1755
  * - `construction` — true by the shape of the code, where a test could only
999
1756
  * sample. A reviewer verifies it; a suite cannot.
1757
+ * ## When a MUST binds both sides — cloud_008 Tier 3
1758
+ *
1759
+ * `AUDIENCE_BOTH_SIDES` says the server and the daemon each enforce. The kit
1760
+ * passed **entirely** with the server's half deleted: every check drove a real
1761
+ * daemon, and a daemon refuses locally, so "the job did not run" looked
1762
+ * identical whichever side refused it. A full-honest-stack test proves only
1763
+ * the conjunction.
1764
+ *
1765
+ * So a `both`-enforced MUST needs **one check per party, each with the honest
1766
+ * counterpart removed** — C032 claims over the raw protocol precisely so no
1767
+ * daemon admission logic runs. Where a check strips one side, its comment
1768
+ * says which; where a MUST is enforced by both and only one side is checked,
1769
+ * that is a gap rather than coverage.
1770
+ *
1000
1771
  * - `operator` — a claim about how someone runs a deployment, verifiable only
1001
1772
  * by audit or by reading source. The honest category, and the one that
1002
1773
  * exists so a property nobody can check from outside is *labelled* as such
1003
1774
  * rather than laundered by association with the checkable ones.
1004
1775
  */
1005
1776
  type MustVerification = "conformance" | "adversarial" | "construction" | "operator";
1777
+ /**
1778
+ * How a MUST is verified — one kind, or several.
1779
+ *
1780
+ * Several is not hedging. `SITES_LOCALLY_APPROVED` is the case that forced it:
1781
+ * the fence is **construction** — a daemon cannot serve a site that is not in
1782
+ * its map, and admission refuses before a payload is fetched — while the
1783
+ * property that a *removed and re-offered* id is still refused needs a hostile
1784
+ * sequence of heartbeats no honest client would send, which is
1785
+ * **adversarial**. Recording one and dropping the other would either overstate
1786
+ * what a type check proves or understate what the suites do.
1787
+ *
1788
+ * The alternative was a second field for the second kind, which is two answers
1789
+ * to one question — the shape this project keeps deleting.
1790
+ */
1791
+ type MustVerifiedBy = MustVerification | readonly [MustVerification, ...MustVerification[]];
1792
+ /** The kinds a MUST claims, always as a list. */
1793
+ declare function kindsOf(must: {
1794
+ readonly verifiedBy: MustVerifiedBy;
1795
+ }): readonly MustVerification[];
1006
1796
  /** A single normative requirement of the protocol. */
1007
1797
  interface Must {
1008
1798
  /** Stable public id, cited by conformance output. */
@@ -1015,7 +1805,7 @@ interface Must {
1015
1805
  * How this is verified. `conformance` is the only kind the kit can assert;
1016
1806
  * see {@link MustVerification} for why the others exist.
1017
1807
  */
1018
- readonly verifiedBy: MustVerification;
1808
+ readonly verifiedBy: MustVerifiedBy;
1019
1809
  /** Spec section this was adjudicated in. */
1020
1810
  readonly source: string;
1021
1811
  }
@@ -1032,6 +1822,8 @@ declare const MUSTS: Readonly<{
1032
1822
  readonly PAIR_INTERACTIVE: Must;
1033
1823
  readonly PAIR_CODE_EXPIRES: Must;
1034
1824
  readonly VERSION_HANDSHAKE_REQUIRED: Must;
1825
+ readonly SITE_KEY_BY_STUB: Must;
1826
+ readonly SITES_LOCALLY_APPROVED: Must;
1035
1827
  readonly KEYS_EXCHANGED_AT_CONSENT: Must;
1036
1828
  readonly REQUESTS_SIGNED_NOT_BEARER: Must;
1037
1829
  readonly LEASE_SCOPED_BY_GRANT: Must;
@@ -1058,19 +1850,47 @@ declare const MUSTS: Readonly<{
1058
1850
  readonly TTL_EXPIRY: Must;
1059
1851
  readonly NO_RUNNER_SIGNAL: Must;
1060
1852
  readonly RESULT_IDEMPOTENT: Must;
1061
- readonly RESULT_PROVENANCE: Must;
1853
+ readonly PROVENANCE_NAMES_DEVICE: Must;
1062
1854
  readonly INGRESS_LOGGED_BEFORE_EXECUTION: Must;
1063
1855
  readonly NO_SHELL_INTERPOLATION: Must;
1856
+ /**
1857
+ * Amended for byollm_016 Phase B, and the amendment is deliberately narrow.
1858
+ *
1859
+ * A site may now name a **service** on the stub. The temptation is to read
1860
+ * that as a crack in this law, so the statement below says exactly where the
1861
+ * line is: a name selects from a menu the owner published, and resolves to a
1862
+ * model, backend, base URL and flags **only** through that owner's own
1863
+ * config. The site supplies a key; the owner supplies every value it maps
1864
+ * to. A name the owner does not advertise is refused rather than
1865
+ * substituted, because substitution is how "you may pick from my list" turns
1866
+ * into "you may ask for anything and get something".
1867
+ *
1868
+ * Two properties keep it from drifting into "sites demand models":
1869
+ *
1870
+ * 1. **Nothing the site sends is ever a value.** No model string, no URL,
1871
+ * no flag crosses the wire — only a key that means nothing off this
1872
+ * owner's machine.
1873
+ * 2. **It is a stub field, never a payload field.** The prompt cannot
1874
+ * reach it. That is unchanged and is the sentence the second clause
1875
+ * below still enforces verbatim.
1876
+ */
1064
1877
  readonly NO_PAYLOAD_ROUTING: Must;
1065
1878
  readonly STRIPPED_CHILD_ENV: Must;
1066
1879
  readonly HTTP_BASE_URL_SAFE: Must;
1067
1880
  readonly OUTPUT_INERT: Must;
1068
1881
  readonly COMMUNITY_BUDGETS: Must;
1882
+ readonly REVOCATION_IMMEDIATE: Must;
1883
+ readonly CONSENT_BEFORE_ROUTE: Must;
1884
+ readonly ROSTER_NOT_DISCLOSED: Must;
1885
+ readonly EFFECTIVE_OFFER_ONLY: Must;
1886
+ readonly FALLBACK_LABELED: Must;
1887
+ readonly RELAY_BLIND: Must;
1888
+ readonly SHARED_COMPUTE_DISCLOSED: Must;
1069
1889
  }>;
1070
1890
  /** The id of any normative MUST. */
1071
1891
  type MustId = keyof typeof MUSTS;
1072
1892
  /** 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")[];
1893
+ 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
1894
  /** Every MUST verified a particular way. */
1075
1895
  declare function mustsVerifiedBy(kind: MustVerification): MustId[];
1076
1896
 
@@ -1100,6 +1920,23 @@ interface VersionRefusal {
1100
1920
  readonly supported: readonly string[];
1101
1921
  readonly minimum: string;
1102
1922
  }
1923
+ /**
1924
+ * The version a request declares, wherever it carries it.
1925
+ *
1926
+ * A POST declares it in its body, which is where every request schema has
1927
+ * always put it. A GET has no body, and the relay has one — the site plane's
1928
+ * `pending` read — so it declares it in the query string instead.
1929
+ *
1930
+ * **Two carriers, one rule.** That asymmetry is HTTP's rather than ours, and
1931
+ * the alternative was worse in both directions: a header for everything would
1932
+ * change every existing daemon's request, and skipping GETs would leave an
1933
+ * endpoint outside the handshake — which is precisely the shape B.4 found,
1934
+ * where a whole plane was outside it.
1935
+ */
1936
+ declare function declaredVersion(input: {
1937
+ body?: unknown;
1938
+ query?: URLSearchParams;
1939
+ }): unknown;
1103
1940
  /**
1104
1941
  * Check the protocol version on an incoming request
1105
1942
  * ({@link MUSTS.VERSION_HANDSHAKE_REQUIRED}).
@@ -1144,6 +1981,7 @@ declare const Capability: z.ZodObject<{
1144
1981
  "llm.generate": "llm.generate";
1145
1982
  "llm.chat": "llm.chat";
1146
1983
  }>;
1984
+ service: z.ZodString;
1147
1985
  backendId: z.ZodEnum<{
1148
1986
  ollama: "ollama";
1149
1987
  mlx: "mlx";
@@ -1163,6 +2001,7 @@ declare const Capability: z.ZodObject<{
1163
2001
  mistral: "mistral";
1164
2002
  "openai-http": "openai-http";
1165
2003
  "claude-cli": "claude-cli";
2004
+ "codex-cli": "codex-cli";
1166
2005
  }>;
1167
2006
  backendClass: z.ZodEnum<{
1168
2007
  http: "http";
@@ -1170,9 +2009,8 @@ declare const Capability: z.ZodObject<{
1170
2009
  }>;
1171
2010
  model: z.ZodString;
1172
2011
  offerScope: z.ZodEnum<{
1173
- self: "self";
1174
- named: "named";
1175
- public: "public";
2012
+ private: "private";
2013
+ team: "team";
1176
2014
  }>;
1177
2015
  }, z.core.$strict>;
1178
2016
  type Capability = z.infer<typeof Capability>;
@@ -1182,6 +2020,7 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1182
2020
  "llm.generate": "llm.generate";
1183
2021
  "llm.chat": "llm.chat";
1184
2022
  }>;
2023
+ service: z.ZodString;
1185
2024
  backendId: z.ZodEnum<{
1186
2025
  ollama: "ollama";
1187
2026
  mlx: "mlx";
@@ -1201,6 +2040,7 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1201
2040
  mistral: "mistral";
1202
2041
  "openai-http": "openai-http";
1203
2042
  "claude-cli": "claude-cli";
2043
+ "codex-cli": "codex-cli";
1204
2044
  }>;
1205
2045
  backendClass: z.ZodEnum<{
1206
2046
  http: "http";
@@ -1208,12 +2048,55 @@ declare const CapabilityMatrix: z.ZodArray<z.ZodObject<{
1208
2048
  }>;
1209
2049
  model: z.ZodString;
1210
2050
  offerScope: z.ZodEnum<{
1211
- self: "self";
1212
- named: "named";
1213
- public: "public";
2051
+ private: "private";
2052
+ team: "team";
1214
2053
  }>;
1215
2054
  }, z.core.$strict>>;
1216
2055
  type CapabilityMatrix = z.infer<typeof CapabilityMatrix>;
2056
+ /**
2057
+ * A kind this device could serve and deliberately does not — byollm_016.
2058
+ *
2059
+ * Two services answer one kind and the owner has not said which wins, so the
2060
+ * kind is not advertised. That is correct and, unsaid, invisible: the owner
2061
+ * adds a second service, jobs stop matching, and no surface explains it.
2062
+ *
2063
+ * It travels because the surfaces that must say so are not all on the device.
2064
+ * The owner's card names the claimants; a teammate's card says only that the
2065
+ * owner has a choice to make. Claimant **offer scopes** ride along so the hub
2066
+ * can compute that difference without the device deciding who is asking —
2067
+ * carry for computation, filter for display, the same shape the effective
2068
+ * offer already uses.
2069
+ */
2070
+ declare const WithheldKind: z.ZodObject<{
2071
+ kind: z.ZodEnum<{
2072
+ "llm.generate": "llm.generate";
2073
+ "llm.chat": "llm.chat";
2074
+ }>;
2075
+ claimants: z.ZodArray<z.ZodObject<{
2076
+ service: z.ZodString;
2077
+ offer: z.ZodEnum<{
2078
+ private: "private";
2079
+ team: "team";
2080
+ }>;
2081
+ }, z.core.$strict>>;
2082
+ }, z.core.$strict>;
2083
+ type WithheldKind = z.infer<typeof WithheldKind>;
2084
+ /**
2085
+ * One grant, named by both halves — V1-3.
2086
+ *
2087
+ * A job id is chosen per site, so the lease id is the unique thing an upstream
2088
+ * and a daemon can both point at. Anywhere a request says "this piece of work,
2089
+ * held by me", it says it with both.
2090
+ *
2091
+ * Declared once because it was written out twice — `activeLeases` and
2092
+ * `ReleaseRequest.leases` — and both needed the same `.strict()` added. Two
2093
+ * copies of a shape are two places to forget it.
2094
+ */
2095
+ declare const GrantRef: z.ZodObject<{
2096
+ jobId: z.ZodString;
2097
+ leaseId: z.ZodString;
2098
+ }, z.core.$strict>;
2099
+ type GrantRef = z.infer<typeof GrantRef>;
1217
2100
  /**
1218
2101
  * Pairing is a device-code exchange, not a pasted secret
1219
2102
  * ({@link MUSTS.PAIR_INTERACTIVE}). The daemon starts a pairing, shows the
@@ -1232,7 +2115,7 @@ declare const PairStartRequest: z.ZodObject<{
1232
2115
  linux: "linux";
1233
2116
  win32: "win32";
1234
2117
  }>;
1235
- }, z.core.$strip>;
2118
+ }, z.core.$strict>;
1236
2119
  device: z.ZodObject<{
1237
2120
  identity: z.ZodString;
1238
2121
  encryption: z.ZodString;
@@ -1243,6 +2126,7 @@ declare const PairStartRequest: z.ZodObject<{
1243
2126
  "llm.generate": "llm.generate";
1244
2127
  "llm.chat": "llm.chat";
1245
2128
  }>;
2129
+ service: z.ZodString;
1246
2130
  backendId: z.ZodEnum<{
1247
2131
  ollama: "ollama";
1248
2132
  mlx: "mlx";
@@ -1262,6 +2146,7 @@ declare const PairStartRequest: z.ZodObject<{
1262
2146
  mistral: "mistral";
1263
2147
  "openai-http": "openai-http";
1264
2148
  "claude-cli": "claude-cli";
2149
+ "codex-cli": "codex-cli";
1265
2150
  }>;
1266
2151
  backendClass: z.ZodEnum<{
1267
2152
  http: "http";
@@ -1269,9 +2154,8 @@ declare const PairStartRequest: z.ZodObject<{
1269
2154
  }>;
1270
2155
  model: z.ZodString;
1271
2156
  offerScope: z.ZodEnum<{
1272
- self: "self";
1273
- named: "named";
1274
- public: "public";
2157
+ private: "private";
2158
+ team: "team";
1275
2159
  }>;
1276
2160
  }, z.core.$strict>>;
1277
2161
  }, z.core.$strict>;
@@ -1298,15 +2182,15 @@ declare const PairPollResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
1298
2182
  status: z.ZodLiteral<"expired">;
1299
2183
  }, z.core.$strict>, z.ZodObject<{
1300
2184
  status: z.ZodLiteral<"approved">;
1301
- runnerToken: z.ZodString;
1302
2185
  runnerId: z.ZodString;
1303
2186
  owner: z.ZodString;
1304
2187
  ownerLabel: z.ZodOptional<z.ZodString>;
1305
- site: z.ZodObject<{
2188
+ sites: z.ZodRecord<z.ZodString, z.ZodObject<{
1306
2189
  identity: z.ZodString;
1307
2190
  encryption: z.ZodString;
1308
2191
  encryptionSig: z.ZodString;
1309
- }, z.core.$strict>;
2192
+ }, z.core.$strict>>;
2193
+ controlPlanePublic: z.ZodOptional<z.ZodString>;
1310
2194
  }, z.core.$strict>], "status">;
1311
2195
  type PairPollResponse = z.infer<typeof PairPollResponse>;
1312
2196
  declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -1320,7 +2204,7 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1320
2204
  linux: "linux";
1321
2205
  win32: "win32";
1322
2206
  }>;
1323
- }, z.core.$strip>;
2207
+ }, z.core.$strict>;
1324
2208
  device: z.ZodObject<{
1325
2209
  identity: z.ZodString;
1326
2210
  encryption: z.ZodString;
@@ -1331,6 +2215,7 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1331
2215
  "llm.generate": "llm.generate";
1332
2216
  "llm.chat": "llm.chat";
1333
2217
  }>;
2218
+ service: z.ZodString;
1334
2219
  backendId: z.ZodEnum<{
1335
2220
  ollama: "ollama";
1336
2221
  mlx: "mlx";
@@ -1350,6 +2235,7 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1350
2235
  mistral: "mistral";
1351
2236
  "openai-http": "openai-http";
1352
2237
  "claude-cli": "claude-cli";
2238
+ "codex-cli": "codex-cli";
1353
2239
  }>;
1354
2240
  backendClass: z.ZodEnum<{
1355
2241
  http: "http";
@@ -1357,9 +2243,8 @@ declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
1357
2243
  }>;
1358
2244
  model: z.ZodString;
1359
2245
  offerScope: z.ZodEnum<{
1360
- self: "self";
1361
- named: "named";
1362
- public: "public";
2246
+ private: "private";
2247
+ team: "team";
1363
2248
  }>;
1364
2249
  }, z.core.$strict>>;
1365
2250
  }, z.core.$strict>, z.ZodObject<{
@@ -1376,6 +2261,7 @@ declare const ClaimRequest: z.ZodObject<{
1376
2261
  "llm.generate": "llm.generate";
1377
2262
  "llm.chat": "llm.chat";
1378
2263
  }>;
2264
+ service: z.ZodString;
1379
2265
  backendId: z.ZodEnum<{
1380
2266
  ollama: "ollama";
1381
2267
  mlx: "mlx";
@@ -1395,6 +2281,7 @@ declare const ClaimRequest: z.ZodObject<{
1395
2281
  mistral: "mistral";
1396
2282
  "openai-http": "openai-http";
1397
2283
  "claude-cli": "claude-cli";
2284
+ "codex-cli": "codex-cli";
1398
2285
  }>;
1399
2286
  backendClass: z.ZodEnum<{
1400
2287
  http: "http";
@@ -1402,9 +2289,8 @@ declare const ClaimRequest: z.ZodObject<{
1402
2289
  }>;
1403
2290
  model: z.ZodString;
1404
2291
  offerScope: z.ZodEnum<{
1405
- self: "self";
1406
- named: "named";
1407
- public: "public";
2292
+ private: "private";
2293
+ team: "team";
1408
2294
  }>;
1409
2295
  }, z.core.$strict>>;
1410
2296
  max: z.ZodNumber;
@@ -1418,12 +2304,12 @@ declare const ClaimResponse: z.ZodObject<{
1418
2304
  "llm.chat": "llm.chat";
1419
2305
  }>;
1420
2306
  owner: z.ZodString;
2307
+ site: z.ZodString;
1421
2308
  audience: z.ZodEnum<{
1422
- self: "self";
1423
- named: "named";
1424
- public: "public";
2309
+ private: "private";
2310
+ team: "team";
1425
2311
  }>;
1426
- audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
2312
+ purpose: z.ZodOptional<z.ZodString>;
1427
2313
  sizeClass: z.ZodEnum<{
1428
2314
  small: "small";
1429
2315
  medium: "medium";
@@ -1436,7 +2322,19 @@ declare const ClaimResponse: z.ZodObject<{
1436
2322
  id: z.ZodString;
1437
2323
  runnerId: z.ZodString;
1438
2324
  expiresAt: z.ZodNumber;
1439
- }, z.core.$strip>;
2325
+ }, z.core.$strict>;
2326
+ grant: z.ZodOptional<z.ZodObject<{
2327
+ grantId: z.ZodString;
2328
+ jobId: z.ZodString;
2329
+ site: z.ZodString;
2330
+ user: z.ZodString;
2331
+ owner: z.ZodString;
2332
+ purpose: z.ZodString;
2333
+ kind: z.ZodString;
2334
+ service: z.ZodString;
2335
+ issuedAt: z.ZodNumber;
2336
+ signature: z.ZodString;
2337
+ }, z.core.$strict>>;
1440
2338
  }, z.core.$strict>>;
1441
2339
  leaseMs: z.ZodNumber;
1442
2340
  }, z.core.$strict>;
@@ -1450,6 +2348,7 @@ declare const HeartbeatRequest: z.ZodObject<{
1450
2348
  "llm.generate": "llm.generate";
1451
2349
  "llm.chat": "llm.chat";
1452
2350
  }>;
2351
+ service: z.ZodString;
1453
2352
  backendId: z.ZodEnum<{
1454
2353
  ollama: "ollama";
1455
2354
  mlx: "mlx";
@@ -1469,6 +2368,7 @@ declare const HeartbeatRequest: z.ZodObject<{
1469
2368
  mistral: "mistral";
1470
2369
  "openai-http": "openai-http";
1471
2370
  "claude-cli": "claude-cli";
2371
+ "codex-cli": "codex-cli";
1472
2372
  }>;
1473
2373
  backendClass: z.ZodEnum<{
1474
2374
  http: "http";
@@ -1476,27 +2376,57 @@ declare const HeartbeatRequest: z.ZodObject<{
1476
2376
  }>;
1477
2377
  model: z.ZodString;
1478
2378
  offerScope: z.ZodEnum<{
1479
- self: "self";
1480
- named: "named";
1481
- public: "public";
2379
+ private: "private";
2380
+ team: "team";
1482
2381
  }>;
1483
2382
  }, z.core.$strict>>;
2383
+ withheld: z.ZodDefault<z.ZodArray<z.ZodObject<{
2384
+ kind: z.ZodEnum<{
2385
+ "llm.generate": "llm.generate";
2386
+ "llm.chat": "llm.chat";
2387
+ }>;
2388
+ claimants: z.ZodArray<z.ZodObject<{
2389
+ service: z.ZodString;
2390
+ offer: z.ZodEnum<{
2391
+ private: "private";
2392
+ team: "team";
2393
+ }>;
2394
+ }, z.core.$strict>>;
2395
+ }, z.core.$strict>>>;
1484
2396
  activeLeases: z.ZodArray<z.ZodObject<{
1485
2397
  jobId: z.ZodString;
1486
2398
  leaseId: z.ZodString;
1487
- }, z.core.$strip>>;
2399
+ }, z.core.$strict>>;
1488
2400
  paused: z.ZodBoolean;
1489
2401
  }, z.core.$strict>;
1490
2402
  type HeartbeatRequest = z.infer<typeof HeartbeatRequest>;
1491
2403
  declare const HeartbeatResponse: z.ZodObject<{
1492
- revoked: z.ZodBoolean;
1493
- cancel: z.ZodArray<z.ZodString>;
1494
- leases: z.ZodArray<z.ZodObject<{
2404
+ sites: z.ZodRecord<z.ZodString, z.ZodObject<{
2405
+ identity: z.ZodString;
2406
+ encryption: z.ZodString;
2407
+ encryptionSig: z.ZodString;
2408
+ }, z.core.$strict>>;
2409
+ successions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
2410
+ succeeds: z.ZodArray<z.ZodObject<{
2411
+ identity: z.ZodObject<{
2412
+ identity: z.ZodString;
2413
+ encryption: z.ZodString;
2414
+ encryptionSig: z.ZodString;
2415
+ }, z.core.$strict>;
2416
+ signature: z.ZodString;
2417
+ }, z.core.$strict>>;
2418
+ retiringUntil: z.ZodOptional<z.ZodNumber>;
2419
+ }, z.core.$strict>>>;
2420
+ cancel: z.ZodArray<z.ZodObject<{
1495
2421
  jobId: z.ZodString;
1496
- expiresAt: z.ZodNumber;
2422
+ leaseId: z.ZodString;
2423
+ }, z.core.$strict>>;
2424
+ lost: z.ZodArray<z.ZodObject<{
2425
+ jobId: z.ZodString;
2426
+ leaseId: z.ZodString;
1497
2427
  }, z.core.$strict>>;
1498
- lost: z.ZodArray<z.ZodString>;
1499
2428
  serverTime: z.ZodNumber;
2429
+ awaitingConsent: z.ZodArray<z.ZodString>;
1500
2430
  }, z.core.$strict>;
1501
2431
  type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;
1502
2432
  /**
@@ -1521,6 +2451,7 @@ declare const ResultRequest: z.ZodObject<{
1521
2451
  protocolVersion: z.ZodLiteral<"0">;
1522
2452
  runnerId: z.ZodString;
1523
2453
  jobId: z.ZodString;
2454
+ leaseId: z.ZodString;
1524
2455
  envelope: z.ZodObject<{
1525
2456
  ciphertext: z.ZodString;
1526
2457
  recipientKeyId: z.ZodString;
@@ -1536,16 +2467,11 @@ declare const ResultRequest: z.ZodObject<{
1536
2467
  error: "error";
1537
2468
  canceled: "canceled";
1538
2469
  }>;
1539
- model: z.ZodString;
1540
- backendClass: z.ZodEnum<{
1541
- http: "http";
1542
- process: "process";
1543
- }>;
1544
- durationMs: z.ZodNumber;
1545
2470
  }, z.core.$strict>;
1546
2471
  type ResultRequest = z.infer<typeof ResultRequest>;
1547
2472
  declare const ResultResponse: z.ZodObject<{
1548
2473
  accepted: z.ZodBoolean;
2474
+ duplicate: z.ZodOptional<z.ZodBoolean>;
1549
2475
  state: z.ZodString;
1550
2476
  }, z.core.$strict>;
1551
2477
  type ResultResponse = z.infer<typeof ResultResponse>;
@@ -1555,13 +2481,13 @@ declare const ReleaseRequest: z.ZodObject<{
1555
2481
  leases: z.ZodArray<z.ZodObject<{
1556
2482
  jobId: z.ZodString;
1557
2483
  leaseId: z.ZodString;
1558
- }, z.core.$strip>>;
2484
+ }, z.core.$strict>>;
1559
2485
  reason: z.ZodEnum<{
1560
- revoked: "revoked";
2486
+ refused: "refused";
1561
2487
  shutdown: "shutdown";
1562
2488
  pause: "pause";
2489
+ revoked: "revoked";
1563
2490
  "backend-down": "backend-down";
1564
- refused: "refused";
1565
2491
  }>;
1566
2492
  }, z.core.$strict>;
1567
2493
  type ReleaseRequest = z.infer<typeof ReleaseRequest>;
@@ -1582,7 +2508,11 @@ declare const WireErrorCode: z.ZodEnum<{
1582
2508
  revoked: "revoked";
1583
2509
  "bad-request": "bad-request";
1584
2510
  unauthorized: "unauthorized";
2511
+ forbidden: "forbidden";
1585
2512
  "not-found": "not-found";
2513
+ "not-ready": "not-ready";
2514
+ "too-late": "too-late";
2515
+ "clock-skew": "clock-skew";
1586
2516
  "rate-limited": "rate-limited";
1587
2517
  "server-error": "server-error";
1588
2518
  }>;
@@ -1593,18 +2523,26 @@ declare const WireError: z.ZodObject<{
1593
2523
  revoked: "revoked";
1594
2524
  "bad-request": "bad-request";
1595
2525
  unauthorized: "unauthorized";
2526
+ forbidden: "forbidden";
1596
2527
  "not-found": "not-found";
2528
+ "not-ready": "not-ready";
2529
+ "too-late": "too-late";
2530
+ "clock-skew": "clock-skew";
1597
2531
  "rate-limited": "rate-limited";
1598
2532
  "server-error": "server-error";
1599
2533
  }>;
1600
2534
  message: z.ZodString;
2535
+ supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
2536
+ minimum: z.ZodOptional<z.ZodString>;
1601
2537
  retryAfter: z.ZodOptional<z.ZodNumber>;
2538
+ serverTime: z.ZodOptional<z.ZodNumber>;
2539
+ maxSkewMs: z.ZodOptional<z.ZodNumber>;
1602
2540
  }, z.core.$strict>;
1603
2541
  type WireError = z.infer<typeof WireError>;
1604
2542
  /** HTTP status each error code is served with. */
1605
2543
  declare const ERROR_STATUS: Readonly<Record<WireErrorCode, number>>;
1606
2544
  declare const FetchRequest: z.ZodObject<{
1607
- protocolVersion: z.ZodString;
2545
+ protocolVersion: z.ZodLiteral<"0">;
1608
2546
  runnerId: z.ZodString;
1609
2547
  jobId: z.ZodString;
1610
2548
  leaseId: z.ZodString;
@@ -1624,4 +2562,4 @@ declare const FetchResponse: z.ZodObject<{
1624
2562
  }, z.core.$strict>;
1625
2563
  type FetchResponse = z.infer<typeof FetchResponse>;
1626
2564
 
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 };
2565
+ export { AUDIENCES, Audience, BACKENDS, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, CLOCK_ATTRIBUTION_MS, CLOCK_SKEW_WARN_MS, 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, GRANT_CONTEXT, GRANT_MAX_AGE_MS, GRANT_SIGNED_FIELDS, GeneratePayload, type GrantClaims, GrantRef, type GrantRefusal, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_PURPOSES, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, Manifest, 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, Purpose, REFUSAL_MESSAGES, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkProtocolVersion, classifyCost, cryptoReady, declaredVersion, effectiveOfferScope, fingerprint, generateKeys, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signGrant, signRequest, signSiteRequest, signSuccession, signWith, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, verifyGrant, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession };