@byollm/protocol 0.1.0-alpha.4 → 0.1.0-alpha.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -2
- package/dist/index.d.ts +358 -26
- package/dist/index.js +692 -115
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -314,6 +314,13 @@ declare const REFUSAL_MESSAGES: Readonly<Record<MatchRefusal, string>>;
|
|
|
314
314
|
* Upper bounds on payload size, enforced at the schema so oversized input is
|
|
315
315
|
* refused at parse time rather than somewhere deeper.
|
|
316
316
|
*
|
|
317
|
+
* All three are enforced — cloud_008 Tier 4, finding 30. `maxTotalChars` was
|
|
318
|
+
* declared here and referenced nowhere, under this docstring's claim that the
|
|
319
|
+
* schema enforces them, so a chat payload of 256 messages at a million
|
|
320
|
+
* characters each parsed cleanly at sixty-four times the stated ceiling. The
|
|
321
|
+
* per-field limits were real and the aggregate one was a number in a frozen
|
|
322
|
+
* object.
|
|
323
|
+
*
|
|
317
324
|
* byollm_004 §4 requires stricter limits for community (`named`/`public`)
|
|
318
325
|
* jobs; those are applied on top of these by the daemon's budget check, which
|
|
319
326
|
* knows the job's audience. These are the absolute ceilings for any job.
|
|
@@ -503,7 +510,7 @@ declare const ClaimedJob: z.ZodObject<{
|
|
|
503
510
|
public: "public";
|
|
504
511
|
}>;
|
|
505
512
|
owner: z.ZodString;
|
|
506
|
-
|
|
513
|
+
site: z.ZodOptional<z.ZodString>;
|
|
507
514
|
lease: z.ZodObject<{
|
|
508
515
|
id: z.ZodString;
|
|
509
516
|
runnerId: z.ZodString;
|
|
@@ -516,7 +523,7 @@ type ClaimedJob = z.infer<typeof ClaimedJob>;
|
|
|
516
523
|
*
|
|
517
524
|
* byollm_003 Rev 1: a `named`/`public` result is attacker-controlled text.
|
|
518
525
|
* The app must never render volunteer output as its own AI's answer without
|
|
519
|
-
* knowing that is what it is ({@link MUSTS.
|
|
526
|
+
* knowing that is what it is ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).
|
|
520
527
|
*/
|
|
521
528
|
declare const ResultProvenance: z.ZodObject<{
|
|
522
529
|
audience: z.ZodEnum<{
|
|
@@ -545,6 +552,30 @@ declare function provenanceFor(input: {
|
|
|
545
552
|
backendClass: BackendClass;
|
|
546
553
|
model: string;
|
|
547
554
|
}): ResultProvenance;
|
|
555
|
+
/**
|
|
556
|
+
* What the daemon did, sealed with the answer — cloud_008 §2.5.
|
|
557
|
+
*
|
|
558
|
+
* These travelled in the clear on `ResultRequest`, which meant two things at
|
|
559
|
+
* once. On the direct plane the site believed unauthenticated fields beside
|
|
560
|
+
* an authenticated envelope — a daemon could seal one answer and *declare* it
|
|
561
|
+
* came from a different model, and only the field it did not sign would be
|
|
562
|
+
* recorded. Through a relay they reached a third party that acts on none of
|
|
563
|
+
* them, and `model` in particular is the kind of detail Amendment A's rule
|
|
564
|
+
* keeps off the wire.
|
|
565
|
+
*
|
|
566
|
+
* Sealed, they are the daemon's signed statement about its own run: the site
|
|
567
|
+
* opens them, nothing in between sees them, and the disposition check that
|
|
568
|
+
* already compares clear-text against ciphertext extends to cover them.
|
|
569
|
+
*/
|
|
570
|
+
declare const RunMetadata: z.ZodObject<{
|
|
571
|
+
model: z.ZodString;
|
|
572
|
+
backendClass: z.ZodEnum<{
|
|
573
|
+
http: "http";
|
|
574
|
+
process: "process";
|
|
575
|
+
}>;
|
|
576
|
+
durationMs: z.ZodNumber;
|
|
577
|
+
}, z.core.$strict>;
|
|
578
|
+
type RunMetadata = z.infer<typeof RunMetadata>;
|
|
548
579
|
/** Successful outcome. */
|
|
549
580
|
declare const JobResultOk: z.ZodObject<{
|
|
550
581
|
outcome: z.ZodLiteral<"ok">;
|
|
@@ -575,6 +606,37 @@ declare const JobOutcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
575
606
|
outcome: z.ZodLiteral<"canceled">;
|
|
576
607
|
}, z.core.$strict>], "outcome">;
|
|
577
608
|
type JobOutcome = z.infer<typeof JobOutcome>;
|
|
609
|
+
/**
|
|
610
|
+
* The plaintext inside a result envelope.
|
|
611
|
+
*
|
|
612
|
+
* The outcome and how it was produced, together, because they are one
|
|
613
|
+
* statement by one signer. A site that opened only the outcome would be
|
|
614
|
+
* trusting the envelope for the answer and the request body for everything
|
|
615
|
+
* about it.
|
|
616
|
+
*/
|
|
617
|
+
declare const SealedOutcome: z.ZodObject<{
|
|
618
|
+
outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
619
|
+
outcome: z.ZodLiteral<"ok">;
|
|
620
|
+
text: z.ZodString;
|
|
621
|
+
artifactUrl: z.ZodOptional<z.ZodURL>;
|
|
622
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
623
|
+
outcome: z.ZodLiteral<"error">;
|
|
624
|
+
code: z.ZodString;
|
|
625
|
+
message: z.ZodString;
|
|
626
|
+
retryable: z.ZodBoolean;
|
|
627
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
628
|
+
outcome: z.ZodLiteral<"canceled">;
|
|
629
|
+
}, z.core.$strict>], "outcome">;
|
|
630
|
+
ran: z.ZodObject<{
|
|
631
|
+
model: z.ZodString;
|
|
632
|
+
backendClass: z.ZodEnum<{
|
|
633
|
+
http: "http";
|
|
634
|
+
process: "process";
|
|
635
|
+
}>;
|
|
636
|
+
durationMs: z.ZodNumber;
|
|
637
|
+
}, z.core.$strict>;
|
|
638
|
+
}, z.core.$strict>;
|
|
639
|
+
type SealedOutcome = z.infer<typeof SealedOutcome>;
|
|
578
640
|
/** A completed job as delivered to the app, provenance attached. */
|
|
579
641
|
declare const DeliveredResult: z.ZodObject<{
|
|
580
642
|
jobId: z.ZodString;
|
|
@@ -677,12 +739,12 @@ declare const JobStub: z.ZodObject<{
|
|
|
677
739
|
"llm.chat": "llm.chat";
|
|
678
740
|
}>;
|
|
679
741
|
owner: z.ZodString;
|
|
742
|
+
site: z.ZodString;
|
|
680
743
|
audience: z.ZodEnum<{
|
|
681
744
|
self: "self";
|
|
682
745
|
named: "named";
|
|
683
746
|
public: "public";
|
|
684
747
|
}>;
|
|
685
|
-
audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
686
748
|
sizeClass: z.ZodEnum<{
|
|
687
749
|
small: "small";
|
|
688
750
|
medium: "medium";
|
|
@@ -701,12 +763,12 @@ declare const ClaimedStub: z.ZodObject<{
|
|
|
701
763
|
"llm.chat": "llm.chat";
|
|
702
764
|
}>;
|
|
703
765
|
owner: z.ZodString;
|
|
766
|
+
site: z.ZodString;
|
|
704
767
|
audience: z.ZodEnum<{
|
|
705
768
|
self: "self";
|
|
706
769
|
named: "named";
|
|
707
770
|
public: "public";
|
|
708
771
|
}>;
|
|
709
|
-
audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
710
772
|
sizeClass: z.ZodEnum<{
|
|
711
773
|
small: "small";
|
|
712
774
|
medium: "medium";
|
|
@@ -763,6 +825,18 @@ declare const StoredKeys: z.ZodObject<{
|
|
|
763
825
|
createdAt: z.ZodNumber;
|
|
764
826
|
}, z.core.$strict>;
|
|
765
827
|
type StoredKeys = z.infer<typeof StoredKeys>;
|
|
828
|
+
/** Domain separator, so a signature over an encryption key cannot be
|
|
829
|
+
* replayed as a signature over anything else. */
|
|
830
|
+
/**
|
|
831
|
+
* What an encryption key's signature covers.
|
|
832
|
+
*
|
|
833
|
+
* Exported because a rotation is a real event this protocol has to be able to
|
|
834
|
+
* *test* — a record whose encryption key moved under an identity that signed
|
|
835
|
+
* the move is the one case pinning must refuse loudly, and building one
|
|
836
|
+
* outside this file otherwise means re-typing this string, which is how two
|
|
837
|
+
* copies of a constant start disagreeing.
|
|
838
|
+
*/
|
|
839
|
+
declare const ENCRYPTION_KEY_CONTEXT = "byollm/v1/encryption-key";
|
|
766
840
|
/** Generate a fresh pair of keypairs and bind them together. */
|
|
767
841
|
declare function generateKeys(now: number): StoredKeys;
|
|
768
842
|
/** The public half, for the wire. */
|
|
@@ -946,7 +1020,77 @@ declare function signRequest(keys: StoredKeys, input: {
|
|
|
946
1020
|
issuedAt: number;
|
|
947
1021
|
body: string;
|
|
948
1022
|
}): RequestSignature;
|
|
949
|
-
/**
|
|
1023
|
+
/**
|
|
1024
|
+
* The same scheme, for the party at the other end: a **site** calling a relay.
|
|
1025
|
+
*
|
|
1026
|
+
* A site talking to a relay is in exactly the daemon's position — an outbound
|
|
1027
|
+
* caller with an identity keypair the other side already pins — so it gets the
|
|
1028
|
+
* daemon's authentication rather than a second scheme. Bearer tokens for the
|
|
1029
|
+
* site plane were the alternative, and they would have reintroduced the
|
|
1030
|
+
* credential-in-a-file that §4.2 removed from the daemon plane, on the plane
|
|
1031
|
+
* that carries *every* site's traffic.
|
|
1032
|
+
*
|
|
1033
|
+
* Two things make this safe to build on the same canonical string:
|
|
1034
|
+
*
|
|
1035
|
+
* 1. **The endpoint is namespaced.** Site endpoints sign `site/enqueue`, never
|
|
1036
|
+
* `enqueue`. The daemon plane's `result` and the site plane's `results` are
|
|
1037
|
+
* one character apart, and a naming collision between planes must not be
|
|
1038
|
+
* what stands between a signature and a replay onto the wrong handler. The
|
|
1039
|
+
* prefix is applied *inside* these helpers, so the two ends cannot disagree
|
|
1040
|
+
* about it — the alternative is two implementations of one bound value,
|
|
1041
|
+
* which is this project's most-repeated bug.
|
|
1042
|
+
* 2. **The caller slot carries the site id.** `canonicalRequest` names that
|
|
1043
|
+
* field `runnerId` because the daemon plane got there first; here it holds
|
|
1044
|
+
* the site id, and the verifier looks the key up in the projection's site
|
|
1045
|
+
* registry rather than its device registry. The two registries never share
|
|
1046
|
+
* an entry, so a device signature cannot authenticate as a site.
|
|
1047
|
+
*
|
|
1048
|
+
* §4.2's replay argument carries over **only because the site plane's writes
|
|
1049
|
+
* are idempotent per addressed instance**, which is a property that had to be
|
|
1050
|
+
* built rather than found: `enqueue` reset a job of the same id, so a replayed
|
|
1051
|
+
* enqueue inside the freshness window returned a claimed job to the queue and
|
|
1052
|
+
* threw away a device's live lease. Identical in shape to the `release` bug
|
|
1053
|
+
* above, on the other plane. Anything added to the site plane later must be
|
|
1054
|
+
* idempotent by the instance it names, or this scheme does not cover it.
|
|
1055
|
+
*/
|
|
1056
|
+
declare function signSiteRequest(keys: StoredKeys, input: {
|
|
1057
|
+
endpoint: string;
|
|
1058
|
+
siteId: string;
|
|
1059
|
+
issuedAt: number;
|
|
1060
|
+
body: string;
|
|
1061
|
+
}): RequestSignature;
|
|
1062
|
+
/** Verify a site's call against the identity the control plane registered. */
|
|
1063
|
+
declare function verifySiteRequest(input: {
|
|
1064
|
+
identityPublic: string;
|
|
1065
|
+
endpoint: string;
|
|
1066
|
+
body: string;
|
|
1067
|
+
signature: RequestSignature;
|
|
1068
|
+
now: number;
|
|
1069
|
+
maxSkewMs?: number;
|
|
1070
|
+
}): SignatureFailure | null;
|
|
1071
|
+
/**
|
|
1072
|
+
* Why a signed request was refused.
|
|
1073
|
+
*
|
|
1074
|
+
* **`bad-signature` is never returned verbatim; `stale` is, deliberately.**
|
|
1075
|
+
* They are different kinds of refusal and conflating them costs a real user
|
|
1076
|
+
* more than it costs an attacker.
|
|
1077
|
+
*
|
|
1078
|
+
* A bad signature is an authentication failure and the server says only
|
|
1079
|
+
* "unauthorized" — telling a prober which part they got wrong is free help.
|
|
1080
|
+
*
|
|
1081
|
+
* A stale timestamp is a **precondition** failure: the signature may be
|
|
1082
|
+
* perfectly valid and the caller's clock is simply wrong. Saying so reveals
|
|
1083
|
+
* nothing, for two reasons that both have to hold. The server's time is
|
|
1084
|
+
* already public — every response carries a `Date` header and the heartbeat
|
|
1085
|
+
* response returns `serverTime` outright. And freshness is checked *before*
|
|
1086
|
+
* the signature is verified, so a stale answer says nothing about whether the
|
|
1087
|
+
* signature was any good.
|
|
1088
|
+
*
|
|
1089
|
+
* What conflating them costs: a machine whose clock has drifted gets
|
|
1090
|
+
* `401 unauthorized` on every request, forever, with nothing anywhere pointing
|
|
1091
|
+
* at the clock. That is the shape byollm_013 was filed about — a refusal that
|
|
1092
|
+
* is correct, silent, and sends somebody to read our source.
|
|
1093
|
+
*/
|
|
950
1094
|
type SignatureFailure = "stale" | "bad-signature";
|
|
951
1095
|
/**
|
|
952
1096
|
* Verify a signed request against a runner's pinned identity key.
|
|
@@ -964,6 +1108,110 @@ declare function verifyRequest(input: {
|
|
|
964
1108
|
maxSkewMs?: number;
|
|
965
1109
|
}): SignatureFailure | null;
|
|
966
1110
|
|
|
1111
|
+
/**
|
|
1112
|
+
* Rotation — byollm_009 Amendment C.
|
|
1113
|
+
*
|
|
1114
|
+
* A site holding identity key **K1** wants to be known by **K2**. It publishes
|
|
1115
|
+
* a *succession*: K2, plus a signature by K1 over a statement naming both key
|
|
1116
|
+
* ids. That signature is the entire mechanism, and the reason rotation can be
|
|
1117
|
+
* automatic without becoming a hole is that **the relay cannot mint one** — it
|
|
1118
|
+
* never holds K1. It is the same trust step a daemon already performs at
|
|
1119
|
+
* pairing, applied to the site's own succession.
|
|
1120
|
+
*
|
|
1121
|
+
* ## Why the statement names both keys
|
|
1122
|
+
*
|
|
1123
|
+
* A signature over K2 alone could be lifted from this site's record and
|
|
1124
|
+
* replayed into another site's, moving *that* site to K2 — a key the attacker
|
|
1125
|
+
* holds. Naming the predecessor binds the succession to one chain, and it is
|
|
1126
|
+
* the reason `verifyLink` takes the id it expects to be succeeding from
|
|
1127
|
+
* rather than reading it out of the statement it is checking.
|
|
1128
|
+
*/
|
|
1129
|
+
/** The domain separator. Distinct from every other thing an identity signs. */
|
|
1130
|
+
declare const SUCCESSION_CONTEXT = "byollm/v1/site-succession";
|
|
1131
|
+
/**
|
|
1132
|
+
* How long a retired key may still sign work — Amendment C, ruling 2.
|
|
1133
|
+
*
|
|
1134
|
+
* A protocol constant and not the site's to choose. Per-site overlap
|
|
1135
|
+
* arithmetic is exactly the kind of number that has to mean one thing
|
|
1136
|
+
* everywhere, and a site that could choose it could choose *forever*, which is
|
|
1137
|
+
* a two-key site permanently and a second key nobody ever notices retiring.
|
|
1138
|
+
*
|
|
1139
|
+
* Seven days: long enough that a daemon which polls daily and a laptop shut
|
|
1140
|
+
* for a long weekend both see the new record before the old key stops working,
|
|
1141
|
+
* short enough that "which key is live" is never an interesting question.
|
|
1142
|
+
*/
|
|
1143
|
+
declare const RETIREMENT_WINDOW_MS: number;
|
|
1144
|
+
/**
|
|
1145
|
+
* The longest chain a daemon will walk — Amendment C, ruling 1.
|
|
1146
|
+
*
|
|
1147
|
+
* **A denial-of-service guard, not policy.** The bound exists so a projection
|
|
1148
|
+
* cannot make a daemon verify ten thousand signatures, not to express an
|
|
1149
|
+
* opinion about how often a site may rotate. A site that legitimately exceeds
|
|
1150
|
+
* it has a re-pair ahead of it, which is why it is generous: at one rotation a
|
|
1151
|
+
* quarter this is sixteen years.
|
|
1152
|
+
*/
|
|
1153
|
+
declare const MAX_SUCCESSION_CHAIN = 64;
|
|
1154
|
+
/** One step of a chain: a key, and the signature by it over its successor. */
|
|
1155
|
+
declare const Succession: z.ZodObject<{
|
|
1156
|
+
identity: z.ZodObject<{
|
|
1157
|
+
identity: z.ZodString;
|
|
1158
|
+
encryption: z.ZodString;
|
|
1159
|
+
encryptionSig: z.ZodString;
|
|
1160
|
+
}, z.core.$strict>;
|
|
1161
|
+
signature: z.ZodString;
|
|
1162
|
+
}, z.core.$strict>;
|
|
1163
|
+
type Succession = z.infer<typeof Succession>;
|
|
1164
|
+
/** The exact bytes signed. One definition; both sides call it. */
|
|
1165
|
+
declare function successionStatement(fromKeyId: string, toKeyId: string): Uint8Array;
|
|
1166
|
+
/**
|
|
1167
|
+
* Sign a succession from the keys being retired to the identity taking over.
|
|
1168
|
+
*
|
|
1169
|
+
* Takes `StoredKeys` for the predecessor because only the holder of K1's
|
|
1170
|
+
* private half can produce this, which is the property the whole design rests
|
|
1171
|
+
* on. A site calls this once, at rotation, on the machine holding its keys.
|
|
1172
|
+
*/
|
|
1173
|
+
declare function signSuccession(previous: StoredKeys, next: PublicIdentity): Succession;
|
|
1174
|
+
/**
|
|
1175
|
+
* Check one link: did `link.identity` sign over succeeding to `toKeyId`?
|
|
1176
|
+
*
|
|
1177
|
+
* `toKeyId` is passed in rather than read from anywhere in `link`, and that is
|
|
1178
|
+
* the load-bearing detail. A verifier that recovered the successor from the
|
|
1179
|
+
* signed statement would accept a statement about *any* successor, which is
|
|
1180
|
+
* the replay this design names in C.1 — the signature is genuine, the
|
|
1181
|
+
* successor it names is not the one being installed.
|
|
1182
|
+
*/
|
|
1183
|
+
declare function verifyLink(link: Succession, toKeyId: string): boolean;
|
|
1184
|
+
/** Why a chain was refused, in the words a log line uses. */
|
|
1185
|
+
type SuccessionFailure = "no-chain" | "too-long" | "unknown-origin" | "broken-link";
|
|
1186
|
+
interface SuccessionWalk {
|
|
1187
|
+
/** The ids the chain passes through, oldest first, ending at the current. */
|
|
1188
|
+
readonly path: string[];
|
|
1189
|
+
/** The approved id the chain reached, when it reached one. */
|
|
1190
|
+
readonly from?: string;
|
|
1191
|
+
readonly failure?: SuccessionFailure;
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Walk a chain from the key being presented back to a key already approved.
|
|
1195
|
+
*
|
|
1196
|
+
* `chain` is ordered oldest last, as the projection carries it — so walking it
|
|
1197
|
+
* means starting at the current key and stepping backwards, each link proving
|
|
1198
|
+
* that its holder signed for the id in front of it.
|
|
1199
|
+
*
|
|
1200
|
+
* Returns the approved id it reached, or why it did not. **Deliberately
|
|
1201
|
+
* returns rather than throws**: a chain that does not verify is ordinary
|
|
1202
|
+
* hostile input, and the caller's job is to keep its existing pin and say so.
|
|
1203
|
+
*
|
|
1204
|
+
* `approved` is asked as a predicate rather than taken as a set because the
|
|
1205
|
+
* daemon's notion of "already approved" includes tombstoned ids — a site that
|
|
1206
|
+
* left the allowlist and came back is still a site this machine has vouched
|
|
1207
|
+
* for, and rotation must not become a way to launder that distinction away.
|
|
1208
|
+
*/
|
|
1209
|
+
declare function walkSuccession(input: {
|
|
1210
|
+
current: string;
|
|
1211
|
+
chain: readonly Succession[];
|
|
1212
|
+
approved: (keyId: string) => boolean;
|
|
1213
|
+
}): SuccessionWalk;
|
|
1214
|
+
|
|
967
1215
|
/**
|
|
968
1216
|
* The normative MUSTs of protocol v0, as data.
|
|
969
1217
|
*
|
|
@@ -997,12 +1245,45 @@ type MustEnforcer = "daemon" | "server" | "both";
|
|
|
997
1245
|
* someone else's, so the kit cannot carry it.
|
|
998
1246
|
* - `construction` — true by the shape of the code, where a test could only
|
|
999
1247
|
* sample. A reviewer verifies it; a suite cannot.
|
|
1248
|
+
* ## When a MUST binds both sides — cloud_008 Tier 3
|
|
1249
|
+
*
|
|
1250
|
+
* `AUDIENCE_BOTH_SIDES` says the server and the daemon each enforce. The kit
|
|
1251
|
+
* passed **entirely** with the server's half deleted: every check drove a real
|
|
1252
|
+
* daemon, and a daemon refuses locally, so "the job did not run" looked
|
|
1253
|
+
* identical whichever side refused it. A full-honest-stack test proves only
|
|
1254
|
+
* the conjunction.
|
|
1255
|
+
*
|
|
1256
|
+
* So a `both`-enforced MUST needs **one check per party, each with the honest
|
|
1257
|
+
* counterpart removed** — C032 claims over the raw protocol precisely so no
|
|
1258
|
+
* daemon admission logic runs. Where a check strips one side, its comment
|
|
1259
|
+
* says which; where a MUST is enforced by both and only one side is checked,
|
|
1260
|
+
* that is a gap rather than coverage.
|
|
1261
|
+
*
|
|
1000
1262
|
* - `operator` — a claim about how someone runs a deployment, verifiable only
|
|
1001
1263
|
* by audit or by reading source. The honest category, and the one that
|
|
1002
1264
|
* exists so a property nobody can check from outside is *labelled* as such
|
|
1003
1265
|
* rather than laundered by association with the checkable ones.
|
|
1004
1266
|
*/
|
|
1005
1267
|
type MustVerification = "conformance" | "adversarial" | "construction" | "operator";
|
|
1268
|
+
/**
|
|
1269
|
+
* How a MUST is verified — one kind, or several.
|
|
1270
|
+
*
|
|
1271
|
+
* Several is not hedging. `SITES_LOCALLY_APPROVED` is the case that forced it:
|
|
1272
|
+
* the fence is **construction** — a daemon cannot serve a site that is not in
|
|
1273
|
+
* its map, and admission refuses before a payload is fetched — while the
|
|
1274
|
+
* property that a *removed and re-offered* id is still refused needs a hostile
|
|
1275
|
+
* sequence of heartbeats no honest client would send, which is
|
|
1276
|
+
* **adversarial**. Recording one and dropping the other would either overstate
|
|
1277
|
+
* what a type check proves or understate what the suites do.
|
|
1278
|
+
*
|
|
1279
|
+
* The alternative was a second field for the second kind, which is two answers
|
|
1280
|
+
* to one question — the shape this project keeps deleting.
|
|
1281
|
+
*/
|
|
1282
|
+
type MustVerifiedBy = MustVerification | readonly [MustVerification, ...MustVerification[]];
|
|
1283
|
+
/** The kinds a MUST claims, always as a list. */
|
|
1284
|
+
declare function kindsOf(must: {
|
|
1285
|
+
readonly verifiedBy: MustVerifiedBy;
|
|
1286
|
+
}): readonly MustVerification[];
|
|
1006
1287
|
/** A single normative requirement of the protocol. */
|
|
1007
1288
|
interface Must {
|
|
1008
1289
|
/** Stable public id, cited by conformance output. */
|
|
@@ -1015,7 +1296,7 @@ interface Must {
|
|
|
1015
1296
|
* How this is verified. `conformance` is the only kind the kit can assert;
|
|
1016
1297
|
* see {@link MustVerification} for why the others exist.
|
|
1017
1298
|
*/
|
|
1018
|
-
readonly verifiedBy:
|
|
1299
|
+
readonly verifiedBy: MustVerifiedBy;
|
|
1019
1300
|
/** Spec section this was adjudicated in. */
|
|
1020
1301
|
readonly source: string;
|
|
1021
1302
|
}
|
|
@@ -1032,6 +1313,8 @@ declare const MUSTS: Readonly<{
|
|
|
1032
1313
|
readonly PAIR_INTERACTIVE: Must;
|
|
1033
1314
|
readonly PAIR_CODE_EXPIRES: Must;
|
|
1034
1315
|
readonly VERSION_HANDSHAKE_REQUIRED: Must;
|
|
1316
|
+
readonly SITE_KEY_BY_STUB: Must;
|
|
1317
|
+
readonly SITES_LOCALLY_APPROVED: Must;
|
|
1035
1318
|
readonly KEYS_EXCHANGED_AT_CONSENT: Must;
|
|
1036
1319
|
readonly REQUESTS_SIGNED_NOT_BEARER: Must;
|
|
1037
1320
|
readonly LEASE_SCOPED_BY_GRANT: Must;
|
|
@@ -1058,7 +1341,7 @@ declare const MUSTS: Readonly<{
|
|
|
1058
1341
|
readonly TTL_EXPIRY: Must;
|
|
1059
1342
|
readonly NO_RUNNER_SIGNAL: Must;
|
|
1060
1343
|
readonly RESULT_IDEMPOTENT: Must;
|
|
1061
|
-
readonly
|
|
1344
|
+
readonly PROVENANCE_NAMES_DEVICE: Must;
|
|
1062
1345
|
readonly INGRESS_LOGGED_BEFORE_EXECUTION: Must;
|
|
1063
1346
|
readonly NO_SHELL_INTERPOLATION: Must;
|
|
1064
1347
|
readonly NO_PAYLOAD_ROUTING: Must;
|
|
@@ -1066,11 +1349,18 @@ declare const MUSTS: Readonly<{
|
|
|
1066
1349
|
readonly HTTP_BASE_URL_SAFE: Must;
|
|
1067
1350
|
readonly OUTPUT_INERT: Must;
|
|
1068
1351
|
readonly COMMUNITY_BUDGETS: Must;
|
|
1352
|
+
readonly REVOCATION_IMMEDIATE: Must;
|
|
1353
|
+
readonly CONSENT_BEFORE_ROUTE: Must;
|
|
1354
|
+
readonly ROSTER_NOT_DISCLOSED: Must;
|
|
1355
|
+
readonly EFFECTIVE_OFFER_ONLY: Must;
|
|
1356
|
+
readonly FALLBACK_LABELED: Must;
|
|
1357
|
+
readonly RELAY_BLIND: Must;
|
|
1358
|
+
readonly SHARED_COMPUTE_DISCLOSED: Must;
|
|
1069
1359
|
}>;
|
|
1070
1360
|
/** The id of any normative MUST. */
|
|
1071
1361
|
type MustId = keyof typeof MUSTS;
|
|
1072
1362
|
/** 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" | "
|
|
1363
|
+
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
1364
|
/** Every MUST verified a particular way. */
|
|
1075
1365
|
declare function mustsVerifiedBy(kind: MustVerification): MustId[];
|
|
1076
1366
|
|
|
@@ -1100,6 +1390,23 @@ interface VersionRefusal {
|
|
|
1100
1390
|
readonly supported: readonly string[];
|
|
1101
1391
|
readonly minimum: string;
|
|
1102
1392
|
}
|
|
1393
|
+
/**
|
|
1394
|
+
* The version a request declares, wherever it carries it.
|
|
1395
|
+
*
|
|
1396
|
+
* A POST declares it in its body, which is where every request schema has
|
|
1397
|
+
* always put it. A GET has no body, and the relay has one — the site plane's
|
|
1398
|
+
* `pending` read — so it declares it in the query string instead.
|
|
1399
|
+
*
|
|
1400
|
+
* **Two carriers, one rule.** That asymmetry is HTTP's rather than ours, and
|
|
1401
|
+
* the alternative was worse in both directions: a header for everything would
|
|
1402
|
+
* change every existing daemon's request, and skipping GETs would leave an
|
|
1403
|
+
* endpoint outside the handshake — which is precisely the shape B.4 found,
|
|
1404
|
+
* where a whole plane was outside it.
|
|
1405
|
+
*/
|
|
1406
|
+
declare function declaredVersion(input: {
|
|
1407
|
+
body?: unknown;
|
|
1408
|
+
query?: URLSearchParams;
|
|
1409
|
+
}): unknown;
|
|
1103
1410
|
/**
|
|
1104
1411
|
* Check the protocol version on an incoming request
|
|
1105
1412
|
* ({@link MUSTS.VERSION_HANDSHAKE_REQUIRED}).
|
|
@@ -1298,15 +1605,14 @@ declare const PairPollResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1298
1605
|
status: z.ZodLiteral<"expired">;
|
|
1299
1606
|
}, z.core.$strict>, z.ZodObject<{
|
|
1300
1607
|
status: z.ZodLiteral<"approved">;
|
|
1301
|
-
runnerToken: z.ZodString;
|
|
1302
1608
|
runnerId: z.ZodString;
|
|
1303
1609
|
owner: z.ZodString;
|
|
1304
1610
|
ownerLabel: z.ZodOptional<z.ZodString>;
|
|
1305
|
-
|
|
1611
|
+
sites: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1306
1612
|
identity: z.ZodString;
|
|
1307
1613
|
encryption: z.ZodString;
|
|
1308
1614
|
encryptionSig: z.ZodString;
|
|
1309
|
-
}, z.core.$strict
|
|
1615
|
+
}, z.core.$strict>>;
|
|
1310
1616
|
}, z.core.$strict>], "status">;
|
|
1311
1617
|
type PairPollResponse = z.infer<typeof PairPollResponse>;
|
|
1312
1618
|
declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -1418,12 +1724,12 @@ declare const ClaimResponse: z.ZodObject<{
|
|
|
1418
1724
|
"llm.chat": "llm.chat";
|
|
1419
1725
|
}>;
|
|
1420
1726
|
owner: z.ZodString;
|
|
1727
|
+
site: z.ZodString;
|
|
1421
1728
|
audience: z.ZodEnum<{
|
|
1422
1729
|
self: "self";
|
|
1423
1730
|
named: "named";
|
|
1424
1731
|
public: "public";
|
|
1425
1732
|
}>;
|
|
1426
|
-
audienceAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1427
1733
|
sizeClass: z.ZodEnum<{
|
|
1428
1734
|
small: "small";
|
|
1429
1735
|
medium: "medium";
|
|
@@ -1489,14 +1795,32 @@ declare const HeartbeatRequest: z.ZodObject<{
|
|
|
1489
1795
|
}, z.core.$strict>;
|
|
1490
1796
|
type HeartbeatRequest = z.infer<typeof HeartbeatRequest>;
|
|
1491
1797
|
declare const HeartbeatResponse: z.ZodObject<{
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1798
|
+
sites: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1799
|
+
identity: z.ZodString;
|
|
1800
|
+
encryption: z.ZodString;
|
|
1801
|
+
encryptionSig: z.ZodString;
|
|
1802
|
+
}, z.core.$strict>>;
|
|
1803
|
+
successions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1804
|
+
succeeds: z.ZodArray<z.ZodObject<{
|
|
1805
|
+
identity: z.ZodObject<{
|
|
1806
|
+
identity: z.ZodString;
|
|
1807
|
+
encryption: z.ZodString;
|
|
1808
|
+
encryptionSig: z.ZodString;
|
|
1809
|
+
}, z.core.$strict>;
|
|
1810
|
+
signature: z.ZodString;
|
|
1811
|
+
}, z.core.$strict>>;
|
|
1812
|
+
retiringUntil: z.ZodOptional<z.ZodNumber>;
|
|
1813
|
+
}, z.core.$strict>>>;
|
|
1814
|
+
cancel: z.ZodArray<z.ZodObject<{
|
|
1495
1815
|
jobId: z.ZodString;
|
|
1496
|
-
|
|
1816
|
+
leaseId: z.ZodString;
|
|
1817
|
+
}, z.core.$strict>>;
|
|
1818
|
+
lost: z.ZodArray<z.ZodObject<{
|
|
1819
|
+
jobId: z.ZodString;
|
|
1820
|
+
leaseId: z.ZodString;
|
|
1497
1821
|
}, z.core.$strict>>;
|
|
1498
|
-
lost: z.ZodArray<z.ZodString>;
|
|
1499
1822
|
serverTime: z.ZodNumber;
|
|
1823
|
+
awaitingConsent: z.ZodArray<z.ZodString>;
|
|
1500
1824
|
}, z.core.$strict>;
|
|
1501
1825
|
type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;
|
|
1502
1826
|
/**
|
|
@@ -1521,6 +1845,7 @@ declare const ResultRequest: z.ZodObject<{
|
|
|
1521
1845
|
protocolVersion: z.ZodLiteral<"0">;
|
|
1522
1846
|
runnerId: z.ZodString;
|
|
1523
1847
|
jobId: z.ZodString;
|
|
1848
|
+
leaseId: z.ZodString;
|
|
1524
1849
|
envelope: z.ZodObject<{
|
|
1525
1850
|
ciphertext: z.ZodString;
|
|
1526
1851
|
recipientKeyId: z.ZodString;
|
|
@@ -1536,16 +1861,11 @@ declare const ResultRequest: z.ZodObject<{
|
|
|
1536
1861
|
error: "error";
|
|
1537
1862
|
canceled: "canceled";
|
|
1538
1863
|
}>;
|
|
1539
|
-
model: z.ZodString;
|
|
1540
|
-
backendClass: z.ZodEnum<{
|
|
1541
|
-
http: "http";
|
|
1542
|
-
process: "process";
|
|
1543
|
-
}>;
|
|
1544
|
-
durationMs: z.ZodNumber;
|
|
1545
1864
|
}, z.core.$strict>;
|
|
1546
1865
|
type ResultRequest = z.infer<typeof ResultRequest>;
|
|
1547
1866
|
declare const ResultResponse: z.ZodObject<{
|
|
1548
1867
|
accepted: z.ZodBoolean;
|
|
1868
|
+
duplicate: z.ZodOptional<z.ZodBoolean>;
|
|
1549
1869
|
state: z.ZodString;
|
|
1550
1870
|
}, z.core.$strict>;
|
|
1551
1871
|
type ResultResponse = z.infer<typeof ResultResponse>;
|
|
@@ -1557,9 +1877,9 @@ declare const ReleaseRequest: z.ZodObject<{
|
|
|
1557
1877
|
leaseId: z.ZodString;
|
|
1558
1878
|
}, z.core.$strip>>;
|
|
1559
1879
|
reason: z.ZodEnum<{
|
|
1560
|
-
revoked: "revoked";
|
|
1561
1880
|
shutdown: "shutdown";
|
|
1562
1881
|
pause: "pause";
|
|
1882
|
+
revoked: "revoked";
|
|
1563
1883
|
"backend-down": "backend-down";
|
|
1564
1884
|
refused: "refused";
|
|
1565
1885
|
}>;
|
|
@@ -1582,7 +1902,11 @@ declare const WireErrorCode: z.ZodEnum<{
|
|
|
1582
1902
|
revoked: "revoked";
|
|
1583
1903
|
"bad-request": "bad-request";
|
|
1584
1904
|
unauthorized: "unauthorized";
|
|
1905
|
+
forbidden: "forbidden";
|
|
1585
1906
|
"not-found": "not-found";
|
|
1907
|
+
"not-ready": "not-ready";
|
|
1908
|
+
"too-late": "too-late";
|
|
1909
|
+
"clock-skew": "clock-skew";
|
|
1586
1910
|
"rate-limited": "rate-limited";
|
|
1587
1911
|
"server-error": "server-error";
|
|
1588
1912
|
}>;
|
|
@@ -1593,18 +1917,26 @@ declare const WireError: z.ZodObject<{
|
|
|
1593
1917
|
revoked: "revoked";
|
|
1594
1918
|
"bad-request": "bad-request";
|
|
1595
1919
|
unauthorized: "unauthorized";
|
|
1920
|
+
forbidden: "forbidden";
|
|
1596
1921
|
"not-found": "not-found";
|
|
1922
|
+
"not-ready": "not-ready";
|
|
1923
|
+
"too-late": "too-late";
|
|
1924
|
+
"clock-skew": "clock-skew";
|
|
1597
1925
|
"rate-limited": "rate-limited";
|
|
1598
1926
|
"server-error": "server-error";
|
|
1599
1927
|
}>;
|
|
1600
1928
|
message: z.ZodString;
|
|
1929
|
+
supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1930
|
+
minimum: z.ZodOptional<z.ZodString>;
|
|
1601
1931
|
retryAfter: z.ZodOptional<z.ZodNumber>;
|
|
1932
|
+
serverTime: z.ZodOptional<z.ZodNumber>;
|
|
1933
|
+
maxSkewMs: z.ZodOptional<z.ZodNumber>;
|
|
1602
1934
|
}, z.core.$strict>;
|
|
1603
1935
|
type WireError = z.infer<typeof WireError>;
|
|
1604
1936
|
/** HTTP status each error code is served with. */
|
|
1605
1937
|
declare const ERROR_STATUS: Readonly<Record<WireErrorCode, number>>;
|
|
1606
1938
|
declare const FetchRequest: z.ZodObject<{
|
|
1607
|
-
protocolVersion: z.
|
|
1939
|
+
protocolVersion: z.ZodLiteral<"0">;
|
|
1608
1940
|
runnerId: z.ZodString;
|
|
1609
1941
|
jobId: z.ZodString;
|
|
1610
1942
|
leaseId: z.ZodString;
|
|
@@ -1624,4 +1956,4 @@ declare const FetchResponse: z.ZodObject<{
|
|
|
1624
1956
|
}, z.core.$strict>;
|
|
1625
1957
|
type FetchResponse = z.infer<typeof FetchResponse>;
|
|
1626
1958
|
|
|
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 };
|
|
1959
|
+
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, 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, 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, backendDescriptor, canTransition, canonicalRequest, checkProtocolVersion, cryptoReady, declaredVersion, effectiveOfferScope, fingerprint, generateKeys, isBackendId, 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 };
|