ftown 0.19.53 → 0.19.55

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.
Files changed (53) hide show
  1. package/dist/bridge-auth.d.ts +3 -0
  2. package/dist/bridge-auth.js.map +1 -1
  3. package/dist/command-rpc.js +21 -12
  4. package/dist/command-rpc.js.map +1 -1
  5. package/dist/index.js +124 -7
  6. package/dist/index.js.map +1 -1
  7. package/dist/pairing-client.d.ts +24 -12
  8. package/dist/pairing-client.js +383 -13
  9. package/dist/pairing-client.js.map +1 -1
  10. package/dist/security/account-crypto.d.ts +33 -0
  11. package/dist/security/account-crypto.js +649 -0
  12. package/dist/security/account-crypto.js.map +1 -0
  13. package/dist/security/account-protocol.d.ts +228 -0
  14. package/dist/security/account-protocol.js +2 -0
  15. package/dist/security/account-protocol.js.map +1 -0
  16. package/dist/security/bridge/account-client.d.ts +7 -0
  17. package/dist/security/bridge/account-client.js +87 -0
  18. package/dist/security/bridge/account-client.js.map +1 -0
  19. package/dist/security/bridge/account-state.d.ts +471 -0
  20. package/dist/security/bridge/account-state.js +78 -0
  21. package/dist/security/bridge/account-state.js.map +1 -0
  22. package/dist/security/bridge/execute.js +2 -1
  23. package/dist/security/bridge/execute.js.map +1 -1
  24. package/dist/security/bridge/identity.d.ts +436 -0
  25. package/dist/security/bridge/identity.js +27 -5
  26. package/dist/security/bridge/identity.js.map +1 -1
  27. package/dist/security/bridge/pending-approval.d.ts +21 -0
  28. package/dist/security/bridge/pending-approval.js +79 -0
  29. package/dist/security/bridge/pending-approval.js.map +1 -0
  30. package/dist/security/bridge/pending-enrollment.d.ts +33 -0
  31. package/dist/security/bridge/pending-enrollment.js +177 -0
  32. package/dist/security/bridge/pending-enrollment.js.map +1 -0
  33. package/dist/security/bridge/permissions.js +2 -2
  34. package/dist/security/bridge/permissions.js.map +1 -1
  35. package/dist/security/bridge/policy.d.ts +20 -0
  36. package/dist/security/bridge/policy.js +159 -7
  37. package/dist/security/bridge/policy.js.map +1 -1
  38. package/dist/security/bridge/security.d.ts +11 -0
  39. package/dist/security/bridge/security.js +93 -3
  40. package/dist/security/bridge/security.js.map +1 -1
  41. package/dist/security/bridge/snapshot.d.ts +1 -1
  42. package/dist/security/bridge/snapshot.js.map +1 -1
  43. package/dist/security/pending-approval-crypto.d.ts +29 -0
  44. package/dist/security/pending-approval-crypto.js +716 -0
  45. package/dist/security/pending-approval-crypto.js.map +1 -0
  46. package/dist/security/pending-approval-protocol.d.ts +329 -0
  47. package/dist/security/pending-approval-protocol.js +2 -0
  48. package/dist/security/pending-approval-protocol.js.map +1 -0
  49. package/dist/security/protocol.d.ts +12 -1
  50. package/dist/security-cli.js +15 -0
  51. package/dist/security-cli.js.map +1 -1
  52. package/dist/types.d.ts +1 -1
  53. package/package.json +2 -2
@@ -0,0 +1,177 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { createAccountEnrollment, encodeAccountEnrollmentLink, generateAccountBridgeCandidate, importAccountBridgeCandidate, parseAccountEnrollmentLink, verifyAccountHistory } from '../account-crypto.js';
5
+ import { parsePendingOffer, pendingHelloHash, pendingOfferHash } from '../pending-approval-crypto.js';
6
+ import { AtomicSnapshot } from './snapshot.js';
7
+ import { endpointSchema } from './metadata.js';
8
+ import { identityRecordSchema } from './identity.js';
9
+ import { importEndpoint } from '../crypto.js';
10
+ import { BridgeSecurityError } from './errors.js';
11
+ import { accountReadUrl } from './account-client.js';
12
+ const legacySchema = z.object({ version: z.literal(1), bridgeId: z.string().regex(/^[A-Za-z0-9_-]{1,200}$/),
13
+ endpoint: endpointSchema, privateKey: identityRecordSchema.shape.privateKey,
14
+ link: z.custom(), verificationUrl: z.string().max(4096).nullable() }).strict();
15
+ const automaticSchema = z.object({ version: z.literal(2), bridgeId: z.string().regex(/^[A-Za-z0-9_-]{1,200}$/),
16
+ endpoint: endpointSchema, privateKey: identityRecordSchema.shape.privateKey,
17
+ request: z.custom(), candidateToken: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
18
+ offer: z.custom().nullable(), hello: z.custom().nullable() }).strict();
19
+ const pendingSchema = z.discriminatedUnion('version', [legacySchema, automaticSchema]);
20
+ const pendingPath = (dataDir) => join(dataDir, 'security', 'account-enrollment.json');
21
+ async function readPending(dataDir) {
22
+ let raw;
23
+ try {
24
+ raw = await readFile(pendingPath(dataDir), 'utf8');
25
+ }
26
+ catch (error) {
27
+ if (error.code === 'ENOENT')
28
+ return null;
29
+ throw new BridgeSecurityError('account_storage_unavailable');
30
+ }
31
+ try {
32
+ const saved = pendingSchema.parse(JSON.parse(raw));
33
+ if (saved.version === 1)
34
+ encodeAccountEnrollmentLink(saved.link);
35
+ else {
36
+ const parsed = await parseAccountEnrollmentLink({ request: saved.request, secret: saved.candidateToken }, Date.parse(saved.request.expiresAt) - 1);
37
+ if (parsed.request.kind !== 'bridge' || parsed.request.bridgeId !== saved.bridgeId)
38
+ throw new Error('Wrong pending context');
39
+ }
40
+ return saved;
41
+ }
42
+ catch {
43
+ throw new BridgeSecurityError('account_storage_unavailable');
44
+ }
45
+ }
46
+ export async function previousPendingBridgeAttempt(dataDir) {
47
+ const existing = await readPending(dataDir);
48
+ return existing?.version === 2 ? { requestId: existing.request.requestId, candidateToken: existing.candidateToken } : null;
49
+ }
50
+ export async function prepareBridgeEnrollment(dataDir, bridgeId, label, now = Date.now(), fresh = false) {
51
+ const existing = await readPending(dataDir);
52
+ if (existing && existing.bridgeId !== bridgeId)
53
+ throw new BridgeSecurityError('wrong_context');
54
+ const unexpired = !fresh && existing?.version === 1 && Date.parse(existing.link.request.expiresAt) > now;
55
+ let initial = existing;
56
+ if (!unexpired) {
57
+ // Expiring a request rotates its challenge/secret, never the pending bridge point.
58
+ // Approval may already exist remotely even when its response was lost.
59
+ const identity = existing ? await importAccountBridgeCandidate(existing.endpoint, existing.privateKey, false)
60
+ : await generateAccountBridgeCandidate(bridgeId, true);
61
+ initial = legacySchema.parse({ version: 1, bridgeId, endpoint: identity.endpoint,
62
+ privateKey: existing?.privateKey ?? await crypto.subtle.exportKey('jwk', identity.keyPair.privateKey),
63
+ link: await createAccountEnrollment(identity, label.slice(0, 200), null, bridgeId, now), verificationUrl: null });
64
+ }
65
+ const store = new AtomicSnapshot(pendingPath(dataDir), pendingSchema, () => initial);
66
+ const saved = await store.transact(state => {
67
+ if (!unexpired && existing) {
68
+ for (const key of Object.keys(state))
69
+ delete state[key];
70
+ Object.assign(state, initial);
71
+ }
72
+ return state;
73
+ });
74
+ if (saved.version !== 1)
75
+ throw new BridgeSecurityError('account_enrollment_invalid');
76
+ const link = await parseAccountEnrollmentLink(saved.link, now);
77
+ if (link.request.kind !== 'bridge' || link.request.bridgeId !== bridgeId || link.request.candidate.id !== saved.endpoint.id
78
+ || link.request.candidate.publicKey !== saved.endpoint.publicKey || link.request.candidate.ownerId !== saved.endpoint.ownerId)
79
+ throw new BridgeSecurityError('account_enrollment_invalid');
80
+ return { identity: await importAccountBridgeCandidate(saved.endpoint, saved.privateKey, false), link };
81
+ }
82
+ /** One fresh process attempt, retaining the long-term point even after cancellation/restart. */
83
+ export async function preparePendingBridgeEnrollment(dataDir, bridgeId, label, now = Date.now()) {
84
+ const existing = await readPending(dataDir);
85
+ if (existing && existing.bridgeId !== bridgeId)
86
+ throw new BridgeSecurityError('wrong_context');
87
+ const identity = existing ? await importAccountBridgeCandidate(existing.endpoint, existing.privateKey, false)
88
+ : await generateAccountBridgeCandidate(bridgeId, true);
89
+ const generated = await createAccountEnrollment(identity, label.slice(0, 200), null, bridgeId, now);
90
+ const record = automaticSchema.parse({ version: 2, bridgeId, endpoint: identity.endpoint,
91
+ privateKey: existing?.privateKey ?? await crypto.subtle.exportKey('jwk', identity.keyPair.privateKey),
92
+ request: generated.request, candidateToken: Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('base64url'), offer: null, hello: null });
93
+ // transact replaces fields in place; its initial state must not alias the source record.
94
+ const store = new AtomicSnapshot(pendingPath(dataDir), pendingSchema, () => structuredClone(record));
95
+ await store.transact(state => {
96
+ for (const key of Object.keys(state))
97
+ delete state[key];
98
+ Object.assign(state, record);
99
+ });
100
+ const current = async (update) => store.transact(async (state) => {
101
+ if (state.version !== 2 || state.request.requestId !== record.request.requestId || state.candidateToken !== record.candidateToken)
102
+ throw new BridgeSecurityError('account_trust_conflict');
103
+ await update(state);
104
+ });
105
+ return {
106
+ identity: await importAccountBridgeCandidate(record.endpoint, record.privateKey, false),
107
+ request: record.request, candidateToken: record.candidateToken,
108
+ previous: existing?.version === 2 ? { requestId: existing.request.requestId, candidateToken: existing.candidateToken } : null,
109
+ pinOffer: offer => current(async (state) => {
110
+ await parsePendingOffer(offer, state.request);
111
+ if (state.offer && await pendingOfferHash(state.offer) !== await pendingOfferHash(offer))
112
+ throw new BridgeSecurityError('account_trust_conflict');
113
+ state.offer ??= offer;
114
+ }),
115
+ pinHello: hello => current(async (state) => {
116
+ if (!state.offer || hello.requestId !== state.request.requestId || hello.offerHash !== await pendingOfferHash(state.offer))
117
+ throw new BridgeSecurityError('wrong_context');
118
+ if (state.hello && await pendingHelloHash(state.hello) !== await pendingHelloHash(hello))
119
+ throw new BridgeSecurityError('account_trust_conflict');
120
+ state.hello ??= hello;
121
+ }),
122
+ };
123
+ }
124
+ /** URL is constructed from operator configuration, never a service-provided redirect. */
125
+ export async function savePairingVerificationUrl(dataDir, apiUrl, userCode) {
126
+ accountReadUrl(apiUrl);
127
+ if (!/^[A-Za-z0-9-]{1,100}$/.test(userCode))
128
+ throw new BridgeSecurityError('account_enrollment_invalid');
129
+ const url = new URL('/pair', apiUrl);
130
+ url.searchParams.set('code', userCode);
131
+ const existing = await readPending(dataDir);
132
+ if (!existing || existing.version !== 1)
133
+ throw new BridgeSecurityError('account_enrollment_invalid');
134
+ const store = new AtomicSnapshot(pendingPath(dataDir), pendingSchema, () => existing);
135
+ await store.transact(state => { if (state.version !== 1)
136
+ throw new BridgeSecurityError('account_trust_conflict'); state.verificationUrl = url.href; });
137
+ return url.href;
138
+ }
139
+ export async function pendingPairingLink(dataDir, now = Date.now()) {
140
+ const saved = await readPending(dataDir);
141
+ if (!saved || saved.version !== 1 || !saved.verificationUrl)
142
+ throw new BridgeSecurityError('account_enrollment_invalid');
143
+ const link = await parseAccountEnrollmentLink(saved.link, now);
144
+ const url = new URL(saved.verificationUrl);
145
+ accountReadUrl(url.origin);
146
+ if (url.pathname !== '/pair' || url.hash || url.username || url.password || [...url.searchParams.keys()].some(key => key !== 'code')) {
147
+ throw new BridgeSecurityError('account_enrollment_invalid');
148
+ }
149
+ url.hash = `account=${encodeAccountEnrollmentLink(link)}`;
150
+ return url.href;
151
+ }
152
+ /** Save the normalized SAME private/public key and trusted bootstrap in one durable identity record. */
153
+ export async function bindPendingBridgeIdentity(dataDir, trust) {
154
+ const pending = await readPending(dataDir);
155
+ if (!pending)
156
+ throw new BridgeSecurityError('account_enrollment_invalid');
157
+ const checkpoint = await verifyAccountHistory(trust.history, trust.checkpoint);
158
+ const own = checkpoint.bridges.find(item => item.bridgeId === pending.bridgeId);
159
+ if (!own || own.endpoint.id !== pending.endpoint.id || own.endpoint.publicKey !== pending.endpoint.publicKey)
160
+ throw new BridgeSecurityError('wrong_context');
161
+ await importAccountBridgeCandidate(pending.endpoint, pending.privateKey, false);
162
+ const record = identityRecordSchema.parse({ version: 1, endpoint: own.endpoint, privateKey: pending.privateKey,
163
+ accountBootstrap: { checkpoint, history: trust.history } });
164
+ const store = new AtomicSnapshot(join(dataDir, 'security', 'bridge-identity.json'), identityRecordSchema, () => record);
165
+ const saved = await store.transact(state => {
166
+ if (state.endpoint.id !== record.endpoint.id || state.endpoint.ownerId !== record.endpoint.ownerId
167
+ || state.endpoint.publicKey !== record.endpoint.publicKey)
168
+ throw new BridgeSecurityError('wrong_context');
169
+ // A pre-existing bound identity is never repinned by repeating bootstrap.
170
+ if (state.accountBootstrap && state.accountBootstrap.checkpoint.rootId !== checkpoint.rootId)
171
+ throw new BridgeSecurityError('account_trust_rollback');
172
+ state.accountBootstrap ??= record.accountBootstrap;
173
+ return state;
174
+ });
175
+ return importEndpoint(saved.endpoint, saved.privateKey, false);
176
+ }
177
+ //# sourceMappingURL=pending-enrollment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-enrollment.js","sourceRoot":"","sources":["../../../src/security/bridge/pending-enrollment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,4BAA4B,EACzH,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjF,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEtG,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC;IACzG,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,oBAAoB,CAAC,KAAK,CAAC,UAAU;IAC3E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAyB,EAAE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACxG,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC;IAC5G,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,oBAAoB,CAAC,KAAK,CAAC,UAAU;IAC3E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAyD,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC;IACnI,KAAK,EAAE,CAAC,CAAC,MAAM,EAAgB,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACrG,MAAM,aAAa,GAAG,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC;AAGvF,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,yBAAyB,CAAC,CAAC;AAE9F,KAAK,UAAU,WAAW,CAAC,OAAe;IACxC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QAAC,GAAG,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IAC3D,OAAO,KAAK,EAAE,CAAC;QACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpE,MAAM,IAAI,mBAAmB,CAAC,6BAA6B,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;YAAE,2BAA2B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aAC5D,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;YACnJ,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC/H,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC;QAAC,MAAM,IAAI,mBAAmB,CAAC,6BAA6B,CAAC,CAAC;IAAC,CAAC;AACzE,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,OAAe;IAChE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,QAAQ,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7H,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,OAAe,EAAE,QAAgB,EAAE,KAAa,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,GAAG,KAAK;IAC7H,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAC/F,MAAM,SAAS,GAAG,CAAC,KAAK,IAAI,QAAQ,EAAE,OAAO,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;IACzG,IAAI,OAAO,GAAG,QAAQ,CAAC;IACvB,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,mFAAmF;QACnF,uEAAuE;QACvE,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,4BAA4B,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;YAC3G,CAAC,CAAC,MAAM,8BAA8B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACzD,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC9E,UAAU,EAAE,QAAQ,EAAE,UAAU,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC;YACrG,IAAI,EAAE,MAAM,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IACtH,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,OAAQ,CAAC,CAAC;IACtF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QACzC,IAAI,CAAC,SAAS,IAAI,QAAQ,EAAE,CAAC;YAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAQ,KAA4C,CAAC,GAAG,CAAC,CAAC;YAChG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,MAAM,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,KAAK,KAAK,CAAC,QAAQ,CAAC,EAAE;WACtH,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,KAAK,KAAK,CAAC,QAAQ,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,CAAC,QAAQ,CAAC,OAAO;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IAC7L,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;AACzG,CAAC;AAOD,gGAAgG;AAChG,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,OAAe,EAAE,QAAgB,EAAE,KAAa,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACrH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAC/F,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,4BAA4B,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;QAC3G,CAAC,CAAC,MAAM,8BAA8B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IACpG,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QACtF,UAAU,EAAE,QAAQ,EAAE,UAAU,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC;QACrG,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzJ,yFAAyF;IACzF,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IACrG,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAQ,KAA4C,CAAC,GAAG,CAAC,CAAC;QAChG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,KAAK,EAAE,MAAiE,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAC,KAAK,EAAC,EAAE;QACxH,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,KAAK,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,cAAc,KAAK,MAAM,CAAC,cAAc;YAAE,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,CAAC,CAAC;QAC3L,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,OAAO;QACL,QAAQ,EAAE,MAAM,4BAA4B,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC;QACvF,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc;QAC9D,QAAQ,EAAE,QAAQ,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI;QAC7H,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,MAAM,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAI,KAAK,CAAC,KAAK,IAAI,MAAM,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,MAAM,gBAAgB,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,CAAC,CAAC;YAClJ,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC;QACxB,CAAC,CAAC;QACF,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,MAAM,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,CAAC,CAAC;YAC3K,IAAI,KAAK,CAAC,KAAK,IAAI,MAAM,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,MAAM,gBAAgB,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,CAAC,CAAC;YAClJ,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC;QACxB,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AACD,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,OAAe,EAAE,MAAc,EAAE,QAAgB;IAChG,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IACzG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IACrG,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;IACtF,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvJ,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACxE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IACzH,MAAM,IAAI,GAAG,MAAM,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC3C,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,MAAM,CAAC,EAAE,CAAC;QACrI,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IAC9D,CAAC;IACD,GAAG,CAAC,IAAI,GAAG,WAAW,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;IAC1D,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AACD,wGAAwG;AACxG,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAAe,EAAE,KAA2B;IAC1F,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChF,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,SAAS;QAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAC7J,MAAM,4BAA4B,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU;QAC5G,gBAAgB,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,sBAAsB,CAAC,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;IACxH,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QACzC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,KAAK,MAAM,CAAC,QAAQ,CAAC,OAAO;eAC7F,KAAK,CAAC,QAAQ,CAAC,SAAS,KAAK,MAAM,CAAC,QAAQ,CAAC,SAAS;YAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,CAAC,CAAC;QAC5G,0EAA0E;QAC1E,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,CAAC,CAAC;QACtJ,KAAK,CAAC,gBAAgB,KAAK,MAAM,CAAC,gBAAgB,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IACH,OAAO,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AACjE,CAAC"}
@@ -3,8 +3,8 @@ const session = /^[A-Za-z0-9_-]+$/;
3
3
  const readCommands = new Set(['list_sessions', 'get_history', 'get_session_usage', 'get_sessions_usage', 'get_session_attention']);
4
4
  const controlCommands = new Set(['create_session', 'stop_session', 'retry_session', 'send_message', 'rename_session', 'remove_session',
5
5
  'bridge_exec', 'clear_terminal', 'update_session_parent', 'resolve_session_attention']);
6
- const adminCommands = new Set(['security_devices', 'security_invite', 'security_revoke', 'security_approve_mcp', 'security_activity']);
7
- const result = (permission, operation, sessionId = null) => ({ permission, mutating: permission === 'control' || ['security_invite', 'security_revoke', 'security_approve_mcp'].includes(operation), operation, sessionId });
6
+ const adminCommands = new Set(['security_devices', 'security_invite', 'security_revoke', 'security_approve_mcp', 'security_activity', 'security_account_adopt']);
7
+ const result = (permission, operation, sessionId = null) => ({ permission, mutating: permission === 'control' || ['security_invite', 'security_revoke', 'security_approve_mcp', 'security_account_adopt'].includes(operation), operation, sessionId });
8
8
  const deny = () => { throw new BridgeSecurityError('permission_denied'); };
9
9
  const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
10
10
  /** Deliberately semantic: GET inbox consumes mail unless explicitly non-consuming. */
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/security/bridge/permissions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/security/bridge/permissions.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,11 @@
1
1
  import type { ApprovedEndpoint, BridgeInvitation, EndpointPermission, EndpointPublic, EnrollmentMessage, InvitationOptions, LocalRecoveryAuthority, McpApproval, McpApprovalReceipt, VerifiedPrincipal } from '../protocol.js';
2
+ import type { AccountHead, AccountHistory, StoredAccountTrust, VerifiedAccountTrust } from '../account-protocol.js';
3
+ import type { SecurityErrorCode } from '../protocol.js';
2
4
  /** Identity comparison matters: request JSON cannot manufacture this recovery capability. */
3
5
  export declare const LOCAL_RECOVERY_AUTHORITY: LocalRecoveryAuthority;
4
6
  export interface PolicyOptions {
5
7
  now?: () => number;
8
+ monotonicNow?: () => number;
6
9
  relayUrl?: string;
7
10
  localOrigin?: string;
8
11
  }
@@ -13,6 +16,7 @@ export declare class BridgePolicyStore {
13
16
  private readonly options;
14
17
  private readonly snapshot;
15
18
  private readonly now;
19
+ private readonly connectivity;
16
20
  constructor(dataDir: string, bridgeId: string, bridge: EndpointPublic, options?: PolicyOptions);
17
21
  private check;
18
22
  private active;
@@ -31,5 +35,21 @@ export declare class BridgePolicyStore {
31
35
  }>;
32
36
  revoke(actor: VerifiedPrincipal, endpointId: string): Promise<void>;
33
37
  provisionLocalEndpoint(endpoint: EndpointPublic): Promise<void>;
38
+ accountTrust(): Promise<StoredAccountTrust | null>;
39
+ accountEndpointIds(): Promise<string[]>;
40
+ blockAccount(code: SecurityErrorCode): void;
41
+ accountAvailable(): Promise<boolean>;
42
+ /** Trusted startup bootstrap only; no remote endpoint can call this entry point. */
43
+ pinBootstrap(trust: VerifiedAccountTrust): Promise<void>;
44
+ private requireOwnBridge;
45
+ private projectAccount;
46
+ /** Only an authenticated API read reaches here; browser-supplied timestamps never do. */
47
+ applyAccountRead(history: AccountHistory, adoption?: {
48
+ actor: VerifiedPrincipal;
49
+ head: AccountHead;
50
+ }): Promise<{
51
+ head: AccountHead;
52
+ revoked: string[];
53
+ }>;
34
54
  close(): Promise<void>;
35
55
  }
@@ -4,18 +4,23 @@ import { z } from 'zod';
4
4
  import { BridgeSecurityError } from './errors.js';
5
5
  import { approvedSchema, boundedId, displayLabel, endpointSchema, permissionSchema } from './metadata.js';
6
6
  import { AtomicSnapshot } from './snapshot.js';
7
+ import { verifyAccountHistory } from '../account-crypto.js';
8
+ import { AccountConnectivity, storedAccountSchema } from './account-state.js';
7
9
  const FIVE_MINUTES = 300_000;
8
10
  const THIRTY_DAYS = 30 * 86_400_000;
9
11
  const MAX_ENDPOINTS = 1000;
10
12
  const digest = (secret) => createHash('sha256').update(secret).digest('hex');
11
13
  const invitationSchema = z.object({ id: z.string().uuid(), secretHash: z.string().regex(/^[a-f0-9]{64}$/),
12
14
  label: z.string().max(100), permissions: z.array(permissionSchema).max(3), expiresAt: z.string().datetime(),
13
- consumedBy: z.string().nullable() }).strict();
15
+ consumedBy: z.string().nullable(), provenance: z.enum(['legacy', 'local-recovery']).default('legacy') }).strict();
14
16
  const pendingSchema = z.object({ approved: approvedSchema, approvalId: z.string().uuid(), requestId: boundedId,
15
17
  expiresAt: z.string().datetime(), activeExpiresAt: z.string().datetime(), active: z.boolean() }).strict();
16
18
  const policySchema = z.object({ version: z.literal(1), ownerId: z.string(), bridgeId: z.string(), bridgeEndpointId: z.string(),
17
19
  generation: z.number().int().nonnegative(), devices: z.array(approvedSchema).max(MAX_ENDPOINTS),
18
- invitations: z.array(invitationSchema).max(256), mcp: z.array(pendingSchema).max(MAX_ENDPOINTS) }).strict().superRefine((state, context) => {
20
+ invitations: z.array(invitationSchema).max(256), mcp: z.array(pendingSchema).max(MAX_ENDPOINTS),
21
+ account: storedAccountSchema.nullable().default(null),
22
+ provenance: z.record(z.enum(['legacy', 'local-recovery', 'account', 'local-cli', 'mcp'])).default({}),
23
+ }).strict().superRefine((state, context) => {
19
24
  if (new Set(state.devices.map(device => device.endpoint.id)).size !== state.devices.length
20
25
  || new Set(state.mcp.map(item => item.approved.grantId)).size !== state.mcp.length
21
26
  || state.devices.some(device => device.endpoint.ownerId !== state.ownerId
@@ -34,6 +39,7 @@ export class BridgePolicyStore {
34
39
  options;
35
40
  snapshot;
36
41
  now;
42
+ connectivity;
37
43
  constructor(dataDir, bridgeId, bridge, options = {}) {
38
44
  this.bridgeId = bridgeId;
39
45
  this.bridge = bridge;
@@ -42,9 +48,10 @@ export class BridgePolicyStore {
42
48
  if (bridge.kind !== 'bridge')
43
49
  throw new BridgeSecurityError('wrong_context');
44
50
  this.now = options.now ?? Date.now;
51
+ this.connectivity = new AccountConnectivity(this.now, options.monotonicNow ?? (() => performance.now()));
45
52
  this.snapshot = new AtomicSnapshot(join(dataDir, 'security', 'policy.json'), policySchema, () => ({
46
53
  version: 1, ownerId: bridge.ownerId, bridgeId, bridgeEndpointId: bridge.id, generation: 0,
47
- devices: [], invitations: [], mcp: [],
54
+ devices: [], invitations: [], mcp: [], account: null, provenance: {},
48
55
  }));
49
56
  }
50
57
  check(state) {
@@ -54,6 +61,16 @@ export class BridgePolicyStore {
54
61
  }
55
62
  active(state, endpointId, pendingAllowed = false) {
56
63
  this.check(state);
64
+ const account = state.account;
65
+ const member = account?.checkpoint.devices.find(item => item.endpoint.id === endpointId);
66
+ if (member) {
67
+ this.connectivity.check(account);
68
+ const own = account.checkpoint.bridges.find(item => item.bridgeId === this.bridgeId);
69
+ if (!own || own.endpoint.id !== this.bridge.id || own.endpoint.publicKey !== this.bridge.publicKey)
70
+ throw new BridgeSecurityError('account_bridge_revoked');
71
+ }
72
+ else if (account?.checkpoint.revokedDeviceIds.includes(endpointId))
73
+ throw new BridgeSecurityError('account_device_revoked');
57
74
  const record = state.devices.find(device => device.endpoint.id === endpointId);
58
75
  if (!record)
59
76
  throw new BridgeSecurityError('approval_required');
@@ -103,13 +120,16 @@ export class BridgePolicyStore {
103
120
  const expiresAt = new Date(this.now() + FIVE_MINUTES).toISOString();
104
121
  await this.snapshot.transact(state => {
105
122
  this.check(state);
106
- if (actor !== LOCAL_RECOVERY_AUTHORITY)
123
+ if (actor !== LOCAL_RECOVERY_AUTHORITY) {
107
124
  this.authorize(state, actor, 'admin');
125
+ if (state.account && actor.endpoint.kind !== 'local-cli')
126
+ throw new BridgeSecurityError('account_approval_required');
127
+ }
108
128
  state.invitations = state.invitations.filter(invitation => Date.parse(invitation.expiresAt) > this.now());
109
129
  if (state.invitations.length >= 256)
110
130
  throw new BridgeSecurityError('capacity_exceeded');
111
131
  state.invitations.push({ id: invitationId, secretHash: digest(secret), label: displayLabel(options.label),
112
- permissions: ['read', 'control', 'admin'], expiresAt, consumedBy: null });
132
+ permissions: ['read', 'control', 'admin'], expiresAt, consumedBy: null, provenance: state.account ? 'local-recovery' : 'legacy' });
113
133
  });
114
134
  return { version: 1, invitationId, ownerId: this.bridge.ownerId, bridgeId: this.bridgeId, bridge: this.bridge,
115
135
  secret, expiresAt, relayUrl: this.options.relayUrl ?? null, localOrigin: this.options.localOrigin ?? null };
@@ -133,6 +153,12 @@ export class BridgePolicyStore {
133
153
  }
134
154
  if (Date.parse(invitation.expiresAt) <= this.now())
135
155
  throw new BridgeSecurityError('invitation_expired');
156
+ if (state.account && invitation.provenance !== 'local-recovery')
157
+ throw new BridgeSecurityError('invitation_invalid');
158
+ // Independent recovery must never shadow an account membership or revive its tombstone.
159
+ if (state.account && (state.account.checkpoint.devices.some(item => item.endpoint.id === candidate.id)
160
+ || state.account.checkpoint.revokedDeviceIds.includes(candidate.id)))
161
+ throw new BridgeSecurityError('account_approval_required');
136
162
  if (invitation.consumedBy) {
137
163
  if (invitation.consumedBy !== candidate.id)
138
164
  throw new BridgeSecurityError('invitation_consumed');
@@ -147,12 +173,23 @@ export class BridgePolicyStore {
147
173
  permissions: [...invitation.permissions], approvedAt: new Date(this.now()).toISOString(), grantId: null, expiresAt: null };
148
174
  invitation.consumedBy = candidate.id;
149
175
  state.devices.push(approved);
176
+ state.provenance[candidate.id] = invitation.provenance;
150
177
  state.generation++;
151
178
  return approved;
152
179
  });
153
180
  }
154
181
  async list(actor) {
155
- return this.snapshot.read(state => { this.authorize(state, actor, 'admin'); return state.devices; });
182
+ return this.snapshot.read(state => {
183
+ this.authorize(state, actor, 'admin');
184
+ return state.devices.map(device => {
185
+ const provenance = state.provenance[device.endpoint.id];
186
+ if (provenance === 'account')
187
+ return { ...device, source: 'account' };
188
+ if (provenance === 'local-cli' || provenance === 'local-recovery')
189
+ return { ...device, source: 'local' };
190
+ return { ...device };
191
+ });
192
+ });
156
193
  }
157
194
  async recipients() {
158
195
  return this.snapshot.read(state => {
@@ -238,7 +275,10 @@ export class BridgePolicyStore {
238
275
  async revoke(actor, endpointId) {
239
276
  await this.snapshot.transact(state => {
240
277
  this.authorize(state, actor, 'admin');
278
+ if (state.account?.checkpoint.devices.some(item => item.endpoint.id === endpointId))
279
+ throw new BridgeSecurityError('account_approval_required');
241
280
  state.devices = state.devices.filter(device => device.endpoint.id !== endpointId);
281
+ delete state.provenance[endpointId];
242
282
  state.mcp = state.mcp.filter(item => item.approved.endpoint.id !== endpointId);
243
283
  state.generation++;
244
284
  });
@@ -256,8 +296,120 @@ export class BridgePolicyStore {
256
296
  state.devices.push({ endpoint: candidate, label: 'Local CLI', permissions: ['read', 'control', 'admin'],
257
297
  approvedAt: new Date(this.now()).toISOString(), grantId: null, expiresAt: null });
258
298
  state.generation++;
299
+ state.provenance[candidate.id] = 'local-cli';
300
+ });
301
+ }
302
+ async accountTrust() {
303
+ return this.snapshot.read(state => { this.check(state); return state.account; });
304
+ }
305
+ async accountEndpointIds() {
306
+ return this.snapshot.read(state => { this.check(state); return state.account?.checkpoint.devices.map(item => item.endpoint.id) ?? []; });
307
+ }
308
+ blockAccount(code) { this.connectivity.block(code); }
309
+ async accountAvailable() {
310
+ return this.snapshot.read(state => {
311
+ this.check(state);
312
+ if (!state.account)
313
+ return true;
314
+ try {
315
+ this.connectivity.check(state.account);
316
+ return state.account.checkpoint.bridges.some(item => item.bridgeId === this.bridgeId && item.endpoint.id === this.bridge.id);
317
+ }
318
+ catch {
319
+ return false;
320
+ }
321
+ });
322
+ }
323
+ /** Trusted startup bootstrap only; no remote endpoint can call this entry point. */
324
+ async pinBootstrap(trust) {
325
+ await this.snapshot.transact(async (state) => {
326
+ this.check(state);
327
+ if (state.account) {
328
+ if (state.account.checkpoint.rootId !== trust.checkpoint.rootId)
329
+ throw new BridgeSecurityError('account_trust_rollback');
330
+ return;
331
+ }
332
+ const checkpoint = await verifyAccountHistory(trust.history, trust.checkpoint, this.now());
333
+ this.requireOwnBridge(checkpoint);
334
+ this.projectAccount(state, { ...trust, checkpoint, lastServerReadAt: 0, lastObservedAt: 0 });
335
+ });
336
+ }
337
+ requireOwnBridge(checkpoint) {
338
+ const own = checkpoint.bridges.find(item => item.bridgeId === this.bridgeId);
339
+ if (checkpoint.ownerId !== this.bridge.ownerId || !own || own.endpoint.id !== this.bridge.id
340
+ || own.endpoint.publicKey !== this.bridge.publicKey || own.endpoint.ownerId !== this.bridge.ownerId)
341
+ throw new BridgeSecurityError('wrong_context');
342
+ }
343
+ projectAccount(state, trust) {
344
+ const previous = state.devices.filter(item => item.endpoint.kind === 'browser');
345
+ const first = state.account === null;
346
+ const unchanged = state.account?.checkpoint.head === trust.checkpoint.head;
347
+ state.devices = state.devices.filter(item => item.endpoint.kind !== 'browser'
348
+ || (!first && state.provenance[item.endpoint.id] === 'local-recovery'
349
+ && !trust.checkpoint.devices.some(member => member.endpoint.id === item.endpoint.id)
350
+ && !trust.checkpoint.revokedDeviceIds.includes(item.endpoint.id)));
351
+ if (first)
352
+ state.invitations = [];
353
+ for (const entry of previous)
354
+ if (!state.devices.some(item => item.endpoint.id === entry.endpoint.id))
355
+ delete state.provenance[entry.endpoint.id];
356
+ const own = trust.checkpoint.bridges.find(item => item.bridgeId === this.bridgeId);
357
+ if (own?.endpoint.id === this.bridge.id && own.endpoint.publicKey === this.bridge.publicKey) {
358
+ for (const member of trust.checkpoint.devices) {
359
+ if (state.devices.some(item => item.endpoint.id === member.endpoint.id))
360
+ throw new BridgeSecurityError('wrong_context');
361
+ state.devices.push({ endpoint: member.endpoint, label: displayLabel(member.label), permissions: ['read', 'control', 'admin'],
362
+ approvedAt: member.approvedAt, grantId: null, expiresAt: null });
363
+ state.provenance[member.endpoint.id] = 'account';
364
+ }
365
+ }
366
+ state.account = trust;
367
+ if (!unchanged)
368
+ state.generation++;
369
+ return previous.filter(item => !state.devices.some(next => next.endpoint.id === item.endpoint.id)).map(item => item.endpoint.id);
370
+ }
371
+ /** Only an authenticated API read reaches here; browser-supplied timestamps never do. */
372
+ async applyAccountRead(history, adoption) {
373
+ const result = await this.snapshot.transact(async (state) => {
374
+ this.check(state);
375
+ if (!state.account && !adoption)
376
+ throw new BridgeSecurityError('account_approval_required');
377
+ if (adoption) {
378
+ if (adoption.actor.endpoint.kind !== 'browser')
379
+ throw new BridgeSecurityError('permission_denied');
380
+ this.authorize(state, adoption.actor, 'admin');
381
+ }
382
+ const checkpoint = await verifyAccountHistory(history, state.account?.checkpoint ?? null, this.now());
383
+ if (checkpoint.ownerId !== this.bridge.ownerId)
384
+ throw new BridgeSecurityError('wrong_context');
385
+ if (adoption) {
386
+ const head = adoption.head;
387
+ if (head.rootId !== checkpoint.rootId || head.revision !== checkpoint.revision || head.head !== checkpoint.head)
388
+ throw new BridgeSecurityError('account_trust_conflict');
389
+ this.requireOwnBridge(checkpoint);
390
+ const member = checkpoint.devices.find(item => item.endpoint.id === adoption.actor.endpoint.id);
391
+ if (!member || member.endpoint.publicKey !== adoption.actor.endpoint.publicKey || member.endpoint.ownerId !== adoption.actor.endpoint.ownerId) {
392
+ throw new BridgeSecurityError('account_device_revoked');
393
+ }
394
+ this.authorize(state, adoption.actor, 'admin');
395
+ }
396
+ const initial = state.account ?? { checkpoint, history, lastServerReadAt: 0, lastObservedAt: 0 };
397
+ const now = this.connectivity.observed(initial);
398
+ const trust = { checkpoint, history, lastServerReadAt: Math.max(initial.lastServerReadAt, now),
399
+ lastObservedAt: Math.max(initial.lastObservedAt, now) };
400
+ const revoked = this.projectAccount(state, trust);
401
+ return { head: { rootId: checkpoint.rootId, revision: checkpoint.revision, head: checkpoint.head }, revoked, trust };
402
+ });
403
+ this.connectivity.renew(result.trust);
404
+ return { head: result.head, revoked: result.revoked };
405
+ }
406
+ async close() {
407
+ await this.snapshot.transact(state => {
408
+ this.check(state);
409
+ if (state.account)
410
+ state.account.lastObservedAt = Math.max(state.account.lastObservedAt, this.connectivity.highwater());
259
411
  });
412
+ await this.snapshot.close();
260
413
  }
261
- async close() { await this.snapshot.close(); }
262
414
  }
263
415
  //# sourceMappingURL=policy.js.map