ftown 0.20.8 → 0.21.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/centrifugo-client.d.ts +15 -11
- package/dist/centrifugo-client.js +63 -70
- package/dist/centrifugo-client.js.map +1 -1
- package/dist/cli-client.d.ts +11 -1
- package/dist/cli-client.js +23 -3
- package/dist/cli-client.js.map +1 -1
- package/dist/command-rpc.js +4 -8
- package/dist/command-rpc.js.map +1 -1
- package/dist/identity-reset.d.ts +11 -0
- package/dist/identity-reset.js +192 -0
- package/dist/identity-reset.js.map +1 -0
- package/dist/index.js +276 -279
- package/dist/index.js.map +1 -1
- package/dist/ledger-pairing-client.d.ts +28 -0
- package/dist/ledger-pairing-client.js +152 -0
- package/dist/ledger-pairing-client.js.map +1 -0
- package/dist/ledger-relay.d.ts +124 -0
- package/dist/ledger-relay.js +453 -0
- package/dist/ledger-relay.js.map +1 -0
- package/dist/ledger-startup.d.ts +151 -0
- package/dist/ledger-startup.js +203 -0
- package/dist/ledger-startup.js.map +1 -0
- package/dist/relay-token.d.ts +39 -0
- package/dist/relay-token.js +130 -0
- package/dist/relay-token.js.map +1 -0
- package/dist/revocation-gating.d.ts +15 -0
- package/dist/revocation-gating.js +16 -0
- package/dist/revocation-gating.js.map +1 -0
- package/dist/revoked-restart.d.ts +21 -0
- package/dist/revoked-restart.js +55 -0
- package/dist/revoked-restart.js.map +1 -0
- package/dist/security/bridge/identity.d.ts +2 -47
- package/dist/security/bridge/identity.js +3 -3
- package/dist/security/bridge/identity.js.map +1 -1
- package/dist/security/bridge/ledger-identity.d.ts +48 -0
- package/dist/security/bridge/ledger-identity.js +342 -0
- package/dist/security/bridge/ledger-identity.js.map +1 -0
- package/dist/security/bridge/ledger-store.d.ts +84 -0
- package/dist/security/bridge/ledger-store.js +485 -0
- package/dist/security/bridge/ledger-store.js.map +1 -0
- package/dist/security/bridge/metadata.d.ts +2 -2
- package/dist/security/bridge/permissions.js +2 -2
- package/dist/security/bridge/permissions.js.map +1 -1
- package/dist/security/bridge/policy.d.ts +42 -21
- package/dist/security/bridge/policy.js +217 -135
- package/dist/security/bridge/policy.js.map +1 -1
- package/dist/security/bridge/security.d.ts +91 -28
- package/dist/security/bridge/security.js +274 -117
- package/dist/security/bridge/security.js.map +1 -1
- package/dist/security/crypto.js +1 -1
- package/dist/security/crypto.js.map +1 -1
- package/dist/security/ledger/channels.d.ts +5 -0
- package/dist/security/ledger/channels.js +21 -0
- package/dist/security/ledger/channels.js.map +1 -0
- package/dist/security/ledger/ledger.d.ts +61 -0
- package/dist/security/ledger/ledger.js +597 -0
- package/dist/security/ledger/ledger.js.map +1 -0
- package/dist/security/ledger/pairing.d.ts +81 -0
- package/dist/security/ledger/pairing.js +414 -0
- package/dist/security/ledger/pairing.js.map +1 -0
- package/dist/security/ledger/proof.d.ts +24 -0
- package/dist/security/ledger/proof.js +161 -0
- package/dist/security/ledger/proof.js.map +1 -0
- package/dist/security/ledger/protocol.d.ts +100 -0
- package/dist/security/ledger/protocol.js +16 -0
- package/dist/security/ledger/protocol.js.map +1 -0
- package/dist/security/ledger/testing.d.ts +27 -0
- package/dist/security/ledger/testing.js +70 -0
- package/dist/security/ledger/testing.js.map +1 -0
- package/dist/security/ledger/webauthn.d.ts +12 -0
- package/dist/security/ledger/webauthn.js +147 -0
- package/dist/security/ledger/webauthn.js.map +1 -0
- package/dist/security/protocol.d.ts +5 -10
- package/dist/security-cli.d.ts +5 -0
- package/dist/security-cli.js +28 -16
- package/dist/security-cli.js.map +1 -1
- package/dist/token-provider.d.ts +39 -0
- package/dist/token-provider.js +101 -0
- package/dist/token-provider.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
- package/skills/ftown/references/sessions.md +4 -0
- package/dist/bridge-auth.d.ts +0 -25
- package/dist/bridge-auth.js +0 -56
- package/dist/bridge-auth.js.map +0 -1
- package/dist/pairing-client.d.ts +0 -60
- package/dist/pairing-client.js +0 -460
- package/dist/pairing-client.js.map +0 -1
- package/dist/rotating-token-refresher.d.ts +0 -42
- package/dist/rotating-token-refresher.js +0 -91
- package/dist/rotating-token-refresher.js.map +0 -1
- package/dist/security/account-crypto.d.ts +0 -33
- package/dist/security/account-crypto.js +0 -649
- package/dist/security/account-crypto.js.map +0 -1
- package/dist/security/account-protocol.d.ts +0 -228
- package/dist/security/account-protocol.js +0 -2
- package/dist/security/account-protocol.js.map +0 -1
- package/dist/security/bridge/account-client.d.ts +0 -7
- package/dist/security/bridge/account-client.js +0 -87
- package/dist/security/bridge/account-client.js.map +0 -1
- package/dist/security/bridge/account-state.d.ts +0 -105
- package/dist/security/bridge/account-state.js +0 -79
- package/dist/security/bridge/account-state.js.map +0 -1
- package/dist/security/bridge/pending-approval.d.ts +0 -21
- package/dist/security/bridge/pending-approval.js +0 -80
- package/dist/security/bridge/pending-approval.js.map +0 -1
- package/dist/security/bridge/pending-enrollment.d.ts +0 -33
- package/dist/security/bridge/pending-enrollment.js +0 -177
- package/dist/security/bridge/pending-enrollment.js.map +0 -1
- package/dist/security/pending-approval-crypto.d.ts +0 -29
- package/dist/security/pending-approval-crypto.js +0 -716
- package/dist/security/pending-approval-crypto.js.map +0 -1
- package/dist/security/pending-approval-protocol.d.ts +0 -329
- package/dist/security/pending-approval-protocol.js +0 -2
- package/dist/security/pending-approval-protocol.js.map +0 -1
- package/dist/security/security-nudge.d.ts +0 -31
- package/dist/security/security-nudge.js +0 -98
- package/dist/security/security-nudge.js.map +0 -1
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Account-mode wiring between the bridge's ledger replica (via BridgeSecurityService), the ftown server and the relay:
|
|
3
|
+
* - the server's public ledger endpoints, bounded (createLedgerServer);
|
|
4
|
+
* - the relay token by proof (createRelayTokenFetcher) behind the cooldown/caching provider, with the ledger hooks (createAccountRelay);
|
|
5
|
+
* - the relay's ledger messages and (re)subscriptions, spaced so a relay publisher cannot force back-to-back reads and verifies;
|
|
6
|
+
* - the last relay URL the server named (security/relay-url.json), for an offline start.
|
|
7
|
+
* Nothing here decides trust: every ledger goes to security.offerLedger / refreshLedger, which verify it.
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { UnauthorizedError } from 'centrifuge';
|
|
13
|
+
import { createRelayTokenFetcher } from './relay-token.js';
|
|
14
|
+
import { createTokenProvider } from './token-provider.js';
|
|
15
|
+
export const LEDGER_REQUEST_TIMEOUT_MS = 15_000;
|
|
16
|
+
/** A ledger is at most 2 MiB (LEDGER_LIMITS.maxBytes); its JSON envelope stays well under this. */
|
|
17
|
+
const MAX_LEDGER_RESPONSE_BYTES = 3 * 1024 * 1024;
|
|
18
|
+
const MAX_RELAY_URL_LENGTH = 2048;
|
|
19
|
+
/** Relay-triggered ledger work (offers and refreshes) runs at least this far apart... */
|
|
20
|
+
export const RELAY_LEDGER_MIN_SPACING_MS = 1_000;
|
|
21
|
+
/** ...doubling up to this while it keeps changing nothing; an adopted ledger resets it. */
|
|
22
|
+
export const RELAY_LEDGER_MAX_SPACING_MS = 60_000;
|
|
23
|
+
/** Refreshes pulled by requests from endpoints the replica does not list run at most this often (single-flight, trailing). */
|
|
24
|
+
export const UNKNOWN_ENDPOINT_REFRESH_SPACING_MS = 2_000;
|
|
25
|
+
/** An HTTP answer that is not a success, with its status. */
|
|
26
|
+
export class HttpStatusError extends Error {
|
|
27
|
+
status;
|
|
28
|
+
constructor(message, status) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.status = status;
|
|
31
|
+
this.name = 'HttpStatusError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
35
|
+
const apiBase = (apiUrl) => apiUrl.replace(/\/+$/, '');
|
|
36
|
+
/** The HTTP status an error reports: its `status`, else a `(NNN)` in its message (relay-token errors), else null. */
|
|
37
|
+
export function httpStatusOf(error) {
|
|
38
|
+
if (typeof error === 'object' && error !== null && 'status' in error && typeof error.status === 'number')
|
|
39
|
+
return error.status;
|
|
40
|
+
const match = /\((\d{3})\)/.exec(messageOf(error));
|
|
41
|
+
return match ? Number(match[1]) : null;
|
|
42
|
+
}
|
|
43
|
+
/** A refusal (401/403, or an UnauthorizedError) rather than a temporary failure. */
|
|
44
|
+
export function isRefusal(error) {
|
|
45
|
+
if (error instanceof UnauthorizedError)
|
|
46
|
+
return true;
|
|
47
|
+
const status = httpStatusOf(error);
|
|
48
|
+
return status === 401 || status === 403;
|
|
49
|
+
}
|
|
50
|
+
function timeoutError(error, timeoutMs) {
|
|
51
|
+
return error instanceof Error && (error.name === 'TimeoutError' || error.name === 'AbortError')
|
|
52
|
+
? new Error(`Ledger request timed out after ${timeoutMs}ms`) : error;
|
|
53
|
+
}
|
|
54
|
+
export function createLedgerServer(input) {
|
|
55
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
56
|
+
const timeoutMs = input.timeoutMs ?? LEDGER_REQUEST_TIMEOUT_MS;
|
|
57
|
+
const maxBytes = input.maxBytes ?? MAX_LEDGER_RESPONSE_BYTES;
|
|
58
|
+
const base = apiBase(input.apiUrl);
|
|
59
|
+
return {
|
|
60
|
+
async fetchLedger(accountId) {
|
|
61
|
+
let text;
|
|
62
|
+
let status;
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetchImpl(`${base}/api/account/ledger?accountId=${encodeURIComponent(accountId)}`, {
|
|
65
|
+
method: 'GET', headers: { accept: 'application/json' }, cache: 'no-store', signal: AbortSignal.timeout(timeoutMs),
|
|
66
|
+
});
|
|
67
|
+
status = response.status;
|
|
68
|
+
if (Number(response.headers.get('content-length')) > maxBytes) {
|
|
69
|
+
await response.body?.cancel().catch(() => undefined);
|
|
70
|
+
throw new Error('ledger_response_too_large');
|
|
71
|
+
}
|
|
72
|
+
text = await response.text();
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw timeoutError(error, timeoutMs);
|
|
76
|
+
}
|
|
77
|
+
if (Buffer.byteLength(text, 'utf8') > maxBytes)
|
|
78
|
+
throw new Error('ledger_response_too_large');
|
|
79
|
+
if (status !== 200)
|
|
80
|
+
throw new HttpStatusError(`Ledger read failed (${status})`, status);
|
|
81
|
+
let body;
|
|
82
|
+
try {
|
|
83
|
+
body = JSON.parse(text);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new Error('Ledger response malformed');
|
|
87
|
+
}
|
|
88
|
+
const ledger = typeof body === 'object' && body !== null && !Array.isArray(body) ? body.ledger : undefined;
|
|
89
|
+
if (typeof ledger !== 'object' || ledger === null)
|
|
90
|
+
throw new Error('Ledger response malformed');
|
|
91
|
+
return ledger;
|
|
92
|
+
},
|
|
93
|
+
async postLedger(ledger) {
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetchImpl(`${base}/api/account/ledger`, {
|
|
96
|
+
method: 'POST', headers: { 'Content-Type': 'application/json', accept: 'application/json' }, cache: 'no-store',
|
|
97
|
+
body: JSON.stringify({ ledger }), signal: AbortSignal.timeout(timeoutMs),
|
|
98
|
+
});
|
|
99
|
+
await response.body?.cancel().catch(() => undefined);
|
|
100
|
+
return response.status;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw timeoutError(error, timeoutMs);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Publishes ONLY this bridge's own ledger endpoint in the account's endpoint directory (PUT /api/security/endpoints), authenticated
|
|
110
|
+
* with its relay token. Throws HttpStatusError when the server does not accept it.
|
|
111
|
+
*/
|
|
112
|
+
export async function registerBridgeEndpoint(input) {
|
|
113
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
114
|
+
const timeoutMs = input.timeoutMs ?? LEDGER_REQUEST_TIMEOUT_MS;
|
|
115
|
+
let status;
|
|
116
|
+
try {
|
|
117
|
+
const response = await fetchImpl(`${apiBase(input.apiUrl)}/api/security/endpoints`, {
|
|
118
|
+
method: 'PUT', headers: { Authorization: `Bearer ${input.token}`, 'Content-Type': 'application/json' },
|
|
119
|
+
body: JSON.stringify({ endpoint: input.endpoint, bridgeId: input.bridgeId, label: input.label, protocolVersion: 1 }),
|
|
120
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
121
|
+
});
|
|
122
|
+
await response.body?.cancel().catch(() => undefined);
|
|
123
|
+
status = response.status;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw timeoutError(error, timeoutMs);
|
|
127
|
+
}
|
|
128
|
+
if (status < 200 || status >= 300)
|
|
129
|
+
throw new HttpStatusError(`Endpoint directory registration failed (${status})`, status);
|
|
130
|
+
}
|
|
131
|
+
/** The line printed when the startup relay token is refused (exit 1). */
|
|
132
|
+
export function relayTokenFailureMessage(error, apiUrl) {
|
|
133
|
+
if (error instanceof UnauthorizedError) {
|
|
134
|
+
return "[Bridge] The ftown server did not accept this computer's key. If it was removed from your account, run `ftown start --new-identity` to pair it again.";
|
|
135
|
+
}
|
|
136
|
+
if (isRefusal(error))
|
|
137
|
+
return `[Bridge] The ftown server at ${apiBase(apiUrl)} refused this computer (${messageOf(error)}).`;
|
|
138
|
+
return `[Bridge] Could not get a relay token from ${apiBase(apiUrl)} (${messageOf(error)}). Check the connection and run \`ftown start\` again.`;
|
|
139
|
+
}
|
|
140
|
+
export const MISSING_RELAY_URL_MESSAGE = '[Bridge] The ftown server did not name its relay (centrifugoUrl) in the relay-token response. '
|
|
141
|
+
+ 'Update the server, or set FTOWN_CENTRIFUGO_URL to its wss:// URL.';
|
|
142
|
+
function isLoopbackHost(hostname) {
|
|
143
|
+
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]' || hostname.endsWith('.localhost');
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A Centrifugo URL the bridge may connect to, or null: wss://, or ws:// only to a loopback host or when the API itself is plain
|
|
147
|
+
* http (the operator already chose an insecure transport). The token rides this connection.
|
|
148
|
+
*/
|
|
149
|
+
export function parseRelayUrl(value, apiUrl) {
|
|
150
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > MAX_RELAY_URL_LENGTH)
|
|
151
|
+
return null;
|
|
152
|
+
let url;
|
|
153
|
+
try {
|
|
154
|
+
url = new URL(value);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
if (url.username || url.password)
|
|
160
|
+
return null;
|
|
161
|
+
if (url.protocol === 'wss:')
|
|
162
|
+
return url.href;
|
|
163
|
+
if (url.protocol !== 'ws:')
|
|
164
|
+
return null;
|
|
165
|
+
let insecureApi = false;
|
|
166
|
+
try {
|
|
167
|
+
insecureApi = new URL(apiUrl).protocol === 'http:';
|
|
168
|
+
}
|
|
169
|
+
catch { /* treated as secure */ }
|
|
170
|
+
return insecureApi || isLoopbackHost(url.hostname) ? url.href : null;
|
|
171
|
+
}
|
|
172
|
+
const relayUrlFile = (dataDir) => join(dataDir, 'security', 'relay-url.json');
|
|
173
|
+
/** The last relay URL the server named (`security/relay-url.json`), if it is still acceptable for this API; else null. */
|
|
174
|
+
export function readSavedRelayUrl(dataDir, apiUrl) {
|
|
175
|
+
try {
|
|
176
|
+
const value = JSON.parse(readFileSync(relayUrlFile(dataDir), 'utf8'));
|
|
177
|
+
if (typeof value !== 'object' || value === null || value.version !== 1)
|
|
178
|
+
return null;
|
|
179
|
+
return parseRelayUrl(value.url, apiUrl);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** Atomically saves the relay URL (mode 0600). The identity reset deletes it. */
|
|
186
|
+
export function saveRelayUrl(dataDir, url) {
|
|
187
|
+
const file = relayUrlFile(dataDir);
|
|
188
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
189
|
+
mkdirSync(join(dataDir, 'security'), { recursive: true, mode: 0o700 });
|
|
190
|
+
try {
|
|
191
|
+
writeFileSync(temporary, JSON.stringify({ version: 1, url }), { mode: 0o600, flag: 'wx' });
|
|
192
|
+
renameSync(temporary, file);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
rmSync(temporary, { force: true });
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Subscribes `listener` when the security service offers onApprovalRequired; false when it does not (nothing subscribed). */
|
|
200
|
+
export function subscribeApprovalRequired(security, listener) {
|
|
201
|
+
const signal = security.onApprovalRequired;
|
|
202
|
+
if (typeof signal !== 'function')
|
|
203
|
+
return false;
|
|
204
|
+
signal.call(security, listener);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
const defaultSchedule = (run, ms) => {
|
|
208
|
+
const timer = setTimeout(run, ms);
|
|
209
|
+
timer.unref();
|
|
210
|
+
return () => clearTimeout(timer);
|
|
211
|
+
};
|
|
212
|
+
export function createAccountRelay(deps) {
|
|
213
|
+
const log = deps.log ?? ((line) => console.warn(line));
|
|
214
|
+
const monotonic = deps.now ?? (() => performance.now());
|
|
215
|
+
const schedule = deps.schedule ?? defaultSchedule;
|
|
216
|
+
const baseFetch = deps.fetchImpl ?? fetch;
|
|
217
|
+
const relayTokenUrl = `${apiBase(deps.apiUrl)}/api/auth/relay-token`;
|
|
218
|
+
const override = deps.relayUrlOverride === undefined || deps.relayUrlOverride === '' ? null : parseRelayUrl(deps.relayUrlOverride, deps.apiUrl);
|
|
219
|
+
if (deps.relayUrlOverride && override === null)
|
|
220
|
+
log('[Bridge] Ignoring FTOWN_CENTRIFUGO_URL: it must be a wss:// URL (ws:// only for a local relay).');
|
|
221
|
+
let learnedRelayUrl = null;
|
|
222
|
+
const savedRelayUrl = deps.savedRelayUrl ?? null;
|
|
223
|
+
const head = () => deps.security.ledgerStatus().head;
|
|
224
|
+
/** The relay connection was refused (terminal for centrifuge-js) and has not been reconnected since. */
|
|
225
|
+
let relayRefused = false;
|
|
226
|
+
/** The local head whose chain was last POSTed after a plain 401 (at most once per head). */
|
|
227
|
+
let postedHead;
|
|
228
|
+
let stopped = false;
|
|
229
|
+
const reconnectIfRefused = () => {
|
|
230
|
+
if (!relayRefused || stopped)
|
|
231
|
+
return;
|
|
232
|
+
relayRefused = false;
|
|
233
|
+
try {
|
|
234
|
+
deps.reconnect?.();
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
log(`[Bridge] Relay reconnect failed: ${messageOf(error)}`);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
// Reads `centrifugoUrl` from a successful relay-token response without consuming the body the fetcher reads.
|
|
241
|
+
const fetchImpl = async (input, init) => {
|
|
242
|
+
const response = await baseFetch(input, init);
|
|
243
|
+
if (response.ok && String(input) === relayTokenUrl) {
|
|
244
|
+
try {
|
|
245
|
+
const body = JSON.parse(await response.clone().text());
|
|
246
|
+
const named = typeof body === 'object' && body !== null ? parseRelayUrl(body.centrifugoUrl, deps.apiUrl) : null;
|
|
247
|
+
if (named !== null) {
|
|
248
|
+
const previous = learnedRelayUrl ?? savedRelayUrl;
|
|
249
|
+
learnedRelayUrl = named;
|
|
250
|
+
if (named !== previous) {
|
|
251
|
+
try {
|
|
252
|
+
deps.onRelayUrl?.(named);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
log(`[Bridge] Could not save the relay URL: ${messageOf(error)}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch { /* the fetcher reports a malformed response */ }
|
|
261
|
+
}
|
|
262
|
+
return response;
|
|
263
|
+
};
|
|
264
|
+
const fetcher = createRelayTokenFetcher({
|
|
265
|
+
apiUrl: deps.apiUrl,
|
|
266
|
+
accountId: () => deps.accountId,
|
|
267
|
+
bridgeId: deps.bridgeId,
|
|
268
|
+
authPrivateKey: deps.authPrivateKey,
|
|
269
|
+
head,
|
|
270
|
+
// A newer server ledger rides the token response. An invalid one is only logged: the token is still returned.
|
|
271
|
+
onLedger: async (ledger) => {
|
|
272
|
+
try {
|
|
273
|
+
if (await deps.security.offerLedger(ledger, 'server') === 'reject')
|
|
274
|
+
log('[Bridge] Ignored an account ledger from the relay-token response.');
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
log(`[Bridge] Account ledger from the relay-token response failed: ${messageOf(error)}`);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
// Evidence of a revoked key: only a ledger that verifies and revokes this bridge triggers the reset (security's onBridgeRevoked).
|
|
281
|
+
onRevoked: async (ledger) => {
|
|
282
|
+
try {
|
|
283
|
+
await deps.security.offerLedger(ledger, 'server');
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
log(`[Bridge] Revocation evidence failed: ${messageOf(error)}`);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
// The server may be missing our add_bridge (e.g. the approving browser's write was lost): offer it our chain once per local head,
|
|
290
|
+
// then retry once.
|
|
291
|
+
onUnauthorized: async () => {
|
|
292
|
+
const current = head();
|
|
293
|
+
if (postedHead !== undefined && postedHead === current)
|
|
294
|
+
return false;
|
|
295
|
+
try {
|
|
296
|
+
const state = await deps.store.read();
|
|
297
|
+
if (state === null)
|
|
298
|
+
return false;
|
|
299
|
+
const status = await deps.server.postLedger(state.ledger);
|
|
300
|
+
postedHead = current;
|
|
301
|
+
return status === 200 || status === 201;
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
log(`[Bridge] Could not send the account ledger to the server: ${messageOf(error)}`);
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
hostname: deps.hostname,
|
|
309
|
+
localPort: deps.localPort,
|
|
310
|
+
fetchImpl,
|
|
311
|
+
timeoutMs: LEDGER_REQUEST_TIMEOUT_MS,
|
|
312
|
+
});
|
|
313
|
+
const tokens = createTokenProvider({
|
|
314
|
+
fetchToken: async () => {
|
|
315
|
+
const token = await fetcher();
|
|
316
|
+
// Every relay-token answer is a server contact: it renews the offline grace of the account devices.
|
|
317
|
+
deps.security.noteServerContact();
|
|
318
|
+
// The key is accepted again (e.g. the server learned our add_bridge): a refused relay connection may come back.
|
|
319
|
+
reconnectIfRefused();
|
|
320
|
+
return token;
|
|
321
|
+
},
|
|
322
|
+
now: monotonic,
|
|
323
|
+
random: deps.random,
|
|
324
|
+
head,
|
|
325
|
+
});
|
|
326
|
+
// Relay-triggered ledger work: at most one at a time, at least RELAY_LEDGER_MIN_SPACING_MS apart, only the latest kept.
|
|
327
|
+
let pending = null;
|
|
328
|
+
let running = false;
|
|
329
|
+
let cancelTimer = null;
|
|
330
|
+
let readyAt = 0;
|
|
331
|
+
let idleRuns = 0;
|
|
332
|
+
let lastRunEndedAt = Number.NEGATIVE_INFINITY;
|
|
333
|
+
const run = async (work) => {
|
|
334
|
+
const before = head();
|
|
335
|
+
try {
|
|
336
|
+
if (work.kind === 'offer') {
|
|
337
|
+
if (await deps.security.offerLedger(work.ledger, 'relay') !== 'adopt')
|
|
338
|
+
return 'idle';
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
if (work.head !== null && work.head === before)
|
|
342
|
+
return 'skipped';
|
|
343
|
+
await deps.security.refreshLedger();
|
|
344
|
+
if (head() === before)
|
|
345
|
+
return 'idle';
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
log(`[Bridge] Relay ledger update failed: ${messageOf(error)}`);
|
|
350
|
+
return 'idle';
|
|
351
|
+
}
|
|
352
|
+
reconnectIfRefused();
|
|
353
|
+
return 'adopt';
|
|
354
|
+
};
|
|
355
|
+
const pump = () => {
|
|
356
|
+
if (stopped || running || cancelTimer !== null || pending === null)
|
|
357
|
+
return;
|
|
358
|
+
const wait = readyAt - monotonic();
|
|
359
|
+
if (wait > 0) {
|
|
360
|
+
cancelTimer = schedule(() => { cancelTimer = null; pump(); }, wait);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const work = pending;
|
|
364
|
+
pending = null;
|
|
365
|
+
running = true;
|
|
366
|
+
void run(work).then(outcome => {
|
|
367
|
+
if (outcome === 'skipped')
|
|
368
|
+
return;
|
|
369
|
+
lastRunEndedAt = monotonic();
|
|
370
|
+
if (outcome === 'adopt')
|
|
371
|
+
idleRuns = 0;
|
|
372
|
+
const spacing = outcome === 'adopt' ? RELAY_LEDGER_MIN_SPACING_MS
|
|
373
|
+
: Math.min(RELAY_LEDGER_MIN_SPACING_MS * 2 ** idleRuns++, RELAY_LEDGER_MAX_SPACING_MS);
|
|
374
|
+
readyAt = monotonic() + spacing;
|
|
375
|
+
}).finally(() => { running = false; pump(); });
|
|
376
|
+
};
|
|
377
|
+
const submit = (work) => {
|
|
378
|
+
if (stopped)
|
|
379
|
+
return;
|
|
380
|
+
// Leading edge: the first work after a quiet second runs at once with a fresh backoff; only a burst is spaced and backed off.
|
|
381
|
+
if (!running && pending === null && cancelTimer === null && monotonic() - lastRunEndedAt >= RELAY_LEDGER_MIN_SPACING_MS) {
|
|
382
|
+
idleRuns = 0;
|
|
383
|
+
readyAt = 0;
|
|
384
|
+
}
|
|
385
|
+
pending = work;
|
|
386
|
+
pump();
|
|
387
|
+
};
|
|
388
|
+
// Refreshes pulled by unknown endpoints: their own limit, independent of the relay spacing.
|
|
389
|
+
let unknownInFlight = false;
|
|
390
|
+
let unknownQueued = false;
|
|
391
|
+
let unknownCancel = null;
|
|
392
|
+
let unknownReadyAt = Number.NEGATIVE_INFINITY;
|
|
393
|
+
const refreshForUnknown = () => {
|
|
394
|
+
if (stopped)
|
|
395
|
+
return;
|
|
396
|
+
if (unknownInFlight) {
|
|
397
|
+
unknownQueued = true;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (unknownCancel !== null)
|
|
401
|
+
return;
|
|
402
|
+
const wait = unknownReadyAt - monotonic();
|
|
403
|
+
if (wait > 0) {
|
|
404
|
+
unknownCancel = schedule(() => { unknownCancel = null; refreshForUnknown(); }, wait);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
unknownInFlight = true;
|
|
408
|
+
unknownReadyAt = monotonic() + UNKNOWN_ENDPOINT_REFRESH_SPACING_MS;
|
|
409
|
+
const before = head();
|
|
410
|
+
deps.security.refreshLedger()
|
|
411
|
+
.then(() => { if (head() !== before)
|
|
412
|
+
reconnectIfRefused(); })
|
|
413
|
+
.catch((error) => log(`[Bridge] Account ledger refresh failed: ${messageOf(error)}`))
|
|
414
|
+
.finally(() => {
|
|
415
|
+
unknownInFlight = false;
|
|
416
|
+
if (unknownQueued) {
|
|
417
|
+
unknownQueued = false;
|
|
418
|
+
refreshForUnknown();
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
return {
|
|
423
|
+
tokens,
|
|
424
|
+
async connectionToken() {
|
|
425
|
+
try {
|
|
426
|
+
return await tokens.getToken();
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
if (error instanceof UnauthorizedError)
|
|
430
|
+
relayRefused = true;
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
relayUrl: () => override ?? learnedRelayUrl ?? savedRelayUrl,
|
|
435
|
+
onLedgerMessage(message) {
|
|
436
|
+
if (message.type === 'ledger')
|
|
437
|
+
submit({ kind: 'offer', ledger: message.ledger });
|
|
438
|
+
else if (message.head !== head())
|
|
439
|
+
submit({ kind: 'refresh', head: message.head });
|
|
440
|
+
},
|
|
441
|
+
onLedgerSubscribed: () => submit({ kind: 'refresh', head: null }),
|
|
442
|
+
onUnknownEndpoint: refreshForUnknown,
|
|
443
|
+
stop() {
|
|
444
|
+
stopped = true;
|
|
445
|
+
pending = null;
|
|
446
|
+
cancelTimer?.();
|
|
447
|
+
cancelTimer = null;
|
|
448
|
+
unknownCancel?.();
|
|
449
|
+
unknownCancel = null;
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=ledger-relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-relay.js","sourceRoot":"","sources":["../src/ledger-relay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { type IdentityResetReason, type IdentityResetResult } from './identity-reset.js';
|
|
2
|
+
import type { BridgeMode } from './revocation-gating.js';
|
|
3
|
+
import { type LedgerBridgeIdentity } from './security/bridge/ledger-identity.js';
|
|
4
|
+
import { BridgeLedgerStore, type OwnBridgeStatus, type ReconcileResult } from './security/bridge/ledger-store.js';
|
|
5
|
+
import type { EndpointPublic } from './security/protocol.js';
|
|
6
|
+
export declare const LEGACY_ACCOUNT_RESET_MESSAGE = "[Bridge] Old account data from a previous ftown version was cleared. Pair this computer again with the link below.";
|
|
7
|
+
export declare const NOT_IN_LEDGER_MESSAGE = "This computer is not in your account's ledger. Run `ftown start --new-identity` to pair it again.";
|
|
8
|
+
export declare const PAIRING_REJECTED_HINT = "Pairing did not complete. Run `ftown start` to try again.";
|
|
9
|
+
export declare const SERVER_LEDGER_MISMATCH_MESSAGE = "[Bridge] The server's account ledger does not list this computer. If it was removed from your account, run `ftown start --new-identity` to pair it again.";
|
|
10
|
+
export declare const UPDATE_RESTART_UNPAIRED_MESSAGE = "[Bridge] An update restart cannot pair this computer. Run `ftown start` to pair it.";
|
|
11
|
+
export declare const UPDATED_TO_LEDGER_MESSAGE = "[Bridge] ftown was updated to the passkey ledger; run `ftown start` to pair this computer.";
|
|
12
|
+
/** Only account bridges use the passkey ledger (legacy reset, pairing, ledger replica, relay token by proof). */
|
|
13
|
+
export declare function usesLedger(mode: BridgeMode): boolean;
|
|
14
|
+
export interface IdentityResetDeps {
|
|
15
|
+
hasLegacyAccountState: (dataDir: string) => boolean;
|
|
16
|
+
beginIdentityReset: (dataDir: string, reason: IdentityResetReason) => void;
|
|
17
|
+
completePendingIdentityReset: (dataDir: string) => IdentityResetResult | null;
|
|
18
|
+
}
|
|
19
|
+
export type IdentityResetOutcome = {
|
|
20
|
+
kind: 'continue';
|
|
21
|
+
reset: IdentityResetResult | null;
|
|
22
|
+
}
|
|
23
|
+
/** Exit 1 after printing `message`; nothing was changed on disk. */
|
|
24
|
+
| {
|
|
25
|
+
kind: 'exit';
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Step 2. Account mode: `--new-identity`, or any v1 account state on disk, starts a manual reset (the latter says so in one line).
|
|
30
|
+
* An update restart (a v1 -> v2 auto-update) has no visible terminal for the pairing link: with v1 state on disk it changes nothing
|
|
31
|
+
* and exits, and the next `ftown start` resets and pairs. Every mode then finishes a pending reset, as before (a revoked restart or a
|
|
32
|
+
* crash may have left one).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resetIdentityForStart(input: {
|
|
35
|
+
dataDir: string;
|
|
36
|
+
mode: BridgeMode;
|
|
37
|
+
newIdentity: boolean;
|
|
38
|
+
updateRestart: boolean;
|
|
39
|
+
log: (line: string) => void;
|
|
40
|
+
}, deps?: Partial<IdentityResetDeps>): IdentityResetOutcome;
|
|
41
|
+
export type OwnStatusAction = 'continue' | 'revoked_restart' | 'exit_absent';
|
|
42
|
+
/** Step 4. Only a signed revocation resets the identity; a bridge the ledger does not list fails closed and is never reset. */
|
|
43
|
+
export declare function ownStatusAction(own: OwnBridgeStatus): OwnStatusAction;
|
|
44
|
+
/** A ledger identity bound to its account: endpoint and accountId are set. */
|
|
45
|
+
export type BoundLedgerIdentity = LedgerBridgeIdentity & {
|
|
46
|
+
endpoint: EndpointPublic;
|
|
47
|
+
accountId: string;
|
|
48
|
+
};
|
|
49
|
+
export type AccountStartup = {
|
|
50
|
+
kind: 'ready';
|
|
51
|
+
identity: BoundLedgerIdentity;
|
|
52
|
+
store: BridgeLedgerStore;
|
|
53
|
+
reconciled: ReconcileResult;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'revoked';
|
|
56
|
+
}
|
|
57
|
+
/** Exit 1 after printing `message` (null: the cause was already printed). */
|
|
58
|
+
| {
|
|
59
|
+
kind: 'exit';
|
|
60
|
+
message: string | null;
|
|
61
|
+
};
|
|
62
|
+
export interface AccountStartupDeps {
|
|
63
|
+
dataDir: string;
|
|
64
|
+
bridgeId: string;
|
|
65
|
+
/** False during an update restart: it must come back with its existing identity and never pair. */
|
|
66
|
+
allowPairing: boolean;
|
|
67
|
+
/** One pairing attempt (runLedgerPairing) with the unbound identity and the empty store; binds both on success. Throws
|
|
68
|
+
* Error('pairing_expired') (its hint already printed) or Error('pairing_rejected'). */
|
|
69
|
+
pair: (identity: LedgerBridgeIdentity, store: BridgeLedgerStore) => Promise<unknown>;
|
|
70
|
+
/** The server's ledger for this account (GET /api/account/ledger?accountId=…, bounded). */
|
|
71
|
+
fetchLedger: (accountId: string) => Promise<unknown>;
|
|
72
|
+
/** Injectable for tests. */
|
|
73
|
+
loadOrCreateIdentity?: (dataDir: string) => Promise<LedgerBridgeIdentity>;
|
|
74
|
+
reconcile?: (dataDir: string) => Promise<ReconcileResult>;
|
|
75
|
+
readIdentity?: (dataDir: string) => Promise<LedgerBridgeIdentity | null>;
|
|
76
|
+
}
|
|
77
|
+
export declare function ledgerUnavailableMessage(reason: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Steps 3-4, account mode only, before any network use of the identity:
|
|
80
|
+
* - keys exist (created when absent), then identity and replica are reconciled;
|
|
81
|
+
* - 'unpaired' -> one pairing attempt (expired/rejected -> exit 1 with the hint);
|
|
82
|
+
* - 'needs_ledger' -> the server ledger is adopted only through adoptBound (any failure -> exit 1; the identity stays bound);
|
|
83
|
+
* - own status from the trusted replica: 'revoked' -> revoked restart; 'absent' -> exit 1 (never a reset); 'active' -> ready.
|
|
84
|
+
* Corrupted local files throw (they are never reset).
|
|
85
|
+
*/
|
|
86
|
+
export declare function startAccountIdentity(deps: AccountStartupDeps): Promise<AccountStartup>;
|
|
87
|
+
export type RelayStartup = {
|
|
88
|
+
kind: 'revoked';
|
|
89
|
+
}
|
|
90
|
+
/** Exit 1 after printing `message`. */
|
|
91
|
+
| {
|
|
92
|
+
kind: 'exit';
|
|
93
|
+
message: string;
|
|
94
|
+
} | {
|
|
95
|
+
kind: 'online';
|
|
96
|
+
token: string;
|
|
97
|
+
relayUrl: string;
|
|
98
|
+
}
|
|
99
|
+
/** The server is unreachable for now: start with the saved relay URL and keep retrying in the background. */
|
|
100
|
+
| {
|
|
101
|
+
kind: 'offline';
|
|
102
|
+
relayUrl: string;
|
|
103
|
+
reason: string;
|
|
104
|
+
};
|
|
105
|
+
export declare function offlineWithoutRelayUrlMessage(reason: string): string;
|
|
106
|
+
/**
|
|
107
|
+
* Step 5, the first relay token. A proven revocation (the token hooks offer the server's evidence first) wins over everything; a
|
|
108
|
+
* refusal (401/403) exits; a temporary failure (network, timeout, 5xx, cooldown) starts offline with the saved relay URL, and exits
|
|
109
|
+
* only when there is none. After a successful token the revocation is checked again: the token response's ledger may prove it.
|
|
110
|
+
*/
|
|
111
|
+
export declare function startRelay(input: {
|
|
112
|
+
getToken: () => Promise<string>;
|
|
113
|
+
relayUrl: () => string | null;
|
|
114
|
+
bridgeRevoked: () => boolean;
|
|
115
|
+
apiUrl: string;
|
|
116
|
+
}): Promise<RelayStartup>;
|
|
117
|
+
export declare function endpointRefusedMessage(reason: string): string;
|
|
118
|
+
export type RegistrationAttempt = {
|
|
119
|
+
kind: 'registered';
|
|
120
|
+
} | {
|
|
121
|
+
kind: 'retry';
|
|
122
|
+
} | {
|
|
123
|
+
kind: 'revoked';
|
|
124
|
+
} | {
|
|
125
|
+
kind: 'exit';
|
|
126
|
+
message: string;
|
|
127
|
+
};
|
|
128
|
+
export interface EndpointRegistrarDeps {
|
|
129
|
+
/** PUT /api/security/endpoints with this bridge's own ledger endpoint (registerBridgeEndpoint). */
|
|
130
|
+
register: (token: string) => Promise<void>;
|
|
131
|
+
tokens: {
|
|
132
|
+
getToken(): Promise<string>;
|
|
133
|
+
cooldownRemainingMs(): number;
|
|
134
|
+
};
|
|
135
|
+
bridgeRevoked: () => boolean;
|
|
136
|
+
log: (line: string) => void;
|
|
137
|
+
/** Returns a cancel function. Defaults to an unref'd setTimeout. */
|
|
138
|
+
schedule?: (run: () => void, ms: number) => () => void;
|
|
139
|
+
/** First background delay (default 15 s), doubling to maxDelayMs (default 10 min); never earlier than the token cooldown. */
|
|
140
|
+
baseDelayMs?: number;
|
|
141
|
+
maxDelayMs?: number;
|
|
142
|
+
}
|
|
143
|
+
export interface EndpointRegistrar {
|
|
144
|
+
/** Step 6 at an online start, with the first token: a refusal exits, a temporary failure is retried in the background. */
|
|
145
|
+
registerNow(token: string): Promise<RegistrationAttempt>;
|
|
146
|
+
/** Retries getToken() + registration in the background until it succeeds or is refused, honouring the token cooldown. */
|
|
147
|
+
retryInBackground(): void;
|
|
148
|
+
registered(): boolean;
|
|
149
|
+
stop(): void;
|
|
150
|
+
}
|
|
151
|
+
export declare function createEndpointRegistrar(deps: EndpointRegistrarDeps): EndpointRegistrar;
|