@byok-sdk/core 0.2.0 → 0.3.0

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.js CHANGED
@@ -13,6 +13,12 @@ var CORE_ERROR_CODES = {
13
13
  // device proof (§12.6.3)
14
14
  proof_envelope_invalid: "proof_envelope_invalid",
15
15
  proof_canonicalization_failed: "proof_canonicalization_failed",
16
+ // device assertion (plan device-assertion-broker) — deliberately its own
17
+ // code rather than reusing `proof_envelope_invalid`: the two envelopes are
18
+ // non-interchangeable by design, and one shared "envelope was bad" code is
19
+ // exactly how a log stops being able to say which authentication surface was
20
+ // probed.
21
+ assertion_envelope_invalid: "assertion_envelope_invalid",
16
22
  // mailbox (§12.7.3)
17
23
  mailbox_message_not_found: "mailbox_message_not_found",
18
24
  mailbox_cursor_regression: "mailbox_cursor_regression",
@@ -33,6 +39,7 @@ var CORE_ERROR_CODES = {
33
39
  hint_ttl_invalid: "hint_ttl_invalid",
34
40
  hint_rate_limited: "hint_rate_limited",
35
41
  // object manifest (§12.7.4, §12.7.8)
42
+ object_key_prefix_invalid: "object_key_prefix_invalid",
36
43
  object_not_found: "object_not_found",
37
44
  object_state_invalid: "object_state_invalid",
38
45
  // storage entitlement / usage / reservation (§12.7.6-12.7.7)
@@ -43,7 +50,10 @@ var CORE_ERROR_CODES = {
43
50
  storage_quota_exceeded: "storage_quota_exceeded",
44
51
  storage_reservation_expired: "storage_reservation_expired",
45
52
  storage_integrity_mismatch: "storage_integrity_mismatch",
46
- storage_write_suspended: "storage_write_suspended"
53
+ storage_write_suspended: "storage_write_suspended",
54
+ // skill packs (plan `skill-pack-delivery-channel`)
55
+ skill_pack_manifest_invalid: "skill_pack_manifest_invalid",
56
+ skill_pack_frontmatter_invalid: "skill_pack_frontmatter_invalid"
47
57
  };
48
58
  var ByokCoreError = class extends Error {
49
59
  code;
@@ -179,9 +189,20 @@ function contentHash(value) {
179
189
  function isContentHash(value) {
180
190
  return typeof value === "string" && CONTENT_HASH_PATTERN.test(value);
181
191
  }
182
- function tenantObjectKey(tenant, hash) {
192
+ var OBJECT_KEY_PREFIX_PATTERN = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
193
+ function objectKeyPrefix(value) {
194
+ if (!OBJECT_KEY_PREFIX_PATTERN.test(value)) {
195
+ throw new ByokCoreError(
196
+ "object_key_prefix_invalid",
197
+ `Object key prefix must match ${OBJECT_KEY_PREFIX_PATTERN.source}, received ${JSON.stringify(value)}.`
198
+ );
199
+ }
200
+ return value;
201
+ }
202
+ function tenantObjectKey(tenant, hash, prefix) {
183
203
  const hex = hash.slice("sha256:".length);
184
- return `tenants/${tenant}/objects/sha256/${hex}`;
204
+ const key = `tenants/${tenant}/objects/sha256/${hex}`;
205
+ return prefix === void 0 ? key : `${prefix}/${key}`;
185
206
  }
186
207
  var OBJECT_STATES = ["pending", "committed", "delete_pending", "deleted"];
187
208
  var OBJECT_STATE_TRANSITIONS = {
@@ -259,6 +280,248 @@ function assertCapability(declaration, capability) {
259
280
  );
260
281
  }
261
282
  }
283
+ var SKILL_PACK_MANIFEST_SCHEMA_ID = "byok-skill-pack-v1";
284
+ var SKILL_PACK_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
285
+ var SKILL_PACK_NAME_MAX_LENGTH = 64;
286
+ var SKILL_PACK_DESCRIPTION_MAX_LENGTH = 1024;
287
+ var SKILL_PACK_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
288
+ var SKILL_PACK_FILE_PATH_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*(\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/;
289
+ var SKILL_PACK_FILE_PATH_MAX_LENGTH = 200;
290
+ var SKILL_PACK_ENTRY_PATH = "SKILL.md";
291
+ var SKILL_PACK_FILE_MAX_BYTES = 262144;
292
+ var SKILL_PACK_MAX_BYTES = 1048576;
293
+ var SKILL_PACK_MAX_FILES = 64;
294
+ var SKILL_PACK_FORBIDDEN_FIELDS = [
295
+ "exec",
296
+ "command",
297
+ "entrypoint",
298
+ "run",
299
+ "script",
300
+ "shell",
301
+ "env",
302
+ "environment",
303
+ "credential",
304
+ "credentials",
305
+ "secret",
306
+ "secrets",
307
+ "token",
308
+ "apiKey",
309
+ "api_key",
310
+ "allowedTools",
311
+ "allowed-tools",
312
+ "hooks",
313
+ "preinstall",
314
+ "postinstall"
315
+ ];
316
+ var ContentHashSchema = z.string().regex(CONTENT_HASH_PATTERN).transform((value) => contentHash(value));
317
+ var SkillPackFileSchema = z.strictObject({
318
+ path: z.string().min(1).max(SKILL_PACK_FILE_PATH_MAX_LENGTH).regex(SKILL_PACK_FILE_PATH_PATTERN),
319
+ contentHash: ContentHashSchema,
320
+ byteSize: z.number().int().nonnegative().max(SKILL_PACK_FILE_MAX_BYTES)
321
+ });
322
+ var SkillPackManifestSchema = z.strictObject({
323
+ schema: z.literal(SKILL_PACK_MANIFEST_SCHEMA_ID),
324
+ name: z.string().min(1).max(SKILL_PACK_NAME_MAX_LENGTH).regex(SKILL_PACK_NAME_PATTERN),
325
+ version: z.string().regex(SKILL_PACK_VERSION_PATTERN),
326
+ description: z.string().min(1).max(SKILL_PACK_DESCRIPTION_MAX_LENGTH),
327
+ files: z.array(SkillPackFileSchema).min(1).max(SKILL_PACK_MAX_FILES),
328
+ contentHash: ContentHashSchema
329
+ });
330
+ function parseSkillPackManifest(input) {
331
+ if (typeof input === "object" && input !== null) {
332
+ const present = SKILL_PACK_FORBIDDEN_FIELDS.filter(
333
+ (field) => Object.prototype.hasOwnProperty.call(input, field)
334
+ );
335
+ if (present.length > 0) {
336
+ throw new ByokCoreError(
337
+ "skill_pack_manifest_invalid",
338
+ `A skill pack manifest carries no executable or credential surface; refusing ${present.map((field) => JSON.stringify(field)).join(", ")}.`
339
+ );
340
+ }
341
+ }
342
+ const result = SkillPackManifestSchema.safeParse(input);
343
+ if (!result.success) {
344
+ throw new ByokCoreError(
345
+ "skill_pack_manifest_invalid",
346
+ `Invalid skill pack manifest: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
347
+ { cause: result.error }
348
+ );
349
+ }
350
+ return result.data;
351
+ }
352
+ function skillPackContentHashInput(manifest) {
353
+ const rows = [...manifest.files].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0).map((file) => `${file.path}
354
+ ${file.contentHash}
355
+ ${file.byteSize}
356
+ `);
357
+ return [
358
+ `${SKILL_PACK_MANIFEST_SCHEMA_ID}
359
+ `,
360
+ `${manifest.name}
361
+ `,
362
+ `${manifest.version}
363
+ `,
364
+ `${manifest.description.length}
365
+ `,
366
+ `${manifest.description}
367
+ `,
368
+ `${manifest.files.length}
369
+ `,
370
+ ...rows
371
+ ].join("");
372
+ }
373
+ var SKILL_PACK_REJECTIONS = [
374
+ "path-unsafe",
375
+ "duplicate-path",
376
+ "entry-missing",
377
+ "file-count-over-cap",
378
+ "file-over-cap",
379
+ "pack-over-cap",
380
+ "size-mismatch",
381
+ "hash-mismatch",
382
+ "name-mismatch"
383
+ ];
384
+ function isSkillPackPathSafe(path) {
385
+ return path.length > 0 && path.length <= SKILL_PACK_FILE_PATH_MAX_LENGTH && SKILL_PACK_FILE_PATH_PATTERN.test(path);
386
+ }
387
+ function checkSkillPackManifest(manifest) {
388
+ if (manifest.files.length > SKILL_PACK_MAX_FILES) {
389
+ return {
390
+ ok: false,
391
+ reason: "file-count-over-cap",
392
+ detail: `${manifest.files.length} files exceeds the ${SKILL_PACK_MAX_FILES} file limit.`
393
+ };
394
+ }
395
+ const seen = /* @__PURE__ */ new Set();
396
+ let bytes = 0;
397
+ for (const file of manifest.files) {
398
+ if (!isSkillPackPathSafe(file.path)) {
399
+ return { ok: false, reason: "path-unsafe", detail: `${JSON.stringify(file.path)} is not a safe relative path.` };
400
+ }
401
+ if (seen.has(file.path)) {
402
+ return { ok: false, reason: "duplicate-path", detail: `${JSON.stringify(file.path)} is declared twice.` };
403
+ }
404
+ seen.add(file.path);
405
+ if (file.byteSize > SKILL_PACK_FILE_MAX_BYTES) {
406
+ return {
407
+ ok: false,
408
+ reason: "file-over-cap",
409
+ detail: `${JSON.stringify(file.path)} declares ${file.byteSize} bytes, over the ${SKILL_PACK_FILE_MAX_BYTES} byte per-file limit.`
410
+ };
411
+ }
412
+ bytes += file.byteSize;
413
+ }
414
+ if (!seen.has(SKILL_PACK_ENTRY_PATH)) {
415
+ return {
416
+ ok: false,
417
+ reason: "entry-missing",
418
+ detail: `A skill pack must carry ${SKILL_PACK_ENTRY_PATH}.`
419
+ };
420
+ }
421
+ if (bytes > SKILL_PACK_MAX_BYTES) {
422
+ return {
423
+ ok: false,
424
+ reason: "pack-over-cap",
425
+ detail: `${bytes} declared bytes exceeds the ${SKILL_PACK_MAX_BYTES} byte pack limit.`
426
+ };
427
+ }
428
+ return { ok: true, bytes };
429
+ }
430
+ function checkSkillPackFileContent(declared, observed) {
431
+ if (!isSkillPackPathSafe(declared.path)) {
432
+ return { ok: false, reason: "path-unsafe", detail: `${JSON.stringify(declared.path)} is not a safe relative path.` };
433
+ }
434
+ if (observed.byteSize > SKILL_PACK_FILE_MAX_BYTES) {
435
+ return {
436
+ ok: false,
437
+ reason: "file-over-cap",
438
+ detail: `${JSON.stringify(declared.path)} delivered ${observed.byteSize} bytes, over the ${SKILL_PACK_FILE_MAX_BYTES} byte per-file limit.`
439
+ };
440
+ }
441
+ if (observed.byteSize !== declared.byteSize) {
442
+ return {
443
+ ok: false,
444
+ reason: "size-mismatch",
445
+ detail: `${JSON.stringify(declared.path)} declared ${declared.byteSize} bytes and delivered ${observed.byteSize}.`
446
+ };
447
+ }
448
+ if (observed.contentHash !== declared.contentHash) {
449
+ return {
450
+ ok: false,
451
+ reason: "hash-mismatch",
452
+ detail: `${JSON.stringify(declared.path)} declared ${declared.contentHash} and delivered ${observed.contentHash}.`
453
+ };
454
+ }
455
+ return { ok: true, bytes: observed.byteSize };
456
+ }
457
+ var SKILL_FRONTMATTER_FIELDS = ["name", "description"];
458
+ var FRONTMATTER_DELIMITER = "---";
459
+ var FRONTMATTER_LINE_PATTERN = /^([A-Za-z][A-Za-z0-9_-]*):[ \t]*(.*)$/;
460
+ function unquote(value) {
461
+ const trimmed = value.trim();
462
+ if (trimmed.length >= 2) {
463
+ const first = trimmed[0];
464
+ const last = trimmed[trimmed.length - 1];
465
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
466
+ const inner = trimmed.slice(1, -1);
467
+ return inner.includes(first) ? void 0 : inner;
468
+ }
469
+ }
470
+ return trimmed;
471
+ }
472
+ function parseSkillFrontmatter(text) {
473
+ const reject = (message) => {
474
+ throw new ByokCoreError("skill_pack_frontmatter_invalid", message);
475
+ };
476
+ const lines = text.split("\n");
477
+ if (lines[0]?.trimEnd() !== FRONTMATTER_DELIMITER) {
478
+ reject(`A skill file must open with a ${FRONTMATTER_DELIMITER} frontmatter delimiter.`);
479
+ }
480
+ const closing = lines.findIndex((line, index) => index > 0 && line.trimEnd() === FRONTMATTER_DELIMITER);
481
+ if (closing < 0) reject(`The frontmatter block is not closed by a ${FRONTMATTER_DELIMITER} line.`);
482
+ const fields = /* @__PURE__ */ new Map();
483
+ for (const line of lines.slice(1, closing)) {
484
+ if (line.trim().length === 0) continue;
485
+ const match = FRONTMATTER_LINE_PATTERN.exec(line);
486
+ if (match === null) reject(`Frontmatter line ${JSON.stringify(line)} is not a \`key: value\` pair.`);
487
+ const key = match[1];
488
+ if (!SKILL_FRONTMATTER_FIELDS.includes(key)) {
489
+ reject(
490
+ `Frontmatter key ${JSON.stringify(key)} is not one of ${SKILL_FRONTMATTER_FIELDS.join(", ")}; a skill declares no tools, hooks, or environment.`
491
+ );
492
+ }
493
+ if (fields.has(key)) reject(`Frontmatter key ${JSON.stringify(key)} is declared twice.`);
494
+ const value = unquote(match[2]);
495
+ if (value === void 0 || value.length === 0) {
496
+ reject(`Frontmatter key ${JSON.stringify(key)} has no usable value.`);
497
+ }
498
+ fields.set(key, value);
499
+ }
500
+ const name = fields.get("name");
501
+ const description = fields.get("description");
502
+ if (name === void 0) reject("Frontmatter is missing `name`.");
503
+ if (description === void 0) reject("Frontmatter is missing `description`.");
504
+ if (name.length > SKILL_PACK_NAME_MAX_LENGTH || !SKILL_PACK_NAME_PATTERN.test(name)) {
505
+ reject(
506
+ `Frontmatter \`name\` must match ${SKILL_PACK_NAME_PATTERN.source} and be at most ${SKILL_PACK_NAME_MAX_LENGTH} characters.`
507
+ );
508
+ }
509
+ if (description.length > SKILL_PACK_DESCRIPTION_MAX_LENGTH) {
510
+ reject(`Frontmatter \`description\` exceeds ${SKILL_PACK_DESCRIPTION_MAX_LENGTH} characters.`);
511
+ }
512
+ return { name, description };
513
+ }
514
+ function checkSkillPackEntry(manifest, entryText) {
515
+ const frontmatter = parseSkillFrontmatter(entryText);
516
+ if (frontmatter.name !== manifest.name) {
517
+ return {
518
+ ok: false,
519
+ reason: "name-mismatch",
520
+ detail: `${SKILL_PACK_ENTRY_PATH} declares ${JSON.stringify(frontmatter.name)} but the manifest declares ${JSON.stringify(manifest.name)}.`
521
+ };
522
+ }
523
+ return { ok: true, bytes: new TextEncoder().encode(entryText).length };
524
+ }
262
525
 
263
526
  // src/stores.ts
264
527
  var CORE_STORE_NAMES = [
@@ -268,10 +531,12 @@ var CORE_STORE_NAMES = [
268
531
  "presence",
269
532
  "activity",
270
533
  "objects",
271
- "quota"
534
+ "quota",
535
+ "skillPacks"
272
536
  ];
273
537
 
274
538
  // src/ports-contract.ts
539
+ var CORE_NON_COMPOSITION_PORT_NAMES = [];
275
540
  var CORE_PORT_METHODS = {
276
541
  mailbox: ["append", "readAfter", "advanceCursor", "readCursor", "collectRetired"],
277
542
  board: ["create", "get", "list", "claim", "unclaim", "updateStatus"],
@@ -299,7 +564,8 @@ var CORE_PORT_METHODS = {
299
564
  "abortReservation",
300
565
  "expireReservations",
301
566
  "applyMailboxDelta"
302
- ]
567
+ ],
568
+ skillPacks: ["publish", "get", "list", "readFile"]
303
569
  };
304
570
  var CORE_PORT_INTERFACES = {
305
571
  mailbox: "MailboxStore",
@@ -308,12 +574,14 @@ var CORE_PORT_INTERFACES = {
308
574
  presence: "PresenceStore",
309
575
  activity: "ActivityStore",
310
576
  objects: "ObjectStore",
311
- quota: "QuotaStore"
577
+ quota: "QuotaStore",
578
+ skillPacks: "SkillPackStore"
312
579
  };
313
580
  var DEVICE_PROOF_SCHEMA_ID = "byok-device-proof-v1";
314
581
  var DEVICE_PROOF_DOMAIN_PREFIX = "byok-device-proof-v1\n";
315
582
  var DEVICE_PROOF_VERSION = 1;
316
583
  var DEVICE_PROOF_ALGORITHMS = ["ed25519"];
584
+ var DEVICE_PROOF_HEADER = "x-byok-device-proof";
317
585
  var BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
318
586
  var HTTP_METHOD_PATTERN = /^[A-Z]+$/;
319
587
  function canonicalizationFailure(path, reason) {
@@ -478,6 +746,122 @@ function deviceProofSigningInput(claims) {
478
746
  DEVICE_PROOF_DOMAIN_PREFIX + deviceProofCanonicalJson(claims)
479
747
  );
480
748
  }
749
+ var DEVICE_ASSERTION_SCHEMA_ID = "byok-device-assertion-v1";
750
+ var DEVICE_ASSERTION_DOMAIN_PREFIX = "byok-device-assertion-v1\n";
751
+ var DEVICE_ASSERTION_VERSION = 1;
752
+ var DEVICE_ASSERTION_ALGORITHMS = ["ed25519"];
753
+ var DEVICE_ASSERTION_DEFAULT_TTL_MS = 12e4;
754
+ var DEVICE_ASSERTION_MAX_TTL_MS = 3e5;
755
+ var DEVICE_ASSERTION_AUDIENCE_MAX_BYTES = 256;
756
+ var JTI_PATTERN = /^[A-Za-z0-9_-]{22}$/;
757
+ var SIGNATURE_PATTERN = /^[A-Za-z0-9_-]{86}$/;
758
+ function utf8ByteLength(value) {
759
+ return new TextEncoder().encode(value).length;
760
+ }
761
+ var DeviceAssertionClaimsSchema = z.strictObject({
762
+ version: z.literal(DEVICE_ASSERTION_VERSION),
763
+ /** The paired server's origin, normalized (`new URL(serverUrl).origin`). */
764
+ issuer: z.string().min(1),
765
+ productId: z.string().min(1),
766
+ /** The device row this assertion claims to be. A lookup key, never authority — the row is the authority. */
767
+ deviceId: z.string().min(1),
768
+ /** Exactly one audience, compared with `===` by every verifier. Never an array, never a prefix. */
769
+ audience: z.string().min(1).refine((value) => utf8ByteLength(value) <= DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, {
770
+ message: `audience must be at most ${DEVICE_ASSERTION_AUDIENCE_MAX_BYTES} UTF-8 bytes`
771
+ }),
772
+ /** 128-bit CSPRNG token, base64url unpadded. The verifier burns it; the signer never reuses one. */
773
+ jti: z.string().regex(JTI_PATTERN),
774
+ issuedAt: z.iso.datetime(),
775
+ expiresAt: z.iso.datetime()
776
+ });
777
+ var DeviceAssertionEnvelopeV1Schema = z.strictObject({
778
+ schema: z.literal(DEVICE_ASSERTION_SCHEMA_ID),
779
+ algorithm: z.enum(DEVICE_ASSERTION_ALGORITHMS),
780
+ protected: DeviceAssertionClaimsSchema,
781
+ /** Raw 64-byte Ed25519 signature, base64url unpadded. */
782
+ signature: z.string().regex(SIGNATURE_PATTERN)
783
+ });
784
+ function parseDeviceAssertionEnvelope(input) {
785
+ const result = DeviceAssertionEnvelopeV1Schema.safeParse(input);
786
+ if (!result.success) {
787
+ throw new ByokCoreError(
788
+ "assertion_envelope_invalid",
789
+ `Invalid device assertion envelope: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
790
+ { cause: result.error }
791
+ );
792
+ }
793
+ return result.data;
794
+ }
795
+ function deviceAssertionCanonicalClaims(claims) {
796
+ const canonical = {
797
+ version: claims.version,
798
+ issuer: claims.issuer,
799
+ productId: claims.productId,
800
+ deviceId: claims.deviceId,
801
+ audience: claims.audience,
802
+ jti: claims.jti,
803
+ issuedAt: claims.issuedAt,
804
+ expiresAt: claims.expiresAt
805
+ };
806
+ return canonical;
807
+ }
808
+ function deviceAssertionCanonicalJson(claims) {
809
+ return canonicalizeJson(deviceAssertionCanonicalClaims(claims));
810
+ }
811
+ function deviceAssertionSigningInput(claims) {
812
+ return new TextEncoder().encode(
813
+ DEVICE_ASSERTION_DOMAIN_PREFIX + deviceAssertionCanonicalJson(claims)
814
+ );
815
+ }
816
+ function parseInstant(value) {
817
+ const parsed = Date.parse(value);
818
+ return Number.isFinite(parsed) ? parsed : void 0;
819
+ }
820
+ var EDDSA_JWK_X_PATTERN = /^[A-Za-z0-9_-]{43}$/;
821
+ function isUsableDeviceRow(row) {
822
+ if (row === null || typeof row !== "object") return false;
823
+ if (row.revoked !== false) return false;
824
+ const key = row.publicKeyJwkX;
825
+ return typeof key === "string" && EDDSA_JWK_X_PATTERN.test(key);
826
+ }
827
+ async function verifyDeviceAssertion(input, deps) {
828
+ const maxLifetimeMs = deps.maxLifetimeMs ?? DEVICE_ASSERTION_MAX_TTL_MS;
829
+ if (!Number.isSafeInteger(maxLifetimeMs) || maxLifetimeMs <= 0 || maxLifetimeMs > DEVICE_ASSERTION_MAX_TTL_MS) {
830
+ return void 0;
831
+ }
832
+ let envelope;
833
+ try {
834
+ envelope = parseDeviceAssertionEnvelope(input);
835
+ } catch {
836
+ return void 0;
837
+ }
838
+ const claims = envelope.protected;
839
+ const row = await deps.lookupDevice(claims.deviceId);
840
+ if (!isUsableDeviceRow(row)) return void 0;
841
+ const issuedAt = parseInstant(claims.issuedAt);
842
+ const expiresAt = parseInstant(claims.expiresAt);
843
+ if (issuedAt === void 0 || expiresAt === void 0) return void 0;
844
+ if (expiresAt <= issuedAt) return void 0;
845
+ if (expiresAt - issuedAt > maxLifetimeMs) return void 0;
846
+ const now = deps.now.getTime();
847
+ if (!Number.isFinite(now)) return void 0;
848
+ if (now < issuedAt || now >= expiresAt) return void 0;
849
+ const verified = await deps.verifier.verify({
850
+ algorithm: envelope.algorithm,
851
+ // The row's key, never an envelope-supplied one.
852
+ publicKey: row.publicKeyJwkX,
853
+ signature: envelope.signature,
854
+ signingInput: deviceAssertionSigningInput(claims)
855
+ });
856
+ if (!verified) return void 0;
857
+ return claims;
858
+ }
859
+
860
+ // src/pairing.ts
861
+ var NONCE_SIGNING_DOMAIN = "byok-nonce-v1\n";
862
+ function nonceSigningBytes(nonce) {
863
+ return new TextEncoder().encode(NONCE_SIGNING_DOMAIN + nonce);
864
+ }
481
865
 
482
866
  // src/in-memory/presence.ts
483
867
  function assertTtl(ttlMs) {
@@ -810,23 +1194,30 @@ var InMemoryMailboxStore = class {
810
1194
  }
811
1195
  async append(tenant, input) {
812
1196
  const device = this.#device(tenant, input.deviceId);
813
- const existing = device.byMessageId.get(input.messageId);
814
- if (existing !== void 0) return existing;
815
- const message = {
816
- tenantId: tenant,
817
- deviceId: input.deviceId,
818
- seq: device.nextSeq,
819
- messageId: input.messageId,
820
- body: input.body,
821
- bodyHash: input.bodyHash,
822
- byteSize: input.byteSize,
823
- state: "pending",
824
- appendedAt: this.#now()
825
- };
826
- device.nextSeq += 1;
827
- device.messages.push(message);
828
- device.byMessageId.set(message.messageId, message);
829
- return message;
1197
+ const appended = device.mutationTail.then(async () => {
1198
+ const existing = device.byMessageId.get(input.messageId);
1199
+ if (existing !== void 0) return existing;
1200
+ const seq = device.nextSeq;
1201
+ const materialized = await input.materialize(seq);
1202
+ const message = {
1203
+ tenantId: tenant,
1204
+ deviceId: input.deviceId,
1205
+ seq,
1206
+ messageId: input.messageId,
1207
+ ...materialized,
1208
+ state: "pending",
1209
+ appendedAt: this.#now()
1210
+ };
1211
+ device.nextSeq += 1;
1212
+ device.messages.push(message);
1213
+ device.byMessageId.set(message.messageId, message);
1214
+ return message;
1215
+ });
1216
+ device.mutationTail = appended.then(
1217
+ () => void 0,
1218
+ () => void 0
1219
+ );
1220
+ return appended;
830
1221
  }
831
1222
  async readAfter(tenant, query) {
832
1223
  const device = this.#devices.get(tenantKey(tenant, query.deviceId));
@@ -847,23 +1238,30 @@ var InMemoryMailboxStore = class {
847
1238
  }
848
1239
  async advanceCursor(tenant, input) {
849
1240
  const device = this.#device(tenant, input.deviceId);
850
- if (input.ackedSeq < device.ackedSeq) {
851
- throw new CoreConflictError(
852
- "mailbox_cursor_regression",
853
- `Cursor for device ${input.deviceId} is at ${device.ackedSeq}; refusing to move it back to ${input.ackedSeq}.`,
854
- this.#cursorState(tenant, input.deviceId, device),
855
- this.#now()
856
- );
857
- }
858
- device.ackedSeq = input.ackedSeq;
859
- device.cursorUpdatedAt = this.#now();
860
- for (const [index, message] of device.messages.entries()) {
861
- if (message.state === "pending" && message.seq <= input.ackedSeq) {
862
- device.messages[index] = { ...message, state: "acked" };
863
- device.byMessageId.set(message.messageId, device.messages[index]);
1241
+ const advanced = device.mutationTail.then(() => {
1242
+ if (input.ackedSeq < device.ackedSeq) {
1243
+ throw new CoreConflictError(
1244
+ "mailbox_cursor_regression",
1245
+ `Cursor for device ${input.deviceId} is at ${device.ackedSeq}; refusing to move it back to ${input.ackedSeq}.`,
1246
+ this.#cursorState(tenant, input.deviceId, device),
1247
+ this.#now()
1248
+ );
864
1249
  }
865
- }
866
- return this.#cursorState(tenant, input.deviceId, device);
1250
+ device.ackedSeq = input.ackedSeq;
1251
+ device.cursorUpdatedAt = this.#now();
1252
+ for (const [index, message] of device.messages.entries()) {
1253
+ if (message.state === "pending" && message.seq <= input.ackedSeq) {
1254
+ device.messages[index] = { ...message, state: "acked" };
1255
+ device.byMessageId.set(message.messageId, device.messages[index]);
1256
+ }
1257
+ }
1258
+ return this.#cursorState(tenant, input.deviceId, device);
1259
+ });
1260
+ device.mutationTail = advanced.then(
1261
+ () => void 0,
1262
+ () => void 0
1263
+ );
1264
+ return advanced;
867
1265
  }
868
1266
  async readCursor(tenant, deviceId) {
869
1267
  const device = this.#devices.get(tenantKey(tenant, deviceId));
@@ -911,6 +1309,7 @@ var InMemoryMailboxStore = class {
911
1309
  nextSeq: 1,
912
1310
  ackedSeq: 0,
913
1311
  cursorUpdatedAt: this.#now(),
1312
+ mutationTail: Promise.resolve(),
914
1313
  messages: [],
915
1314
  byMessageId: /* @__PURE__ */ new Map()
916
1315
  };
@@ -1360,6 +1759,89 @@ var InMemoryQuotaStore = class {
1360
1759
  }
1361
1760
  };
1362
1761
 
1762
+ // src/in-memory/skill-pack.ts
1763
+ var DEFAULT_LIST_LIMIT3 = 50;
1764
+ var InMemorySkillPackStore = class {
1765
+ #packs = /* @__PURE__ */ new Map();
1766
+ async publish(tenant, input) {
1767
+ const { manifest } = input;
1768
+ const structural = checkSkillPackManifest(manifest);
1769
+ if (!structural.ok) {
1770
+ throw new ByokCoreError(
1771
+ "skill_pack_manifest_invalid",
1772
+ `Refusing to publish ${JSON.stringify(manifest.name)}: ${structural.reason} \u2014 ${structural.detail}`
1773
+ );
1774
+ }
1775
+ const contents = /* @__PURE__ */ new Map();
1776
+ for (const file of input.files) {
1777
+ if (contents.has(file.path)) {
1778
+ throw new ByokCoreError(
1779
+ "skill_pack_manifest_invalid",
1780
+ `Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(file.path)} was supplied twice.`
1781
+ );
1782
+ }
1783
+ contents.set(file.path, file.content);
1784
+ }
1785
+ for (const declared of manifest.files) {
1786
+ const content = contents.get(declared.path);
1787
+ if (content === void 0) {
1788
+ throw new ByokCoreError(
1789
+ "skill_pack_manifest_invalid",
1790
+ `Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(declared.path)} is declared but was not supplied.`
1791
+ );
1792
+ }
1793
+ const bytes = new TextEncoder().encode(content).length;
1794
+ if (bytes !== declared.byteSize) {
1795
+ throw new ByokCoreError(
1796
+ "skill_pack_manifest_invalid",
1797
+ `Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(declared.path)} declares ${declared.byteSize} bytes and supplies ${bytes}.`
1798
+ );
1799
+ }
1800
+ }
1801
+ if (contents.size !== manifest.files.length) {
1802
+ throw new ByokCoreError(
1803
+ "skill_pack_manifest_invalid",
1804
+ `Refusing to publish ${JSON.stringify(manifest.name)}: ${contents.size} files were supplied for ${manifest.files.length} declared rows.`
1805
+ );
1806
+ }
1807
+ const entry = checkSkillPackEntry(manifest, contents.get(SKILL_PACK_ENTRY_PATH));
1808
+ if (!entry.ok) {
1809
+ throw new ByokCoreError(
1810
+ "skill_pack_manifest_invalid",
1811
+ `Refusing to publish ${JSON.stringify(manifest.name)}: ${entry.reason} \u2014 ${entry.detail}`
1812
+ );
1813
+ }
1814
+ this.#packs.set(tenantKey(tenant, manifest.name), { manifest, contents });
1815
+ return manifest;
1816
+ }
1817
+ async get(tenant, name) {
1818
+ return this.#packs.get(tenantKey(tenant, name))?.manifest;
1819
+ }
1820
+ async list(tenant, query) {
1821
+ const prefix = tenantKey(tenant, "");
1822
+ const manifests = [];
1823
+ for (const [key, pack] of this.#packs.entries()) {
1824
+ if (!key.startsWith(prefix)) continue;
1825
+ manifests.push(pack.manifest);
1826
+ }
1827
+ manifests.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
1828
+ return manifests.slice(0, query.limit ?? DEFAULT_LIST_LIMIT3);
1829
+ }
1830
+ async readFile(tenant, name, path) {
1831
+ const pack = this.#packs.get(tenantKey(tenant, name));
1832
+ if (pack === void 0) return void 0;
1833
+ const declared = pack.manifest.files.find((file) => file.path === path);
1834
+ const content = pack.contents.get(path);
1835
+ if (declared === void 0 || content === void 0) return void 0;
1836
+ return {
1837
+ path: declared.path,
1838
+ contentHash: declared.contentHash,
1839
+ byteSize: declared.byteSize,
1840
+ content
1841
+ };
1842
+ }
1843
+ };
1844
+
1363
1845
  // src/in-memory/truth.ts
1364
1846
  var DEFAULT_MANIFEST_LIMIT = 100;
1365
1847
  var InMemoryTruthStore = class {
@@ -1484,7 +1966,8 @@ function createInMemoryCoreStores(options = {}) {
1484
1966
  presence: new InMemoryPresenceStore(clock),
1485
1967
  activity: new InMemoryActivityStore(clock),
1486
1968
  objects,
1487
- quota: new InMemoryQuotaStore(clock, objects)
1969
+ quota: new InMemoryQuotaStore(clock, objects),
1970
+ skillPacks: new InMemorySkillPackStore()
1488
1971
  };
1489
1972
  return { stores, clock };
1490
1973
  }
@@ -1493,6 +1976,6 @@ function createInMemoryCoreCompositionWithClock() {
1493
1976
  return { stores: createInMemoryCoreStores({ clock }).stores, clock };
1494
1977
  }
1495
1978
 
1496
- export { BOARD_STATUSES, BOARD_TRANSITIONS, ByokCoreError, CANONICAL_TIMESTAMP_PATTERN, CAPABILITY_DECLARATION_SCHEMA_ID, CAPABILITY_NAME_PATTERN, CONTENT_HASH_PATTERN, CORE_ERROR_CODES, CORE_PORT_INTERFACES, CORE_PORT_METHODS, CORE_STORE_NAMES, CapabilityDeclarationSchema, CoreConflictError, DEFAULT_ACTIVITY_CAPACITY, DEVICE_PROOF_ALGORITHMS, DEVICE_PROOF_DOMAIN_PREFIX, DEVICE_PROOF_SCHEMA_ID, DEVICE_PROOF_VERSION, DeviceProofEnvelopeV1Schema, DeviceProofProtectedClaimsSchema, IN_MEMORY_CLOCK_EPOCH, InMemoryActivityStore, InMemoryBoardStore, InMemoryMailboxStore, InMemoryObjectStore, InMemoryPresenceStore, InMemoryQuotaStore, InMemoryTruthStore, MAILBOX_MESSAGE_STATES, OBJECT_STATES, OBJECT_STATE_TRANSITIONS, PRESENCE_LEVELS, PRINCIPAL_KINDS, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS, STORAGE_RESERVATION_STATES, STORAGE_WRITE_KINDS, STORAGE_WRITE_POSTURES, TENANT_ID_MAX_LENGTH, TENANT_KEY_SEPARATOR, TRUTH_RECORD_KINDS, assertCanonicalTimestamp, assertCapability, canonicalizeJson, canonicalizeJsonBytes, contentHash, createInMemoryCoreCompositionWithClock, createInMemoryCoreStores, createMutableClock, deviceProofCanonicalClaims, deviceProofCanonicalJson, deviceProofSigningInput, hasCapability, isCanonicalTimestamp, isContentHash, isControlPlanePrincipal, isCoreConflictError, isCoreError, isDevicePrincipal, isLegalBoardTransition, isLegalObjectTransition, isTenantId, parseCapabilityDeclaration, parseDeviceProofEnvelope, principalTenant, tenantId, tenantKey, tenantObjectKey };
1979
+ export { BOARD_STATUSES, BOARD_TRANSITIONS, ByokCoreError, CANONICAL_TIMESTAMP_PATTERN, CAPABILITY_DECLARATION_SCHEMA_ID, CAPABILITY_NAME_PATTERN, CONTENT_HASH_PATTERN, CORE_ERROR_CODES, CORE_NON_COMPOSITION_PORT_NAMES, CORE_PORT_INTERFACES, CORE_PORT_METHODS, CORE_STORE_NAMES, CapabilityDeclarationSchema, CoreConflictError, DEFAULT_ACTIVITY_CAPACITY, DEVICE_ASSERTION_ALGORITHMS, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_DOMAIN_PREFIX, DEVICE_ASSERTION_MAX_TTL_MS, DEVICE_ASSERTION_SCHEMA_ID, DEVICE_ASSERTION_VERSION, DEVICE_PROOF_ALGORITHMS, DEVICE_PROOF_DOMAIN_PREFIX, DEVICE_PROOF_HEADER, DEVICE_PROOF_SCHEMA_ID, DEVICE_PROOF_VERSION, DeviceAssertionClaimsSchema, DeviceAssertionEnvelopeV1Schema, DeviceProofEnvelopeV1Schema, DeviceProofProtectedClaimsSchema, IN_MEMORY_CLOCK_EPOCH, InMemoryActivityStore, InMemoryBoardStore, InMemoryMailboxStore, InMemoryObjectStore, InMemoryPresenceStore, InMemoryQuotaStore, InMemorySkillPackStore, InMemoryTruthStore, MAILBOX_MESSAGE_STATES, NONCE_SIGNING_DOMAIN, OBJECT_KEY_PREFIX_PATTERN, OBJECT_STATES, OBJECT_STATE_TRANSITIONS, PRESENCE_LEVELS, PRINCIPAL_KINDS, SKILL_FRONTMATTER_FIELDS, SKILL_PACK_DESCRIPTION_MAX_LENGTH, SKILL_PACK_ENTRY_PATH, SKILL_PACK_FILE_MAX_BYTES, SKILL_PACK_FILE_PATH_MAX_LENGTH, SKILL_PACK_FILE_PATH_PATTERN, SKILL_PACK_FORBIDDEN_FIELDS, SKILL_PACK_MANIFEST_SCHEMA_ID, SKILL_PACK_MAX_BYTES, SKILL_PACK_MAX_FILES, SKILL_PACK_NAME_MAX_LENGTH, SKILL_PACK_NAME_PATTERN, SKILL_PACK_REJECTIONS, SKILL_PACK_VERSION_PATTERN, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS, STORAGE_RESERVATION_STATES, STORAGE_WRITE_KINDS, STORAGE_WRITE_POSTURES, SkillPackFileSchema, SkillPackManifestSchema, TENANT_ID_MAX_LENGTH, TENANT_KEY_SEPARATOR, TRUTH_RECORD_KINDS, assertCanonicalTimestamp, assertCapability, canonicalizeJson, canonicalizeJsonBytes, checkSkillPackEntry, checkSkillPackFileContent, checkSkillPackManifest, contentHash, createInMemoryCoreCompositionWithClock, createInMemoryCoreStores, createMutableClock, deviceAssertionCanonicalClaims, deviceAssertionCanonicalJson, deviceAssertionSigningInput, deviceProofCanonicalClaims, deviceProofCanonicalJson, deviceProofSigningInput, hasCapability, isCanonicalTimestamp, isContentHash, isControlPlanePrincipal, isCoreConflictError, isCoreError, isDevicePrincipal, isLegalBoardTransition, isLegalObjectTransition, isSkillPackPathSafe, isTenantId, nonceSigningBytes, objectKeyPrefix, parseCapabilityDeclaration, parseDeviceAssertionEnvelope, parseDeviceProofEnvelope, parseSkillFrontmatter, parseSkillPackManifest, principalTenant, skillPackContentHashInput, tenantId, tenantKey, tenantObjectKey, verifyDeviceAssertion };
1497
1980
  //# sourceMappingURL=index.js.map
1498
1981
  //# sourceMappingURL=index.js.map