synomem 0.9.0 → 0.9.1
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/CHANGELOG.md +12 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +1 -1
- package/dist/credentials.d.ts +13 -4
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +121 -19
- package/dist/credentials.js.map +1 -1
- package/dist/oauth.d.ts +1 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +18 -1
- package/dist/oauth.js.map +1 -1
- package/dist/profiles.d.ts.map +1 -1
- package/dist/profiles.js +18 -4
- package/dist/profiles.js.map +1 -1
- package/dist/resolvers.d.ts +11 -4
- package/dist/resolvers.d.ts.map +1 -1
- package/dist/resolvers.js +91 -10
- package/dist/resolvers.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +5 -10
- package/src/credentials.ts +126 -19
- package/src/oauth.ts +20 -1
- package/src/profiles.ts +21 -4
- package/src/resolvers.ts +96 -10
package/dist/resolvers.js
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* each other's attribution. The resolver narrows (pins a context, restricts to a
|
|
9
9
|
* preset); the server alone authorizes.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { randomBytes } from 'node:crypto';
|
|
12
|
+
import { closeSync, existsSync, openSync, readFileSync, rmSync, statSync, writeSync, } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { atomicWriteFile } from './fs-utils.js';
|
|
12
15
|
import { SynomemClient } from './client.js';
|
|
13
16
|
import { describeIdentity, discoverContexts } from './discover.js';
|
|
14
17
|
import { SynomemError } from './errors.js';
|
|
@@ -29,15 +32,93 @@ function required() {
|
|
|
29
32
|
return new SynomemError('CONTEXT_REQUIRED', 'This connection can act as more than one workspace/actor. Pass contextId: call synomem_context_list and choose the context that matches what the user asked for.');
|
|
30
33
|
}
|
|
31
34
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
+
* The stable local context id for one actor in one store, from the store's own
|
|
36
|
+
* context registry (`<home>/contexts.json`).
|
|
37
|
+
*
|
|
38
|
+
* The id is cryptographically random (128 bits) and minted the first time the
|
|
39
|
+
* actor is addressed, never derived from a name or from the canonical tuple:
|
|
40
|
+
* an id reveals nothing about its target and cannot be guessed. It survives
|
|
41
|
+
* renames and moving the store (the registry moves with it), and an unrelated
|
|
42
|
+
* store with a same-named agent has its own registry and so its own id.
|
|
43
|
+
* Minting is serialized across processes so two harnesses opening the store
|
|
44
|
+
* at once agree on one id.
|
|
35
45
|
*/
|
|
36
|
-
export function localContextId(
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
export function localContextId(home, actor) {
|
|
47
|
+
const path = join(home, 'contexts.json');
|
|
48
|
+
const key = `${actor.kind}:${actor.id}`;
|
|
49
|
+
const existing = readRegistry(path).contexts[key];
|
|
50
|
+
if (existing)
|
|
51
|
+
return existing;
|
|
52
|
+
return withRegistryLock(home, () => {
|
|
53
|
+
const registry = readRegistry(path);
|
|
54
|
+
const current = registry.contexts[key];
|
|
55
|
+
if (current)
|
|
56
|
+
return current;
|
|
57
|
+
const id = `lctx_${randomBytes(16).toString('hex')}`;
|
|
58
|
+
registry.contexts[key] = id;
|
|
59
|
+
atomicWriteFile(path, `${JSON.stringify(registry, null, 2)}\n`, 0o600);
|
|
60
|
+
return id;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function readRegistry(path) {
|
|
64
|
+
if (!existsSync(path))
|
|
65
|
+
return { version: 1, contexts: {} };
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
68
|
+
if (parsed.version === 1 && parsed.contexts && typeof parsed.contexts === 'object') {
|
|
69
|
+
return { version: 1, contexts: { ...parsed.contexts } };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* fall through */
|
|
74
|
+
}
|
|
75
|
+
throw new SynomemError('CONFIG_INVALID', `${path} is not a valid Synomem context registry.`);
|
|
76
|
+
}
|
|
77
|
+
/** A short synchronous O_EXCL lock; a dead or old owner is broken. */
|
|
78
|
+
function withRegistryLock(home, operation) {
|
|
79
|
+
const lock = join(home, 'contexts.json.lock');
|
|
80
|
+
const deadline = Date.now() + 10_000;
|
|
81
|
+
const sleeper = new Int32Array(new SharedArrayBuffer(4));
|
|
82
|
+
for (;;) {
|
|
83
|
+
try {
|
|
84
|
+
const descriptor = openSync(lock, 'wx', 0o600);
|
|
85
|
+
writeSync(descriptor, `${process.pid}\n`);
|
|
86
|
+
closeSync(descriptor);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error.code !== 'EEXIST')
|
|
91
|
+
throw error;
|
|
92
|
+
try {
|
|
93
|
+
const pid = Number(readFileSync(lock, 'utf8').trim());
|
|
94
|
+
const old = Date.now() - statSync(lock).mtimeMs > 5_000;
|
|
95
|
+
let alive = Number.isSafeInteger(pid) && pid > 0;
|
|
96
|
+
if (alive) {
|
|
97
|
+
try {
|
|
98
|
+
process.kill(pid, 0);
|
|
99
|
+
}
|
|
100
|
+
catch (probe) {
|
|
101
|
+
alive = probe.code === 'EPERM';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!alive || old)
|
|
105
|
+
rmSync(lock, { force: true });
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* released meanwhile */
|
|
109
|
+
}
|
|
110
|
+
if (Date.now() > deadline) {
|
|
111
|
+
throw new SynomemError('DATABASE_BUSY', 'The local context registry is locked. Try again.');
|
|
112
|
+
}
|
|
113
|
+
Atomics.wait(sleeper, 0, 0, 20);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return operation();
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
rmSync(lock, { force: true });
|
|
121
|
+
}
|
|
41
122
|
}
|
|
42
123
|
/**
|
|
43
124
|
* A remote (hosted API) resolver over one credential.
|
|
@@ -194,7 +275,7 @@ export function createLocalResolver(options) {
|
|
|
194
275
|
return {
|
|
195
276
|
service: client,
|
|
196
277
|
context: {
|
|
197
|
-
contextId: localContextId(
|
|
278
|
+
contextId: localContextId(options.home, actor),
|
|
198
279
|
organizationId: null,
|
|
199
280
|
workspaceId: capabilities.binding.workspaceId,
|
|
200
281
|
actor,
|
package/dist/resolvers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvers.js","sourceRoot":"","sources":["../src/resolvers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolvers.js","sourceRoot":"","sources":["../src/resolvers.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -4,12 +4,7 @@ import { spawn } from 'node:child_process';
|
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import { Command, CommanderError, Option } from 'commander';
|
|
7
|
-
import {
|
|
8
|
-
ensureLocalStore,
|
|
9
|
-
LOCAL_OPERATOR,
|
|
10
|
-
localStoreWorkspaceId,
|
|
11
|
-
openLocalService,
|
|
12
|
-
} from './backend.js';
|
|
7
|
+
import { ensureLocalStore, LOCAL_OPERATOR, openLocalService } from './backend.js';
|
|
13
8
|
import { cloudApiUrl } from './cloud.js';
|
|
14
9
|
import { resolveHome } from './config.js';
|
|
15
10
|
import {
|
|
@@ -509,7 +504,7 @@ export function createCli(io: CliIo = defaultIo, dependencies: CliDependencies =
|
|
|
509
504
|
if (!handle) throw new SynomemError('INVALID_INPUT', 'An agent handle is required.');
|
|
510
505
|
const profileName = assertName(options.profileName ?? handle, 'profile');
|
|
511
506
|
const store = profileStoreFor(global.home);
|
|
512
|
-
|
|
507
|
+
ensureLocalStore(global.home);
|
|
513
508
|
const config = store.read();
|
|
514
509
|
|
|
515
510
|
const existing = config.profiles[profileName];
|
|
@@ -568,7 +563,7 @@ export function createCli(io: CliIo = defaultIo, dependencies: CliDependencies =
|
|
|
568
563
|
...(global.explicitHome ? { home: global.home } : {}),
|
|
569
564
|
actorId: agent.id,
|
|
570
565
|
actorName: agent.displayName,
|
|
571
|
-
contextId: localContextId(
|
|
566
|
+
contextId: localContextId(global.home, actor),
|
|
572
567
|
};
|
|
573
568
|
const next: ProfilesConfig = {
|
|
574
569
|
...config,
|
|
@@ -1017,7 +1012,7 @@ export function createCli(io: CliIo = defaultIo, dependencies: CliDependencies =
|
|
|
1017
1012
|
...(storeHome !== global.home ? { home: storeHome } : {}),
|
|
1018
1013
|
actorId: agent.id,
|
|
1019
1014
|
actorName: agent.displayName,
|
|
1020
|
-
contextId: localContextId(
|
|
1015
|
+
contextId: localContextId(storeHome, {
|
|
1021
1016
|
kind: 'agent',
|
|
1022
1017
|
id: agent.id,
|
|
1023
1018
|
}),
|
|
@@ -1629,7 +1624,7 @@ export function createCli(io: CliIo = defaultIo, dependencies: CliDependencies =
|
|
|
1629
1624
|
...(created.storeHome !== global.home ? { home: created.storeHome } : {}),
|
|
1630
1625
|
actorId: agent.id,
|
|
1631
1626
|
actorName: agent.displayName,
|
|
1632
|
-
contextId: localContextId(
|
|
1627
|
+
contextId: localContextId(created.storeHome, {
|
|
1633
1628
|
kind: 'agent',
|
|
1634
1629
|
id: agent.id,
|
|
1635
1630
|
}),
|
package/src/credentials.ts
CHANGED
|
@@ -21,19 +21,21 @@
|
|
|
21
21
|
* that names the explicit alternative.
|
|
22
22
|
*/
|
|
23
23
|
import { spawn } from 'node:child_process';
|
|
24
|
-
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
25
25
|
import {
|
|
26
26
|
chmodSync,
|
|
27
27
|
closeSync,
|
|
28
28
|
existsSync,
|
|
29
|
+
linkSync,
|
|
29
30
|
mkdirSync,
|
|
30
31
|
openSync,
|
|
31
32
|
readFileSync,
|
|
33
|
+
renameSync,
|
|
32
34
|
rmSync,
|
|
33
35
|
statSync,
|
|
34
|
-
unlinkSync,
|
|
35
36
|
writeSync,
|
|
36
37
|
} from 'node:fs';
|
|
38
|
+
import { hostname } from 'node:os';
|
|
37
39
|
import { join } from 'node:path';
|
|
38
40
|
import { z } from 'zod';
|
|
39
41
|
import { SynomemError } from './errors.js';
|
|
@@ -154,7 +156,18 @@ export class OsCredentialStore implements CredentialStore {
|
|
|
154
156
|
|
|
155
157
|
constructor(options: { platform?: NodeJS.Platform; run?: CredentialCommandRunner } = {}) {
|
|
156
158
|
this.platform = options.platform ?? process.platform;
|
|
157
|
-
|
|
159
|
+
const run = options.run ?? command;
|
|
160
|
+
// A missing helper (a headless Linux box with no Secret Service) is a
|
|
161
|
+
// configuration answer, not a crash: name the explicit alternatives. There
|
|
162
|
+
// is still no fallback — the caller chooses --store file deliberately.
|
|
163
|
+
this.run = async (executable, arguments_, input) => {
|
|
164
|
+
try {
|
|
165
|
+
return await run(executable, arguments_, input);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') this.unsupported();
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
async get(reference: string): Promise<StoredCredential | undefined> {
|
|
@@ -235,7 +248,7 @@ export class OsCredentialStore implements CredentialStore {
|
|
|
235
248
|
'CONFIG_INVALID',
|
|
236
249
|
this.platform === 'win32'
|
|
237
250
|
? 'Windows Credential Manager storage is not supported yet. Use --store file, or --store environment with SYNOMEM_ACCESS_TOKEN.'
|
|
238
|
-
: '
|
|
251
|
+
: 'No operating-system credential store is available here (it needs the macOS Keychain, or secret-tool with a Secret Service on Linux). Choose one explicitly: --store file keeps it in a 0600 file under your Synomem home; --store environment reads SYNOMEM_ACCESS_TOKEN.',
|
|
239
252
|
);
|
|
240
253
|
}
|
|
241
254
|
}
|
|
@@ -261,6 +274,8 @@ export class FileCredentialStore implements CredentialStore {
|
|
|
261
274
|
async get(reference: string): Promise<StoredCredential | undefined> {
|
|
262
275
|
const path = this.path(reference);
|
|
263
276
|
if (!existsSync(path)) return undefined;
|
|
277
|
+
assertPrivate(this.directory, 0o700);
|
|
278
|
+
assertPrivate(path, 0o600);
|
|
264
279
|
return parseStoredCredential(readFileSync(path, 'utf8'));
|
|
265
280
|
}
|
|
266
281
|
|
|
@@ -282,6 +297,31 @@ export class FileCredentialStore implements CredentialStore {
|
|
|
282
297
|
}
|
|
283
298
|
}
|
|
284
299
|
|
|
300
|
+
/*
|
|
301
|
+
* Like ssh with a private key: a credential file (or its directory) that other
|
|
302
|
+
* users can read, or that another user owns, is refused rather than used. The
|
|
303
|
+
* fix is the owner's to make; silently tightening it would hide that the
|
|
304
|
+
* secret may already have been exposed.
|
|
305
|
+
*/
|
|
306
|
+
function assertPrivate(path: string, expected: number): void {
|
|
307
|
+
if (process.platform === 'win32') return;
|
|
308
|
+
const stat = statSync(path);
|
|
309
|
+
if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) {
|
|
310
|
+
throw new SynomemError(
|
|
311
|
+
'CONFIG_INVALID',
|
|
312
|
+
`${path} is owned by another user; refusing to use it.`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
if ((stat.mode & 0o077) !== 0) {
|
|
316
|
+
throw new SynomemError(
|
|
317
|
+
'CONFIG_INVALID',
|
|
318
|
+
`${path} is accessible to other users (mode ${(stat.mode & 0o777).toString(8)}). ` +
|
|
319
|
+
`Run \`chmod ${expected.toString(8)} ${path}\` if you are sure it was not exposed, ` +
|
|
320
|
+
'otherwise remove it and sign in again.',
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
285
325
|
export interface CredentialStores {
|
|
286
326
|
keychain: CredentialStore;
|
|
287
327
|
file: CredentialStore;
|
|
@@ -294,10 +334,19 @@ export function defaultCredentialStores(home: string): CredentialStores {
|
|
|
294
334
|
/**
|
|
295
335
|
* A cross-process lock around one credential's refresh.
|
|
296
336
|
*
|
|
297
|
-
* `O_EXCL` creation is the mutual exclusion
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
337
|
+
* `O_EXCL` creation is the mutual exclusion. The lock file records its owner
|
|
338
|
+
* (pid, host) and a random token. A lock is broken only when its owner is
|
|
339
|
+
* provably gone — a dead pid on this host — or, for an owner on another host
|
|
340
|
+
* or an unreadable file, once it is older than `staleMs`. A live owner is never
|
|
341
|
+
* preempted however long its refresh takes (the refresh request itself times
|
|
342
|
+
* out well inside `staleMs`).
|
|
343
|
+
*
|
|
344
|
+
* Breaking renames the file aside first and checks the token: if another
|
|
345
|
+
* process already broke and re-acquired it in between, the fresh lock is put
|
|
346
|
+
* back rather than deleted. Correctness against a concurrent writer is still
|
|
347
|
+
* the generation compare-and-swap in the refresh path; the lock exists so that
|
|
348
|
+
* only one process ever spends a given refresh token. Locks assume one host's
|
|
349
|
+
* filesystem; a home shared over a network filesystem is not supported.
|
|
301
350
|
*/
|
|
302
351
|
export async function withCredentialLock<T>(
|
|
303
352
|
home: string,
|
|
@@ -314,22 +363,16 @@ export async function withCredentialLock<T>(
|
|
|
314
363
|
const staleMs = options.staleMs ?? 60_000;
|
|
315
364
|
const deadline = Date.now() + (options.timeoutMs ?? 30_000);
|
|
316
365
|
const pollMs = options.pollMs ?? 50;
|
|
366
|
+
const token = randomBytes(16).toString('hex');
|
|
367
|
+
const host = hostname();
|
|
317
368
|
let descriptor: number | undefined;
|
|
318
369
|
while (descriptor === undefined) {
|
|
319
370
|
try {
|
|
320
371
|
descriptor = openSync(path, 'wx', 0o600);
|
|
321
|
-
writeSync(descriptor, `${process.pid}\n`);
|
|
372
|
+
writeSync(descriptor, `${process.pid}\n${host}\n${token}\n`);
|
|
322
373
|
} catch (error) {
|
|
323
374
|
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
324
|
-
|
|
325
|
-
if (Date.now() - statSync(path).mtimeMs > staleMs) {
|
|
326
|
-
unlinkSync(path);
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
} catch {
|
|
330
|
-
// Removed between the failed open and the stat: try again at once.
|
|
331
|
-
continue;
|
|
332
|
-
}
|
|
375
|
+
if (breakIfAbandoned(path, host, staleMs)) continue;
|
|
333
376
|
if (Date.now() > deadline) {
|
|
334
377
|
throw new SynomemError(
|
|
335
378
|
'DATABASE_BUSY',
|
|
@@ -343,6 +386,70 @@ export async function withCredentialLock<T>(
|
|
|
343
386
|
return await operation();
|
|
344
387
|
} finally {
|
|
345
388
|
closeSync(descriptor);
|
|
346
|
-
|
|
389
|
+
// Release only our own lock.
|
|
390
|
+
if (readLock(path)?.token === token) rmSync(path, { force: true });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
interface LockOwner {
|
|
395
|
+
pid: number;
|
|
396
|
+
host: string;
|
|
397
|
+
token: string;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function readLock(path: string): LockOwner | undefined {
|
|
401
|
+
try {
|
|
402
|
+
const [pid, host, token] = readFileSync(path, 'utf8').split('\n');
|
|
403
|
+
const parsed = Number(pid);
|
|
404
|
+
if (!Number.isSafeInteger(parsed) || !host || !token) return undefined;
|
|
405
|
+
return { pid: parsed, host, token };
|
|
406
|
+
} catch {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function processAlive(pid: number): boolean {
|
|
412
|
+
try {
|
|
413
|
+
process.kill(pid, 0);
|
|
414
|
+
return true;
|
|
415
|
+
} catch (error) {
|
|
416
|
+
// EPERM: it exists but belongs to someone else — alive.
|
|
417
|
+
return (error as NodeJS.ErrnoException).code === 'EPERM';
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** Returns true when the caller should immediately retry acquiring. */
|
|
422
|
+
function breakIfAbandoned(path: string, host: string, staleMs: number): boolean {
|
|
423
|
+
let age: number;
|
|
424
|
+
try {
|
|
425
|
+
age = Date.now() - statSync(path).mtimeMs;
|
|
426
|
+
} catch {
|
|
427
|
+
return true; // Released between the failed open and now.
|
|
428
|
+
}
|
|
429
|
+
const owner = readLock(path);
|
|
430
|
+
// A lock whose contents are still being written is fresh, not abandoned.
|
|
431
|
+
if (!owner && age < 1_000) return false;
|
|
432
|
+
const abandoned = owner
|
|
433
|
+
? owner.host === host
|
|
434
|
+
? !processAlive(owner.pid)
|
|
435
|
+
: age > staleMs
|
|
436
|
+
: age > staleMs;
|
|
437
|
+
if (!abandoned) return false;
|
|
438
|
+
const aside = `${path}.broken-${randomBytes(6).toString('hex')}`;
|
|
439
|
+
try {
|
|
440
|
+
renameSync(path, aside);
|
|
441
|
+
} catch {
|
|
442
|
+
return true; // Someone else broke or released it first.
|
|
443
|
+
}
|
|
444
|
+
const taken = readLock(aside);
|
|
445
|
+
if (owner && taken?.token !== owner.token) {
|
|
446
|
+
// We grabbed a lock re-acquired after our inspection: restore it.
|
|
447
|
+
try {
|
|
448
|
+
linkSync(aside, path);
|
|
449
|
+
} catch {
|
|
450
|
+
/* a third process holds the path now; ours is the one to discard */
|
|
451
|
+
}
|
|
347
452
|
}
|
|
453
|
+
rmSync(aside, { force: true });
|
|
454
|
+
return true;
|
|
348
455
|
}
|
package/src/oauth.ts
CHANGED
|
@@ -156,6 +156,20 @@ export async function discoverApiAuthorization(
|
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
export const TOKEN_REQUEST_TIMEOUT_MS = 20_000;
|
|
160
|
+
|
|
161
|
+
/*
|
|
162
|
+
* Failures that prove the request never reached the server. Anything else — a
|
|
163
|
+
* timeout, a reset mid-response — may have followed a committed rotation, so
|
|
164
|
+
* the refresh token it carried must be treated as spent (plan §6: never retry
|
|
165
|
+
* a possibly consumed token; reauthorize instead).
|
|
166
|
+
*/
|
|
167
|
+
const NOT_SENT = new Set(['ECONNREFUSED', 'ENOTFOUND', 'EAI_AGAIN', 'ENETUNREACH', 'EHOSTUNREACH']);
|
|
168
|
+
function neverDelivered(error: unknown): boolean {
|
|
169
|
+
const cause = (error as { cause?: { code?: unknown } } | undefined)?.cause;
|
|
170
|
+
return typeof cause?.code === 'string' && NOT_SENT.has(cause.code);
|
|
171
|
+
}
|
|
172
|
+
|
|
159
173
|
async function tokenRequest(
|
|
160
174
|
endpoint: string,
|
|
161
175
|
parameters: URLSearchParams,
|
|
@@ -168,10 +182,15 @@ async function tokenRequest(
|
|
|
168
182
|
redirect: 'error',
|
|
169
183
|
headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
|
|
170
184
|
body: parameters,
|
|
185
|
+
// Well inside the credential lock's stale window, so a refresh can never
|
|
186
|
+
// outlive the lock that serializes it.
|
|
187
|
+
signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
|
|
171
188
|
});
|
|
172
189
|
} catch (error) {
|
|
173
190
|
if (error instanceof SynomemError) throw error;
|
|
174
|
-
throw new SynomemError('REMOTE_UNAVAILABLE', 'The OAuth token endpoint is unavailable.'
|
|
191
|
+
throw new SynomemError('REMOTE_UNAVAILABLE', 'The OAuth token endpoint is unavailable.', {
|
|
192
|
+
delivery: neverDelivered(error) ? 'not_sent' : 'indeterminate',
|
|
193
|
+
});
|
|
175
194
|
}
|
|
176
195
|
const text = await response.text();
|
|
177
196
|
if (Buffer.byteLength(text) > maximumDocumentBytes) {
|
package/src/profiles.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import { existsSync } from 'node:fs';
|
|
28
28
|
import { join } from 'node:path';
|
|
29
29
|
import { z } from 'zod';
|
|
30
|
-
import {
|
|
30
|
+
import { readSynomemConfig } from './backend.js';
|
|
31
31
|
import { resolveHome } from './config.js';
|
|
32
32
|
import {
|
|
33
33
|
withCredentialLock,
|
|
@@ -96,7 +96,7 @@ const localProfileSchema = z
|
|
|
96
96
|
home: z.string().optional(),
|
|
97
97
|
actorId: z.string().min(1),
|
|
98
98
|
actorName: z.string().optional(),
|
|
99
|
-
contextId: z.string().regex(/^lctx_[0-9a-f]{
|
|
99
|
+
contextId: z.string().regex(/^lctx_[0-9a-f]{32}$/),
|
|
100
100
|
})
|
|
101
101
|
.strict();
|
|
102
102
|
|
|
@@ -374,7 +374,24 @@ export class ConnectionCredentialSource implements RemoteCredentialSource {
|
|
|
374
374
|
try {
|
|
375
375
|
refreshed = await refreshOAuthCredential(current, this.options.fetch ?? fetch);
|
|
376
376
|
} catch (error) {
|
|
377
|
-
if (
|
|
377
|
+
if (
|
|
378
|
+
error instanceof SynomemError &&
|
|
379
|
+
error.code === 'REMOTE_UNAVAILABLE' &&
|
|
380
|
+
error.details?.delivery === 'not_sent'
|
|
381
|
+
) {
|
|
382
|
+
// The refresh token provably never left this machine: safe to retry later.
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
// Refused, or delivery unknown (the server may have rotated it): the
|
|
386
|
+
// stored refresh token is treated as spent and removed, so no process
|
|
387
|
+
// can replay it and trip reuse detection for the whole family.
|
|
388
|
+
const withoutRefresh: StoredOAuthCredential = { ...current };
|
|
389
|
+
delete withoutRefresh.refreshToken;
|
|
390
|
+
await store.set(entry.secretRef!, {
|
|
391
|
+
...withoutRefresh,
|
|
392
|
+
expiresAt: 0,
|
|
393
|
+
generation: current.generation + 1,
|
|
394
|
+
});
|
|
378
395
|
throw this.reauthorize(`Connection ${name} could not be refreshed.`);
|
|
379
396
|
}
|
|
380
397
|
// Compare-and-swap: only this process may have spent the refresh token
|
|
@@ -440,7 +457,7 @@ export function localProfileTarget(
|
|
|
440
457
|
id: profile.actorId,
|
|
441
458
|
...(profile.actorName ? { displayName: profile.actorName } : {}),
|
|
442
459
|
};
|
|
443
|
-
const expected = localContextId(
|
|
460
|
+
const expected = localContextId(home, actor);
|
|
444
461
|
if (expected !== profile.contextId) {
|
|
445
462
|
throw new SynomemError(
|
|
446
463
|
'CONTEXT_FORBIDDEN',
|
package/src/resolvers.ts
CHANGED
|
@@ -8,7 +8,18 @@
|
|
|
8
8
|
* each other's attribution. The resolver narrows (pins a context, restricts to a
|
|
9
9
|
* preset); the server alone authorizes.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { randomBytes } from 'node:crypto';
|
|
12
|
+
import {
|
|
13
|
+
closeSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
openSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
rmSync,
|
|
18
|
+
statSync,
|
|
19
|
+
writeSync,
|
|
20
|
+
} from 'node:fs';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { atomicWriteFile } from './fs-utils.js';
|
|
12
23
|
import { SynomemClient } from './client.js';
|
|
13
24
|
import { describeIdentity, discoverContexts } from './discover.js';
|
|
14
25
|
import { SynomemError } from './errors.js';
|
|
@@ -68,15 +79,90 @@ function required(): SynomemError {
|
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
82
|
+
* The stable local context id for one actor in one store, from the store's own
|
|
83
|
+
* context registry (`<home>/contexts.json`).
|
|
84
|
+
*
|
|
85
|
+
* The id is cryptographically random (128 bits) and minted the first time the
|
|
86
|
+
* actor is addressed, never derived from a name or from the canonical tuple:
|
|
87
|
+
* an id reveals nothing about its target and cannot be guessed. It survives
|
|
88
|
+
* renames and moving the store (the registry moves with it), and an unrelated
|
|
89
|
+
* store with a same-named agent has its own registry and so its own id.
|
|
90
|
+
* Minting is serialized across processes so two harnesses opening the store
|
|
91
|
+
* at once agree on one id.
|
|
74
92
|
*/
|
|
75
|
-
export function localContextId(
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
export function localContextId(home: string, actor: ActorIdentity): string {
|
|
94
|
+
const path = join(home, 'contexts.json');
|
|
95
|
+
const key = `${actor.kind}:${actor.id}`;
|
|
96
|
+
const existing = readRegistry(path).contexts[key];
|
|
97
|
+
if (existing) return existing;
|
|
98
|
+
return withRegistryLock(home, () => {
|
|
99
|
+
const registry = readRegistry(path);
|
|
100
|
+
const current = registry.contexts[key];
|
|
101
|
+
if (current) return current;
|
|
102
|
+
const id = `lctx_${randomBytes(16).toString('hex')}`;
|
|
103
|
+
registry.contexts[key] = id;
|
|
104
|
+
atomicWriteFile(path, `${JSON.stringify(registry, null, 2)}\n`, 0o600);
|
|
105
|
+
return id;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface ContextRegistry {
|
|
110
|
+
version: 1;
|
|
111
|
+
contexts: Record<string, string>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function readRegistry(path: string): ContextRegistry {
|
|
115
|
+
if (!existsSync(path)) return { version: 1, contexts: {} };
|
|
116
|
+
try {
|
|
117
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8')) as Partial<ContextRegistry>;
|
|
118
|
+
if (parsed.version === 1 && parsed.contexts && typeof parsed.contexts === 'object') {
|
|
119
|
+
return { version: 1, contexts: { ...parsed.contexts } };
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
/* fall through */
|
|
123
|
+
}
|
|
124
|
+
throw new SynomemError('CONFIG_INVALID', `${path} is not a valid Synomem context registry.`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A short synchronous O_EXCL lock; a dead or old owner is broken. */
|
|
128
|
+
function withRegistryLock<T>(home: string, operation: () => T): T {
|
|
129
|
+
const lock = join(home, 'contexts.json.lock');
|
|
130
|
+
const deadline = Date.now() + 10_000;
|
|
131
|
+
const sleeper = new Int32Array(new SharedArrayBuffer(4));
|
|
132
|
+
for (;;) {
|
|
133
|
+
try {
|
|
134
|
+
const descriptor = openSync(lock, 'wx', 0o600);
|
|
135
|
+
writeSync(descriptor, `${process.pid}\n`);
|
|
136
|
+
closeSync(descriptor);
|
|
137
|
+
break;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
140
|
+
try {
|
|
141
|
+
const pid = Number(readFileSync(lock, 'utf8').trim());
|
|
142
|
+
const old = Date.now() - statSync(lock).mtimeMs > 5_000;
|
|
143
|
+
let alive = Number.isSafeInteger(pid) && pid > 0;
|
|
144
|
+
if (alive) {
|
|
145
|
+
try {
|
|
146
|
+
process.kill(pid, 0);
|
|
147
|
+
} catch (probe) {
|
|
148
|
+
alive = (probe as NodeJS.ErrnoException).code === 'EPERM';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (!alive || old) rmSync(lock, { force: true });
|
|
152
|
+
} catch {
|
|
153
|
+
/* released meanwhile */
|
|
154
|
+
}
|
|
155
|
+
if (Date.now() > deadline) {
|
|
156
|
+
throw new SynomemError('DATABASE_BUSY', 'The local context registry is locked. Try again.');
|
|
157
|
+
}
|
|
158
|
+
Atomics.wait(sleeper, 0, 0, 20);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
return operation();
|
|
163
|
+
} finally {
|
|
164
|
+
rmSync(lock, { force: true });
|
|
165
|
+
}
|
|
80
166
|
}
|
|
81
167
|
|
|
82
168
|
/**
|
|
@@ -240,7 +326,7 @@ export function createLocalResolver(
|
|
|
240
326
|
return {
|
|
241
327
|
service: client,
|
|
242
328
|
context: {
|
|
243
|
-
contextId: localContextId(
|
|
329
|
+
contextId: localContextId(options.home, actor),
|
|
244
330
|
organizationId: null,
|
|
245
331
|
workspaceId: capabilities.binding.workspaceId,
|
|
246
332
|
actor,
|