@byollm/protocol 0.1.0-alpha.54 → 0.1.0-alpha.56

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.54`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.56`) — 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.54` is that release, whole.
86
+ > so `0.1.0-alpha.56` 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
@@ -1029,7 +1029,17 @@ declare function publicIdentityOf(keys: StoredKeys): PublicIdentity;
1029
1029
  */
1030
1030
  declare function verifyPublicIdentity(identity: PublicIdentity): boolean;
1031
1031
  /** Sign arbitrary bytes with an identity key. */
1032
- declare function signWith(keys: StoredKeys, data: Uint8Array): string;
1032
+ /**
1033
+ * Sign bytes with an identity key.
1034
+ *
1035
+ * Takes only the private half it uses. A signer that demanded a whole
1036
+ * {@link StoredKeys} would make every caller hold an encryption keypair for a
1037
+ * job that has no encryption in it — and the control plane, which signs
1038
+ * rosters and opens nothing, would be generating and storing secret material
1039
+ * it can never need. Every existing caller passes a full `StoredKeys`, which
1040
+ * satisfies this.
1041
+ */
1042
+ declare function signWith(keys: Pick<StoredKeys, "identityPrivate">, data: Uint8Array): string;
1033
1043
  /** Verify bytes against a raw Ed25519 public key. */
1034
1044
  declare function verifyWith(identityPublic: string, data: Uint8Array, signature: string): boolean;
1035
1045
  /**
@@ -1286,6 +1296,107 @@ declare function verifyRequest(input: {
1286
1296
  maxSkewMs?: number;
1287
1297
  }): SignatureFailure | null;
1288
1298
 
1299
+ /**
1300
+ * The roster a daemon holds, and how it knows the roster is real.
1301
+ *
1302
+ * byollm_001 Amendment G, RATIFIED 2026-08-25. A `team` job is admitted by a
1303
+ * list **this device holds**, signed by the owner's control plane and verified
1304
+ * against a key pinned at pairing — never by an assertion from the party
1305
+ * routing the job, per-job or in bulk.
1306
+ *
1307
+ * The relay's power over this is exactly denial: it can withhold a roster as
1308
+ * it can withhold a job, and it can forge neither. That is what lets the hub
1309
+ * go on reading and filtering rosters for its own routing without being
1310
+ * trusted for admission — non-authorship, not blindness.
1311
+ */
1312
+ /**
1313
+ * How old a held roster may be before a device stops honouring it.
1314
+ *
1315
+ * One hour, and the shortest constant in this protocol on purpose: every other
1316
+ * one bounds how long a *thing* stays valid, and this one alone bounds how
1317
+ * long a *person* keeps access after the owner has said no.
1318
+ *
1319
+ * A failure bound, not a sync interval. A daemon refreshes on every heartbeat
1320
+ * and a removal propagates in seconds; the hour is what a device gets when
1321
+ * that conversation stops working — enough that a closed laptop or a flaky
1322
+ * café network does not narrow a working device, and not so much that a
1323
+ * removed teammate outlives the owner's patience.
1324
+ */
1325
+ declare const ROSTER_MAX_AGE_MS: number;
1326
+ /**
1327
+ * The domain separator, and why a roster gets its own.
1328
+ *
1329
+ * Every signature in this system says what kind of statement it is before it
1330
+ * says anything else. Without that, bytes signed for one purpose verify for
1331
+ * another: a roster document and a request body are both "some bytes this key
1332
+ * signed", and a scheme that could not tell them apart would let a captured
1333
+ * roster be replayed as a request — or worse, let a control plane that signs
1334
+ * one thing be held to have signed the other.
1335
+ */
1336
+ declare const ROSTER_CONTEXT = "byollm/v1/roster";
1337
+ declare const SignedRoster: z.ZodObject<{
1338
+ owner: z.ZodString;
1339
+ members: z.ZodArray<z.ZodString>;
1340
+ issuedAt: z.ZodNumber;
1341
+ signature: z.ZodString;
1342
+ }, z.core.$strict>;
1343
+ type SignedRoster = z.infer<typeof SignedRoster>;
1344
+ /**
1345
+ * The exact bytes both sides sign and verify.
1346
+ *
1347
+ * Every field that decides what the roster *means* is in here. Leave one out
1348
+ * and it becomes something an intermediary can change without breaking the
1349
+ * signature — `owner` most of all: a roster whose owner was not signed over
1350
+ * could be lifted from one account and delivered to another's devices, and
1351
+ * every signature check would pass.
1352
+ *
1353
+ * Members are joined with NUL, which cannot appear in an id, so no arrangement
1354
+ * of member names can imitate a different membership. Joining on a comma would
1355
+ * let `["a,b"]` and `["a","b"]` sign identically.
1356
+ */
1357
+ declare function rosterStatement(input: {
1358
+ owner: string;
1359
+ members: readonly string[];
1360
+ issuedAt: number;
1361
+ }): Uint8Array;
1362
+ /** Sign a roster with the control plane's own key. */
1363
+ declare function signRoster(keys: Pick<StoredKeys, "identityPrivate">, input: {
1364
+ owner: string;
1365
+ members: readonly string[];
1366
+ issuedAt: number;
1367
+ }): SignedRoster;
1368
+ /** Why a roster was refused. Typed for logs; never returned to a caller. */
1369
+ type RosterRefusal = "bad-signature" | "wrong-owner" | "stale" | "from-the-future"
1370
+ /**
1371
+ * A roster arrived and this device pinned no key to check it against.
1372
+ *
1373
+ * Not a bad signature — nothing was checked. It is the one refusal that is
1374
+ * evidence *about the pairing* rather than about the document: an upstream
1375
+ * that sends rosters has a control plane, so a device holding no key from
1376
+ * it paired before roster sync existed and can never verify one. The remedy
1377
+ * is re-pairing, and this is the only refusal here that has one.
1378
+ */
1379
+ | "no-pinned-key";
1380
+ /**
1381
+ * Is this roster one this device may admit people from, right now?
1382
+ *
1383
+ * `owner` is passed in rather than read out of the document, for the reason
1384
+ * {@link verifyLink} takes its successor as an argument: a verifier that
1385
+ * recovered the owner from the signed bytes would accept a genuine roster
1386
+ * belonging to somebody else, and every check would pass.
1387
+ *
1388
+ * Age is checked in **both** directions. A clock far ahead is as much a
1389
+ * problem as one behind: an `issuedAt` in the future would extend a roster's
1390
+ * life past the bound, which is the whole thing being enforced.
1391
+ */
1392
+ declare function verifyRoster(input: {
1393
+ roster: SignedRoster;
1394
+ owner: string;
1395
+ controlPlanePublic: string;
1396
+ now: number;
1397
+ maxAgeMs?: number;
1398
+ }): RosterRefusal | null;
1399
+
1289
1400
  /**
1290
1401
  * Rotation — byollm_009 Amendment C.
1291
1402
  *
@@ -1850,6 +1961,7 @@ declare const PairPollResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
1850
1961
  encryption: z.ZodString;
1851
1962
  encryptionSig: z.ZodString;
1852
1963
  }, z.core.$strict>>;
1964
+ controlPlanePublic: z.ZodOptional<z.ZodString>;
1853
1965
  }, z.core.$strict>], "status">;
1854
1966
  type PairPollResponse = z.infer<typeof PairPollResponse>;
1855
1967
  declare const PairRequest: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -2082,6 +2194,12 @@ declare const HeartbeatResponse: z.ZodObject<{
2082
2194
  }, z.core.$strict>>;
2083
2195
  serverTime: z.ZodNumber;
2084
2196
  awaitingConsent: z.ZodArray<z.ZodString>;
2197
+ roster: z.ZodOptional<z.ZodObject<{
2198
+ owner: z.ZodString;
2199
+ members: z.ZodArray<z.ZodString>;
2200
+ issuedAt: z.ZodNumber;
2201
+ signature: z.ZodString;
2202
+ }, z.core.$strict>>;
2085
2203
  }, z.core.$strict>;
2086
2204
  type HeartbeatResponse = z.infer<typeof HeartbeatResponse>;
2087
2205
  /**
@@ -2217,4 +2335,4 @@ declare const FetchResponse: z.ZodObject<{
2217
2335
  }, z.core.$strict>;
2218
2336
  type FetchResponse = z.infer<typeof FetchResponse>;
2219
2337
 
2220
- export { AUDIENCES, Audience, BACKENDS, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, FetchRequest, FetchResponse, GeneratePayload, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, REFUSAL_MESSAGES, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SizeClass, type SpendConsent, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkProtocolVersion, classifyCost, cryptoReady, declaredVersion, effectiveOfferScope, fingerprint, generateKeys, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signRequest, signSiteRequest, signSuccession, signWith, sizeClassCeiling, sizeClassOf, successionStatement, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession };
2338
+ export { AUDIENCES, Audience, BACKENDS, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, FetchRequest, FetchResponse, GeneratePayload, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, REFUSAL_MESSAGES, RETIREMENT_WINDOW_MS, ROSTER_CONTEXT, ROSTER_MAX_AGE_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, type RosterRefusal, RunMetadata, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SignedRoster, 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, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, rosterStatement, seal, signRequest, signRoster, signSiteRequest, signSuccession, signWith, sizeClassCeiling, sizeClassOf, successionStatement, verifyLink, verifyPublicIdentity, verifyRequest, verifyRoster, verifySiteRequest, verifyWith, walkSuccession };