@solarpunkltd/swarm-id 0.0.0-stage → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +89 -2
- package/dist/bus/account-bus.d.ts +104 -0
- package/dist/bus/account-delta.d.ts +52 -0
- package/dist/bus/bus-context.d.ts +7 -0
- package/dist/bus/envelope.d.ts +9 -0
- package/dist/bus/messages.d.ts +241 -0
- package/dist/bus/presence.d.ts +62 -0
- package/dist/bus/signaling-transport.d.ts +79 -0
- package/dist/chunk/bmt.d.ts +16 -0
- package/dist/chunk/cac.d.ts +17 -0
- package/dist/chunk/constants.d.ts +10 -0
- package/dist/chunk/encrypted-cac.d.ts +47 -0
- package/dist/chunk/encryption.d.ts +85 -0
- package/dist/chunk/index.d.ts +5 -0
- package/dist/chunks/chat-binding-DO5aGhPl.js +23 -0
- package/dist/chunks/chat-binding-DO5aGhPl.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/index.d.ts +27 -0
- package/dist/internal.d.ts +49 -0
- package/dist/internal.esm.js +2 -0
- package/dist/internal.esm.js.map +1 -0
- package/dist/proxy/act/act.d.ts +58 -0
- package/dist/proxy/act/crypto.d.ts +46 -0
- package/dist/proxy/act/grantee-list.d.ts +81 -0
- package/dist/proxy/act/history.d.ts +182 -0
- package/dist/proxy/act/index.d.ts +114 -0
- package/dist/proxy/app-stamp-limits.d.ts +62 -0
- package/dist/proxy/chunk-upload-stream.d.ts +37 -0
- package/dist/proxy/chunking.d.ts +15 -0
- package/dist/proxy/collection.d.ts +38 -0
- package/dist/proxy/download-data.d.ts +27 -0
- package/dist/proxy/errors.d.ts +8 -0
- package/dist/proxy/feed-manifest.d.ts +60 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts +70 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts +79 -0
- package/dist/proxy/feeds/epochs/finder.d.ts +60 -0
- package/dist/proxy/feeds/epochs/index.d.ts +30 -0
- package/dist/proxy/feeds/epochs/types.d.ts +110 -0
- package/dist/proxy/feeds/epochs/updater.d.ts +76 -0
- package/dist/proxy/feeds/epochs/utils.d.ts +21 -0
- package/dist/proxy/feeds/index.d.ts +4 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
- package/dist/proxy/feeds/sequence/finder.d.ts +11 -0
- package/dist/proxy/feeds/sequence/index.d.ts +22 -0
- package/dist/proxy/feeds/sequence/types.d.ts +82 -0
- package/dist/proxy/feeds/sequence/updater.d.ts +20 -0
- package/dist/proxy/index.d.ts +5 -0
- package/dist/proxy/manifest-builder.d.ts +164 -0
- package/dist/proxy/mantaray.d.ts +76 -0
- package/dist/proxy/soc-restamp-slots.d.ts +48 -0
- package/dist/proxy/stamp-chunk.d.ts +45 -0
- package/dist/proxy/stamp-worker-handler.d.ts +32 -0
- package/dist/proxy/stamp-worker-pool.d.ts +52 -0
- package/dist/proxy/stamp-worker.d.ts +1 -0
- package/dist/proxy/types.d.ts +38 -0
- package/dist/proxy/upload.d.ts +207 -0
- package/dist/schemas.d.ts +671 -0
- package/dist/stamp-worker.iife.js +1 -0
- package/dist/storage/debounced-uploader.d.ts +61 -0
- package/dist/storage/utilization-store.d.ts +113 -0
- package/dist/swarm-id-client.d.ts +1143 -0
- package/dist/swarm-id-proxy.d.ts +1079 -0
- package/dist/swarm-id.esm.js +2 -0
- package/dist/swarm-id.esm.js.map +1 -0
- package/dist/sync/batch-write-coordinator.d.ts +348 -0
- package/dist/sync/device-roster.d.ts +59 -0
- package/dist/sync/device-state.d.ts +199 -0
- package/dist/sync/fold-account-from-swarm.d.ts +53 -0
- package/dist/sync/index.d.ts +11 -0
- package/dist/sync/merge-snapshot.d.ts +57 -0
- package/dist/sync/partition-intent.d.ts +218 -0
- package/dist/sync/partition-lease.d.ts +531 -0
- package/dist/sync/partition-lock.d.ts +218 -0
- package/dist/sync/partition-state.d.ts +287 -0
- package/dist/sync/session-check.d.ts +29 -0
- package/dist/sync/store-interfaces.d.ts +48 -0
- package/dist/sync/sync-account.d.ts +45 -0
- package/dist/sync/timing-constants.d.ts +32 -0
- package/dist/sync/types.d.ts +18 -0
- package/dist/types.d.ts +3042 -0
- package/dist/utils/account-state-snapshot.d.ts +43 -0
- package/dist/utils/active-devices.d.ts +45 -0
- package/dist/utils/avatar.d.ts +30 -0
- package/dist/utils/batch-utilization.d.ts +840 -0
- package/dist/utils/batch-write-lock.d.ts +12 -0
- package/dist/utils/bee-error.d.ts +11 -0
- package/dist/utils/chat-binding.d.ts +113 -0
- package/dist/utils/coalesced-task.d.ts +28 -0
- package/dist/utils/collection.d.ts +6 -0
- package/dist/utils/connection-info.d.ts +11 -0
- package/dist/utils/constants.d.ts +28 -0
- package/dist/utils/device-id.d.ts +109 -0
- package/dist/utils/hex.d.ts +31 -0
- package/dist/utils/json-rpc.d.ts +34 -0
- package/dist/utils/key-derivation.d.ts +124 -0
- package/dist/utils/lock-soc.d.ts +35 -0
- package/dist/utils/postage-contract.d.ts +143 -0
- package/dist/utils/postage-stamp-association.d.ts +39 -0
- package/dist/utils/promise.d.ts +37 -0
- package/dist/utils/sealed-values.d.ts +23 -0
- package/dist/utils/seed-encryption.d.ts +17 -0
- package/dist/utils/site-allow-list.d.ts +54 -0
- package/dist/utils/stamp-lifespan.d.ts +37 -0
- package/dist/utils/storage-managers.d.ts +62 -0
- package/dist/utils/storage-probe.d.ts +40 -0
- package/dist/utils/tag.d.ts +11 -0
- package/dist/utils/ttl.d.ts +120 -0
- package/dist/utils/url.d.ts +55 -0
- package/dist/utils/versioned-storage.d.ts +183 -0
- package/package.json +91 -3
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
import type { SiteAllowList } from "./utils/site-allow-list";
|
|
2
|
+
/**
|
|
3
|
+
* Debounce window for proxy-side account-state publishing. Coalesces a burst of
|
|
4
|
+
* triggers (lease acquisition + storage changes) into a single feed write.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PUBLISH_DEBOUNCE_MS = 1500;
|
|
7
|
+
/**
|
|
8
|
+
* Swarm ID Proxy - Runs inside the iframe
|
|
9
|
+
*
|
|
10
|
+
* Responsibilities:
|
|
11
|
+
* - Receive app-specific secrets from auth popup
|
|
12
|
+
* - Keep a partitioned session in this partition's own localStorage (#635)
|
|
13
|
+
* - Proxy Bee API calls from parent dApp
|
|
14
|
+
* - Augment requests with authentication
|
|
15
|
+
* - Return responses to parent dApp
|
|
16
|
+
*/
|
|
17
|
+
export declare class SwarmIdProxy {
|
|
18
|
+
private parentOrigin;
|
|
19
|
+
private parentIdentified;
|
|
20
|
+
/**
|
|
21
|
+
* Set by the first `parentIdentify`, before its allow-list check awaits, so a
|
|
22
|
+
* second one cannot start a check of its own. A refused parent leaves it set:
|
|
23
|
+
* the frame answers nothing more until the page loads it again.
|
|
24
|
+
*/
|
|
25
|
+
private parentIdentifyReceived;
|
|
26
|
+
private siteAllowList;
|
|
27
|
+
private authenticated;
|
|
28
|
+
private authLoading;
|
|
29
|
+
private appSecret;
|
|
30
|
+
/**
|
|
31
|
+
* The chat binding the popup handed over with this session, already checked
|
|
32
|
+
* against it (`acceptHandedOverChatBinding`). Only a handover sets it: an
|
|
33
|
+
* unpartitioned session reads its connection out of shared storage and
|
|
34
|
+
* reports none.
|
|
35
|
+
*/
|
|
36
|
+
private chatBinding;
|
|
37
|
+
private postageBatchId;
|
|
38
|
+
private signerKey;
|
|
39
|
+
private stamper;
|
|
40
|
+
/**
|
|
41
|
+
* Hash of `<owner>-<encryptionKey>` that the current `stamper` was built
|
|
42
|
+
* with. Account-level inputs are baked into `UtilizationAwareStamper` at
|
|
43
|
+
* construction, so when the underlying account changes (e.g. a local
|
|
44
|
+
* account migrates to a synced one) we need to detect that even if the
|
|
45
|
+
* postage stamp's batch/signer didn't change, and rebuild the stamper.
|
|
46
|
+
*/
|
|
47
|
+
private stamperAccountFingerprint;
|
|
48
|
+
private stampWorkerPool;
|
|
49
|
+
private storagePartitioned;
|
|
50
|
+
private pendingChallenge;
|
|
51
|
+
/**
|
|
52
|
+
* Hydrated account view for the storage-partitioning fallback: the synced
|
|
53
|
+
* projection handed over by the connect popup (`AuthData.account`), held in
|
|
54
|
+
* memory only — the stored-account schema requires a vault. What survives a
|
|
55
|
+
* reload is the handover itself, under its own key (#635), which this view
|
|
56
|
+
* is then rebuilt from. When set, the proxy is a
|
|
57
|
+
* first-class writer despite the partition (docs/Account-Bus.md, phase 3).
|
|
58
|
+
*/
|
|
59
|
+
private partitionAccount;
|
|
60
|
+
/** Account-derived topic the attached bus transports share; undefined when
|
|
61
|
+
* none are attached. */
|
|
62
|
+
private busTopic;
|
|
63
|
+
private removeBusLocal;
|
|
64
|
+
private removeBusSignaling;
|
|
65
|
+
/** Derivation key of a join that attached everything it wanted. Lets the
|
|
66
|
+
* synchronous `joinAccountBus` return without re-deriving, and — because it
|
|
67
|
+
* stays unset when a transport failed to attach — makes the next join a
|
|
68
|
+
* RETRY rather than a dedup'd no-op. */
|
|
69
|
+
private busJoinedKey;
|
|
70
|
+
/** Bumped by every bus join and by `clearAuthData`, so a join whose key
|
|
71
|
+
* derivation is still in flight can tell it has been superseded. */
|
|
72
|
+
private busJoinGeneration;
|
|
73
|
+
/** Account whose room the attached transports currently speak into. A
|
|
74
|
+
* transport encrypts with its construction-time key, so this — not whichever
|
|
75
|
+
* connection resolves now — is who a publish actually reaches. */
|
|
76
|
+
private busBoundAccountId;
|
|
77
|
+
/** Scheduled answers to peers' `lease-request`s, by request id, so a peer
|
|
78
|
+
* earlier in the rank order can call ours off (`yieldRankDelayMs`). */
|
|
79
|
+
private pendingYields;
|
|
80
|
+
/** Request ids somebody has claimed (us or a peer). Insertion-ordered and
|
|
81
|
+
* capped at `ANSWERED_REQUEST_MEMORY`. Separate from `pendingYields`
|
|
82
|
+
* because the cancel window has to outlive the rank timer: the timer only
|
|
83
|
+
* starts the yield, whose first Swarm write is still ahead of it. */
|
|
84
|
+
private answeredRequests;
|
|
85
|
+
/**
|
|
86
|
+
* Utilization deltas that arrived while the stamper had no lane, held until
|
|
87
|
+
* a bind names one (`applyPendingLaneUpdate`). Keyed by lane so a delta for
|
|
88
|
+
* a lane we do not end up binding cannot displace the one we do — the point
|
|
89
|
+
* of buffering is that nothing is silently lost. Buckets merge per index
|
|
90
|
+
* with `max`, matching `applyUtilizationUpdate`'s own monotonic rule.
|
|
91
|
+
* Bounded by the account's partition count.
|
|
92
|
+
*/
|
|
93
|
+
private pendingLaneUpdates;
|
|
94
|
+
/**
|
|
95
|
+
* The newest timestamp this frame gave a stamp again of a single-owner
|
|
96
|
+
* chunk, in milliseconds, by site, stamp and chunk address. Set under the
|
|
97
|
+
* write lock when the stamp is made, while the stored slot record is
|
|
98
|
+
* written only after the lock, so a second stamp of that address inside
|
|
99
|
+
* the lock still gets a newer timestamp than the first.
|
|
100
|
+
*/
|
|
101
|
+
private lastRestampMs;
|
|
102
|
+
private utilizationStore;
|
|
103
|
+
/** The Bee node the person set in Swarm ID's network settings. */
|
|
104
|
+
private ownBeeApiUrl;
|
|
105
|
+
/**
|
|
106
|
+
* The Bee node the embedding site named for its own session, from
|
|
107
|
+
* `parentIdentify` or `setBeeApiUrl`. In memory only: a frame
|
|
108
|
+
* serves the one parent origin it was identified by, so this is that
|
|
109
|
+
* site's alone, and nothing writes it to storage, the account or a sync
|
|
110
|
+
* feed. The site sends it again on every load.
|
|
111
|
+
*/
|
|
112
|
+
private siteBeeApiUrl;
|
|
113
|
+
/**
|
|
114
|
+
* The subsidised gateway the site asked for, kept apart from
|
|
115
|
+
* `subsidisedGatewayUrl`, which the person's own custom node can switch
|
|
116
|
+
* off, so the gateway can come back once the site names its own node.
|
|
117
|
+
*/
|
|
118
|
+
private siteSubsidisedGatewayUrl;
|
|
119
|
+
private gnosisRpcUrl;
|
|
120
|
+
private postageStampContractAddress;
|
|
121
|
+
private signalingUrl;
|
|
122
|
+
private authButtonContainer;
|
|
123
|
+
private buttonConfig;
|
|
124
|
+
private popupMode;
|
|
125
|
+
private appMetadata;
|
|
126
|
+
/**
|
|
127
|
+
* Bee clients by the URL they were asked for. Keyed on that input URL, not
|
|
128
|
+
* on the client's own `url`, which bee-js keeps without the trailing slash.
|
|
129
|
+
*/
|
|
130
|
+
private beeClients;
|
|
131
|
+
private unsubscribeStorageListeners;
|
|
132
|
+
private storageWorkQueue;
|
|
133
|
+
private lastConnectionInfo;
|
|
134
|
+
private isConnecting;
|
|
135
|
+
private parentWindow;
|
|
136
|
+
private bus;
|
|
137
|
+
/** Who else is live in the account's room, from their `presence` beats. */
|
|
138
|
+
private presence;
|
|
139
|
+
private presenceTimer;
|
|
140
|
+
private subsidisedGatewayUrl;
|
|
141
|
+
/**
|
|
142
|
+
* The resolved stamp's stored lifetime fields (#745), set wherever the
|
|
143
|
+
* stamp is. Expiry is derived from them on every read, so a tab left open
|
|
144
|
+
* past the drive's lifetime refuses its own writes rather than letting Bee
|
|
145
|
+
* refuse the lease claim.
|
|
146
|
+
*/
|
|
147
|
+
private stampLifetime;
|
|
148
|
+
/** Bumped by every `initializeStamper`; an older init sees it move and stops */
|
|
149
|
+
private stamperInitGeneration;
|
|
150
|
+
/** Why the session refuses to stamp with the resolved drive, if it does. */
|
|
151
|
+
private get stampRefusal();
|
|
152
|
+
/**
|
|
153
|
+
* The node the site's own content goes through: the site's address when it
|
|
154
|
+
* named one, the person's setting otherwise. Content only, the reads and
|
|
155
|
+
* stamped uploads the site asks for. Nothing that decides whether this
|
|
156
|
+
* session may write, or that coordinates the person's devices, reads it
|
|
157
|
+
* (`accountBee`), because a site could answer those from a node of its own.
|
|
158
|
+
*/
|
|
159
|
+
private get beeApiUrl();
|
|
160
|
+
/**
|
|
161
|
+
* Switch off the site's subsidised gateway when the node in effect is the
|
|
162
|
+
* person's own custom one, so their uploads go to the node they chose. A
|
|
163
|
+
* node the site named is not the person's choice, and the site asked for
|
|
164
|
+
* both, so it keeps the gateway.
|
|
165
|
+
*/
|
|
166
|
+
private dropGatewayForOwnCustomNode;
|
|
167
|
+
/**
|
|
168
|
+
* The client for the node the current mode writes to: the dApp's gateway
|
|
169
|
+
* while the session is subsidised, the configured node otherwise. Reads have
|
|
170
|
+
* to come from where the uploads go, or a dApp reads back a 404 for the
|
|
171
|
+
* chunk it just wrote.
|
|
172
|
+
*
|
|
173
|
+
* Derived on every read rather than assigned, because a mode change is not
|
|
174
|
+
* always an event — a drive's expiry is read off the clock — and a client
|
|
175
|
+
* picked once keeps pointing wherever the session started: at the gateway
|
|
176
|
+
* for a session that connected after the page loaded, or at the node for
|
|
177
|
+
* one whose drive has since run out or been removed.
|
|
178
|
+
*/
|
|
179
|
+
private get bee();
|
|
180
|
+
/**
|
|
181
|
+
* The client for the site's content node, whatever the mode: where the
|
|
182
|
+
* site's stamped uploads go in user-stamp mode, which `bee` also reads from
|
|
183
|
+
* then.
|
|
184
|
+
*/
|
|
185
|
+
private get nodeBee();
|
|
186
|
+
/**
|
|
187
|
+
* The client for the person's own node, whatever the site named. The
|
|
188
|
+
* coordinator's lock, lease and state chunks, the device roster and the
|
|
189
|
+
* restored session's revocation check go through it: they decide what this
|
|
190
|
+
* session may do with the person's account and stamp, so the embedding
|
|
191
|
+
* site must not be able to answer them.
|
|
192
|
+
*/
|
|
193
|
+
private get accountBee();
|
|
194
|
+
/**
|
|
195
|
+
* A client for `url`, kept per URL so the content, account and gateway
|
|
196
|
+
* clients do not rebuild each other. Bounded, because a site can name a
|
|
197
|
+
* new address at any time.
|
|
198
|
+
*/
|
|
199
|
+
private beeClientFor;
|
|
200
|
+
/**
|
|
201
|
+
* The write path (lock + partition lease + stamp flush) for the current
|
|
202
|
+
* account+batch. Assigned in `initializeStamper` together with the stamper it
|
|
203
|
+
* writes through, and dropped with it whenever the resolved stamp changes
|
|
204
|
+
* (`setResolvedStamp`); undefined before auth / after disconnect. The proxy
|
|
205
|
+
* delegates all writes to it via `withModeAwareWriteLock`.
|
|
206
|
+
*/
|
|
207
|
+
private coordinator;
|
|
208
|
+
/**
|
|
209
|
+
* Debounce timer + in-flight guard for proxy-side account-state publishing.
|
|
210
|
+
* The proxy publishes the account snapshot to the shared sync feed when it
|
|
211
|
+
* first acquires a partition (so the device announces itself — see #336
|
|
212
|
+
* motivation #4) and when account state changes while a lease is held.
|
|
213
|
+
*/
|
|
214
|
+
private publishTimer;
|
|
215
|
+
private publishInFlight;
|
|
216
|
+
/**
|
|
217
|
+
* Why the next debounced publish was scheduled. `"acquired"` (announce on
|
|
218
|
+
* lease acquisition) is gated on this device not yet being in the feed, so a
|
|
219
|
+
* plain reload doesn't republish. `"change"` (account-state delta) always
|
|
220
|
+
* publishes. `"change"` dominates a coalesced burst — a real delta must never
|
|
221
|
+
* be skipped just because an acquisition was also pending.
|
|
222
|
+
*/
|
|
223
|
+
private publishReason;
|
|
224
|
+
/**
|
|
225
|
+
* This device's identity, captured ONCE at authentication and reused for
|
|
226
|
+
* every lease operation. Never re-read from `getOrCreateDeviceId()` mid-
|
|
227
|
+
* session, so acquire / refresh / validate always agree on one identity.
|
|
228
|
+
*/
|
|
229
|
+
private deviceId;
|
|
230
|
+
/**
|
|
231
|
+
* A session this load restored from storage, rather than one the person
|
|
232
|
+
* connected in it, waits on this before its first write, so a session ended
|
|
233
|
+
* while no page of the site was open cannot write.
|
|
234
|
+
* Unset for a fresh connection. Kept through `clearAuthData`, so a write
|
|
235
|
+
* that was waiting when the check ended the session is told why.
|
|
236
|
+
*/
|
|
237
|
+
private restoredSessionCheck;
|
|
238
|
+
constructor(config: ProxyConfig);
|
|
239
|
+
/**
|
|
240
|
+
* Subscribe to shared localStorage changes that can affect the dApp-visible
|
|
241
|
+
* ConnectionInfo (auth state, identity, postage stamps, account type).
|
|
242
|
+
*
|
|
243
|
+
* - `connectedApps` drives auth transitions (new connection / identity switch /
|
|
244
|
+
* disconnect) and triggers `authSuccess` / `disconnectResponse` to the parent.
|
|
245
|
+
* - `identities`, `accounts`, `postageStamps` may change the derived
|
|
246
|
+
* ConnectionInfo without flipping auth state (e.g. rename, default-stamp
|
|
247
|
+
* change, local→synced migration). On change we re-derive ConnectionInfo
|
|
248
|
+
* and emit `connectionInfoChanged` if it actually differs.
|
|
249
|
+
*
|
|
250
|
+
* Note: We always set up these listeners, even when storage might be partitioned.
|
|
251
|
+
* In some browsers/configurations (like localhost development), storage events
|
|
252
|
+
* work between same-origin windows even in iframes. If storage IS partitioned,
|
|
253
|
+
* the listeners simply won't fire, and we fall back to postMessage from the popup.
|
|
254
|
+
*/
|
|
255
|
+
private setupStorageListeners;
|
|
256
|
+
/**
|
|
257
|
+
* Queue a reconcile behind whatever reconcile is already running.
|
|
258
|
+
*
|
|
259
|
+
* `refreshStampFromStorage` / `initializeStamper` mutate shared
|
|
260
|
+
* `stamper` / `postageBatchId` state across awaits, so two of them in flight
|
|
261
|
+
* at once emit inconsistent ConnectionInfo. ONE queue for every source: a
|
|
262
|
+
* storage event and an `account-delta` fold are the same work arriving two
|
|
263
|
+
* ways, and a queue each would serialize neither against the other.
|
|
264
|
+
*
|
|
265
|
+
* Rejections are caught and logged here — the chain must survive one failure,
|
|
266
|
+
* and nothing else catches them (the bus dispatch's try/catch only covers a
|
|
267
|
+
* synchronous throw).
|
|
268
|
+
*/
|
|
269
|
+
private enqueueReconcile;
|
|
270
|
+
/**
|
|
271
|
+
* Re-read network settings. `beeApiUrl` / `gnosisRpcUrl` are read fresh per
|
|
272
|
+
* call — by the TTL/contract helpers, and by `bee`, which builds the client
|
|
273
|
+
* for a changed URL on its next read — so updating them is all it takes.
|
|
274
|
+
*/
|
|
275
|
+
private applyNetworkSettings;
|
|
276
|
+
/**
|
|
277
|
+
* The body of `applyNetworkSettings`, taking the settings as a value: a
|
|
278
|
+
* partitioned session cannot read them from storage and is handed them by
|
|
279
|
+
* the connect popup instead (`hydratePartitionAccount`). One implementation,
|
|
280
|
+
* or the two paths drift — an RPC-only change is invisible to a `beeNodeUrl`
|
|
281
|
+
* guard, and the subsidised-gateway rules below are easy to forget.
|
|
282
|
+
*/
|
|
283
|
+
private applyNetworkSettingsValues;
|
|
284
|
+
/**
|
|
285
|
+
* Handle changes to the nested account document. Covers auth transitions
|
|
286
|
+
* (connect / secret change / disconnect) AND derived-ConnectionInfo changes
|
|
287
|
+
* (default-stamp change, new stamp purchased, account rename) — all of which
|
|
288
|
+
* now live in one document.
|
|
289
|
+
*
|
|
290
|
+
* `"storage"`: a `storage` event from another window, or a same-window write
|
|
291
|
+
* the shell made on its own — this device's change, so it publishes.
|
|
292
|
+
* `"bus"`: a same-window write the shell marked `noRepublish` — a peer's
|
|
293
|
+
* delta it folded, a volatile field, a device-local sign-out. The shell
|
|
294
|
+
* sharing this window (the identity UI runs inside the proxy iframe too)
|
|
295
|
+
* makes those beside the proxy, and they reconcile the way a bus delta does,
|
|
296
|
+
* without a publish: republishing a fold stamped a fresh clock on every hop
|
|
297
|
+
* and kept two devices publishing at each other every few seconds (#707).
|
|
298
|
+
*/
|
|
299
|
+
private handleAccountStorageChange;
|
|
300
|
+
/**
|
|
301
|
+
* Re-resolve this origin's connection and reconcile the session with it.
|
|
302
|
+
*
|
|
303
|
+
* `source` is where the news came from, and only the disconnect branch cares.
|
|
304
|
+
* A partitioned iframe cannot see connected apps in storage, so a storage
|
|
305
|
+
* event saying "no valid connection" means nothing there — but an
|
|
306
|
+
* `account-delta` from a peer is authoritative, and acting on it is the whole
|
|
307
|
+
* point of that message (docs/Account-Bus.md).
|
|
308
|
+
*/
|
|
309
|
+
private reevaluateConnection;
|
|
310
|
+
/**
|
|
311
|
+
* Re-resolve postage stamp and account inputs for the current connection
|
|
312
|
+
* from shared storage. Reinitializes the stamper if the batch, signer
|
|
313
|
+
* key, or account-level inputs (owner / encryption key) changed — the
|
|
314
|
+
* last case handles e.g. local→synced account migration where the
|
|
315
|
+
* postage stamp is unchanged but the underlying account isn't.
|
|
316
|
+
*/
|
|
317
|
+
private refreshStampFromStorage;
|
|
318
|
+
/**
|
|
319
|
+
* The only way the resolved stamp changes. The write path — the stamper and
|
|
320
|
+
* the coordinator that writes through it — belongs to the stamp it was built
|
|
321
|
+
* for, so it goes whenever the fields naming that stamp do, before anything
|
|
322
|
+
* is built for the next one. Otherwise the previous coordinator outlives its
|
|
323
|
+
* stamp: after the drive is removed, or the connection moves to another
|
|
324
|
+
* account, an upload still takes that coordinator's lease and spends the old
|
|
325
|
+
* batch while the dApp is told there is no stamp at all.
|
|
326
|
+
*/
|
|
327
|
+
private setResolvedStamp;
|
|
328
|
+
/**
|
|
329
|
+
* Authenticate using data from connected apps storage
|
|
330
|
+
*/
|
|
331
|
+
private authenticateFromStorage;
|
|
332
|
+
/**
|
|
333
|
+
* Handle messages from the auth popup (same-origin postMessage).
|
|
334
|
+
* Used as storage partitioning fallback when storage events don't fire due to partitioning.
|
|
335
|
+
*/
|
|
336
|
+
private handlePopupMessage;
|
|
337
|
+
/**
|
|
338
|
+
* End the connect attempt a refused handover belonged to.
|
|
339
|
+
*
|
|
340
|
+
* The popup cannot be told it was refused — there is no channel back — so it
|
|
341
|
+
* shows "Connected!" while the button here spins on, and the only other
|
|
342
|
+
* thing that ever cleared the flag was the user closing that popup
|
|
343
|
+
* (`checkPopupClosed`), which nothing gave them a reason to do.
|
|
344
|
+
*
|
|
345
|
+
* `isConnecting` first: `showAuthButton` early-returns while it is set, so
|
|
346
|
+
* clearing the flag is what lets the button come back. `authLoading` is
|
|
347
|
+
* already false on every path that reaches here — belt-and-braces, not a
|
|
348
|
+
* second fix.
|
|
349
|
+
*
|
|
350
|
+
* The challenge is deliberately kept. The popup is still open and may yet
|
|
351
|
+
* send a payload this bundle can read; refusing one message ends the
|
|
352
|
+
* attempt's spinner, not the ceremony behind it.
|
|
353
|
+
*/
|
|
354
|
+
private endRefusedConnect;
|
|
355
|
+
/**
|
|
356
|
+
* The handed-over chat binding, if it names exactly this parent origin, the
|
|
357
|
+
* address of the chat key of the secret it came with and the account it came
|
|
358
|
+
* with, so the dApp is never shown a statement about another site, key or
|
|
359
|
+
* account. A restored session runs through here again, so a stored binding
|
|
360
|
+
* naming any other address is dropped on reload as well.
|
|
361
|
+
*
|
|
362
|
+
* Local http is allowed in the parse because the comparison with
|
|
363
|
+
* `parentOrigin`, which the browser reported, is the check here. Which sites
|
|
364
|
+
* a binding may name at all is the popup's to decide, and a production popup
|
|
365
|
+
* refuses local http.
|
|
366
|
+
*/
|
|
367
|
+
private acceptHandedOverChatBinding;
|
|
368
|
+
/**
|
|
369
|
+
* Write the handed-over session to this partition's own store, sealed so
|
|
370
|
+
* none of it is plain text on disk: it holds the app secret, the derivation
|
|
371
|
+
* key and the stamp's signer key.
|
|
372
|
+
*
|
|
373
|
+
* The wire form, not the parsed one: `AuthDataSchema` revives `BatchId` and
|
|
374
|
+
* `PrivateKey` instances, which do not survive `JSON.stringify`, so what goes
|
|
375
|
+
* in is what the popup sent and what comes back out is parsed the same way it
|
|
376
|
+
* was on arrival.
|
|
377
|
+
*
|
|
378
|
+
* The deadline is the one `hydratePartitionAccount` just computed, read back
|
|
379
|
+
* off the connection rather than recomputed, so the session at rest and the
|
|
380
|
+
* session in memory cannot disagree about when it ends.
|
|
381
|
+
*/
|
|
382
|
+
private savePartitionSession;
|
|
383
|
+
/**
|
|
384
|
+
* Re-persist the stored session's account view after a live fold moved it.
|
|
385
|
+
*
|
|
386
|
+
* Without this the record is frozen at the handover: a rotated signer key or
|
|
387
|
+
* a moved default stamp arrives over the bus, updates `partitionAccount`, and
|
|
388
|
+
* then a reload resurrects the pre-fold copy and uploads with it until the
|
|
389
|
+
* next live overlap.
|
|
390
|
+
*
|
|
391
|
+
* Only the account. The deadline stays the one the handover recorded, because
|
|
392
|
+
* `hydratePartitionAccount` recomputes `connectedUntil` on every call — write
|
|
393
|
+
* the in-memory one back and a session extends itself for as long as deltas
|
|
394
|
+
* keep arriving. And the record is edited in the shape it was WRITTEN in
|
|
395
|
+
* (raw JSON), not the parsed one: `AuthDataSchema` revives byte classes that
|
|
396
|
+
* do not survive a second `JSON.stringify`.
|
|
397
|
+
*
|
|
398
|
+
* Never creates a record — a session that failed to persist stays unpersisted.
|
|
399
|
+
*/
|
|
400
|
+
private refreshStoredPartitionAccount;
|
|
401
|
+
/** Forget the stored session. Every path that ends one calls this. */
|
|
402
|
+
private clearPartitionSession;
|
|
403
|
+
/**
|
|
404
|
+
* Writes to the stored session wait for sealing, so they run one after
|
|
405
|
+
* another in call order. Otherwise a save that started before a clear could
|
|
406
|
+
* land after it and bring an ended session back. `build` answers the record
|
|
407
|
+
* to write, `null` to remove it, or `undefined` to leave it as it is.
|
|
408
|
+
*/
|
|
409
|
+
private writePartitionSession;
|
|
410
|
+
/**
|
|
411
|
+
* The stored record for this parent and its opened wire payload, or
|
|
412
|
+
* `"unavailable"` when this page could not read the wrapping key, so the
|
|
413
|
+
* record may still open on a later load.
|
|
414
|
+
*/
|
|
415
|
+
private openStoredPartitionSession;
|
|
416
|
+
/**
|
|
417
|
+
* The stored session for THIS parent, if it is still within its deadline.
|
|
418
|
+
*
|
|
419
|
+
* The key is per dApp origin, because the partition is not: it is scoped to
|
|
420
|
+
* the top-level SITE, so same-site dApps share this store (#671). The
|
|
421
|
+
* `parentOrigin` check below is now the belt to that pair of braces rather
|
|
422
|
+
* than the only thing keeping two dApps apart. An expired, malformed or
|
|
423
|
+
* unopenable record is removed on the way past: it will never become valid
|
|
424
|
+
* again, and a record whose seal does not open is no session. So is a record
|
|
425
|
+
* carrying a chat binding that fails `acceptHandedOverChatBinding`, such as
|
|
426
|
+
* one naming the app secret's address from before sites had a chat key:
|
|
427
|
+
* restored without it, the session would hand out a chat key no binding
|
|
428
|
+
* names, so the person unlocks once and the window signs a fresh one. A record whose
|
|
429
|
+
* wrapping key this page could not read is no session for this page only,
|
|
430
|
+
* and stays stored for a later load to open.
|
|
431
|
+
*/
|
|
432
|
+
private readPartitionSession;
|
|
433
|
+
/**
|
|
434
|
+
* Build the in-memory account view for a partitioned session and initialize
|
|
435
|
+
* the write path from it. The vault fields are inert placeholders — the
|
|
436
|
+
* record never touches storage and the seed never leaves first-party
|
|
437
|
+
* context; `SignedInAccount` merely requires their presence.
|
|
438
|
+
*/
|
|
439
|
+
private hydratePartitionAccount;
|
|
440
|
+
/**
|
|
441
|
+
* Join the account bus for whatever connection is resolvable right now
|
|
442
|
+
* (idempotent per account). Called from every path that establishes or
|
|
443
|
+
* re-resolves a connection — the unpartitioned ones included, or the bus
|
|
444
|
+
* would never reach past this origin+partition outside Safari.
|
|
445
|
+
*/
|
|
446
|
+
private joinAccountBus;
|
|
447
|
+
/**
|
|
448
|
+
* Attach the account's bus transports (docs/Account-Bus.md). Idempotent per
|
|
449
|
+
* account; same code path on every browser — Safari is not special.
|
|
450
|
+
*
|
|
451
|
+
* BOTH transports hang off the account-derived topic, so a context only ever
|
|
452
|
+
* shares a channel with other contexts of the same account. The local one is
|
|
453
|
+
* attached whether or not a signaling URL is configured: without it there is
|
|
454
|
+
* still same-device tab↔tab traffic to carry, and a build with no signaling
|
|
455
|
+
* URL must not lose cross-tab utilization.
|
|
456
|
+
*
|
|
457
|
+
* Because the topic is derived, both transports now attach a few ticks after
|
|
458
|
+
* an auth event rather than at construction, so a peer publishing inside
|
|
459
|
+
* that window loses that one message. Bounded by a single HMAC derivation,
|
|
460
|
+
* and no worse than a tab that has not finished loading — which has no
|
|
461
|
+
* listener either; durable counters reconcile on the next read. `busTopic`
|
|
462
|
+
* is set exactly when the local transport goes live, so it is the signal to
|
|
463
|
+
* wait on.
|
|
464
|
+
*
|
|
465
|
+
* The two attaches are INDEPENDENT. `SignalingTransport` connects in its
|
|
466
|
+
* constructor, where `new URL` / `new WebSocket` throw synchronously on a
|
|
467
|
+
* url that is set but unusable (`ws://` from an https page, a CSP that omits
|
|
468
|
+
* the bus host, a typo) — and that must cost the signaling transport only.
|
|
469
|
+
* Letting it take the local one down produces "no bus at all, for the whole
|
|
470
|
+
* session", silently and indistinguishably from a healthy build with no
|
|
471
|
+
* signaling url at all.
|
|
472
|
+
*/
|
|
473
|
+
private ensureAccountBusTransports;
|
|
474
|
+
/**
|
|
475
|
+
* "This device is live", to the account's room. Reads the room and the
|
|
476
|
+
* device id at fire time — the same guard `publishAccountDelta` carries —
|
|
477
|
+
* so a beat scheduled for one account can never name it into another's
|
|
478
|
+
* room during a switch, and one that fires before auth finished says nothing.
|
|
479
|
+
*/
|
|
480
|
+
private publishPresence;
|
|
481
|
+
/** Stop beating and forget who was live: the next room starts from nothing. */
|
|
482
|
+
private stopPresence;
|
|
483
|
+
/** Leave the account's bus room: both transports detached and closed, and
|
|
484
|
+
* every latch cleared so the next join is a fresh one. */
|
|
485
|
+
private detachBusTransports;
|
|
486
|
+
/**
|
|
487
|
+
* Clean up resources when the proxy is destroyed.
|
|
488
|
+
* Call this method when the proxy iframe is being unloaded.
|
|
489
|
+
*/
|
|
490
|
+
destroy(): void;
|
|
491
|
+
/**
|
|
492
|
+
* Tear the write coordinator down, announcing the partition it held so
|
|
493
|
+
* waiting peers wake now instead of sleeping out their poll interval
|
|
494
|
+
* (docs/Account-Bus.md, bus-accelerated leases). The announcement races the
|
|
495
|
+
* Swarm release it describes, which is harmless: the lock SOCs stay the
|
|
496
|
+
* authority, so a peer woken too early just spends one extra read round.
|
|
497
|
+
*/
|
|
498
|
+
private teardownCoordinator;
|
|
499
|
+
private terminateStampWorkerPool;
|
|
500
|
+
private static laneKey;
|
|
501
|
+
/**
|
|
502
|
+
* Hold a lane-scoped delta that arrived with no lane bound to compare it
|
|
503
|
+
* against, merged into whatever that lane already has.
|
|
504
|
+
*/
|
|
505
|
+
private bufferLaneUpdate;
|
|
506
|
+
/**
|
|
507
|
+
* Fold the buffered delta for the lane we just bound in, then drop every
|
|
508
|
+
* buffered lane — the ones we did not bind are another device's business.
|
|
509
|
+
* Called on every lease acquire: on the cold path the counters are already
|
|
510
|
+
* seeded from the durable partition-state feed, so this is a harmless
|
|
511
|
+
* monotonic no-op; on the adopt fast path they are not, and this is the
|
|
512
|
+
* whole point.
|
|
513
|
+
*/
|
|
514
|
+
private applyPendingLaneUpdate;
|
|
515
|
+
/**
|
|
516
|
+
* Run this holder's answer to a peer's `lease-request`, announcing the
|
|
517
|
+
* partition it gave up so the waiter (and holders further down the rank
|
|
518
|
+
* order) hear about it.
|
|
519
|
+
*
|
|
520
|
+
* The `lease-claim` goes out FIRST. Releasing is two stamped Swarm writes
|
|
521
|
+
* (`yieldIdleLease` → `lease.release()`), which is orders of magnitude
|
|
522
|
+
* longer than a rank step, so announcing only on completion would reach the
|
|
523
|
+
* other holders long after every one of them had begun releasing too — #576
|
|
524
|
+
* with extra steps. A claim is cheap to send and is what the rank step
|
|
525
|
+
* actually budgets for.
|
|
526
|
+
*/
|
|
527
|
+
private answerLeaseRequest;
|
|
528
|
+
/**
|
|
529
|
+
* How long this holder waits before answering `requestId`.
|
|
530
|
+
*
|
|
531
|
+
* A waiter needs exactly ONE slot, but the request names no partition, so
|
|
532
|
+
* every idle holder used to release at once: with `partitionCount = 4` all
|
|
533
|
+
* four dropped their lease, each then paid a cold acquire on its next
|
|
534
|
+
* upload, and whoever lost the re-race got "Uploads are unavailable". The
|
|
535
|
+
* waiter re-broadcasts every round, so it repeated.
|
|
536
|
+
*
|
|
537
|
+
* The request id gives every holder the same permutation of the partitions,
|
|
538
|
+
* and each waits its own rank in it. Exactly one draws rank 0 and answers
|
|
539
|
+
* immediately — so the ordinary one-holder handover is not slowed at all —
|
|
540
|
+
* while the rest stand down as soon as they see its `lease-claim`. Only
|
|
541
|
+
* holders that would presently yield take a rank at all (`canYieldForPeer`),
|
|
542
|
+
* so a holder mid-burst does not sit at the front of the order and decline
|
|
543
|
+
* in silence; the next eligible rank answers a step later, which still beats
|
|
544
|
+
* waiting out a poll interval.
|
|
545
|
+
*
|
|
546
|
+
* `scripts/bus-handover-latency.ts` reimplements this election (these
|
|
547
|
+
* handlers are private) to measure the handover against a real cluster.
|
|
548
|
+
* Change the formula here and change it there, or the script starts
|
|
549
|
+
* measuring a race the product does not have.
|
|
550
|
+
*/
|
|
551
|
+
private yieldRankDelayMs;
|
|
552
|
+
/** Stop answering `requestId` — somebody else claimed it (or we just did).
|
|
553
|
+
* Remembered as well as cancelled, because the rank timer may already have
|
|
554
|
+
* fired: it hands off to `answerLeaseRequest`, which re-checks this. */
|
|
555
|
+
private standDownFromRequest;
|
|
556
|
+
private markRequestAnswered;
|
|
557
|
+
/** Drop every scheduled answer — nothing they would release is ours now. */
|
|
558
|
+
private cancelPendingYields;
|
|
559
|
+
/**
|
|
560
|
+
* Account-bus subscription: utilization deltas from other contexts, and the
|
|
561
|
+
* lease fast path (docs/Account-Bus.md, bus-accelerated leases). The Swarm
|
|
562
|
+
* lock-SOC protocol stays authoritative — these messages only shortcut its
|
|
563
|
+
* timers.
|
|
564
|
+
*/
|
|
565
|
+
private setupBusListeners;
|
|
566
|
+
/**
|
|
567
|
+
* Fold a peer's account snapshot into this session's view and reconcile.
|
|
568
|
+
*
|
|
569
|
+
* This is the only push channel a partitioned iframe has: it cannot see
|
|
570
|
+
* shared storage, so without this a revoke reaches it only when the page
|
|
571
|
+
* closes — and since #547 that session is a full writer holding every stamp's
|
|
572
|
+
* signer key (docs/Account-Bus.md, `account-delta`).
|
|
573
|
+
*
|
|
574
|
+
* Durable truth is unchanged. An unpartitioned session still owns its state
|
|
575
|
+
* through storage; the delta only drives the reconcile, and `clearAuthData`
|
|
576
|
+
* invalidates the stored entry if the outcome is a disconnect.
|
|
577
|
+
*/
|
|
578
|
+
private applyAccountDelta;
|
|
579
|
+
/**
|
|
580
|
+
* Create an onProgress callback for upload operations.
|
|
581
|
+
* Returns undefined if progress reporting is disabled.
|
|
582
|
+
*/
|
|
583
|
+
private createProgressCallback;
|
|
584
|
+
/**
|
|
585
|
+
* Announce that proxy is ready to receive messages
|
|
586
|
+
* Broadcasts to parent with wildcard origin since we don't know parent origin yet
|
|
587
|
+
*/
|
|
588
|
+
private announceReady;
|
|
589
|
+
/**
|
|
590
|
+
* Get the stored postage batch ID
|
|
591
|
+
*/
|
|
592
|
+
getPostageBatchId(): string | undefined;
|
|
593
|
+
/**
|
|
594
|
+
* Get the stored signer key
|
|
595
|
+
*/
|
|
596
|
+
getSignerKey(): string | undefined;
|
|
597
|
+
/**
|
|
598
|
+
* Initialize the Stamper for client-side signing
|
|
599
|
+
* Uses UtilizationAwareStamper to track bucket usage
|
|
600
|
+
*/
|
|
601
|
+
private initializeStamper;
|
|
602
|
+
/**
|
|
603
|
+
* The eager lease claim is a write of its own, so a restored session makes
|
|
604
|
+
* it only once its check allows writes. A session whose check could not
|
|
605
|
+
* complete claims lazily instead, on the first write a later check allows.
|
|
606
|
+
*/
|
|
607
|
+
private startLeaseOnceAllowed;
|
|
608
|
+
/**
|
|
609
|
+
* This device's identity, captured once. Lazily initialised here as a
|
|
610
|
+
* fallback; the auth entry points set it eagerly. Reused for every lease
|
|
611
|
+
* operation so acquire / refresh / validate never disagree about who we
|
|
612
|
+
* are (which previously caused false "reclaimed by another device").
|
|
613
|
+
*/
|
|
614
|
+
private requireDeviceId;
|
|
615
|
+
private readLeaseCache;
|
|
616
|
+
private writeLeaseCache;
|
|
617
|
+
/**
|
|
618
|
+
* Get or create a StampWorkerPool for parallel signing.
|
|
619
|
+
* Lazy-initialized on first use and reused across uploads, for as long as
|
|
620
|
+
* it signs for the current stamper and has the requested size.
|
|
621
|
+
*/
|
|
622
|
+
private getOrCreateWorkerPool;
|
|
623
|
+
/**
|
|
624
|
+
* Save stamper bucket state to IndexedDB, logging a failure rather than
|
|
625
|
+
* throwing. The upload path relies on this: the coordinator flushes in a
|
|
626
|
+
* `finally`, after the upload has already gone out.
|
|
627
|
+
*/
|
|
628
|
+
private saveStamperState;
|
|
629
|
+
/**
|
|
630
|
+
* Save stamper bucket state to IndexedDB and tell local peers, throwing when
|
|
631
|
+
* the save fails.
|
|
632
|
+
*/
|
|
633
|
+
private persistStamperState;
|
|
634
|
+
/**
|
|
635
|
+
* Execute an upload operation against the right target for the current mode.
|
|
636
|
+
* In subsidised mode (no usable user stamp) the gateway handles stamping, so
|
|
637
|
+
* there is no local stamp state to protect and we run unlocked. In user-stamp
|
|
638
|
+
* mode the write goes through the {@link BatchWriteCoordinator}, which takes
|
|
639
|
+
* the cross-tab write lock, ensures a held partition, and flushes stamper
|
|
640
|
+
* state — the proxy no longer owns any of that.
|
|
641
|
+
*/
|
|
642
|
+
private withModeAwareWriteLock;
|
|
643
|
+
/**
|
|
644
|
+
* Save stamper state only in stamper mode.
|
|
645
|
+
* In subsidised mode, there's no local stamp state to persist.
|
|
646
|
+
*/
|
|
647
|
+
private saveStamperStateIfNeeded;
|
|
648
|
+
/**
|
|
649
|
+
* Setup message listener for parent and popup messages
|
|
650
|
+
*/
|
|
651
|
+
private setupMessageListener;
|
|
652
|
+
/**
|
|
653
|
+
* Handle parent identification
|
|
654
|
+
*/
|
|
655
|
+
private handleParentIdentify;
|
|
656
|
+
/**
|
|
657
|
+
* Handle messages from parent window
|
|
658
|
+
*/
|
|
659
|
+
private handleParentMessage;
|
|
660
|
+
/**
|
|
661
|
+
* Load authentication data from shared storage (ConnectedApp records).
|
|
662
|
+
*/
|
|
663
|
+
private loadAuthData;
|
|
664
|
+
/**
|
|
665
|
+
* Bring a stored session back up, exactly as the popup handover does.
|
|
666
|
+
*
|
|
667
|
+
* Through the same `hydratePartitionAccount`, so there is one function that
|
|
668
|
+
* builds a partitioned session rather than two that drift.
|
|
669
|
+
*
|
|
670
|
+
* The deadline cannot see a revoke made while the tab was closed, so the
|
|
671
|
+
* session comes up for reads at once and checks the account's published
|
|
672
|
+
* state before its first write (`startRestoredSessionCheck`), as a session
|
|
673
|
+
* restored from shared storage does.
|
|
674
|
+
*/
|
|
675
|
+
private restorePartitionSession;
|
|
676
|
+
/**
|
|
677
|
+
* Ask the account's published state whether this restored session was ended
|
|
678
|
+
* while no page of the site was open. Ended means what it means
|
|
679
|
+
* everywhere else: the entry is gone, `revokedAt` (Remove), or a
|
|
680
|
+
* `disconnectedAt` newer than the connection this session came from
|
|
681
|
+
* (Disconnect, #667), with the rules in `sync/session-check.ts`.
|
|
682
|
+
*
|
|
683
|
+
* Reads start at once. Writes wait on the answer (`ensureCanWrite`), and an
|
|
684
|
+
* answer that could not be read keeps them refused: an unreachable Swarm
|
|
685
|
+
* says nothing about a revoke, so it neither ends the session nor lets it
|
|
686
|
+
* write. The next write asks again.
|
|
687
|
+
*/
|
|
688
|
+
private startRestoredSessionCheck;
|
|
689
|
+
/** Waits for the restored session's check, and refuses unless it allowed. */
|
|
690
|
+
private awaitRestoredSessionAllowed;
|
|
691
|
+
/**
|
|
692
|
+
* The gate every write handler passes. The session check comes first, and
|
|
693
|
+
* the refusal checks run after it because the session can change while it
|
|
694
|
+
* waits.
|
|
695
|
+
*/
|
|
696
|
+
private ensureCanWrite;
|
|
697
|
+
/**
|
|
698
|
+
* Look up the postage stamp for the currently connected app's identity
|
|
699
|
+
* by reading from shared localStorage stores.
|
|
700
|
+
*/
|
|
701
|
+
private lookupPostageStampForApp;
|
|
702
|
+
/**
|
|
703
|
+
* Look up the account for the currently connected app's identity
|
|
704
|
+
* by reading from shared localStorage stores.
|
|
705
|
+
*
|
|
706
|
+
* @returns Account info with owner address and encryption key, or undefined if not found
|
|
707
|
+
*/
|
|
708
|
+
private lookupAccountForApp;
|
|
709
|
+
/**
|
|
710
|
+
* The partition rival set for `accountId`: every device heard live on the
|
|
711
|
+
* account bus, plus the recently signed-in devices of the stored registry
|
|
712
|
+
* (read fresh, so the intent round reflects a device that announced after
|
|
713
|
+
* the coordinator was built).
|
|
714
|
+
*
|
|
715
|
+
* The bus half is the liveness signal — a peer beating in the room is alive
|
|
716
|
+
* by construction, whether or not its roster append ever landed. The
|
|
717
|
+
* registry half is the bootstrap for contexts with no bus (no signaling
|
|
718
|
+
* server configured) and for a device that just signed in and has not
|
|
719
|
+
* beaten yet; it is bounded to `KNOWN_DEVICE_MAX_AGE_MS` because removed
|
|
720
|
+
* (tombstoned) devices won't write, and long-dead ghosts (the device list is
|
|
721
|
+
* append-only) would each add an absent intent read to every acquire — on a
|
|
722
|
+
* flaky gateway that can push a single acquire past its timeout, so a live
|
|
723
|
+
* device can't claim even a FREE partition. A genuinely-live holder this
|
|
724
|
+
* prune drops is still caught by the deviceId-independent occupancy beacon.
|
|
725
|
+
*
|
|
726
|
+
* On any storage error the bus half alone is returned — the lease then falls
|
|
727
|
+
* back to the guard+TTL acquire path against whoever is live.
|
|
728
|
+
*/
|
|
729
|
+
private knownDeviceIdsForAccount;
|
|
730
|
+
private lastDeviceRegistryRefreshAt;
|
|
731
|
+
/**
|
|
732
|
+
* Pull the latest account snapshot from the shared feed and union its
|
|
733
|
+
* `metadata.devices` into the locally-stored account, so `knownDeviceIds`
|
|
734
|
+
* reflects a peer that signed in AFTER this device created the account (else
|
|
735
|
+
* the claimer never reads the peer's beacon and they dual-acquire). Throttled
|
|
736
|
+
* (the slot-wait loop re-acquires) and best-effort — on any failure the
|
|
737
|
+
* current registry is used as-is.
|
|
738
|
+
*/
|
|
739
|
+
private refreshDeviceRegistryFromSwarm;
|
|
740
|
+
/**
|
|
741
|
+
* Assemble the current account-state snapshot for the connected app's
|
|
742
|
+
* account. Needs no feed signing key, so it stays separate from
|
|
743
|
+
* `buildAccountStateSnapshotForPublish` below — reusing that one would cost
|
|
744
|
+
* two key derivations to send a bus message.
|
|
745
|
+
*
|
|
746
|
+
* `requireDefaultStamp` is the FEED path's precondition, not the snapshot's:
|
|
747
|
+
* that write is paid for by the default stamp, while a bus message costs
|
|
748
|
+
* nothing to send and `metadata.defaultPostageStampBatchID` is optional on
|
|
749
|
+
* the wire. An account with no drives is a supported connected state, and
|
|
750
|
+
* gating the delta on a stamp is how such an account's revokes never reach
|
|
751
|
+
* its partitioned sessions at all.
|
|
752
|
+
*/
|
|
753
|
+
private buildAccountStateSnapshot;
|
|
754
|
+
/**
|
|
755
|
+
* Announce this session's account state to the account's other live contexts
|
|
756
|
+
* (docs/Account-Bus.md, `account-delta`). Unlike the feed write below it
|
|
757
|
+
* needs no partition and no lock — it is a message, not a write — so a
|
|
758
|
+
* read-only session still relays what it can see to peers that cannot.
|
|
759
|
+
*
|
|
760
|
+
* `ofEndedConnection` reports a session that has just ENDED (the revoke a
|
|
761
|
+
* storage event revealed), where the entry the snapshot is resolved from is
|
|
762
|
+
* by definition no longer a valid connection.
|
|
763
|
+
*/
|
|
764
|
+
private publishAccountDelta;
|
|
765
|
+
/**
|
|
766
|
+
* Assemble the current account-state snapshot for the connected app's account
|
|
767
|
+
* from shared localStorage (the same shape `sync-account` builds from its DI
|
|
768
|
+
* stores). Returns the snapshot plus the feed signing key + owner. Undefined
|
|
769
|
+
* when storage is partitioned or the account/stamp can't be resolved.
|
|
770
|
+
*/
|
|
771
|
+
private buildAccountStateSnapshotForPublish;
|
|
772
|
+
/**
|
|
773
|
+
* Schedule a debounced account-state publish. Safe to call from inside the
|
|
774
|
+
* coordinator's lease callbacks: it only arms a timer, so the actual publish
|
|
775
|
+
* runs later, outside any held write lock.
|
|
776
|
+
*
|
|
777
|
+
* A coordinator is NOT a precondition — only the feed half of the publish
|
|
778
|
+
* needs one, and `runAccountStatePublish` checks for it there, after the bus
|
|
779
|
+
* delta has gone out.
|
|
780
|
+
*/
|
|
781
|
+
private schedulePublish;
|
|
782
|
+
/**
|
|
783
|
+
* Publish the account snapshot: to the account's live peers over the bus,
|
|
784
|
+
* and to the shared feed via the coordinator (same write lock + held
|
|
785
|
+
* partition). The feed write needs a partition; the bus message does not, so
|
|
786
|
+
* it goes out first and a read-only session still relays to peers that
|
|
787
|
+
* cannot see storage at all. Single-device accounts' feeds are published by
|
|
788
|
+
* the SwarmID UI. Re-arms if a publish is already in flight so the latest
|
|
789
|
+
* state still lands.
|
|
790
|
+
*/
|
|
791
|
+
private runAccountStatePublish;
|
|
792
|
+
/**
|
|
793
|
+
* Lift this device's own `removedAt` on a genuine sign-in — #337's rule, at
|
|
794
|
+
* the one seam that can tell a user unlocking their account from a timer.
|
|
795
|
+
*
|
|
796
|
+
* `mergeDevices` used to do this on every refresh, which meant no removal and
|
|
797
|
+
* no expiry ever stuck (#611). Here it happens once, on a fresh `setSecret`
|
|
798
|
+
* handover, and the next publish re-announces us with the tombstone gone —
|
|
799
|
+
* `ensureInRoster` appends when the removed-state differs, so the roster
|
|
800
|
+
* carries the reactivation to peers without any extra write of our own.
|
|
801
|
+
*
|
|
802
|
+
* Clearing `removedAt` is only half of it: `mergeDevicesList` ranks devices
|
|
803
|
+
* on `max(removedAt, lastSignedInAt)`, so a lift that left the old sign-in
|
|
804
|
+
* stamp in place would lose to the very tombstone it is undoing and the next
|
|
805
|
+
* fold would put it straight back. A handover IS a sign-in — the user
|
|
806
|
+
* clicked connect and unlocked in the popup — so it stamps one.
|
|
807
|
+
*
|
|
808
|
+
* Partitioned only: an unpartitioned proxy has no business editing the
|
|
809
|
+
* shared account document behind the identity UI's back. The identity UI has
|
|
810
|
+
* no reactivation seam of its own, and needs none — nothing writes a device
|
|
811
|
+
* tombstone today, and a device that stops beating on the bus is ignored at
|
|
812
|
+
* read rather than tombstoned (#655). If manual removal is ever built, its
|
|
813
|
+
* seam has to do what this one does, both halves included.
|
|
814
|
+
*/
|
|
815
|
+
private reactivateThisDevice;
|
|
816
|
+
/**
|
|
817
|
+
* The name this context announces itself under in the device registry.
|
|
818
|
+
*
|
|
819
|
+
* A PARTITIONED proxy keeps its own `swarm-id-device-id` in the partition, so
|
|
820
|
+
* it is a per-dApp device and says which dApp (#570). An UNPARTITIONED one
|
|
821
|
+
* reads the trusted domain's first-party store and therefore shares the
|
|
822
|
+
* device id with the identity UI — it is the same device, and naming it after
|
|
823
|
+
* whichever dApp happened to embed it would be wrong.
|
|
824
|
+
*/
|
|
825
|
+
private announcedDeviceName;
|
|
826
|
+
/**
|
|
827
|
+
* Check if a connection is still valid based on connectedUntil timestamp
|
|
828
|
+
*/
|
|
829
|
+
private isConnectionValid;
|
|
830
|
+
/**
|
|
831
|
+
* Find the account + connected-app pair for the current parent origin, reading
|
|
832
|
+
* the nested account documents. Resolves ambiguity (the same app connected
|
|
833
|
+
* under multiple accounts) by sorting valid entries by `lastConnectedAt`
|
|
834
|
+
* descending and returning the most recent.
|
|
835
|
+
*/
|
|
836
|
+
private findConnectionForParent;
|
|
837
|
+
/**
|
|
838
|
+
* Look up the app secret from shared storage for the current parent origin.
|
|
839
|
+
* Returns the secret if a valid connection is found.
|
|
840
|
+
*/
|
|
841
|
+
private lookupAppSecretFromSharedStorage;
|
|
842
|
+
/**
|
|
843
|
+
* Clear authentication data
|
|
844
|
+
*/
|
|
845
|
+
private clearAuthData;
|
|
846
|
+
/**
|
|
847
|
+
* Send error message to parent
|
|
848
|
+
*/
|
|
849
|
+
/**
|
|
850
|
+
* Answer a request with what went wrong, as a dApp can branch on it (#761):
|
|
851
|
+
* the caught value is classified by `toWireError`; `fallback` is the
|
|
852
|
+
* message for a throw that was not an `Error`.
|
|
853
|
+
*/
|
|
854
|
+
private sendErrorToParent;
|
|
855
|
+
private ensureCanUpload;
|
|
856
|
+
/**
|
|
857
|
+
* Whether uploads can be stamped with the account's own postage batch. Both
|
|
858
|
+
* what the dApp is told (`buildConnectionInfo`'s `uploadMode`) and where the
|
|
859
|
+
* bytes go (`isSubsidisedModeActive`) are decided by this one rule.
|
|
860
|
+
*/
|
|
861
|
+
private hasUsableUserStamp;
|
|
862
|
+
/**
|
|
863
|
+
* Whether uploads go to the subsidised gateway: one is configured, and there
|
|
864
|
+
* is no usable user stamp to write with instead.
|
|
865
|
+
*/
|
|
866
|
+
private isSubsidisedModeActive;
|
|
867
|
+
/**
|
|
868
|
+
* Send message to parent
|
|
869
|
+
*/
|
|
870
|
+
private sendToParent;
|
|
871
|
+
/**
|
|
872
|
+
* Send message to the event source (parent window that sent the request)
|
|
873
|
+
*/
|
|
874
|
+
private postMessage;
|
|
875
|
+
private handleCheckAuth;
|
|
876
|
+
/**
|
|
877
|
+
* The two public keys the identity carries, memoised on the derivation key
|
|
878
|
+
* they come from: `buildConnectionInfo` runs from eight sites, and the key
|
|
879
|
+
* changes only when the account does. Public halves only, never a secret.
|
|
880
|
+
*/
|
|
881
|
+
private identityKeys?;
|
|
882
|
+
private identityKeysFor;
|
|
883
|
+
/**
|
|
884
|
+
* Build the current ConnectionInfo snapshot from in-memory auth state and
|
|
885
|
+
* shared localStorage. Pure — does not send anything.
|
|
886
|
+
*/
|
|
887
|
+
private buildConnectionInfo;
|
|
888
|
+
/**
|
|
889
|
+
* Which of the two ways to be upload-less this session hit. Ordered by how
|
|
890
|
+
* early the path gives up, so the answer names the first thing that stopped
|
|
891
|
+
* it rather than the last symptom.
|
|
892
|
+
*
|
|
893
|
+
* Only meaningful once authenticated — an unconnected session has no reason
|
|
894
|
+
* to report beyond not being connected.
|
|
895
|
+
*/
|
|
896
|
+
private uploadUnavailableReason;
|
|
897
|
+
/**
|
|
898
|
+
* Recompute ConnectionInfo and send `connectionInfoChanged` to the parent
|
|
899
|
+
* if it actually differs from the last value we sent. Used both on auth
|
|
900
|
+
* transitions and on storage events that affect derived fields.
|
|
901
|
+
*/
|
|
902
|
+
private emitConnectionInfoIfChanged;
|
|
903
|
+
private handleIsConnected;
|
|
904
|
+
private handleGetNodeInfo;
|
|
905
|
+
private handleDisconnect;
|
|
906
|
+
/**
|
|
907
|
+
* Show authentication button in the UI
|
|
908
|
+
*/
|
|
909
|
+
private showAuthButton;
|
|
910
|
+
private handleConnect;
|
|
911
|
+
/**
|
|
912
|
+
* Open the authentication popup window.
|
|
913
|
+
* Returns true if popup was opened, false if parent origin is not set.
|
|
914
|
+
*/
|
|
915
|
+
private openAuthPopup;
|
|
916
|
+
/**
|
|
917
|
+
* Handle login button click
|
|
918
|
+
*/
|
|
919
|
+
private handleLoginClick;
|
|
920
|
+
/**
|
|
921
|
+
* Handle disconnect button click
|
|
922
|
+
*/
|
|
923
|
+
private handleDisconnectClick;
|
|
924
|
+
/**
|
|
925
|
+
* Set container element for auth button
|
|
926
|
+
*/
|
|
927
|
+
setAuthButtonContainer(container: HTMLElement): void;
|
|
928
|
+
private handleUploadData;
|
|
929
|
+
private handleDownloadData;
|
|
930
|
+
private handleUploadFile;
|
|
931
|
+
private handleDownloadFile;
|
|
932
|
+
/** A folder as one manifest (#750); the Swarm-specific part is `uploadCollection`. */
|
|
933
|
+
private handleUploadFiles;
|
|
934
|
+
private handleListFiles;
|
|
935
|
+
private handleUploadChunk;
|
|
936
|
+
private handleDownloadChunk;
|
|
937
|
+
private handleGsocMine;
|
|
938
|
+
private handleGsocSend;
|
|
939
|
+
private handleSocUpload;
|
|
940
|
+
private handleSocRawUpload;
|
|
941
|
+
private handleSocDownload;
|
|
942
|
+
private handleSocRawDownload;
|
|
943
|
+
private handleSocGetOwner;
|
|
944
|
+
/**
|
|
945
|
+
* Derive a stable, app-scoped secret HMAC(appSecret, label) for the connected
|
|
946
|
+
* app. `appSecret` is genuinely secret (unlike the recoverable appKey public
|
|
947
|
+
* key) and never leaves the iframe — only the derived bytes are returned, so
|
|
948
|
+
* the app can seed an unguessable feed topic without exposing metadata (#520).
|
|
949
|
+
*/
|
|
950
|
+
private handleDeriveAppSecret;
|
|
951
|
+
/**
|
|
952
|
+
* Hand the authenticated page its site's chat key, the key whose address the
|
|
953
|
+
* site's chat binding names (docs/Chat-Binding.md), so the site's chat can
|
|
954
|
+
* sign with it. Same authentication rule as `handleDeriveAppSecret`, and like
|
|
955
|
+
* it the app secret itself never leaves the frame.
|
|
956
|
+
*/
|
|
957
|
+
private handleGetChatKey;
|
|
958
|
+
/**
|
|
959
|
+
* The site names a new Bee node for its session, as the source its viewer
|
|
960
|
+
* picked changes. Needs no sign-in: it says where this site's reads go,
|
|
961
|
+
* which is the site's to decide whether or not anyone is connected.
|
|
962
|
+
*
|
|
963
|
+
* The site naming its node also brings back a subsidised gateway the
|
|
964
|
+
* person's own custom node had switched off (`dropGatewayForOwnCustomNode`).
|
|
965
|
+
*/
|
|
966
|
+
private handleSetBeeApiUrl;
|
|
967
|
+
/**
|
|
968
|
+
* Stamp a chunk with the account's own stamp and hand the stamp back, for
|
|
969
|
+
* the app to send through its own Swarm access. The stamp takes the same
|
|
970
|
+
* write path as an upload, so it is returned only after the coordinator has
|
|
971
|
+
* published the partition counter, the guarantee an upload's ack carries.
|
|
972
|
+
* It also needs the stamper state saved, and refuses when that save fails.
|
|
973
|
+
* A restored session stamps only once its session check allows writes, as
|
|
974
|
+
* every write does (`ensureCanWrite`).
|
|
975
|
+
* The account's own lock and counter chunks never
|
|
976
|
+
* come through here, they stay inside the frame and go through its node.
|
|
977
|
+
*/
|
|
978
|
+
private handleStampChunk;
|
|
979
|
+
/**
|
|
980
|
+
* Save the counter a stamp spent before the stamp is returned. The write
|
|
981
|
+
* path's own flush only logs a failure, which an upload can afford and a
|
|
982
|
+
* stamp cannot: an account on one device keeps its counter nowhere else, so
|
|
983
|
+
* after a reload the slot would be handed out a second time.
|
|
984
|
+
*/
|
|
985
|
+
private persistStamperStateForStamp;
|
|
986
|
+
/**
|
|
987
|
+
* Stamp for this app if its limits allow, and count the stamp. Synchronous
|
|
988
|
+
* from the read of the usage to its write, so two requests can never both
|
|
989
|
+
* take the last stamp an app is allowed. The usage is written before the
|
|
990
|
+
* stamp is returned, so a stamp that could not be counted is never handed
|
|
991
|
+
* out.
|
|
992
|
+
*
|
|
993
|
+
* A single-owner chunk this app had stamped before in this device's lane is
|
|
994
|
+
* stamped again at the same slot with a newer timestamp, which a mutable
|
|
995
|
+
* batch takes as a replacement. That spends no slot, so only the count and
|
|
996
|
+
* the rate apply. A content chunk always takes a new slot.
|
|
997
|
+
*
|
|
998
|
+
* The slot a single-owner chunk went to is not written here. The caller
|
|
999
|
+
* runs `recordSlot` only once the counter that spent it is saved and
|
|
1000
|
+
* published: a record left by a stamp that failed to commit would outlive
|
|
1001
|
+
* the counter, the slot would go to another chunk after a reload, and the
|
|
1002
|
+
* next stamp of this address would replace that chunk on Bee.
|
|
1003
|
+
*/
|
|
1004
|
+
private stampWithinAppLimits;
|
|
1005
|
+
/** Stamp at a new slot within the app's limits, and the usage that counts it */
|
|
1006
|
+
private stampNewSlot;
|
|
1007
|
+
/**
|
|
1008
|
+
* The owner addresses of the account's own chunks and keys, for refusing a
|
|
1009
|
+
* single-owner chunk an app asks to have signed as one of them.
|
|
1010
|
+
*/
|
|
1011
|
+
private accountInternalOwners;
|
|
1012
|
+
private parseFeedTimestamp;
|
|
1013
|
+
private parseFeedIndex;
|
|
1014
|
+
private makeSequentialFeedIdentifier;
|
|
1015
|
+
private findLatestSequentialIndex;
|
|
1016
|
+
private sequentialNextIndex;
|
|
1017
|
+
private handleFeedGetOwner;
|
|
1018
|
+
private handleEpochFeedDownloadReference;
|
|
1019
|
+
private handleEpochFeedUploadReference;
|
|
1020
|
+
private handleSequentialFeedGetOwner;
|
|
1021
|
+
private resolveSequentialOwner;
|
|
1022
|
+
private parseSequentialPayload;
|
|
1023
|
+
private resolveSequentialIndex;
|
|
1024
|
+
private handleSequentialFeedDownloadPayload;
|
|
1025
|
+
private handleSequentialFeedDownloadRawPayload;
|
|
1026
|
+
private handleSequentialFeedDownloadReference;
|
|
1027
|
+
private buildSequentialPayload;
|
|
1028
|
+
private handleSequentialFeedUploadPayload;
|
|
1029
|
+
private handleSequentialFeedUploadRawPayload;
|
|
1030
|
+
private handleSequentialFeedUploadReference;
|
|
1031
|
+
/**
|
|
1032
|
+
* The account-wide ACT key (#519), or undefined when the session carries no
|
|
1033
|
+
* account to derive it from.
|
|
1034
|
+
*/
|
|
1035
|
+
private sharingSecret;
|
|
1036
|
+
/**
|
|
1037
|
+
* The keys this session may read or manage ACT data with: the origin-bound
|
|
1038
|
+
* app key, then the account-wide sharing key. The ACT module picks the one
|
|
1039
|
+
* a share names.
|
|
1040
|
+
*/
|
|
1041
|
+
private actKeys;
|
|
1042
|
+
private handleActUploadData;
|
|
1043
|
+
private handleActDownloadData;
|
|
1044
|
+
private handleActAddGrantees;
|
|
1045
|
+
private handleActRevokeGrantees;
|
|
1046
|
+
private handleActGetGrantees;
|
|
1047
|
+
private handleGetPostageBatch;
|
|
1048
|
+
/**
|
|
1049
|
+
* Handle createFeedManifest request
|
|
1050
|
+
* Creates a feed manifest for accessing feed content via URL
|
|
1051
|
+
*/
|
|
1052
|
+
private handleCreateFeedManifest;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Optional configuration for the proxy, supplied by the host trusted-domain app.
|
|
1056
|
+
*/
|
|
1057
|
+
export interface ProxyConfig {
|
|
1058
|
+
/**
|
|
1059
|
+
* The sites this frame serves (docs/Site-Allow-List.md). A parent whose
|
|
1060
|
+
* origin it refuses gets an `initError` and nothing else.
|
|
1061
|
+
*/
|
|
1062
|
+
siteAllowList: SiteAllowList;
|
|
1063
|
+
/**
|
|
1064
|
+
* PostageStamp contract address for on-chain stamp-TTL reads. Defaults to the
|
|
1065
|
+
* Gnosis mainnet deployment; set this (from a build-time env) to the local
|
|
1066
|
+
* bee-compose anvil address when developing against a local chain.
|
|
1067
|
+
*/
|
|
1068
|
+
postageStampContractAddress?: string;
|
|
1069
|
+
/**
|
|
1070
|
+
* Account-bus signaling server URL (e.g. `wss://bus.example.com/bus`,
|
|
1071
|
+
* `ws://localhost:5520` in dev). Unset disables the cross-partition/
|
|
1072
|
+
* cross-device bus transport (docs/Account-Bus.md).
|
|
1073
|
+
*/
|
|
1074
|
+
signalingUrl?: string;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Initialize the proxy (called from HTML page)
|
|
1078
|
+
*/
|
|
1079
|
+
export declare function initProxy(config: ProxyConfig): SwarmIdProxy;
|