ftown 0.20.9 → 0.24.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/attention-events.d.ts +1 -1
- package/dist/attention-store.d.ts +3 -0
- package/dist/attention-store.js +25 -1
- package/dist/attention-store.js.map +1 -1
- package/dist/centrifugo-client.d.ts +28 -11
- package/dist/centrifugo-client.js +84 -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.d.ts +2 -0
- package/dist/command-rpc.js +59 -17
- package/dist/command-rpc.js.map +1 -1
- package/dist/identity-reset.d.ts +4 -1
- package/dist/identity-reset.js +54 -6
- package/dist/identity-reset.js.map +1 -1
- package/dist/index.js +194 -288
- package/dist/index.js.map +1 -1
- package/dist/ledger-pairing-client.d.ts +28 -0
- package/dist/ledger-pairing-client.js +154 -0
- package/dist/ledger-pairing-client.js.map +1 -0
- package/dist/ledger-relay.d.ts +159 -0
- package/dist/ledger-relay.js +595 -0
- package/dist/ledger-relay.js.map +1 -0
- package/dist/ledger-reoffer.d.ts +56 -0
- package/dist/ledger-reoffer.js +210 -0
- package/dist/ledger-reoffer.js.map +1 -0
- package/dist/ledger-startup.d.ts +176 -0
- package/dist/ledger-startup.js +233 -0
- package/dist/ledger-startup.js.map +1 -0
- package/dist/local-api-server.d.ts +3 -0
- package/dist/local-api-server.js +68 -6
- package/dist/local-api-server.js.map +1 -1
- package/dist/mail-delivery.d.ts +26 -3
- package/dist/mail-delivery.js +65 -7
- package/dist/mail-delivery.js.map +1 -1
- package/dist/preview-protocol.d.ts +19 -3
- package/dist/preview-protocol.js +14 -0
- package/dist/preview-protocol.js.map +1 -1
- package/dist/relay-token.d.ts +45 -0
- package/dist/relay-token.js +138 -0
- package/dist/relay-token.js.map +1 -0
- package/dist/revocation-gating.d.ts +0 -13
- package/dist/revocation-gating.js +0 -16
- package/dist/revocation-gating.js.map +1 -1
- 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-rpc.d.ts +34 -0
- package/dist/security/bridge/ledger-rpc.js +58 -0
- package/dist/security/bridge/ledger-rpc.js.map +1 -0
- package/dist/security/bridge/ledger-store.d.ts +153 -0
- package/dist/security/bridge/ledger-store.js +825 -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 +4 -3
- package/dist/security/bridge/permissions.js.map +1 -1
- package/dist/security/bridge/policy.d.ts +43 -23
- package/dist/security/bridge/policy.js +214 -146
- package/dist/security/bridge/policy.js.map +1 -1
- package/dist/security/bridge/security.d.ts +212 -33
- package/dist/security/bridge/security.js +437 -165
- 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/announce.d.ts +32 -0
- package/dist/security/ledger/announce.js +101 -0
- package/dist/security/ledger/announce.js.map +1 -0
- 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 +154 -0
- package/dist/security/ledger/ledger.js +878 -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 +36 -0
- package/dist/security/ledger/proof.js +193 -0
- package/dist/security/ledger/proof.js.map +1 -0
- package/dist/security/ledger/protocol.d.ts +113 -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/session-controller.d.ts +9 -3
- package/dist/session-controller.js +120 -20
- package/dist/session-controller.js.map +1 -1
- package/dist/session-inbox-protocol.d.ts +2 -0
- package/dist/session-preview.d.ts +20 -5
- package/dist/session-preview.js +185 -37
- package/dist/session-preview.js.map +1 -1
- package/dist/session-store.js +19 -1
- package/dist/session-store.js.map +1 -1
- package/dist/sessions-commands.js +41 -7
- package/dist/sessions-commands.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/opencode-plugin/ftown.js +15 -2
- package/package.json +2 -2
- package/skills/ftown/SKILL.md +18 -7
- package/skills/ftown/references/sessions.md +38 -12
- package/dist/bridge-auth.d.ts +0 -31
- package/dist/bridge-auth.js +0 -70
- package/dist/bridge-auth.js.map +0 -1
- package/dist/pairing-client.d.ts +0 -62
- package/dist/pairing-client.js +0 -469
- 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 -101
- 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/bridge/revocation.d.ts +0 -14
- package/dist/security/bridge/revocation.js +0 -40
- package/dist/security/bridge/revocation.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,56 @@
|
|
|
1
|
+
import type { LedgerOfferOutcome, ServerLedgerRead } from './security/bridge/security.js';
|
|
2
|
+
export declare const REOFFER_PAIR_WINDOW_MS = 30000;
|
|
3
|
+
/** At most one POST per this interval, whatever the (our head, server head) pair. */
|
|
4
|
+
export declare const REOFFER_MIN_POST_SPACING_MS = 30000;
|
|
5
|
+
/** After a 404 (the server does not know the account), nothing is posted for this long unless a read finds the account again. */
|
|
6
|
+
export declare const REOFFER_UNKNOWN_ACCOUNT_PAUSE_MS = 3600000;
|
|
7
|
+
export declare const REOFFER_FIRST_BACKOFF_MS = 30000;
|
|
8
|
+
export declare const REOFFER_MAX_BACKOFF_MS = 600000;
|
|
9
|
+
export declare const REOFFER_MAX_RETRY_AFTER_MS = 3600000;
|
|
10
|
+
/** Each backoff wait is multiplied by 1 - REOFFER_JITTER + 2 * REOFFER_JITTER * random(). */
|
|
11
|
+
export declare const REOFFER_JITTER = 0.2;
|
|
12
|
+
/** The server's answer to POST /api/account/ledger { ledger, expectHead }. */
|
|
13
|
+
export interface LedgerPostAnswer {
|
|
14
|
+
status: number;
|
|
15
|
+
/** The parsed JSON body, or null when it is absent, oversized or not JSON. */
|
|
16
|
+
body: unknown;
|
|
17
|
+
/** Retry-After in ms (seconds or an HTTP date), or null. */
|
|
18
|
+
retryAfterMs: number | null;
|
|
19
|
+
}
|
|
20
|
+
export interface LedgerReofferDeps {
|
|
21
|
+
/** A fresh read of the server's chain, offered to the replica as 'server' (BridgeSecurityService.readServerLedger). */
|
|
22
|
+
read(): Promise<ServerLedgerRead>;
|
|
23
|
+
/** The stored chain and its head; null when there is nothing this bridge may send (no chain, or this bridge is not active in it). */
|
|
24
|
+
local(): Promise<{
|
|
25
|
+
ledger: unknown;
|
|
26
|
+
head: string;
|
|
27
|
+
} | null>;
|
|
28
|
+
/** POST /api/account/ledger { ledger, expectHead }. Throws on a network failure. */
|
|
29
|
+
post(ledger: unknown, expectHead: string): Promise<LedgerPostAnswer>;
|
|
30
|
+
/** Offers a chain the server answered with to the replica as 'server'. */
|
|
31
|
+
offer(ledger: unknown): Promise<LedgerOfferOutcome>;
|
|
32
|
+
/** Monotonic clock (ms). */
|
|
33
|
+
now(): number;
|
|
34
|
+
/** [0, 1), for the jitter. */
|
|
35
|
+
random(): number;
|
|
36
|
+
/** Timer; returns a cancel function. */
|
|
37
|
+
schedule(run: () => void, ms: number): () => void;
|
|
38
|
+
log(line: string): void;
|
|
39
|
+
}
|
|
40
|
+
/** posted: the server answered 2xx (the next read confirms it). not_behind: nothing to send. skipped: the pair, or any POST, was sent less
|
|
41
|
+
* than 30 s ago. failed: backing off. deferred: an earlier failure's wait has not passed (a retry is scheduled). stopped: shut down, or
|
|
42
|
+
* paused after the server said it does not know the account. */
|
|
43
|
+
export type ReofferOutcome = 'posted' | 'not_behind' | 'skipped' | 'failed' | 'deferred' | 'stopped';
|
|
44
|
+
export interface LedgerReoffer {
|
|
45
|
+
/** A server read showed the server holding a strict prefix of our chain (so the server knows the account: a 404 pause ends): start one
|
|
46
|
+
* paced attempt (it reads again before it posts). */
|
|
47
|
+
noteServerBehind(): void;
|
|
48
|
+
/** Runs an attempt now unless one is running (joined) or a backoff wait is pending ('deferred'); for a relay token refused with a plain
|
|
49
|
+
* 401, which may mean the server lacks our records. It posts only when its own fresh read says 'behind'. */
|
|
50
|
+
check(): Promise<ReofferOutcome>;
|
|
51
|
+
/** Cancels the retry timer; nothing runs afterwards. */
|
|
52
|
+
stop(): void;
|
|
53
|
+
}
|
|
54
|
+
/** The backoff wait after `failures` failed attempts in a row (>= 1), for a given random() draw. */
|
|
55
|
+
export declare function reofferBackoffMs(failures: number, random: number): number;
|
|
56
|
+
export declare function createLedgerReoffer(deps: LedgerReofferDeps): LedgerReoffer;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
export const REOFFER_PAIR_WINDOW_MS = 30_000;
|
|
2
|
+
/** At most one POST per this interval, whatever the (our head, server head) pair. */
|
|
3
|
+
export const REOFFER_MIN_POST_SPACING_MS = 30_000;
|
|
4
|
+
/** After a 404 (the server does not know the account), nothing is posted for this long unless a read finds the account again. */
|
|
5
|
+
export const REOFFER_UNKNOWN_ACCOUNT_PAUSE_MS = 3_600_000;
|
|
6
|
+
export const REOFFER_FIRST_BACKOFF_MS = 30_000;
|
|
7
|
+
export const REOFFER_MAX_BACKOFF_MS = 600_000;
|
|
8
|
+
export const REOFFER_MAX_RETRY_AFTER_MS = 3_600_000;
|
|
9
|
+
/** Each backoff wait is multiplied by 1 - REOFFER_JITTER + 2 * REOFFER_JITTER * random(). */
|
|
10
|
+
export const REOFFER_JITTER = 0.2;
|
|
11
|
+
/** The backoff wait after `failures` failed attempts in a row (>= 1), for a given random() draw. */
|
|
12
|
+
export function reofferBackoffMs(failures, random) {
|
|
13
|
+
const base = Math.min(REOFFER_FIRST_BACKOFF_MS * 2 ** Math.max(0, failures - 1), REOFFER_MAX_BACKOFF_MS);
|
|
14
|
+
return Math.round(base * (1 - REOFFER_JITTER + 2 * REOFFER_JITTER * random));
|
|
15
|
+
}
|
|
16
|
+
const messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
17
|
+
const field = (body, key) => typeof body === 'object' && body !== null && !Array.isArray(body) ? body[key] : undefined;
|
|
18
|
+
export function createLedgerReoffer(deps) {
|
|
19
|
+
let running = null;
|
|
20
|
+
let failures = 0;
|
|
21
|
+
/** Monotonic time before which no attempt starts (after a failure). */
|
|
22
|
+
let readyAt = Number.NEGATIVE_INFINITY;
|
|
23
|
+
let cancelTimer = null;
|
|
24
|
+
let stopped = false;
|
|
25
|
+
/** The server said it does not know this account: no attempt before this monotonic time (unless a read finds the account). */
|
|
26
|
+
let pausedUntil = Number.NEGATIVE_INFINITY;
|
|
27
|
+
/** The last POST (any pair), for the global floor. */
|
|
28
|
+
let lastPostAt = Number.NEGATIVE_INFINITY;
|
|
29
|
+
/** The last POST was answered 2xx: the next read must no longer show the server behind, else that counts as a failure. */
|
|
30
|
+
let confirming = false;
|
|
31
|
+
/** (our head, server head) -> when it was last posted. */
|
|
32
|
+
const sent = new Map();
|
|
33
|
+
/** Local heads whose ledger_rejected answer was logged. */
|
|
34
|
+
const rejectedLogged = new Set();
|
|
35
|
+
const cancel = () => { cancelTimer?.(); cancelTimer = null; };
|
|
36
|
+
const paused = () => deps.now() < pausedUntil;
|
|
37
|
+
/** (Re)arms the one retry timer: an attempt `ms` from now, unless one is running then. */
|
|
38
|
+
const arm = (ms) => {
|
|
39
|
+
cancel();
|
|
40
|
+
if (stopped)
|
|
41
|
+
return;
|
|
42
|
+
cancelTimer = deps.schedule(() => { cancelTimer = null; if (running === null)
|
|
43
|
+
void start(); }, Math.max(0, ms));
|
|
44
|
+
};
|
|
45
|
+
const settled = (outcome) => {
|
|
46
|
+
failures = 0;
|
|
47
|
+
readyAt = Number.NEGATIVE_INFINITY;
|
|
48
|
+
cancel();
|
|
49
|
+
return outcome;
|
|
50
|
+
};
|
|
51
|
+
const failed = (reason, retryAfterMs = null, quiet = false) => {
|
|
52
|
+
failures += 1;
|
|
53
|
+
let wait = reofferBackoffMs(failures, deps.random());
|
|
54
|
+
if (retryAfterMs !== null && retryAfterMs > wait)
|
|
55
|
+
wait = Math.min(retryAfterMs, REOFFER_MAX_RETRY_AFTER_MS);
|
|
56
|
+
readyAt = deps.now() + wait;
|
|
57
|
+
// The first failure at each larger step is logged; once the wait stops growing, the bridge stays quiet.
|
|
58
|
+
const grew = failures === 1 || reofferBackoffMs(failures, 0.5) > reofferBackoffMs(failures - 1, 0.5);
|
|
59
|
+
if (!quiet && grew)
|
|
60
|
+
deps.log(`[ledger] could not send our account history to the server (${reason}); retrying in ${Math.round(wait / 1000)} s`);
|
|
61
|
+
arm(wait);
|
|
62
|
+
return 'failed';
|
|
63
|
+
};
|
|
64
|
+
const unknownAccount = () => {
|
|
65
|
+
if (!paused())
|
|
66
|
+
deps.log('[ledger] the server does not know this account (404); this computer will not send its account history to it for an hour');
|
|
67
|
+
pausedUntil = deps.now() + REOFFER_UNKNOWN_ACCOUNT_PAUSE_MS;
|
|
68
|
+
confirming = false;
|
|
69
|
+
failures = 0;
|
|
70
|
+
readyAt = Number.NEGATIVE_INFINITY;
|
|
71
|
+
cancel();
|
|
72
|
+
return 'stopped';
|
|
73
|
+
};
|
|
74
|
+
const offerQuietly = async (ledger) => {
|
|
75
|
+
try {
|
|
76
|
+
return await deps.offer(ledger);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const send = async (serverHead) => {
|
|
83
|
+
const local = await deps.local();
|
|
84
|
+
if (local === null)
|
|
85
|
+
return settled('not_behind');
|
|
86
|
+
const now = deps.now();
|
|
87
|
+
for (const [key, at] of sent)
|
|
88
|
+
if (now < at || now - at >= REOFFER_PAIR_WINDOW_MS)
|
|
89
|
+
sent.delete(key);
|
|
90
|
+
const pair = `${local.head}\n${serverHead}`;
|
|
91
|
+
const pairAt = sent.get(pair);
|
|
92
|
+
// Both floors, after the fresh read: this pair at most once per 30 s, and any POST at most once per 30 s.
|
|
93
|
+
const floorAt = now >= lastPostAt ? lastPostAt + REOFFER_MIN_POST_SPACING_MS : Number.NEGATIVE_INFINITY;
|
|
94
|
+
const until = Math.max(pairAt === undefined ? Number.NEGATIVE_INFINITY : pairAt + REOFFER_PAIR_WINDOW_MS, floorAt);
|
|
95
|
+
if (until > now) {
|
|
96
|
+
// A retry after a failure can come back before a floor ends (the first wait may be 24 s): try again when it does.
|
|
97
|
+
if (failures > 0)
|
|
98
|
+
arm(until - now);
|
|
99
|
+
return 'skipped';
|
|
100
|
+
}
|
|
101
|
+
sent.set(pair, now);
|
|
102
|
+
lastPostAt = now;
|
|
103
|
+
let answer;
|
|
104
|
+
try {
|
|
105
|
+
answer = await deps.post(local.ledger, serverHead);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return failed(messageOf(error));
|
|
109
|
+
}
|
|
110
|
+
if (stopped)
|
|
111
|
+
return 'stopped';
|
|
112
|
+
const { status, body } = answer;
|
|
113
|
+
const error = field(body, 'error');
|
|
114
|
+
const chain = field(body, 'ledger');
|
|
115
|
+
const answered = typeof chain === 'object' && chain !== null;
|
|
116
|
+
if (status >= 200 && status < 300) {
|
|
117
|
+
if (answered)
|
|
118
|
+
await offerQuietly(chain);
|
|
119
|
+
// Not settled yet: the next read must show the server holding our chain.
|
|
120
|
+
confirming = true;
|
|
121
|
+
return 'posted';
|
|
122
|
+
}
|
|
123
|
+
if (status === 404)
|
|
124
|
+
return unknownAccount();
|
|
125
|
+
if (status === 409 && error === 'head_changed') {
|
|
126
|
+
// Another write landed since our read: judge the server's chain; if it is still a prefix of ours, the retry reads and sends again.
|
|
127
|
+
const outcome = answered ? await offerQuietly(chain) : null;
|
|
128
|
+
if (outcome !== null && outcome.decision !== 'reject' && outcome.relation !== 'behind')
|
|
129
|
+
return settled('not_behind');
|
|
130
|
+
return failed('head_changed');
|
|
131
|
+
}
|
|
132
|
+
if (status === 409 && error === 'diverged') {
|
|
133
|
+
if (answered)
|
|
134
|
+
await offerQuietly(chain);
|
|
135
|
+
return failed('the server holds a different history');
|
|
136
|
+
}
|
|
137
|
+
if (status === 409 && error === 'ledger_rejected') {
|
|
138
|
+
const first = !rejectedLogged.has(local.head);
|
|
139
|
+
rejectedLogged.add(local.head);
|
|
140
|
+
if (first)
|
|
141
|
+
deps.log('[ledger] the server rejected our account history; retrying later');
|
|
142
|
+
return failed('ledger_rejected', null, true);
|
|
143
|
+
}
|
|
144
|
+
if (status === 429)
|
|
145
|
+
return failed('rate limited', answer.retryAfterMs);
|
|
146
|
+
return failed(`HTTP ${status}`);
|
|
147
|
+
};
|
|
148
|
+
const attempt = async () => {
|
|
149
|
+
if (stopped)
|
|
150
|
+
return 'stopped';
|
|
151
|
+
if (paused())
|
|
152
|
+
return 'stopped';
|
|
153
|
+
const read = await deps.read();
|
|
154
|
+
if (stopped)
|
|
155
|
+
return 'stopped';
|
|
156
|
+
if (read.kind === 'unknown_account')
|
|
157
|
+
return unknownAccount();
|
|
158
|
+
if (read.kind === 'failed')
|
|
159
|
+
return failed(`read failed: ${read.reason}`);
|
|
160
|
+
// The account is there again.
|
|
161
|
+
pausedUntil = Number.NEGATIVE_INFINITY;
|
|
162
|
+
const { relation, head } = read.outcome;
|
|
163
|
+
const wasConfirming = confirming;
|
|
164
|
+
confirming = false;
|
|
165
|
+
if (relation !== 'behind' || head === null)
|
|
166
|
+
return settled('not_behind');
|
|
167
|
+
// The server answered 2xx to our last POST, yet still lacks our records: a hostile or broken server, not progress.
|
|
168
|
+
if (wasConfirming)
|
|
169
|
+
return failed('the server accepted our history but still lacks it');
|
|
170
|
+
return send(head);
|
|
171
|
+
};
|
|
172
|
+
const start = () => {
|
|
173
|
+
const run = attempt().catch((error) => failed(messageOf(error)));
|
|
174
|
+
running = run.finally(() => { running = null; });
|
|
175
|
+
return running;
|
|
176
|
+
};
|
|
177
|
+
return {
|
|
178
|
+
noteServerBehind() {
|
|
179
|
+
if (stopped)
|
|
180
|
+
return;
|
|
181
|
+
// The caller's read found the account: a 404 pause is over.
|
|
182
|
+
pausedUntil = Number.NEGATIVE_INFINITY;
|
|
183
|
+
if (running !== null || cancelTimer !== null)
|
|
184
|
+
return;
|
|
185
|
+
const wait = readyAt - deps.now();
|
|
186
|
+
if (wait > 0)
|
|
187
|
+
arm(wait);
|
|
188
|
+
else
|
|
189
|
+
void start();
|
|
190
|
+
},
|
|
191
|
+
check() {
|
|
192
|
+
if (stopped || paused())
|
|
193
|
+
return Promise.resolve('stopped');
|
|
194
|
+
if (running !== null)
|
|
195
|
+
return running;
|
|
196
|
+
if (readyAt - deps.now() > 0) {
|
|
197
|
+
if (cancelTimer === null)
|
|
198
|
+
arm(readyAt - deps.now());
|
|
199
|
+
return Promise.resolve('deferred');
|
|
200
|
+
}
|
|
201
|
+
cancel();
|
|
202
|
+
return start();
|
|
203
|
+
},
|
|
204
|
+
stop() {
|
|
205
|
+
stopped = true;
|
|
206
|
+
cancel();
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=ledger-reoffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-reoffer.js","sourceRoot":"","sources":["../src/ledger-reoffer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
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 PAIRING_REVOKED_MESSAGE: string;
|
|
10
|
+
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.";
|
|
11
|
+
export declare const UPDATE_RESTART_UNPAIRED_MESSAGE = "[Bridge] An update restart cannot pair this computer. Run `ftown start` to pair it.";
|
|
12
|
+
export declare const UPDATED_TO_LEDGER_MESSAGE = "[Bridge] ftown was updated to the passkey ledger; run `ftown start` to pair this computer.";
|
|
13
|
+
export declare const DO_NOT_RESET_LINE = "[Bridge] Do NOT run --new-identity: this computer may hold the only copy. Open Security in the dashboard to choose a history.";
|
|
14
|
+
export declare const HELD_OFFLINE_LINE = "[Bridge] Starting offline: local sessions keep working, and the relay is retried in the background.";
|
|
15
|
+
export declare const HELD_NO_RELAY_URL_LINE = "[Bridge] No relay URL was saved by an earlier start, so this computer cannot start offline. Run `ftown start` again later.";
|
|
16
|
+
/** What this computer holds that the server lacks (BridgeSecurityService.uniqueHistory), for the held-history notice. */
|
|
17
|
+
export interface HeldHistory {
|
|
18
|
+
afterRevision: number;
|
|
19
|
+
records: number;
|
|
20
|
+
removals: number;
|
|
21
|
+
passkeys: number;
|
|
22
|
+
}
|
|
23
|
+
/** Printed once when the server refuses the key of a computer that is an active member of its own history (step 5): never reset advice. */
|
|
24
|
+
export declare function heldHistoryNotice(unique: HeldHistory | null): string[];
|
|
25
|
+
/** Only account bridges use the passkey ledger (legacy reset, pairing, ledger replica, relay token by proof). */
|
|
26
|
+
export declare function usesLedger(mode: BridgeMode): boolean;
|
|
27
|
+
export interface IdentityResetDeps {
|
|
28
|
+
hasLegacyAccountState: (dataDir: string) => boolean;
|
|
29
|
+
beginIdentityReset: (dataDir: string, reason: IdentityResetReason) => void;
|
|
30
|
+
completePendingIdentityReset: (dataDir: string) => IdentityResetResult | null;
|
|
31
|
+
}
|
|
32
|
+
export type IdentityResetOutcome = {
|
|
33
|
+
kind: 'continue';
|
|
34
|
+
reset: IdentityResetResult | null;
|
|
35
|
+
}
|
|
36
|
+
/** Exit 1 after printing `message`; nothing was changed on disk. */
|
|
37
|
+
| {
|
|
38
|
+
kind: 'exit';
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Step 2. Account mode: `--new-identity`, or any v1 account state on disk, starts a manual reset (the latter says so in one line).
|
|
43
|
+
* An update restart (a v1 -> v2 auto-update) has no visible terminal for the pairing link: with v1 state on disk it changes nothing
|
|
44
|
+
* and exits, and the next `ftown start` resets and pairs. Every mode then finishes a pending reset, as before (a revoked restart or a
|
|
45
|
+
* crash may have left one).
|
|
46
|
+
*/
|
|
47
|
+
export declare function resetIdentityForStart(input: {
|
|
48
|
+
dataDir: string;
|
|
49
|
+
mode: BridgeMode;
|
|
50
|
+
newIdentity: boolean;
|
|
51
|
+
updateRestart: boolean;
|
|
52
|
+
log: (line: string) => void;
|
|
53
|
+
}, deps?: Partial<IdentityResetDeps>): IdentityResetOutcome;
|
|
54
|
+
export type OwnStatusAction = 'continue' | 'revoked_restart' | 'exit_absent';
|
|
55
|
+
/** Step 4. Only a signed revocation resets the identity; a bridge the ledger does not list fails closed and is never reset. */
|
|
56
|
+
export declare function ownStatusAction(own: OwnBridgeStatus): OwnStatusAction;
|
|
57
|
+
/** A ledger identity bound to its account: endpoint and accountId are set. */
|
|
58
|
+
export type BoundLedgerIdentity = LedgerBridgeIdentity & {
|
|
59
|
+
endpoint: EndpointPublic;
|
|
60
|
+
accountId: string;
|
|
61
|
+
};
|
|
62
|
+
export type AccountStartup = {
|
|
63
|
+
kind: 'ready';
|
|
64
|
+
identity: BoundLedgerIdentity;
|
|
65
|
+
store: BridgeLedgerStore;
|
|
66
|
+
reconciled: ReconcileResult;
|
|
67
|
+
} | {
|
|
68
|
+
kind: 'revoked';
|
|
69
|
+
}
|
|
70
|
+
/** Exit 1 after printing `message` (null: the cause was already printed). */
|
|
71
|
+
| {
|
|
72
|
+
kind: 'exit';
|
|
73
|
+
message: string | null;
|
|
74
|
+
};
|
|
75
|
+
export interface AccountStartupDeps {
|
|
76
|
+
dataDir: string;
|
|
77
|
+
bridgeId: string;
|
|
78
|
+
/** False during an update restart: it must come back with its existing identity and never pair. */
|
|
79
|
+
allowPairing: boolean;
|
|
80
|
+
/** One pairing attempt (runLedgerPairing) with the unbound identity and the empty store; binds both on success. Throws
|
|
81
|
+
* Error('pairing_expired') (its hint already printed), Error('pairing_revoked') (the account already revokes this bridge id) or
|
|
82
|
+
* Error('pairing_rejected'). */
|
|
83
|
+
pair: (identity: LedgerBridgeIdentity, store: BridgeLedgerStore) => Promise<unknown>;
|
|
84
|
+
/** The server's ledger for this account (GET /api/account/ledger?accountId=…, bounded). */
|
|
85
|
+
fetchLedger: (accountId: string) => Promise<unknown>;
|
|
86
|
+
/** Injectable for tests. */
|
|
87
|
+
loadOrCreateIdentity?: (dataDir: string) => Promise<LedgerBridgeIdentity>;
|
|
88
|
+
reconcile?: (dataDir: string) => Promise<ReconcileResult>;
|
|
89
|
+
readIdentity?: (dataDir: string) => Promise<LedgerBridgeIdentity | null>;
|
|
90
|
+
}
|
|
91
|
+
export declare function ledgerUnavailableMessage(reason: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* Steps 3-4, account mode only, before any network use of the identity:
|
|
94
|
+
* - keys exist (created when absent), then identity and replica are reconciled;
|
|
95
|
+
* - 'unpaired' -> one pairing attempt (expired/rejected -> exit 1 with the hint);
|
|
96
|
+
* - 'needs_ledger' -> the server ledger is adopted only through adoptBound (any failure -> exit 1; the identity stays bound);
|
|
97
|
+
* - own status from the trusted replica: 'revoked' -> revoked restart; 'absent' -> exit 1 (never a reset); 'active' -> ready.
|
|
98
|
+
* Corrupted local files throw (they are never reset).
|
|
99
|
+
*/
|
|
100
|
+
export declare function startAccountIdentity(deps: AccountStartupDeps): Promise<AccountStartup>;
|
|
101
|
+
export type RelayStartup = {
|
|
102
|
+
kind: 'revoked';
|
|
103
|
+
}
|
|
104
|
+
/** Exit 1 after printing `message`. */
|
|
105
|
+
| {
|
|
106
|
+
kind: 'exit';
|
|
107
|
+
message: string;
|
|
108
|
+
} | {
|
|
109
|
+
kind: 'online';
|
|
110
|
+
token: string;
|
|
111
|
+
relayUrl: string;
|
|
112
|
+
}
|
|
113
|
+
/** The server is unreachable for now, or refused a computer that may hold the only copy of its history (`notice`, printed once instead
|
|
114
|
+
* of the unreachable line): start with the saved relay URL and keep retrying in the background. */
|
|
115
|
+
| {
|
|
116
|
+
kind: 'offline';
|
|
117
|
+
relayUrl: string;
|
|
118
|
+
reason: string;
|
|
119
|
+
notice?: string[];
|
|
120
|
+
};
|
|
121
|
+
export declare function offlineWithoutRelayUrlMessage(reason: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* Step 5, the first relay token. A proven revocation (the token hooks offer the server's evidence first) wins over everything. A refusal
|
|
124
|
+
* (401/403) of a computer that is still an active member of its own history (`held` returns the notice: a divergence with the server, a
|
|
125
|
+
* server behind us, a failed re-offer, or any refusal without proof of our removal) starts OFFLINE and prints the notice once, never the
|
|
126
|
+
* reset advice: this computer may hold the only copy. Any other refusal (this computer is not an active member: unpaired/absent) exits
|
|
127
|
+
* with the reset advice. A temporary failure (network, timeout, 5xx, cooldown) starts offline with the saved relay URL. Without a saved
|
|
128
|
+
* relay URL there is no offline start: exit. After a successful token the revocation is checked again (the token response's ledger may
|
|
129
|
+
* prove it).
|
|
130
|
+
*/
|
|
131
|
+
export declare function startRelay(input: {
|
|
132
|
+
getToken: () => Promise<string>;
|
|
133
|
+
relayUrl: () => string | null;
|
|
134
|
+
bridgeRevoked: () => boolean;
|
|
135
|
+
apiUrl: string;
|
|
136
|
+
/** The held-history notice when this computer is an active member of its own history (heldHistoryNotice), else null. */
|
|
137
|
+
held?: () => string[] | null;
|
|
138
|
+
}): Promise<RelayStartup>;
|
|
139
|
+
export declare function endpointRefusedMessage(reason: string): string;
|
|
140
|
+
export type RegistrationAttempt = {
|
|
141
|
+
kind: 'registered';
|
|
142
|
+
} | {
|
|
143
|
+
kind: 'retry';
|
|
144
|
+
} | {
|
|
145
|
+
kind: 'revoked';
|
|
146
|
+
} | {
|
|
147
|
+
kind: 'exit';
|
|
148
|
+
message: string;
|
|
149
|
+
};
|
|
150
|
+
export interface EndpointRegistrarDeps {
|
|
151
|
+
/** PUT /api/security/endpoints with this bridge's own ledger endpoint (registerBridgeEndpoint). */
|
|
152
|
+
register: (token: string) => Promise<void>;
|
|
153
|
+
tokens: {
|
|
154
|
+
getToken(): Promise<string>;
|
|
155
|
+
cooldownRemainingMs(): number;
|
|
156
|
+
};
|
|
157
|
+
bridgeRevoked: () => boolean;
|
|
158
|
+
/** This computer is an active member of its own history: a refusal is then not final (it may hold the only copy; the startup notice
|
|
159
|
+
* said so): no reset advice, keep retrying with the backoff until the server accepts the key again. */
|
|
160
|
+
held?: () => boolean;
|
|
161
|
+
log: (line: string) => void;
|
|
162
|
+
/** Returns a cancel function. Defaults to an unref'd setTimeout. */
|
|
163
|
+
schedule?: (run: () => void, ms: number) => () => void;
|
|
164
|
+
/** First background delay (default 15 s), doubling to maxDelayMs (default 10 min); never earlier than the token cooldown. */
|
|
165
|
+
baseDelayMs?: number;
|
|
166
|
+
maxDelayMs?: number;
|
|
167
|
+
}
|
|
168
|
+
export interface EndpointRegistrar {
|
|
169
|
+
/** Step 6 at an online start, with the first token: a refusal exits, a temporary failure is retried in the background. */
|
|
170
|
+
registerNow(token: string): Promise<RegistrationAttempt>;
|
|
171
|
+
/** Retries getToken() + registration in the background until it succeeds or is refused, honouring the token cooldown. */
|
|
172
|
+
retryInBackground(): void;
|
|
173
|
+
registered(): boolean;
|
|
174
|
+
stop(): void;
|
|
175
|
+
}
|
|
176
|
+
export declare function createEndpointRegistrar(deps: EndpointRegistrarDeps): EndpointRegistrar;
|