@byok-sdk/cloud 0.10.1 → 0.11.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 +20 -6
- package/dist/index.js.map +1 -1
- package/dist/stores/in-memory/device-directory.d.ts +13 -3
- package/dist/stores/ports.d.ts +33 -8
- package/package.json +3 -3
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Rows live under a `(tenant, deviceId)` composite key, so a cross-tenant read
|
|
5
5
|
* is not "denied" — it addresses a different key space and finds nothing
|
|
6
|
-
* (§12.6.2 layer 3). The pre-tenant index below
|
|
7
|
-
* so a revocation applied through the composite key is
|
|
8
|
-
* `/byok/challenge` and `/byok/token` with no second
|
|
6
|
+
* (§12.6.2 layer 3). The pre-tenant index below maps a deviceId to that same
|
|
7
|
+
* composite key, so a revocation applied through the composite key is
|
|
8
|
+
* immediately visible to `/byok/challenge` and `/byok/token` with no second
|
|
9
|
+
* copy to keep in sync — and a deleted row is removed from BOTH in one step.
|
|
10
|
+
*
|
|
11
|
+
* Revocation DELETES. `DeviceRecord.revoked` survives as the field every auth
|
|
12
|
+
* path already reads, but no record this store writes ever carries `true`:
|
|
13
|
+
* revocation and machine supersession remove the record outright, so "revoked"
|
|
14
|
+
* and "never registered" are one indistinguishable answer rather than two.
|
|
9
15
|
*/
|
|
10
16
|
import { type PresenceStore, type TenantId, type TenantReadiness } from '@byok-sdk/core';
|
|
11
17
|
import type { DeviceDirectory, DeviceRecord, DeviceRegistration } from '../ports';
|
|
@@ -13,6 +19,10 @@ export declare class InMemoryDeviceDirectory implements DeviceDirectory {
|
|
|
13
19
|
#private;
|
|
14
20
|
register(tenant: TenantId, input: DeviceRegistration): Promise<DeviceRecord>;
|
|
15
21
|
get(tenant: TenantId, deviceId: string): Promise<DeviceRecord | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* Revocation removes the registration. A no-op for a device this tenant does
|
|
24
|
+
* not own: revoking what you cannot address changes nothing.
|
|
25
|
+
*/
|
|
16
26
|
revoke(tenant: TenantId, deviceId: string): Promise<void>;
|
|
17
27
|
recordCapabilities(tenant: TenantId, input: {
|
|
18
28
|
readonly deviceId: string;
|
package/dist/stores/ports.d.ts
CHANGED
|
@@ -50,6 +50,12 @@ export interface DeviceRecord {
|
|
|
50
50
|
readonly proofKeyId: string;
|
|
51
51
|
/** Current proof signing-key rotation generation. */
|
|
52
52
|
readonly proofKeyEpoch: number;
|
|
53
|
+
/**
|
|
54
|
+
* Always `false`. Revocation and machine supersession DELETE the record, so
|
|
55
|
+
* no implementation ever produces a `true` here; the field stays because
|
|
56
|
+
* every auth path reads it and a missing row must fail exactly as a revoked
|
|
57
|
+
* one used to.
|
|
58
|
+
*/
|
|
53
59
|
readonly revoked: boolean;
|
|
54
60
|
/**
|
|
55
61
|
* Client-hashed physical machine identity (lowercase hex SHA-256), when the
|
|
@@ -65,7 +71,7 @@ export interface DeviceRecord {
|
|
|
65
71
|
*/
|
|
66
72
|
readonly capabilities?: readonly string[];
|
|
67
73
|
}
|
|
68
|
-
/** Everything `POST /byok/pair` knows at registration time. `tenantId` is the store's first parameter; `revoked` is the store's own to set. */
|
|
74
|
+
/** Everything `POST /byok/pair` knows at registration time. `tenantId` is the store's first parameter; `revoked` is the store's own to set (always `false`). */
|
|
69
75
|
export interface DeviceRegistration {
|
|
70
76
|
readonly productId: string;
|
|
71
77
|
readonly deviceId: string;
|
|
@@ -75,27 +81,46 @@ export interface DeviceRegistration {
|
|
|
75
81
|
readonly proofKeyEpoch: number;
|
|
76
82
|
/**
|
|
77
83
|
* Optional client-hashed physical machine identity from `PairRequest`. When
|
|
78
|
-
* present, `register`
|
|
79
|
-
*
|
|
80
|
-
*
|
|
84
|
+
* present, `register` DELETES this tenant/product's prior rows carrying the
|
|
85
|
+
* same value — and the device-scoped state those rows were the only reason
|
|
86
|
+
* to keep — before inserting, so one physical machine holds one device row
|
|
87
|
+
* per product. It names no tenant and no product of
|
|
81
88
|
* its own — both still come from the redeemed pairing code's claims — so it
|
|
82
89
|
* can only ever supersede rows the caller already addressed.
|
|
83
90
|
*/
|
|
84
91
|
readonly machineId?: string;
|
|
85
92
|
}
|
|
86
93
|
export interface DeviceDirectory {
|
|
87
|
-
/** Registers the device. With `input.machineId` set this ALSO
|
|
94
|
+
/** Registers the device. With `input.machineId` set this ALSO deletes this tenant/product's prior rows carrying that same machine identity — one physical machine, one row. */
|
|
88
95
|
register(tenant: TenantId, input: DeviceRegistration): Promise<DeviceRecord>;
|
|
89
96
|
/** The row `tenant` owns under `deviceId` — `undefined` including when the device exists under a DIFFERENT tenant. */
|
|
90
97
|
get(tenant: TenantId, deviceId: string): Promise<DeviceRecord | undefined>;
|
|
98
|
+
/**
|
|
99
|
+
* Deletes the registration and the device-scoped state it owned (presence,
|
|
100
|
+
* challenge nonces, assertion-replay entries, inbound dedup) — never the
|
|
101
|
+
* history keyed by the device_id string. A no-op for a device this tenant
|
|
102
|
+
* cannot address. Afterwards every read answers exactly as it does for a
|
|
103
|
+
* device that was never registered.
|
|
104
|
+
*
|
|
105
|
+
* How far the deletion physically reaches is driver-scoped. The durable
|
|
106
|
+
* (Postgres) driver deletes the registration and every dependent
|
|
107
|
+
* device-scoped row in the same transaction, so the state is gone the moment
|
|
108
|
+
* `revoke` resolves. The in-memory reference directory deletes only the
|
|
109
|
+
* registration: its sibling stores are separate maps it does not own, so a
|
|
110
|
+
* presence hint expires on its own `expiresAt` TTL and, until then, is
|
|
111
|
+
* excluded from every projection by the readiness active-device filter
|
|
112
|
+
* (`activeDeviceIds`, built from the surviving rows). The observable contract
|
|
113
|
+
* above holds either way — no read path can see a revoked device.
|
|
114
|
+
*/
|
|
91
115
|
revoke(tenant: TenantId, deviceId: string): Promise<void>;
|
|
92
116
|
list(tenant: TenantId): Promise<readonly DeviceRecord[]>;
|
|
93
|
-
/** Set-wise tenant observation
|
|
117
|
+
/** Set-wise tenant observation. `revokedDeviceCount` is structurally 0 — a revoked device has no row to count. */
|
|
94
118
|
readiness(tenant: TenantId, presence: PresenceStore): Promise<TenantReadiness>;
|
|
95
119
|
/**
|
|
96
120
|
* Persist a capability snapshot obtained from an authenticated device
|
|
97
|
-
* message. Implementations may return `undefined` for an unknown
|
|
98
|
-
* device; callers must fail closed in
|
|
121
|
+
* message. Implementations may return `undefined` for an unknown (including
|
|
122
|
+
* revoked, which is the same absence) device; callers must fail closed in
|
|
123
|
+
* that case.
|
|
99
124
|
*/
|
|
100
125
|
recordCapabilities(tenant: TenantId, input: {
|
|
101
126
|
readonly deviceId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byok-sdk/cloud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "BYOK SDK hosted device surface: stateless frozen-v1 HTTP handlers over tenant-first @byok-sdk/core ports",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"clean": "rm -rf dist"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@byok-sdk/core": "0.
|
|
48
|
-
"@byok-sdk/protocol": "0.
|
|
47
|
+
"@byok-sdk/core": "0.11.0",
|
|
48
|
+
"@byok-sdk/protocol": "0.11.0",
|
|
49
49
|
"hono": "^4.12.30",
|
|
50
50
|
"zod": "^4.4.3"
|
|
51
51
|
}
|