@byok-sdk/cloud 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/README.md +18 -0
- package/dist/auth/verify.d.ts +1 -20
- package/dist/capabilities.d.ts +18 -0
- package/dist/cloud.d.ts +19 -2
- package/dist/composition/in-memory.d.ts +9 -1
- package/dist/errors.d.ts +3 -5
- package/dist/handlers/skill-packs.d.ts +33 -0
- package/dist/handlers/truth.d.ts +2 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +149 -84
- package/dist/index.js.map +1 -1
- package/dist/stores/in-memory/index.d.ts +0 -1
- package/dist/stores/ports.d.ts +1 -16
- package/dist/tenant-stores.d.ts +0 -4
- package/package.json +3 -3
- package/dist/stores/in-memory/sequence.d.ts +0 -11
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BOARD_STATUSES, PRESENCE_LEVELS, CapabilityDeclarationSchema, hasCapability, isTenantId, tenantId, principalTenant, parseDeviceProofEnvelope, deviceProofSigningInput,
|
|
2
|
-
export { isTenantId, tenantId } from '@byok-sdk/core';
|
|
3
|
-
import { AgentEventOrUnknownSchema, DAEMON_TO_SERVER_TYPES, encodeEnvelope, createEnvelope, PairRequestSchema, ChallengeRequestSchema, TokenRequestSchema,
|
|
1
|
+
import { BOARD_STATUSES, PRESENCE_LEVELS, CapabilityDeclarationSchema, NONCE_SIGNING_DOMAIN, hasCapability, isTenantId, tenantId, principalTenant, parseDeviceProofEnvelope, deviceProofSigningInput, parseCapabilityDeclaration, ByokCoreError, tenantKey, createInMemoryCoreStores, assertCapability, contentHash, isCoreConflictError, isCoreError, DEVICE_PROOF_HEADER, TRUTH_RECORD_KINDS, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS } from '@byok-sdk/core';
|
|
2
|
+
export { DEVICE_PROOF_HEADER, NONCE_SIGNING_DOMAIN, isTenantId, tenantId } from '@byok-sdk/core';
|
|
3
|
+
import { AgentEventOrUnknownSchema, DAEMON_TO_SERVER_TYPES, encodeEnvelope, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_BOARD_PATH, BYOK_BOARD_CLAIM_ROUTE, BYOK_BOARD_UNCLAIM_ROUTE, BYOK_BOARD_STATUS_ROUTE, BYOK_BOARD_STREAM_PATH, BYOK_PRESENCE_PATH, BYOK_ACTIVITY_PATH, BYOK_RECORDS_PATH, BYOK_RECORD_ROUTE, BYOK_SKILL_PACKS_PATH, BYOK_SKILL_PACK_FILE_ROUTE, BYOK_BLOBS_PATH, BYOK_BLOB_FINALIZE_ROUTE, BYOK_BLOB_URL_ROUTE, BYOK_BLOB_CONTENT_ROUTE, decodeEnvelope, createEnvelope, PairRequestSchema, ChallengeRequestSchema, TokenRequestSchema, MessagesSendRequestSchema, CreateBlobRequestSchema, byokBlobContentPath } from '@byok-sdk/protocol';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
|
|
@@ -160,9 +160,6 @@ function createHmacTokenSigner(secret, clock) {
|
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
|
-
|
|
164
|
-
// src/auth/verify.ts
|
|
165
|
-
var NONCE_SIGNING_DOMAIN = "byok-nonce-v1\n";
|
|
166
163
|
function verifyNonceSignature(crypto, devicePublicKey, nonce, signature) {
|
|
167
164
|
return crypto.verifyEd25519(devicePublicKey, NONCE_SIGNING_DOMAIN + nonce, signature);
|
|
168
165
|
}
|
|
@@ -256,16 +253,30 @@ var CLOUD_CAPABILITIES = {
|
|
|
256
253
|
/** Bounded task activity batch publication. */
|
|
257
254
|
activityTail: "activity.tail",
|
|
258
255
|
/** Request-bound device proof record manifest/read/write surface (S6). */
|
|
259
|
-
truthRecords: "truth.records"
|
|
256
|
+
truthRecords: "truth.records",
|
|
257
|
+
/**
|
|
258
|
+
* Tenant-scoped skill pack distribution: the manifest list plus the per-file
|
|
259
|
+
* content route a paired device installs from.
|
|
260
|
+
*
|
|
261
|
+
* Hosted HTTP on purpose. A pack is DECLARATIVE CONTENT a device pulls after
|
|
262
|
+
* reading this declaration — not a message — so `@byok-sdk/protocol` gains
|
|
263
|
+
* nothing from it and the frozen v1 envelope stays untouched. Composition-
|
|
264
|
+
* bound like `truth.records`: a deployment that declares it without supplying
|
|
265
|
+
* a `SkillPackStore` is refused at construction rather than publishing two
|
|
266
|
+
* routes it would then 404.
|
|
267
|
+
*/
|
|
268
|
+
skillPacks: "skills.pack"
|
|
260
269
|
};
|
|
261
270
|
var CapabilitiesResponseSchema = CapabilityDeclarationSchema;
|
|
262
271
|
function fullCapabilityDeclaration(version = 1, options = {}) {
|
|
263
272
|
return {
|
|
264
273
|
schema: "byok-capabilities-v1",
|
|
265
274
|
version,
|
|
266
|
-
capabilities: Object.values(CLOUD_CAPABILITIES).filter(
|
|
267
|
-
(capability
|
|
268
|
-
|
|
275
|
+
capabilities: Object.values(CLOUD_CAPABILITIES).filter((capability) => {
|
|
276
|
+
if (capability === CLOUD_CAPABILITIES.truthRecords) return options.includeTruthRecords === true;
|
|
277
|
+
if (capability === CLOUD_CAPABILITIES.skillPacks) return options.includeSkillPacks === true;
|
|
278
|
+
return true;
|
|
279
|
+
})
|
|
269
280
|
};
|
|
270
281
|
}
|
|
271
282
|
function declares(declaration, capability) {
|
|
@@ -279,11 +290,9 @@ var CLOUD_ERROR_CODES = {
|
|
|
279
290
|
/** The composition handed a device row whose tenant is not a mintable `TenantId`. */
|
|
280
291
|
device_tenant_invalid: "device_tenant_invalid",
|
|
281
292
|
/**
|
|
282
|
-
* The mailbox
|
|
283
|
-
* baked into the
|
|
284
|
-
* numbers ARE the daemon's redelivery cursor
|
|
285
|
-
* mailbox numbers rows differently from `DeviceSequenceStore` would
|
|
286
|
-
* mis-deliver every subsequent poll.
|
|
293
|
+
* The mailbox committed a row `seq` that disagrees with the delivery `seq`
|
|
294
|
+
* its body factory baked into the envelope. Loud rather than silent: those
|
|
295
|
+
* two numbers ARE the daemon's redelivery cursor.
|
|
287
296
|
*/
|
|
288
297
|
mailbox_seq_mismatch: "mailbox_seq_mismatch",
|
|
289
298
|
/** A capability declaration the host supplied that core refused. */
|
|
@@ -457,9 +466,6 @@ function tenantStoresFor(principal, root) {
|
|
|
457
466
|
finalizeReservation: (input) => core.quota.finalizeReservation(tenant, input),
|
|
458
467
|
abortReservation: (reservationId) => core.quota.abortReservation(tenant, reservationId)
|
|
459
468
|
},
|
|
460
|
-
sequence: {
|
|
461
|
-
next: (deviceId) => cloud.sequence.next(tenant, deviceId)
|
|
462
|
-
},
|
|
463
469
|
rateLimiter: {
|
|
464
470
|
consume: (deviceId) => cloud.rateLimiter.consume(tenant, deviceId)
|
|
465
471
|
}
|
|
@@ -1104,6 +1110,33 @@ function activityAppendHandler(deps) {
|
|
|
1104
1110
|
}
|
|
1105
1111
|
};
|
|
1106
1112
|
}
|
|
1113
|
+
var DEFAULT_SKILL_PACK_PAGE_LIMIT = 50;
|
|
1114
|
+
function skillPackListHandler(deps) {
|
|
1115
|
+
return async (c) => {
|
|
1116
|
+
const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
|
|
1117
|
+
if (device === void 0) return unauthorized(c);
|
|
1118
|
+
const packs = await deps.skillPacks.list(principalTenant(device), { limit: deps.pageLimit });
|
|
1119
|
+
return c.json({ packs }, 200);
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function skillPackFileHandler(deps) {
|
|
1123
|
+
return async (c) => {
|
|
1124
|
+
const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
|
|
1125
|
+
if (device === void 0) return unauthorized(c);
|
|
1126
|
+
const name = c.req.param("name");
|
|
1127
|
+
const path = c.req.param("path");
|
|
1128
|
+
if (name === void 0 || path === void 0) return notFound(c);
|
|
1129
|
+
const file = await deps.skillPacks.readFile(principalTenant(device), name, path);
|
|
1130
|
+
if (file === void 0) return notFound(c);
|
|
1131
|
+
return c.json(file, 200);
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
function unauthorized(c) {
|
|
1135
|
+
return c.json({ error: "unauthorized" }, 401);
|
|
1136
|
+
}
|
|
1137
|
+
function notFound(c) {
|
|
1138
|
+
return c.json({ error: "skill_pack_not_found" }, 404);
|
|
1139
|
+
}
|
|
1107
1140
|
var DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS = 6e4;
|
|
1108
1141
|
var MAX_DEVICE_PROOF_CLOCK_SKEW_MS = 5 * 6e4;
|
|
1109
1142
|
var DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS = 5 * 6e4;
|
|
@@ -1268,7 +1301,6 @@ function isTruthCommitError(value) {
|
|
|
1268
1301
|
}
|
|
1269
1302
|
|
|
1270
1303
|
// src/handlers/truth.ts
|
|
1271
|
-
var DEVICE_PROOF_HEADER = "x-byok-device-proof";
|
|
1272
1304
|
var DEFAULT_MAX_TRUTH_REQUEST_BYTES = 2 * 1024 * 1024;
|
|
1273
1305
|
var MAX_DEVICE_PROOF_HEADER_BYTES = 16 * 1024;
|
|
1274
1306
|
var EMPTY_BODY = new Uint8Array();
|
|
@@ -1280,7 +1312,7 @@ function truthManifestHandler(deps) {
|
|
|
1280
1312
|
operation: "truth.list",
|
|
1281
1313
|
resource: "records"
|
|
1282
1314
|
});
|
|
1283
|
-
if (authenticated === void 0) return
|
|
1315
|
+
if (authenticated === void 0) return unauthorized2(c);
|
|
1284
1316
|
const query = manifestQuery(c);
|
|
1285
1317
|
if (query === void 0) return c.json({ error: "invalid truth manifest query" }, 400);
|
|
1286
1318
|
const records = await deps.truth.listManifest(principalTenant(authenticated.device), query);
|
|
@@ -1295,7 +1327,7 @@ function truthGetHandler(deps) {
|
|
|
1295
1327
|
operation: "truth.read",
|
|
1296
1328
|
resource
|
|
1297
1329
|
});
|
|
1298
|
-
if (authenticated === void 0) return
|
|
1330
|
+
if (authenticated === void 0) return unauthorized2(c);
|
|
1299
1331
|
const selector = recordSelector(c);
|
|
1300
1332
|
if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
|
|
1301
1333
|
const record = await deps.truth.getRecord(principalTenant(authenticated.device), selector);
|
|
@@ -1322,7 +1354,7 @@ function truthPutHandler(deps) {
|
|
|
1322
1354
|
operation: "truth.write",
|
|
1323
1355
|
resource: rawRecordResource(c)
|
|
1324
1356
|
});
|
|
1325
|
-
if (authenticated === void 0) return
|
|
1357
|
+
if (authenticated === void 0) return unauthorized2(c);
|
|
1326
1358
|
const selector = recordSelector(c);
|
|
1327
1359
|
if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
|
|
1328
1360
|
const parsedJson = parseJsonBytes(body);
|
|
@@ -1467,7 +1499,7 @@ function parseJsonBytes(bytes) {
|
|
|
1467
1499
|
return void 0;
|
|
1468
1500
|
}
|
|
1469
1501
|
}
|
|
1470
|
-
function
|
|
1502
|
+
function unauthorized2(c) {
|
|
1471
1503
|
return c.json({ error: "unauthorized" }, 401);
|
|
1472
1504
|
}
|
|
1473
1505
|
function truthFailure(c, caught) {
|
|
@@ -1535,7 +1567,8 @@ function createByokCloud(options) {
|
|
|
1535
1567
|
declaration,
|
|
1536
1568
|
options.blobContentProxy,
|
|
1537
1569
|
options.truthCommitter,
|
|
1538
|
-
options.truthObjectDownloads
|
|
1570
|
+
options.truthObjectDownloads,
|
|
1571
|
+
options.skillPacks
|
|
1539
1572
|
);
|
|
1540
1573
|
const root = { core: options.core, cloud: options.cloud };
|
|
1541
1574
|
const auth = createAuthPlane({
|
|
@@ -1556,16 +1589,16 @@ function createByokCloud(options) {
|
|
|
1556
1589
|
capacity: options.activityCapacity ?? DEFAULT_ACTIVITY_CAPACITY,
|
|
1557
1590
|
ttlMs: options.activityTtlMs ?? DEFAULT_ACTIVITY_TTL_MS
|
|
1558
1591
|
};
|
|
1559
|
-
registry.register({ method: "POST", path:
|
|
1560
|
-
registry.register({ method: "POST", path:
|
|
1561
|
-
registry.register({ method: "POST", path:
|
|
1592
|
+
registry.register({ method: "POST", path: BYOK_PAIR_PATH, class: "public" }, pairHandler({ auth }));
|
|
1593
|
+
registry.register({ method: "POST", path: BYOK_CHALLENGE_PATH, class: "public" }, challengeHandler({ auth }));
|
|
1594
|
+
registry.register({ method: "POST", path: BYOK_TOKEN_PATH, class: "public" }, tokenHandler({ auth }));
|
|
1562
1595
|
registry.register(
|
|
1563
|
-
{ method: "GET", path:
|
|
1596
|
+
{ method: "GET", path: BYOK_CAPABILITIES_PATH, class: "public" },
|
|
1564
1597
|
capabilitiesHandler({ declaration })
|
|
1565
1598
|
);
|
|
1566
1599
|
if (declares(declaration, CLOUD_CAPABILITIES.eventsLongPoll)) {
|
|
1567
1600
|
registry.register(
|
|
1568
|
-
{ method: "GET", path:
|
|
1601
|
+
{ method: "GET", path: BYOK_EVENTS_PATH, class: "device" },
|
|
1569
1602
|
eventsHandler({
|
|
1570
1603
|
...deviceRouteDeps,
|
|
1571
1604
|
longPollHoldMs: options.longPollHoldMs ?? DEFAULT_LONG_POLL_HOLD_MS,
|
|
@@ -1576,7 +1609,7 @@ function createByokCloud(options) {
|
|
|
1576
1609
|
}
|
|
1577
1610
|
if (declares(declaration, CLOUD_CAPABILITIES.messagesBatch)) {
|
|
1578
1611
|
registry.register(
|
|
1579
|
-
{ method: "POST", path:
|
|
1612
|
+
{ method: "POST", path: BYOK_MESSAGES_PATH, class: "device" },
|
|
1580
1613
|
messagesHandler({ ...deviceRouteDeps, activityBounds })
|
|
1581
1614
|
);
|
|
1582
1615
|
}
|
|
@@ -1585,23 +1618,23 @@ function createByokCloud(options) {
|
|
|
1585
1618
|
...deviceRouteDeps,
|
|
1586
1619
|
pageLimit: options.boardPageLimit ?? DEFAULT_BOARD_PAGE_LIMIT
|
|
1587
1620
|
};
|
|
1588
|
-
registry.register({ method: "GET", path:
|
|
1621
|
+
registry.register({ method: "GET", path: BYOK_BOARD_PATH, class: "device" }, boardListHandler(boardDeps));
|
|
1589
1622
|
registry.register(
|
|
1590
|
-
{ method: "POST", path:
|
|
1623
|
+
{ method: "POST", path: BYOK_BOARD_CLAIM_ROUTE, class: "device" },
|
|
1591
1624
|
boardClaimHandler(deviceRouteDeps)
|
|
1592
1625
|
);
|
|
1593
1626
|
registry.register(
|
|
1594
|
-
{ method: "POST", path:
|
|
1627
|
+
{ method: "POST", path: BYOK_BOARD_UNCLAIM_ROUTE, class: "device" },
|
|
1595
1628
|
boardUnclaimHandler(deviceRouteDeps)
|
|
1596
1629
|
);
|
|
1597
1630
|
registry.register(
|
|
1598
|
-
{ method: "POST", path:
|
|
1631
|
+
{ method: "POST", path: BYOK_BOARD_STATUS_ROUTE, class: "device" },
|
|
1599
1632
|
boardStatusHandler(deviceRouteDeps)
|
|
1600
1633
|
);
|
|
1601
1634
|
}
|
|
1602
1635
|
if (declares(declaration, CLOUD_CAPABILITIES.boardSse)) {
|
|
1603
1636
|
registry.register(
|
|
1604
|
-
{ method: "GET", path:
|
|
1637
|
+
{ method: "GET", path: BYOK_BOARD_STREAM_PATH, class: "device" },
|
|
1605
1638
|
boardStreamHandler({
|
|
1606
1639
|
...deviceRouteDeps,
|
|
1607
1640
|
pageLimit: options.boardPageLimit ?? DEFAULT_BOARD_PAGE_LIMIT,
|
|
@@ -1613,7 +1646,7 @@ function createByokCloud(options) {
|
|
|
1613
1646
|
}
|
|
1614
1647
|
if (declares(declaration, CLOUD_CAPABILITIES.presenceHints)) {
|
|
1615
1648
|
registry.register(
|
|
1616
|
-
{ method: "PUT", path:
|
|
1649
|
+
{ method: "PUT", path: BYOK_PRESENCE_PATH, class: "device" },
|
|
1617
1650
|
presencePublishHandler({
|
|
1618
1651
|
...deviceRouteDeps,
|
|
1619
1652
|
ttlMs: options.presenceTtlMs ?? DEFAULT_PRESENCE_TTL_MS,
|
|
@@ -1624,7 +1657,7 @@ function createByokCloud(options) {
|
|
|
1624
1657
|
}
|
|
1625
1658
|
if (declares(declaration, CLOUD_CAPABILITIES.activityTail)) {
|
|
1626
1659
|
registry.register(
|
|
1627
|
-
{ method: "POST", path:
|
|
1660
|
+
{ method: "POST", path: BYOK_ACTIVITY_PATH, class: "device" },
|
|
1628
1661
|
activityAppendHandler({ ...deviceRouteDeps, bounds: activityBounds })
|
|
1629
1662
|
);
|
|
1630
1663
|
}
|
|
@@ -1642,36 +1675,52 @@ function createByokCloud(options) {
|
|
|
1642
1675
|
maxRequestBytes: options.maxTruthRequestBytes ?? DEFAULT_MAX_TRUTH_REQUEST_BYTES
|
|
1643
1676
|
};
|
|
1644
1677
|
registry.register(
|
|
1645
|
-
{ method: "GET", path:
|
|
1678
|
+
{ method: "GET", path: BYOK_RECORDS_PATH, class: "proof" },
|
|
1646
1679
|
truthManifestHandler(truthDeps)
|
|
1647
1680
|
);
|
|
1648
1681
|
registry.register(
|
|
1649
|
-
{ method: "GET", path:
|
|
1682
|
+
{ method: "GET", path: BYOK_RECORD_ROUTE, class: "proof" },
|
|
1650
1683
|
truthGetHandler(truthDeps)
|
|
1651
1684
|
);
|
|
1652
1685
|
registry.register(
|
|
1653
|
-
{ method: "PUT", path:
|
|
1686
|
+
{ method: "PUT", path: BYOK_RECORD_ROUTE, class: "proof" },
|
|
1654
1687
|
truthPutHandler(truthDeps)
|
|
1655
1688
|
);
|
|
1656
1689
|
}
|
|
1690
|
+
const skillPacks = options.skillPacks;
|
|
1691
|
+
if (skillPacks !== void 0 && declares(declaration, CLOUD_CAPABILITIES.skillPacks)) {
|
|
1692
|
+
const skillPackDeps = {
|
|
1693
|
+
bearer: deviceRouteDeps.bearer,
|
|
1694
|
+
skillPacks,
|
|
1695
|
+
pageLimit: options.skillPackPageLimit ?? DEFAULT_SKILL_PACK_PAGE_LIMIT
|
|
1696
|
+
};
|
|
1697
|
+
registry.register(
|
|
1698
|
+
{ method: "GET", path: BYOK_SKILL_PACKS_PATH, class: "device" },
|
|
1699
|
+
skillPackListHandler(skillPackDeps)
|
|
1700
|
+
);
|
|
1701
|
+
registry.register(
|
|
1702
|
+
{ method: "GET", path: BYOK_SKILL_PACK_FILE_ROUTE, class: "device" },
|
|
1703
|
+
skillPackFileHandler(skillPackDeps)
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1657
1706
|
if (declares(declaration, CLOUD_CAPABILITIES.blobsPresigned)) {
|
|
1658
1707
|
const blobDeps = {
|
|
1659
1708
|
...deviceRouteDeps,
|
|
1660
1709
|
maxBlobSizeBytes: options.maxBlobSizeBytes ?? DEFAULT_MAX_BLOB_SIZE_BYTES
|
|
1661
1710
|
};
|
|
1662
|
-
registry.register({ method: "POST", path:
|
|
1663
|
-
registry.register({ method: "POST", path:
|
|
1664
|
-
registry.register({ method: "GET", path:
|
|
1711
|
+
registry.register({ method: "POST", path: BYOK_BLOBS_PATH, class: "device" }, createBlobHandler(blobDeps));
|
|
1712
|
+
registry.register({ method: "POST", path: BYOK_BLOB_FINALIZE_ROUTE, class: "device" }, finalizeBlobHandler(blobDeps));
|
|
1713
|
+
registry.register({ method: "GET", path: BYOK_BLOB_URL_ROUTE, class: "device" }, blobDownloadUrlHandler(blobDeps));
|
|
1665
1714
|
}
|
|
1666
1715
|
const contentProxy = options.blobContentProxy;
|
|
1667
1716
|
if (contentProxy !== void 0 && declares(declaration, CLOUD_CAPABILITIES.blobsContentProxy)) {
|
|
1668
1717
|
const contentDeps = { contentProxy };
|
|
1669
1718
|
registry.register(
|
|
1670
|
-
{ method: "PUT", path:
|
|
1719
|
+
{ method: "PUT", path: BYOK_BLOB_CONTENT_ROUTE, class: "presigned" },
|
|
1671
1720
|
blobUploadContentHandler(contentDeps)
|
|
1672
1721
|
);
|
|
1673
1722
|
registry.register(
|
|
1674
|
-
{ method: "GET", path:
|
|
1723
|
+
{ method: "GET", path: BYOK_BLOB_CONTENT_ROUTE, class: "presigned" },
|
|
1675
1724
|
blobDownloadContentHandler(contentDeps)
|
|
1676
1725
|
);
|
|
1677
1726
|
}
|
|
@@ -1679,6 +1728,34 @@ function createByokCloud(options) {
|
|
|
1679
1728
|
function controlPlane(tenant) {
|
|
1680
1729
|
return { kind: "control-plane", tenantId: tenant, operatorId };
|
|
1681
1730
|
}
|
|
1731
|
+
async function enqueueTaskEnvelope(tenant, deviceId, requestedTaskId, buildEnvelope) {
|
|
1732
|
+
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
1733
|
+
const taskId = requestedTaskId ?? `task_${options.crypto.randomUuid()}`;
|
|
1734
|
+
const messageId = options.crypto.randomUuid();
|
|
1735
|
+
const message = await stores.mailbox.append({
|
|
1736
|
+
deviceId,
|
|
1737
|
+
messageId,
|
|
1738
|
+
materialize: async (seq) => {
|
|
1739
|
+
const envelope2 = buildEnvelope(taskId, seq, messageId);
|
|
1740
|
+
const body = encodeEnvelope(envelope2);
|
|
1741
|
+
const bytes = new TextEncoder().encode(body);
|
|
1742
|
+
return {
|
|
1743
|
+
body,
|
|
1744
|
+
bodyHash: contentHash(await options.crypto.sha256(bytes)),
|
|
1745
|
+
byteSize: BigInt(bytes.length)
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
});
|
|
1749
|
+
const envelope = decodeEnvelope(message.body);
|
|
1750
|
+
if (envelope.seq !== message.seq) {
|
|
1751
|
+
throw new ByokCloudError(
|
|
1752
|
+
"mailbox_seq_mismatch",
|
|
1753
|
+
`Mailbox stored offer seq ${String(envelope.seq)} at row ${message.seq}; the daemon's redelivery cursor would be wrong.`
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
const attempt = await stores.tasks.open({ taskId, deviceId });
|
|
1757
|
+
return { taskId, seq: message.seq, envelope, attempt };
|
|
1758
|
+
}
|
|
1682
1759
|
return {
|
|
1683
1760
|
fetch: registry.fetch,
|
|
1684
1761
|
routes: registry.routes,
|
|
@@ -1688,27 +1765,20 @@ function createByokCloud(options) {
|
|
|
1688
1765
|
return auth.createPairingCode(tenant, input);
|
|
1689
1766
|
},
|
|
1690
1767
|
async enqueueOffer(tenant, deviceId, input) {
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
const seq = await stores.sequence.next(deviceId);
|
|
1694
|
-
const envelope = createEnvelope("task.offer", input.payload, { taskId, seq });
|
|
1695
|
-
const body = encodeEnvelope(envelope);
|
|
1696
|
-
const bytes = new TextEncoder().encode(body);
|
|
1697
|
-
const message = await stores.mailbox.append({
|
|
1768
|
+
return enqueueTaskEnvelope(
|
|
1769
|
+
tenant,
|
|
1698
1770
|
deviceId,
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
)
|
|
1709
|
-
|
|
1710
|
-
const attempt = await stores.tasks.open({ taskId, deviceId });
|
|
1711
|
-
return { taskId, seq, envelope, attempt };
|
|
1771
|
+
input.taskId,
|
|
1772
|
+
(taskId, seq, messageId) => createEnvelope("task.offer", input.payload, { id: messageId, taskId, seq })
|
|
1773
|
+
);
|
|
1774
|
+
},
|
|
1775
|
+
async enqueueToolsetOffer(tenant, deviceId, input) {
|
|
1776
|
+
return enqueueTaskEnvelope(
|
|
1777
|
+
tenant,
|
|
1778
|
+
deviceId,
|
|
1779
|
+
input.taskId,
|
|
1780
|
+
(taskId, seq, messageId) => createEnvelope("task.offer_with_toolsets", input.payload, { id: messageId, taskId, seq })
|
|
1781
|
+
);
|
|
1712
1782
|
},
|
|
1713
1783
|
readTaskAttempt(tenant, taskId) {
|
|
1714
1784
|
return tenantStoresFor(controlPlane(tenant), root).tasks.get(taskId);
|
|
@@ -1747,7 +1817,7 @@ function createByokCloud(options) {
|
|
|
1747
1817
|
}
|
|
1748
1818
|
};
|
|
1749
1819
|
}
|
|
1750
|
-
function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads) {
|
|
1820
|
+
function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks) {
|
|
1751
1821
|
if (declares(declaration, CLOUD_CAPABILITIES.boardSse) && !declares(declaration, CLOUD_CAPABILITIES.boardCoordination)) {
|
|
1752
1822
|
throw new ByokCloudError(
|
|
1753
1823
|
"capability_over_declared",
|
|
@@ -1772,6 +1842,12 @@ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, trut
|
|
|
1772
1842
|
`This deployment declares ${CLOUD_CAPABILITIES.truthRecords} but was given no content-hash keyed TruthObjectDownloads authority.`
|
|
1773
1843
|
);
|
|
1774
1844
|
}
|
|
1845
|
+
if (skillPacks === void 0 && declares(declaration, CLOUD_CAPABILITIES.skillPacks)) {
|
|
1846
|
+
throw new ByokCloudError(
|
|
1847
|
+
"capability_over_declared",
|
|
1848
|
+
`This deployment declares ${CLOUD_CAPABILITIES.skillPacks} but was given no SkillPackStore, so both /byok/skill-packs routes would be published and unserved.`
|
|
1849
|
+
);
|
|
1850
|
+
}
|
|
1775
1851
|
}
|
|
1776
1852
|
function parseDeclaration(declaration) {
|
|
1777
1853
|
try {
|
|
@@ -1809,7 +1885,7 @@ var InMemoryBlobRegistry = class {
|
|
|
1809
1885
|
async signUrl(blobId, action) {
|
|
1810
1886
|
const exp = this.clock.now().getTime() + this.urlTtlMs;
|
|
1811
1887
|
const sig = await this.computeSig(blobId, action, exp);
|
|
1812
|
-
return
|
|
1888
|
+
return `${byokBlobContentPath(blobId)}?sig=${sig}&exp=${exp}`;
|
|
1813
1889
|
}
|
|
1814
1890
|
computeSig(blobId, action, exp) {
|
|
1815
1891
|
return this.crypto.hmacSha256(this.secret, `${blobId}:${action}:${exp}`);
|
|
@@ -1966,15 +2042,6 @@ var InMemoryDeviceDirectory = class {
|
|
|
1966
2042
|
return key === void 0 ? void 0 : this.#byTenant.get(key);
|
|
1967
2043
|
}
|
|
1968
2044
|
};
|
|
1969
|
-
var InMemoryDeviceSequenceStore = class {
|
|
1970
|
-
#next = /* @__PURE__ */ new Map();
|
|
1971
|
-
async next(tenant, deviceId) {
|
|
1972
|
-
const key = tenantKey(tenant, deviceId);
|
|
1973
|
-
const seq = this.#next.get(key) ?? 1;
|
|
1974
|
-
this.#next.set(key, seq + 1);
|
|
1975
|
-
return seq;
|
|
1976
|
-
}
|
|
1977
|
-
};
|
|
1978
2045
|
var DEDUP_RING_CAPACITY = 1024;
|
|
1979
2046
|
var InMemoryInboundDedupStore = class {
|
|
1980
2047
|
#rings = /* @__PURE__ */ new Map();
|
|
@@ -2173,7 +2240,6 @@ function createInMemoryCloudStores(clock, crypto, objects) {
|
|
|
2173
2240
|
tasks: new InMemoryTaskAttemptStore(clock),
|
|
2174
2241
|
receipts: new InMemoryRequestReceiptStore(clock),
|
|
2175
2242
|
proofReceipts: new InMemoryProofRequestReceiptStore(clock),
|
|
2176
|
-
sequence: new InMemoryDeviceSequenceStore(),
|
|
2177
2243
|
blobs: blobs.blobs,
|
|
2178
2244
|
rateLimiter: new AllowAllRateLimiter()
|
|
2179
2245
|
},
|
|
@@ -2225,7 +2291,9 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
2225
2291
|
...options.activityMaxBytes === void 0 ? {} : { activityMaxBytes: options.activityMaxBytes },
|
|
2226
2292
|
...options.activityCapacity === void 0 ? {} : { activityCapacity: options.activityCapacity },
|
|
2227
2293
|
...options.activityTtlMs === void 0 ? {} : { activityTtlMs: options.activityTtlMs },
|
|
2228
|
-
...options.maxTruthRequestBytes === void 0 ? {} : { maxTruthRequestBytes: options.maxTruthRequestBytes }
|
|
2294
|
+
...options.maxTruthRequestBytes === void 0 ? {} : { maxTruthRequestBytes: options.maxTruthRequestBytes },
|
|
2295
|
+
...options.skillPacks === void 0 ? {} : { skillPacks: options.skillPacks },
|
|
2296
|
+
...options.skillPackPageLimit === void 0 ? {} : { skillPackPageLimit: options.skillPackPageLimit }
|
|
2229
2297
|
});
|
|
2230
2298
|
return { cloud, core, stores, blobContentProxy, clock, crypto };
|
|
2231
2299
|
}
|
|
@@ -2278,7 +2346,7 @@ var BoardFeedClient = class {
|
|
|
2278
2346
|
return this.mode === "sse" ? this.#readSseOnce(afterSeq, signal) : this.#readPollOnce(afterSeq, signal);
|
|
2279
2347
|
}
|
|
2280
2348
|
async #readPollOnce(afterSeq, signal) {
|
|
2281
|
-
const url = new URL(
|
|
2349
|
+
const url = new URL(BYOK_BOARD_PATH, this.#base);
|
|
2282
2350
|
url.searchParams.set("since", String(afterSeq));
|
|
2283
2351
|
const response = await this.#fetch(url, {
|
|
2284
2352
|
headers: { authorization: `Bearer ${this.#accessToken}` },
|
|
@@ -2288,7 +2356,7 @@ var BoardFeedClient = class {
|
|
|
2288
2356
|
return { type: "poll", page: BoardFeedPageSchema.parse(await response.json()) };
|
|
2289
2357
|
}
|
|
2290
2358
|
async #readSseOnce(afterSeq, signal) {
|
|
2291
|
-
const url = new URL(
|
|
2359
|
+
const url = new URL(BYOK_BOARD_STREAM_PATH, this.#base);
|
|
2292
2360
|
const response = await this.#fetch(url, {
|
|
2293
2361
|
headers: {
|
|
2294
2362
|
accept: "text/event-stream",
|
|
@@ -2380,7 +2448,6 @@ var CLOUD_STORE_NAMES = [
|
|
|
2380
2448
|
"tasks",
|
|
2381
2449
|
"receipts",
|
|
2382
2450
|
"proofReceipts",
|
|
2383
|
-
"sequence",
|
|
2384
2451
|
"blobs",
|
|
2385
2452
|
"rateLimiter"
|
|
2386
2453
|
];
|
|
@@ -2394,7 +2461,6 @@ var CLOUD_PORT_METHODS = {
|
|
|
2394
2461
|
tasks: ["open", "get", "claim", "recordStatus"],
|
|
2395
2462
|
receipts: ["record", "get"],
|
|
2396
2463
|
proofReceipts: ["record", "get"],
|
|
2397
|
-
sequence: ["next"],
|
|
2398
2464
|
// Three methods, not six: the byte-proxy trio moved to `BlobContentProxy`,
|
|
2399
2465
|
// which is a composition input rather than a port and therefore has no row
|
|
2400
2466
|
// in this table (docs/researches/s4a-dataplane-design.md §6).
|
|
@@ -2409,11 +2475,10 @@ var CLOUD_PORT_INTERFACES = {
|
|
|
2409
2475
|
tasks: "TaskAttemptStore",
|
|
2410
2476
|
receipts: "RequestReceiptStore",
|
|
2411
2477
|
proofReceipts: "ProofRequestReceiptStore",
|
|
2412
|
-
sequence: "DeviceSequenceStore",
|
|
2413
2478
|
blobs: "CloudBlobStore",
|
|
2414
2479
|
rateLimiter: "InboundRateLimiter"
|
|
2415
2480
|
};
|
|
2416
2481
|
|
|
2417
|
-
export { ACCESS_TOKEN_TTL_SECONDS, AllowAllRateLimiter, BLOB_URL_TTL_MS, BoardFeedClient, BoardFeedRetryableError, BoardFeedStoppedError, ByokCloudError, CLOUD_CAPABILITIES, CLOUD_ERROR_CODES, CLOUD_PORT_INTERFACES, CLOUD_PORT_METHODS, CLOUD_STORE_NAMES, CapabilitiesResponseSchema, CloudRouteRegistry, DEDUP_RING_CAPACITY, DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_PAGE_LIMIT, DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS, DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS, DEFAULT_BOARD_STREAM_RECONCILIATION_INTERVAL_MS, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS, DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS, DEFAULT_EVENTS_PAGE_LIMIT, DEFAULT_LONG_POLL_HOLD_MS, DEFAULT_LONG_POLL_INTERVAL_MS, DEFAULT_MAX_BLOB_SIZE_BYTES, DEFAULT_MAX_TRUTH_REQUEST_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID,
|
|
2482
|
+
export { ACCESS_TOKEN_TTL_SECONDS, AllowAllRateLimiter, BLOB_URL_TTL_MS, BoardFeedClient, BoardFeedRetryableError, BoardFeedStoppedError, ByokCloudError, CLOUD_CAPABILITIES, CLOUD_ERROR_CODES, CLOUD_PORT_INTERFACES, CLOUD_PORT_METHODS, CLOUD_STORE_NAMES, CapabilitiesResponseSchema, CloudRouteRegistry, DEDUP_RING_CAPACITY, DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_PAGE_LIMIT, DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS, DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS, DEFAULT_BOARD_STREAM_RECONCILIATION_INTERVAL_MS, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS, DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS, DEFAULT_EVENTS_PAGE_LIMIT, DEFAULT_LONG_POLL_HOLD_MS, DEFAULT_LONG_POLL_INTERVAL_MS, DEFAULT_MAX_BLOB_SIZE_BYTES, DEFAULT_MAX_TRUTH_REQUEST_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, DEFAULT_SKILL_PACK_PAGE_LIMIT, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, MAX_DEVICE_PROOF_CLOCK_SKEW_MS, MAX_DEVICE_PROOF_HEADER_BYTES, MAX_DEVICE_PROOF_MAX_LIFETIME_MS, NONCE_TTL_MS, PAIRING_CODE_TTL_MS, ROUTE_CLASSES, ROUTE_METHODS, TASK_ATTEMPT_STATUSES, TRUTH_BATCH_MAX_RECORDS, TRUTH_INLINE_CONTENT_TYPE, TRUTH_LABEL_MAX_LENGTH, TRUTH_MANIFEST_MAX_LIMIT, TRUTH_RECORD_CAPABILITY, TRUTH_RECORD_KEY_MAX_LENGTH, TRUTH_REQUEST_ID_MAX_LENGTH, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, authenticateBearer, authenticateDeviceProof, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, verifyNonceSignature };
|
|
2418
2483
|
//# sourceMappingURL=index.js.map
|
|
2419
2484
|
//# sourceMappingURL=index.js.map
|