@byollm/protocol 0.1.0-alpha.31 → 0.1.0-alpha.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  > [!WARNING]
2
- > **Alpha (`0.1.0-alpha.31`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.33`) — under active development. Don't use this yet.**
3
3
  >
4
4
  > Install it deliberately: `npm install @byollm/protocol@alpha`.
5
5
  >
@@ -83,7 +83,7 @@
83
83
  > packages published and `@byollm/server` did not: a Sigstore
84
84
  > transparency-log 409 on its provenance attestation. The workflow's
85
85
  > "already published" guard correctly refuses to resume a partial publish,
86
- > so `0.1.0-alpha.31` is that release, whole.
86
+ > so `0.1.0-alpha.33` is that release, whole.
87
87
  >
88
88
  > If you run the Supabase adapter, `alpha.21` needs
89
89
  > `20260819010000_completed_by_lease_id.sql`: alpha.19 shipped §3.6's
package/dist/index.d.ts CHANGED
@@ -1108,6 +1108,110 @@ declare function verifyRequest(input: {
1108
1108
  maxSkewMs?: number;
1109
1109
  }): SignatureFailure | null;
1110
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
+
1111
1215
  /**
1112
1216
  * The normative MUSTs of protocol v0, as data.
1113
1217
  *
@@ -1696,6 +1800,17 @@ declare const HeartbeatResponse: z.ZodObject<{
1696
1800
  encryption: z.ZodString;
1697
1801
  encryptionSig: z.ZodString;
1698
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>>>;
1699
1814
  cancel: z.ZodArray<z.ZodObject<{
1700
1815
  jobId: z.ZodString;
1701
1816
  leaseId: z.ZodString;
@@ -1841,4 +1956,4 @@ declare const FetchResponse: z.ZodObject<{
1841
1956
  }, z.core.$strict>;
1842
1957
  type FetchResponse = z.infer<typeof FetchResponse>;
1843
1958
 
1844
- 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, 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, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASS_LIMITS, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SizeClass, type SpendConsent, StoredKeys, 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, signWith, sizeClassCeiling, sizeClassOf, verifyPublicIdentity, verifyRequest, verifySiteRequest, 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 };