@byok-sdk/server 0.2.0 → 0.4.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 +19 -1
- package/dist/auth.d.ts +32 -10
- package/dist/hub.d.ts +3 -3
- package/dist/index.js +205 -75
- package/dist/index.js.map +1 -1
- package/dist/sqlite-blob-store.d.ts +1 -0
- package/dist/sqlite-support.d.ts +14 -1
- package/dist/sqlite-task-store.d.ts +1 -0
- package/dist/task-store.d.ts +2 -1
- package/dist/types.d.ts +31 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,7 +4,25 @@ The self-hosted SaaS-side reference coordinator: pairing, authenticated device
|
|
|
4
4
|
HTTP/WebSocket/long-poll transport, task leasing, approvals, and in-memory
|
|
5
5
|
stores over the frozen v1 protocol.
|
|
6
6
|
|
|
7
|
-
Use `@byok-sdk/cloud` plus `@byok-sdk/cloud-
|
|
7
|
+
Use `@byok-sdk/cloud` plus `@byok-sdk/cloud-dataplane` for the durable hosted
|
|
8
8
|
composition.
|
|
9
9
|
|
|
10
|
+
Toolset-aware dispatch names logical device-local MCP toolsets; it never sends
|
|
11
|
+
their commands or credentials:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
const task = await server.dispatch({
|
|
15
|
+
deviceId,
|
|
16
|
+
instruction: 'Find five qualified prospects and draft follow-ups.',
|
|
17
|
+
runtime: 'claude',
|
|
18
|
+
policy: { mode: 'auto' },
|
|
19
|
+
requiredToolsets: ['salesko.prospecting'],
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The self-hosted coordinator rejects this call before task creation unless the
|
|
24
|
+
live device advertises `toolset-selection` and its `configuredToolsets`
|
|
25
|
+
inventory contains every required ID. `machines.list()` projects that same
|
|
26
|
+
logical-ID-only inventory; MCP commands and credentials remain device-local.
|
|
27
|
+
|
|
10
28
|
MIT licensed. Node.js 22.19.0 or newer.
|
package/dist/auth.d.ts
CHANGED
|
@@ -16,14 +16,19 @@ export declare const ACCESS_TOKEN_TTL_SECONDS: number;
|
|
|
16
16
|
* another, and an attacker who can get a device to sign anything shaped like
|
|
17
17
|
* a nonce holds a token-renewal credential.
|
|
18
18
|
*
|
|
19
|
-
* The
|
|
19
|
+
* The literal lives in `@byok-sdk/core` (`src/pairing.ts`) and the client signs
|
|
20
|
+
* the same binding (`packages/client/src/daemon/device-keys.ts`) — it used to
|
|
21
|
+
* be three copies, each commented as byte-identical to the other two, which is
|
|
22
|
+
* an agreement that holds only until someone edits one. Re-exported here so
|
|
23
|
+
* this module's public surface is unchanged.
|
|
24
|
+
*
|
|
20
25
|
* There is deliberately no dual mode: a raw, unprefixed nonce signature is
|
|
21
26
|
* simply invalid here, with no flag, fallback, or grace window that would
|
|
22
27
|
* make the old encoding acceptable again. Because the four packages have no
|
|
23
28
|
* published compatibility contract yet, the recovery path for a device on
|
|
24
29
|
* the old encoding is a re-pair, not a server-side shim.
|
|
25
30
|
*/
|
|
26
|
-
export
|
|
31
|
+
export { NONCE_SIGNING_DOMAIN } from '@byok-sdk/core';
|
|
27
32
|
/**
|
|
28
33
|
* S1: server-local tenant identifier. A plain string alias for now — S2 moves
|
|
29
34
|
* the branded/shared form into `@byok-sdk/core`, which does not exist yet, and
|
|
@@ -133,16 +138,23 @@ export declare class NonceStore {
|
|
|
133
138
|
}
|
|
134
139
|
/**
|
|
135
140
|
* The ONLY nonce-signature check on this server (§6.2): the signed message is
|
|
136
|
-
*
|
|
137
|
-
* rather than at the call site is the point — there is one place
|
|
138
|
-
* what a device signature over a nonce means, so no route can be
|
|
139
|
-
* accepts the undomained form.
|
|
141
|
+
* core's `nonceSigningBytes` — the domain followed by the nonce. Applying the
|
|
142
|
+
* domain here rather than at the call site is the point — there is one place
|
|
143
|
+
* that decides what a device signature over a nonce means, so no route can be
|
|
144
|
+
* written that accepts the undomained form.
|
|
140
145
|
*/
|
|
141
146
|
export declare function verifyNonceSignature(devicePublicKey: string, nonce: string, signature: string): boolean;
|
|
142
147
|
export declare function extractBearerToken(header: string | undefined): string | undefined;
|
|
143
148
|
export interface AuthDeps {
|
|
144
149
|
tokenSigner: TokenSigner;
|
|
145
150
|
devices: DeviceRegistry;
|
|
151
|
+
/**
|
|
152
|
+
* The product THIS server instance serves (`createByokServer`'s
|
|
153
|
+
* `productId`). Part of authentication, not of routing: a device row paired
|
|
154
|
+
* into another product is not a principal here at all — see
|
|
155
|
+
* {@link authenticateBearer}.
|
|
156
|
+
*/
|
|
157
|
+
productId: string;
|
|
146
158
|
}
|
|
147
159
|
/**
|
|
148
160
|
* S1: the authenticated principal every authed surface works with. Built from
|
|
@@ -163,9 +175,19 @@ export interface AuthenticatedDevice {
|
|
|
163
175
|
* S1 shape: the token's `(tenantId, deviceId)` are LOOKUP KEYS into the
|
|
164
176
|
* registry, and the row that comes back is the authority. A token for a
|
|
165
177
|
* device that no longer exists, one whose tenant does not own that device,
|
|
166
|
-
* one whose product disagrees with the row,
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
178
|
+
* one whose product disagrees with the row, one whose row belongs to a
|
|
179
|
+
* different product than this instance serves, and one for a revoked device
|
|
180
|
+
* all fail identically here and are indistinguishable to the caller — there
|
|
181
|
+
* is deliberately no "which of those was it" signal to hand back, so no route
|
|
182
|
+
* can turn a 401 into a cross-tenant (or cross-product) existence oracle.
|
|
183
|
+
*
|
|
184
|
+
* The last two checks are different facts and both are needed. Row vs claims
|
|
185
|
+
* says "the token belongs to this row"; row vs instance says "this row
|
|
186
|
+
* belongs to the product this server serves" — a single server can mint
|
|
187
|
+
* pairing codes for any product (`createPairingCode` takes the claims per
|
|
188
|
+
* code), so a row from another product is a real row holding a real token
|
|
189
|
+
* and is still not a principal here. `conn.hello`'s own product checks
|
|
190
|
+
* (`ws-server.ts`) validate the client's ANNOUNCEMENT, which is a third fact
|
|
191
|
+
* and stays where it is.
|
|
170
192
|
*/
|
|
171
193
|
export declare function authenticateBearer(header: string | undefined, deps: AuthDeps): Promise<AuthenticatedDevice | undefined>;
|
package/dist/hub.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WebSocket } from 'ws';
|
|
2
|
-
import { type Envelope, type RuntimeId, type RuntimeInfo, type TaskState } from '@byok-sdk/protocol';
|
|
2
|
+
import { type Envelope, type RuntimeId, type RuntimeInfo, type TaskState, type ToolsetId } from '@byok-sdk/protocol';
|
|
3
3
|
import type { DeviceRegistry } from './auth';
|
|
4
4
|
import { RateLimiter } from './rate-limiter';
|
|
5
5
|
import type { TaskStore } from './task-store';
|
|
@@ -29,7 +29,7 @@ import type { ByokServerEvent, DispatchInput, HubStats, MachineInfo, TaskHandle,
|
|
|
29
29
|
* handlers below no longer carry their own device-mismatch checks.
|
|
30
30
|
*
|
|
31
31
|
* Outbound delivery (M1, §1.2/§9): every server -> daemon envelope
|
|
32
|
-
* (`conn.ack`, `task.
|
|
32
|
+
* (`conn.ack`, either task offer, `task.approve/reject/cancel/steer`) gets a fresh
|
|
33
33
|
* per-device monotonic `seq` and is retained in a capped ring buffer
|
|
34
34
|
* ({@link OUTBOX_RING_CAPACITY} entries) so it can be redelivered — in `seq`
|
|
35
35
|
* order, skipping anything whose task has since reached a terminal state —
|
|
@@ -276,7 +276,7 @@ export declare class ConnectionHub {
|
|
|
276
276
|
* connection this hub never learns capabilities for simply reads back
|
|
277
277
|
* `undefined` from {@link getDeviceCapabilities}.
|
|
278
278
|
*/
|
|
279
|
-
registerConnection(deviceId: string, ws: WebSocket, runtimes: RuntimeInfo[] | undefined, capabilities?: readonly string[]): void;
|
|
279
|
+
registerConnection(deviceId: string, ws: WebSocket, runtimes: RuntimeInfo[] | undefined, capabilities?: readonly string[], configuredToolsets?: readonly ToolsetId[]): void;
|
|
280
280
|
sendConnAck(deviceId: string, capabilities: string[]): void;
|
|
281
281
|
/**
|
|
282
282
|
* Reconnection procedure step 3 (§9): redeliver, in `seq` order, every
|