@cortexkit/claustrum-client 0.15.0 → 0.17.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 +40 -1
- package/dist/enrollment.d.ts +7 -0
- package/dist/enrollment.js +13 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/wire.d.ts +4 -0
- package/dist/wire.js +4 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -76,6 +76,11 @@ copy. One behaviour reaches callers: while the vault is restarting or refusing r
|
|
|
76
76
|
opens as retryable, a call now keeps retrying for up to 90 s (paced, with jitter) instead
|
|
77
77
|
of 30 s. Give every vault call on a latency-sensitive path its own timeout.
|
|
78
78
|
|
|
79
|
+
**Changed in 0.16.0:** the dependency is now `@cortexkit/subc-client` ^0.24.0.
|
|
80
|
+
The SDK negotiates FIFO request/watch windows and retries only daemon-origin
|
|
81
|
+
`route_busy` refusals within the request deadline. Claustrum adds no busy retry;
|
|
82
|
+
older daemons keep the SDK's previous behavior.
|
|
83
|
+
|
|
79
84
|
## Reading credential material
|
|
80
85
|
|
|
81
86
|
The wire payload is opaque bytes. `getCredential(handle, minTtlMs?)` and
|
|
@@ -161,10 +166,44 @@ for (;;) {
|
|
|
161
166
|
}
|
|
162
167
|
```
|
|
163
168
|
|
|
169
|
+
## Enrollment
|
|
170
|
+
|
|
171
|
+
`enrollPropose` accepts an optional `displayName`, for example
|
|
172
|
+
`{ name: 'consumer', requestSecretHash, displayName: 'Claude sign-in for Pi' }`.
|
|
173
|
+
The person sees and approves the proposed label, or replaces it during approval.
|
|
174
|
+
This option needs a vault build with enrollment display-name support; an older
|
|
175
|
+
vault refuses the `display_name` field as `invalid_params`. Omit `displayName`
|
|
176
|
+
for compatibility with older vaults.
|
|
177
|
+
|
|
178
|
+
Use `newEnrollmentSecret()` to prepare an enrollment proposal. It generates 32
|
|
179
|
+
random bytes as exactly 64 lowercase hex characters, plus `requestSecretHash`:
|
|
180
|
+
the lowercase hex SHA-256 of the **32 decoded bytes**, not of the UTF-8 hex text.
|
|
181
|
+
`enrollmentSecretHash(requestSecret)` recomputes the hash of a persisted secret
|
|
182
|
+
and refuses anything other than exactly 64 lowercase hex characters.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { ClaustrumClient, newEnrollmentSecret } from '@cortexkit/claustrum-client'
|
|
186
|
+
|
|
187
|
+
const client = await ClaustrumClient.connect()
|
|
188
|
+
const { requestSecret, requestSecretHash } = newEnrollmentSecret()
|
|
189
|
+
// Persist the secret privately before proposing so a crash can recover the poll.
|
|
190
|
+
await persistRequestSecret(requestSecret)
|
|
191
|
+
const { requestId } = await client.enrollPropose({ name: 'consumer', requestSecretHash })
|
|
192
|
+
await persistRequestId(requestId)
|
|
193
|
+
// After operator approval, poll with the original secret, not its hash.
|
|
194
|
+
const outcome = await client.enrollPoll({ requestId, requestSecret })
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The persistence functions above are caller-provided. Re-proposing the same name
|
|
198
|
+
with the same persisted secret/hash recovers the request id after a lost reply.
|
|
199
|
+
Only `pending` means poll again; an approved outcome returns the token once, so
|
|
200
|
+
persist it with `writeEnrollmentTokenFile` before using it. Never log the request
|
|
201
|
+
secret or token.
|
|
202
|
+
|
|
164
203
|
## Private files and Windows support
|
|
165
204
|
|
|
166
205
|
Enrollment tokens support Windows as well as Unix. The client uses
|
|
167
|
-
`@cortexkit/store` 0.2.
|
|
206
|
+
`@cortexkit/store` 0.2.3 or newer: exclusive private creation
|
|
168
207
|
protects files before any secret bytes are written, and atomic replacement keeps
|
|
169
208
|
that protection. Unix files use mode 0600 and new directories use 0700. On Windows,
|
|
170
209
|
the store's `@cortexkit/private-files` native module creates owner-only ACLs and the
|
package/dist/enrollment.d.ts
CHANGED
|
@@ -3,5 +3,12 @@ export type EnrollmentTokenFile = {
|
|
|
3
3
|
token: string;
|
|
4
4
|
token_generation: number;
|
|
5
5
|
};
|
|
6
|
+
/** Hash the 32 decoded bytes, not the UTF-8 text of the hex request secret. */
|
|
7
|
+
export declare function enrollmentSecretHash(requestSecret: string): string;
|
|
8
|
+
/** Mint an enrollment request secret and the hash to send with the proposal. */
|
|
9
|
+
export declare function newEnrollmentSecret(): {
|
|
10
|
+
requestSecret: string;
|
|
11
|
+
requestSecretHash: string;
|
|
12
|
+
};
|
|
6
13
|
export declare function createEnrollmentWriter(checkPath?: typeof checkPrivatePath): (path: string, value: EnrollmentTokenFile) => Promise<void>;
|
|
7
14
|
export declare const writeEnrollmentTokenFile: (path: string, value: EnrollmentTokenFile) => Promise<void>;
|
package/dist/enrollment.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { createPrivateFile } from '@cortexkit/store';
|
|
2
2
|
import { checkPrivatePath, createPrivateParents, notPrivate, privateCreationCode } from './private-path.js';
|
|
3
|
-
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
4
4
|
import { open, rename, unlink } from 'node:fs/promises';
|
|
5
5
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
6
6
|
const TOKEN_RE = /^[0-9a-f]{64}$/;
|
|
7
|
+
/** Hash the 32 decoded bytes, not the UTF-8 text of the hex request secret. */
|
|
8
|
+
export function enrollmentSecretHash(requestSecret) {
|
|
9
|
+
if (typeof requestSecret !== 'string' || requestSecret.length !== 64 || !TOKEN_RE.test(requestSecret)) {
|
|
10
|
+
throw new Error('enrollment request secret must be exactly 64 lowercase hex characters');
|
|
11
|
+
}
|
|
12
|
+
return createHash('sha256').update(Buffer.from(requestSecret, 'hex')).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
/** Mint an enrollment request secret and the hash to send with the proposal. */
|
|
15
|
+
export function newEnrollmentSecret() {
|
|
16
|
+
const requestSecret = randomBytes(32).toString('hex');
|
|
17
|
+
return { requestSecret, requestSecretHash: enrollmentSecretHash(requestSecret) };
|
|
18
|
+
}
|
|
7
19
|
function validateTokenFile(value) {
|
|
8
20
|
if (!TOKEN_RE.test(value.token))
|
|
9
21
|
throw new Error('enrollment token must be exactly 64 lowercase hex characters');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { detectClaustrumConnection, getDefaultClaustrumConnectionPath, resolveClaustrumConnectionPath, type ClaustrumDetection, type ClaustrumEndpoint, } from './detect.js';
|
|
2
2
|
export { storeIdentity, storageFingerprint } from './identity.js';
|
|
3
|
-
export { writeEnrollmentTokenFile, type EnrollmentTokenFile, } from './enrollment.js';
|
|
3
|
+
export { enrollmentSecretHash, newEnrollmentSecret, writeEnrollmentTokenFile, type EnrollmentTokenFile, } from './enrollment.js';
|
|
4
4
|
export { ClaustrumCredentialError, credentialErrorAction, ERROR_CLASS_WIRE_SET, type ClaustrumCredentialErrorAction, type ClaustrumCredentialErrorClass, } from './errors.js';
|
|
5
5
|
export { ClaustrumClient, type ClaustrumClientOptions, type ClaustrumConnector, type ClaustrumReporterSource, type CredentialStatus, type EnrollmentPollOutcome, type ScopedGrantTuple, type ScopedInventory, type ScopedInventoryRow, type ServedCredential, type ServedCredentialBytes, } from './wire.js';
|
|
6
6
|
export { ScopedWatchError, type ScopedWatch, type ScopedWatchNotice, type ScopedWatchOptions, type ScopedWatchOutcome, } from './watch.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { detectClaustrumConnection, getDefaultClaustrumConnectionPath, resolveClaustrumConnectionPath, } from './detect.js';
|
|
2
2
|
export { storeIdentity, storageFingerprint } from './identity.js';
|
|
3
|
-
export { writeEnrollmentTokenFile, } from './enrollment.js';
|
|
3
|
+
export { enrollmentSecretHash, newEnrollmentSecret, writeEnrollmentTokenFile, } from './enrollment.js';
|
|
4
4
|
export { ClaustrumCredentialError, credentialErrorAction, ERROR_CLASS_WIRE_SET, } from './errors.js';
|
|
5
5
|
export { ClaustrumClient, } from './wire.js';
|
|
6
6
|
export { ScopedWatchError, } from './watch.js';
|
package/dist/wire.d.ts
CHANGED
|
@@ -179,10 +179,14 @@ export declare class ClaustrumClient {
|
|
|
179
179
|
* BEFORE calling this, and a crash costs nothing.
|
|
180
180
|
*
|
|
181
181
|
* A different secret on the same name is a different caller and is refused.
|
|
182
|
+
* Optional `displayName` proposes a person-facing label for operator approval. It
|
|
183
|
+
* needs a vault build with enrollment display-name support; an older vault refuses
|
|
184
|
+
* the `display_name` field as `invalid_params`. Omit it for older vaults.
|
|
182
185
|
*/
|
|
183
186
|
enrollPropose(input: {
|
|
184
187
|
name: string;
|
|
185
188
|
requestSecretHash: string;
|
|
189
|
+
displayName?: string;
|
|
186
190
|
}): Promise<{
|
|
187
191
|
requestId: string;
|
|
188
192
|
}>;
|
package/dist/wire.js
CHANGED
|
@@ -296,11 +296,15 @@ export class ClaustrumClient {
|
|
|
296
296
|
* BEFORE calling this, and a crash costs nothing.
|
|
297
297
|
*
|
|
298
298
|
* A different secret on the same name is a different caller and is refused.
|
|
299
|
+
* Optional `displayName` proposes a person-facing label for operator approval. It
|
|
300
|
+
* needs a vault build with enrollment display-name support; an older vault refuses
|
|
301
|
+
* the `display_name` field as `invalid_params`. Omit it for older vaults.
|
|
299
302
|
*/
|
|
300
303
|
async enrollPropose(input) {
|
|
301
304
|
const response = await this.#call('auth.enroll_propose', {
|
|
302
305
|
proposed_name: input.name,
|
|
303
306
|
request_secret_hash: input.requestSecretHash,
|
|
307
|
+
...(input.displayName !== undefined ? { display_name: input.displayName } : {}),
|
|
304
308
|
});
|
|
305
309
|
const result = decodeEnrollmentResult(response, this.#logger);
|
|
306
310
|
const requestId = result.request_id;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/claustrum-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cortexkit/store": "^0.2.
|
|
31
|
-
"@cortexkit/subc-client": "^0.
|
|
30
|
+
"@cortexkit/store": "^0.2.3",
|
|
31
|
+
"@cortexkit/subc-client": "^0.24.0"
|
|
32
32
|
}
|
|
33
33
|
}
|