@solarpunkltd/swarm-id 0.0.0-stage → 0.8.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/LICENSE +202 -0
- package/README.md +86 -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-DlH_ZkTn.js +23 -0
- package/dist/chunks/chat-binding-DlH_ZkTn.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 +665 -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 +1114 -0
- package/dist/swarm-id-proxy.d.ts +1031 -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 +347 -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 +3007 -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 +47 -0
- package/dist/utils/versioned-storage.d.ts +183 -0
- package/package.json +91 -3
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account-bus message schemas.
|
|
3
|
+
*
|
|
4
|
+
* Wire format is JSON-serializable (plain objects, hex strings) so the same
|
|
5
|
+
* messages can later travel transports that leave the browser (WebRTC,
|
|
6
|
+
* SWIP-60). Receivers parse with `BusMessageSchema`, which revives typed
|
|
7
|
+
* values (BatchId, PrivateKey, bigint) exactly like the device-state feed
|
|
8
|
+
* payloads do — see `docs/Account-Bus.md`.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
/**
|
|
12
|
+
* A full account-state snapshot from another live context. Merged with the
|
|
13
|
+
* same LWW primitives as a device-state feed payload, minus the session
|
|
14
|
+
* material above.
|
|
15
|
+
*/
|
|
16
|
+
export declare const AccountDeltaMessageSchema: z.ZodObject<{
|
|
17
|
+
type: z.ZodLiteral<"account-delta">;
|
|
18
|
+
snapshot: z.ZodObject<{
|
|
19
|
+
version: z.ZodLiteral<1>;
|
|
20
|
+
timestamp: z.ZodNumber;
|
|
21
|
+
accountId: z.ZodString;
|
|
22
|
+
metadata: z.ZodObject<{
|
|
23
|
+
accountName: z.ZodString;
|
|
24
|
+
defaultPostageStampBatchID: z.ZodOptional<z.ZodString>;
|
|
25
|
+
publicKey: z.ZodString;
|
|
26
|
+
settings: z.ZodOptional<z.ZodObject<{
|
|
27
|
+
appSessionDuration: z.ZodOptional<z.ZodNumber>;
|
|
28
|
+
}, z.core.$strip>>;
|
|
29
|
+
accountNameAt: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
defaultStampAt: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
settingsAt: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
createdAt: z.ZodNumber;
|
|
33
|
+
lastModified: z.ZodNumber;
|
|
34
|
+
devices: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
35
|
+
deviceId: z.ZodString;
|
|
36
|
+
createdAt: z.ZodNumber;
|
|
37
|
+
lastSignedInAt: z.ZodNumber;
|
|
38
|
+
name: z.ZodOptional<z.ZodString>;
|
|
39
|
+
nameUpdatedAt: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
removedAt: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
}, z.core.$strip>>>;
|
|
42
|
+
partitionCount: z.ZodDefault<z.ZodNumber>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
postageStamps: z.ZodArray<z.ZodObject<{
|
|
45
|
+
batchID: z.ZodPipe<z.ZodString, z.ZodTransform<import("@ethersphere/core-sdk").BatchId, string>>;
|
|
46
|
+
signerKey: z.ZodPipe<z.ZodString, z.ZodTransform<import("@ethersphere/core-sdk").PrivateKey, string>>;
|
|
47
|
+
name: z.ZodOptional<z.ZodString>;
|
|
48
|
+
nameUpdatedAt: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
utilization: z.ZodNumber;
|
|
50
|
+
usable: z.ZodBoolean;
|
|
51
|
+
depth: z.ZodNumber;
|
|
52
|
+
amount: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
|
|
53
|
+
bucketDepth: z.ZodNumber;
|
|
54
|
+
blockNumber: z.ZodNumber;
|
|
55
|
+
immutableFlag: z.ZodBoolean;
|
|
56
|
+
exists: z.ZodBoolean;
|
|
57
|
+
batchTTL: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
createdAt: z.ZodNumber;
|
|
59
|
+
updatedAt: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
deletedAt: z.ZodOptional<z.ZodNumber>;
|
|
61
|
+
}, z.core.$strip>>;
|
|
62
|
+
connectedApps: z.ZodArray<z.ZodObject<{
|
|
63
|
+
appName: z.ZodString;
|
|
64
|
+
appDescription: z.ZodOptional<z.ZodString>;
|
|
65
|
+
appIcon: z.ZodOptional<z.ZodString>;
|
|
66
|
+
appUrl: z.ZodString;
|
|
67
|
+
lastConnectedAt: z.ZodNumber;
|
|
68
|
+
postageStampBatchID: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<import("@ethersphere/core-sdk").BatchId, string>>>;
|
|
69
|
+
postageStampBatchIDAt: z.ZodOptional<z.ZodNumber>;
|
|
70
|
+
revokedAt: z.ZodOptional<z.ZodNumber>;
|
|
71
|
+
disconnectedAt: z.ZodOptional<z.ZodNumber>;
|
|
72
|
+
}, z.core.$strip>>;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
}, z.core.$strip>;
|
|
75
|
+
/**
|
|
76
|
+
* A context waiting for a batch-write partition (all slots held by live
|
|
77
|
+
* peers) asks live holders to yield. An idle holder releases via the normal
|
|
78
|
+
* Swarm lease protocol and answers with `lease-released`; holders mid-write
|
|
79
|
+
* ignore it. Repeats are harmless (yield-if-idle is a guarded no-op).
|
|
80
|
+
*
|
|
81
|
+
* `batchId` scopes the ask, because the lock it is a fast path for is scoped
|
|
82
|
+
* that way: lanes are per (batch, partition) since #589, so a holder on batch X
|
|
83
|
+
* and a waiter on batch Y contend for nothing. Answering across batches would
|
|
84
|
+
* drop a lease for a waiter it cannot help — the bus must not be broader than
|
|
85
|
+
* the lock it front-runs, any more than it may be narrower.
|
|
86
|
+
*
|
|
87
|
+
* `requestId` is 8 hex chars, fresh per poll round, and exists so exactly one
|
|
88
|
+
* holder answers: each derives the same permutation of the partitions from it
|
|
89
|
+
* and yields at its own rank (`swarm-id-proxy.ts`, `yieldRankDelayMs`). It is
|
|
90
|
+
* format-checked: the rank is `parseInt(requestId, 16)`, so an unconstrained
|
|
91
|
+
* string reintroduces the very bug this fixes (`NaN` or a negative collapses
|
|
92
|
+
* every holder onto the same rank), silently and with nothing to diagnose.
|
|
93
|
+
*/
|
|
94
|
+
export declare const LeaseRequestMessageSchema: z.ZodObject<{
|
|
95
|
+
type: z.ZodLiteral<"lease-request">;
|
|
96
|
+
accountId: z.ZodString;
|
|
97
|
+
batchId: z.ZodString;
|
|
98
|
+
fromDeviceId: z.ZodString;
|
|
99
|
+
requestId: z.ZodString;
|
|
100
|
+
}, z.core.$strip>;
|
|
101
|
+
/**
|
|
102
|
+
* The holder that drew the lowest rank for `requestId` is answering it. Sent
|
|
103
|
+
* BEFORE the release starts, because the release itself is two stamped Swarm
|
|
104
|
+
* writes — an order of magnitude longer than the rank step — so a signal sent
|
|
105
|
+
* after it would reach the other holders only once they had all begun yielding
|
|
106
|
+
* too, which is #576 again. Holders further down the order stand down on this;
|
|
107
|
+
* the waiter ignores it (no slot is free yet).
|
|
108
|
+
*/
|
|
109
|
+
export declare const LeaseClaimMessageSchema: z.ZodObject<{
|
|
110
|
+
type: z.ZodLiteral<"lease-claim">;
|
|
111
|
+
accountId: z.ZodString;
|
|
112
|
+
batchId: z.ZodString;
|
|
113
|
+
fromDeviceId: z.ZodString;
|
|
114
|
+
requestId: z.ZodString;
|
|
115
|
+
}, z.core.$strip>;
|
|
116
|
+
/**
|
|
117
|
+
* A holder released its partition (on request or teardown) — waiting peers
|
|
118
|
+
* wake their slot poll immediately instead of sleeping out the interval. The
|
|
119
|
+
* lock SOCs on Swarm remain the authority; this is purely a wake-up.
|
|
120
|
+
*
|
|
121
|
+
* `requestId` echoes the `lease-request` this answers, so holders further down
|
|
122
|
+
* the rank order can stand down. Absent on a teardown release, which answers
|
|
123
|
+
* no request.
|
|
124
|
+
*/
|
|
125
|
+
export declare const LeaseReleasedMessageSchema: z.ZodObject<{
|
|
126
|
+
type: z.ZodLiteral<"lease-released">;
|
|
127
|
+
accountId: z.ZodString;
|
|
128
|
+
batchId: z.ZodString;
|
|
129
|
+
partition: z.ZodNumber;
|
|
130
|
+
fromDeviceId: z.ZodString;
|
|
131
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
132
|
+
}, z.core.$strip>;
|
|
133
|
+
/**
|
|
134
|
+
* "This device is live." Published on joining the room and every
|
|
135
|
+
* `PRESENCE_INTERVAL_MS` (`presence.ts`); a receiver stamps its own clock, so
|
|
136
|
+
* there is no timestamp to skew and a replay (#604) only refreshes a device it
|
|
137
|
+
* has already heard. A peer on an older bundle drops it at the schema — a new
|
|
138
|
+
* kind, not an envelope change, so nothing to deploy in step.
|
|
139
|
+
*/
|
|
140
|
+
export declare const PresenceMessageSchema: z.ZodObject<{
|
|
141
|
+
type: z.ZodLiteral<"presence">;
|
|
142
|
+
accountId: z.ZodString;
|
|
143
|
+
fromDeviceId: z.ZodString;
|
|
144
|
+
}, z.core.$strip>;
|
|
145
|
+
export declare const BusMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
146
|
+
type: z.ZodLiteral<"utilization-updated">;
|
|
147
|
+
batchId: z.ZodString;
|
|
148
|
+
partition: z.ZodNumber;
|
|
149
|
+
partitionCount: z.ZodNumber;
|
|
150
|
+
buckets: z.ZodArray<z.ZodObject<{
|
|
151
|
+
index: z.ZodNumber;
|
|
152
|
+
value: z.ZodNumber;
|
|
153
|
+
}, z.core.$strip>>;
|
|
154
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
155
|
+
type: z.ZodLiteral<"account-delta">;
|
|
156
|
+
snapshot: z.ZodObject<{
|
|
157
|
+
version: z.ZodLiteral<1>;
|
|
158
|
+
timestamp: z.ZodNumber;
|
|
159
|
+
accountId: z.ZodString;
|
|
160
|
+
metadata: z.ZodObject<{
|
|
161
|
+
accountName: z.ZodString;
|
|
162
|
+
defaultPostageStampBatchID: z.ZodOptional<z.ZodString>;
|
|
163
|
+
publicKey: z.ZodString;
|
|
164
|
+
settings: z.ZodOptional<z.ZodObject<{
|
|
165
|
+
appSessionDuration: z.ZodOptional<z.ZodNumber>;
|
|
166
|
+
}, z.core.$strip>>;
|
|
167
|
+
accountNameAt: z.ZodOptional<z.ZodNumber>;
|
|
168
|
+
defaultStampAt: z.ZodOptional<z.ZodNumber>;
|
|
169
|
+
settingsAt: z.ZodOptional<z.ZodNumber>;
|
|
170
|
+
createdAt: z.ZodNumber;
|
|
171
|
+
lastModified: z.ZodNumber;
|
|
172
|
+
devices: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
173
|
+
deviceId: z.ZodString;
|
|
174
|
+
createdAt: z.ZodNumber;
|
|
175
|
+
lastSignedInAt: z.ZodNumber;
|
|
176
|
+
name: z.ZodOptional<z.ZodString>;
|
|
177
|
+
nameUpdatedAt: z.ZodOptional<z.ZodNumber>;
|
|
178
|
+
removedAt: z.ZodOptional<z.ZodNumber>;
|
|
179
|
+
}, z.core.$strip>>>;
|
|
180
|
+
partitionCount: z.ZodDefault<z.ZodNumber>;
|
|
181
|
+
}, z.core.$strip>;
|
|
182
|
+
postageStamps: z.ZodArray<z.ZodObject<{
|
|
183
|
+
batchID: z.ZodPipe<z.ZodString, z.ZodTransform<import("@ethersphere/core-sdk").BatchId, string>>;
|
|
184
|
+
signerKey: z.ZodPipe<z.ZodString, z.ZodTransform<import("@ethersphere/core-sdk").PrivateKey, string>>;
|
|
185
|
+
name: z.ZodOptional<z.ZodString>;
|
|
186
|
+
nameUpdatedAt: z.ZodOptional<z.ZodNumber>;
|
|
187
|
+
utilization: z.ZodNumber;
|
|
188
|
+
usable: z.ZodBoolean;
|
|
189
|
+
depth: z.ZodNumber;
|
|
190
|
+
amount: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
|
|
191
|
+
bucketDepth: z.ZodNumber;
|
|
192
|
+
blockNumber: z.ZodNumber;
|
|
193
|
+
immutableFlag: z.ZodBoolean;
|
|
194
|
+
exists: z.ZodBoolean;
|
|
195
|
+
batchTTL: z.ZodOptional<z.ZodNumber>;
|
|
196
|
+
createdAt: z.ZodNumber;
|
|
197
|
+
updatedAt: z.ZodOptional<z.ZodNumber>;
|
|
198
|
+
deletedAt: z.ZodOptional<z.ZodNumber>;
|
|
199
|
+
}, z.core.$strip>>;
|
|
200
|
+
connectedApps: z.ZodArray<z.ZodObject<{
|
|
201
|
+
appName: z.ZodString;
|
|
202
|
+
appDescription: z.ZodOptional<z.ZodString>;
|
|
203
|
+
appIcon: z.ZodOptional<z.ZodString>;
|
|
204
|
+
appUrl: z.ZodString;
|
|
205
|
+
lastConnectedAt: z.ZodNumber;
|
|
206
|
+
postageStampBatchID: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<import("@ethersphere/core-sdk").BatchId, string>>>;
|
|
207
|
+
postageStampBatchIDAt: z.ZodOptional<z.ZodNumber>;
|
|
208
|
+
revokedAt: z.ZodOptional<z.ZodNumber>;
|
|
209
|
+
disconnectedAt: z.ZodOptional<z.ZodNumber>;
|
|
210
|
+
}, z.core.$strip>>;
|
|
211
|
+
}, z.core.$strip>;
|
|
212
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
213
|
+
type: z.ZodLiteral<"lease-request">;
|
|
214
|
+
accountId: z.ZodString;
|
|
215
|
+
batchId: z.ZodString;
|
|
216
|
+
fromDeviceId: z.ZodString;
|
|
217
|
+
requestId: z.ZodString;
|
|
218
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
219
|
+
type: z.ZodLiteral<"lease-claim">;
|
|
220
|
+
accountId: z.ZodString;
|
|
221
|
+
batchId: z.ZodString;
|
|
222
|
+
fromDeviceId: z.ZodString;
|
|
223
|
+
requestId: z.ZodString;
|
|
224
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
225
|
+
type: z.ZodLiteral<"lease-released">;
|
|
226
|
+
accountId: z.ZodString;
|
|
227
|
+
batchId: z.ZodString;
|
|
228
|
+
partition: z.ZodNumber;
|
|
229
|
+
fromDeviceId: z.ZodString;
|
|
230
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
231
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
232
|
+
type: z.ZodLiteral<"presence">;
|
|
233
|
+
accountId: z.ZodString;
|
|
234
|
+
fromDeviceId: z.ZodString;
|
|
235
|
+
}, z.core.$strip>], "type">;
|
|
236
|
+
export type AccountDeltaMessage = z.infer<typeof AccountDeltaMessageSchema>;
|
|
237
|
+
/** The wire form of a delta: hex strings, not revived `BatchId`/`PrivateKey`. */
|
|
238
|
+
export type AccountDeltaInput = z.input<typeof AccountDeltaMessageSchema>;
|
|
239
|
+
export type BusMessage = z.infer<typeof BusMessageSchema>;
|
|
240
|
+
/** What publishers hand to the bus: the JSON-serializable wire form. */
|
|
241
|
+
export type BusMessageInput = z.input<typeof BusMessageSchema>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who is live on the account bus, as seen from one context.
|
|
3
|
+
*
|
|
4
|
+
* Every context in the room publishes a `presence` on join and every
|
|
5
|
+
* `PRESENCE_INTERVAL_MS`; a receiver notes the time it heard each device. A
|
|
6
|
+
* device unheard for `PRESENCE_MAX_AGE_MS` is no longer live. That is the whole
|
|
7
|
+
* mechanism: the bus already knows who is in the room, this only ties room
|
|
8
|
+
* membership to a `deviceId`.
|
|
9
|
+
*
|
|
10
|
+
* A clean departure does not wait for the window. The signaling server sends
|
|
11
|
+
* `peer-left` when a socket closes — including one its own reaper terminated,
|
|
12
|
+
* so a crash and a dropped network count too — and `forgetPeer` drops the
|
|
13
|
+
* device that socket carried (#572). Ageing is the backstop for what that
|
|
14
|
+
* cannot see: a peer the server has not yet reaped, and a device heard only
|
|
15
|
+
* over the local transport. A leave message of our own is not an option — a
|
|
16
|
+
* publish encrypts before it sends, and a page being torn down never gets
|
|
17
|
+
* back to the send.
|
|
18
|
+
*
|
|
19
|
+
* In memory only, and deliberately never persisted or published as state. A
|
|
20
|
+
* `lastSeenAt` that rode the account snapshot would make every publish a byte
|
|
21
|
+
* change to fold and re-persist, and the unpartitioned proxy answers every
|
|
22
|
+
* storage event with a bus delta and a stamped feed write — a loop that never
|
|
23
|
+
* settles, even on one device. Durable truth (`lastSignedInAt`) keeps meaning
|
|
24
|
+
* "signed in"; liveness lives here and is rebuilt from scratch on every load.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PRESENCE_INTERVAL_MS = 20000;
|
|
27
|
+
/**
|
|
28
|
+
* Three missed beats — of the THROTTLED interval, not ours. A window of three
|
|
29
|
+
* of our own beats sits exactly at a hidden tab's clamped cadence, so a
|
|
30
|
+
* backgrounded-but-live device would flap in and out of its peers' live sets:
|
|
31
|
+
* a blinking badge, and a coordinator repeatedly re-including the same peer in
|
|
32
|
+
* an idle-yield round. Being slow to drop a device that really left costs only
|
|
33
|
+
* an absent intent read, which is why the window errs long. Still well past
|
|
34
|
+
* `LEASE_TTL_MS` and far inside `KNOWN_DEVICE_MAX_AGE_MS`.
|
|
35
|
+
*/
|
|
36
|
+
export declare const PRESENCE_MAX_AGE_MS: number;
|
|
37
|
+
export declare class PresenceTracker {
|
|
38
|
+
private seen;
|
|
39
|
+
/**
|
|
40
|
+
* Note a beat. `peerId` is the transport's own name for whoever sent it —
|
|
41
|
+
* present on the signaling transport, absent on the local one, which has no
|
|
42
|
+
* peers. It is what lets a departure be attributed to a device (#572).
|
|
43
|
+
*/
|
|
44
|
+
observe(deviceId: string, nowMs: number, peerId?: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* A socket left the room, so whatever it carried is gone with it.
|
|
47
|
+
*
|
|
48
|
+
* Only when it was the device's LAST socket: two tabs of one dApp share a
|
|
49
|
+
* partition, and therefore a `deviceId`, while holding a socket each — one
|
|
50
|
+
* of them closing is not the device leaving. A device heard only over the
|
|
51
|
+
* local transport has no socket to lose and is left to age out.
|
|
52
|
+
*
|
|
53
|
+
* Every entry is checked rather than stopping at the first match. Nothing
|
|
54
|
+
* here enforces that one socket only ever beats under one `deviceId`, and a
|
|
55
|
+
* device left holding a dead socket id could only age out — the three-minute
|
|
56
|
+
* wait this exists to remove. The loop is over a handful of entries.
|
|
57
|
+
*/
|
|
58
|
+
forgetPeer(peerId: string): void;
|
|
59
|
+
/** Devices heard within `PRESENCE_MAX_AGE_MS` of `nowMs`. */
|
|
60
|
+
liveDeviceIds(nowMs: number): string[];
|
|
61
|
+
clear(): void;
|
|
62
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { BusTransport } from "./account-bus";
|
|
2
|
+
import type { BusMessageInput } from "./messages";
|
|
3
|
+
export interface SignalingTransportOptions {
|
|
4
|
+
/** Signaling server URL, e.g. `wss://bus.example.com/bus`. */
|
|
5
|
+
url: string;
|
|
6
|
+
/** Account-derived room topic (`deriveBusContext`). */
|
|
7
|
+
topic: string;
|
|
8
|
+
/** Account-derived AES-GCM envelope key (`deriveBusContext`). */
|
|
9
|
+
encryptionKey: CryptoKey;
|
|
10
|
+
/**
|
|
11
|
+
* Factory for the WebRTC upgrade; omit to stay relay-only (e.g. in
|
|
12
|
+
* environments without RTCPeerConnection). No STUN/TURN is configured by
|
|
13
|
+
* default: same-device loopback and LAN peers connect via host candidates,
|
|
14
|
+
* everything else falls back to the relay.
|
|
15
|
+
*/
|
|
16
|
+
createPeerConnection?: () => RTCPeerConnection;
|
|
17
|
+
}
|
|
18
|
+
export declare class SignalingTransport implements BusTransport {
|
|
19
|
+
readonly local = false;
|
|
20
|
+
private options;
|
|
21
|
+
private socket;
|
|
22
|
+
private peers;
|
|
23
|
+
private handlers;
|
|
24
|
+
private peerLeftHandlers;
|
|
25
|
+
private closed;
|
|
26
|
+
private reconnectDelayMs;
|
|
27
|
+
private reconnectTimer;
|
|
28
|
+
/** In-flight `deliver` calls, awaited by `close()` so a teardown
|
|
29
|
+
* announcement published in the closing tick still reaches the wire. */
|
|
30
|
+
private pendingDeliveries;
|
|
31
|
+
constructor(options: SignalingTransportOptions);
|
|
32
|
+
/** Live peers currently visible in the room (test/debug aid). */
|
|
33
|
+
get peerCount(): number;
|
|
34
|
+
publish(message: BusMessageInput): void;
|
|
35
|
+
subscribe(handler: (raw: unknown, from?: string) => void): () => void;
|
|
36
|
+
onPeerLeft(handler: (peerId: string) => void): () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Stops reconnecting at once, but lets in-flight publishes reach the wire
|
|
39
|
+
* first: `deliver` awaits envelope encryption, and callers announce a
|
|
40
|
+
* teardown (`lease-released`) and close the bus in the same tick, so tearing
|
|
41
|
+
* the socket down synchronously would drop exactly the message a waiting
|
|
42
|
+
* peer is owed. Each pending delivery is a single encrypt plus synchronous
|
|
43
|
+
* sends, so this settles promptly — it is not a flush of a send queue.
|
|
44
|
+
*/
|
|
45
|
+
close(): void;
|
|
46
|
+
private shutdown;
|
|
47
|
+
private connect;
|
|
48
|
+
private scheduleReconnect;
|
|
49
|
+
private deliver;
|
|
50
|
+
private sendToServer;
|
|
51
|
+
private handleServerMessage;
|
|
52
|
+
private receiveEnvelope;
|
|
53
|
+
private initiatePeerConnection;
|
|
54
|
+
private handleSignal;
|
|
55
|
+
/**
|
|
56
|
+
* Install a freshly created connection on a peer, closing whatever was there.
|
|
57
|
+
* A duplicate or renegotiated offer used to overwrite the field and leak the
|
|
58
|
+
* previous `RTCPeerConnection`, which keeps its ICE agent and sockets alive.
|
|
59
|
+
* The candidate queue resets with it: the new connection has no remote
|
|
60
|
+
* description, and candidates gathered for the old one are meaningless.
|
|
61
|
+
*/
|
|
62
|
+
private adoptConnection;
|
|
63
|
+
/**
|
|
64
|
+
* Replay the candidates that arrived before the remote description. One bad
|
|
65
|
+
* candidate must not strand the rest, so each is applied on its own.
|
|
66
|
+
*
|
|
67
|
+
* Takes the connection whose description just landed, and does nothing if the
|
|
68
|
+
* peer has moved on to another: signals are handled concurrently, so a second
|
|
69
|
+
* offer can be adopted while this one is still awaiting
|
|
70
|
+
* `setRemoteDescription`, and marking the flag then would describe a
|
|
71
|
+
* connection that has no remote description — re-opening the drop window this
|
|
72
|
+
* queue exists to close, for candidates that arrive before the new
|
|
73
|
+
* negotiation finishes.
|
|
74
|
+
*/
|
|
75
|
+
private flushPendingCandidates;
|
|
76
|
+
private adoptDataChannel;
|
|
77
|
+
private forwardIceCandidates;
|
|
78
|
+
private sendSignal;
|
|
79
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Reference } from "@ethersphere/bee-js";
|
|
2
|
+
/**
|
|
3
|
+
* Calculate a Binary Merkle Tree hash for a chunk
|
|
4
|
+
*
|
|
5
|
+
* The BMT chunk address is the hash of the 8 byte span and the root
|
|
6
|
+
* hash of a binary Merkle tree (BMT) built on the 32-byte segments
|
|
7
|
+
* of the underlying data.
|
|
8
|
+
*
|
|
9
|
+
* If the chunk content is less than 4k, the hash is calculated as
|
|
10
|
+
* if the chunk was padded with all zeros up to 4096 bytes.
|
|
11
|
+
*
|
|
12
|
+
* @param chunkContent Chunk data including span and payload as well
|
|
13
|
+
*
|
|
14
|
+
* @returns the keccak256 hash in a byte array
|
|
15
|
+
*/
|
|
16
|
+
export declare function calculateChunkAddress(chunkContent: Uint8Array): Reference;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Bytes, Reference, Span } from "@ethersphere/bee-js";
|
|
2
|
+
/**
|
|
3
|
+
* Content addressed chunk interface
|
|
4
|
+
*/
|
|
5
|
+
export interface ContentAddressedChunk {
|
|
6
|
+
readonly data: Uint8Array;
|
|
7
|
+
readonly span: Span;
|
|
8
|
+
readonly payload: Bytes;
|
|
9
|
+
readonly address: Reference;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a content addressed chunk from payload data
|
|
13
|
+
*
|
|
14
|
+
* @param payloadBytes the data to be stored in the chunk (1-4096 bytes)
|
|
15
|
+
* @param spanOverride optional span value to use instead of payload length
|
|
16
|
+
*/
|
|
17
|
+
export declare function makeContentAddressedChunk(payloadBytes: Uint8Array | string, spanOverride?: Span | bigint): ContentAddressedChunk;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const MIN_PAYLOAD_SIZE = 0;
|
|
2
|
+
export declare const MAX_PAYLOAD_SIZE = 4096;
|
|
3
|
+
export declare const SPAN_SIZE = 8;
|
|
4
|
+
export declare const UNENCRYPTED_REF_SIZE = 32;
|
|
5
|
+
export declare const ENCRYPTED_REF_SIZE = 64;
|
|
6
|
+
export declare const IDENTIFIER_SIZE = 32;
|
|
7
|
+
export declare const SIGNATURE_SIZE = 65;
|
|
8
|
+
export declare const SOC_HEADER_SIZE: number;
|
|
9
|
+
export declare const DEFAULT_DOWNLOAD_CONCURRENCY = 64;
|
|
10
|
+
export declare const DEFAULT_UPLOAD_CONCURRENCY = 8;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Bytes, Reference, Span } from "@ethersphere/bee-js";
|
|
2
|
+
import { type Key } from "./encryption";
|
|
3
|
+
/**
|
|
4
|
+
* Encrypted chunk interface
|
|
5
|
+
*
|
|
6
|
+
* The reference includes both the chunk address and the encryption key (64 bytes total)
|
|
7
|
+
*/
|
|
8
|
+
export interface EncryptedChunk {
|
|
9
|
+
readonly data: Uint8Array;
|
|
10
|
+
readonly encryptionKey: Key;
|
|
11
|
+
readonly span: Span;
|
|
12
|
+
readonly payload: Bytes;
|
|
13
|
+
readonly address: Reference;
|
|
14
|
+
readonly reference: Reference;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates an encrypted content addressed chunk
|
|
18
|
+
*
|
|
19
|
+
* Process:
|
|
20
|
+
* 1. Create chunk with span + payload
|
|
21
|
+
* 2. Encrypt the chunk data
|
|
22
|
+
* 3. Calculate BMT hash on encrypted data
|
|
23
|
+
* 4. Return reference = address + encryption key (64 bytes)
|
|
24
|
+
*
|
|
25
|
+
* @param payloadBytes the data to be stored in the chunk
|
|
26
|
+
* @param encryptionKey optional encryption key (if not provided, a random key will be generated)
|
|
27
|
+
*/
|
|
28
|
+
export declare function makeEncryptedContentAddressedChunk(payloadBytes: Uint8Array | string, encryptionKey?: Key): EncryptedChunk;
|
|
29
|
+
/**
|
|
30
|
+
* Decrypts an encrypted chunk given the encryption key
|
|
31
|
+
*
|
|
32
|
+
* @param encryptedChunkData The encrypted chunk data (span + payload)
|
|
33
|
+
* @param encryptionKey The 32-byte encryption key
|
|
34
|
+
*/
|
|
35
|
+
export declare function decryptEncryptedChunk(encryptedChunkData: Uint8Array, encryptionKey: Key): Uint8Array;
|
|
36
|
+
/**
|
|
37
|
+
* Extracts encryption key from a 64-byte encrypted reference
|
|
38
|
+
*
|
|
39
|
+
* @param reference 64-byte reference (address + key)
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractEncryptionKey(reference: Reference): Key;
|
|
42
|
+
/**
|
|
43
|
+
* Extracts the chunk address from a 64-byte encrypted reference
|
|
44
|
+
*
|
|
45
|
+
* @param reference 64-byte reference (address + key)
|
|
46
|
+
*/
|
|
47
|
+
export declare function extractChunkAddress(reference: Reference): Reference;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export declare const KEY_LENGTH = 32;
|
|
2
|
+
export declare const REFERENCE_SIZE = 64;
|
|
3
|
+
export type Key = Uint8Array;
|
|
4
|
+
export interface Encrypter {
|
|
5
|
+
key(): Key;
|
|
6
|
+
encrypt(data: Uint8Array): Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
export interface Decrypter {
|
|
9
|
+
key(): Key;
|
|
10
|
+
decrypt(data: Uint8Array): Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
export interface EncryptionInterface extends Encrypter, Decrypter {
|
|
13
|
+
reset(): void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Core encryption class implementing CTR-mode encryption with Keccak256
|
|
17
|
+
* This matches the Go implementation in bee/pkg/encryption/encryption.go
|
|
18
|
+
*/
|
|
19
|
+
export declare class Encryption implements EncryptionInterface {
|
|
20
|
+
private readonly encryptionKey;
|
|
21
|
+
private readonly keyLen;
|
|
22
|
+
private readonly padding;
|
|
23
|
+
private index;
|
|
24
|
+
private readonly initCtr;
|
|
25
|
+
constructor(key: Key, padding: number, initCtr: number);
|
|
26
|
+
key(): Key;
|
|
27
|
+
/**
|
|
28
|
+
* Encrypts data with optional padding
|
|
29
|
+
*/
|
|
30
|
+
encrypt(data: Uint8Array): Uint8Array;
|
|
31
|
+
/**
|
|
32
|
+
* Decrypts data (caller must know original length if padding was used)
|
|
33
|
+
*/
|
|
34
|
+
decrypt(data: Uint8Array): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Resets the counter - only safe to call after encryption/decryption is completed
|
|
37
|
+
*/
|
|
38
|
+
reset(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Transforms data by splitting into key-length segments and encrypting sequentially
|
|
41
|
+
*/
|
|
42
|
+
private transform;
|
|
43
|
+
/**
|
|
44
|
+
* Segment-wise transformation using XOR with Keccak256-derived keys
|
|
45
|
+
* Matches the Go implementation's Transcrypt function
|
|
46
|
+
*/
|
|
47
|
+
private transcrypt;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generates a cryptographically secure random key
|
|
51
|
+
*/
|
|
52
|
+
export declare function generateRandomKey(length?: number): Key;
|
|
53
|
+
/**
|
|
54
|
+
* Creates encryption interface for chunk span (first 8 bytes)
|
|
55
|
+
*/
|
|
56
|
+
export declare function newSpanEncryption(key: Key): EncryptionInterface;
|
|
57
|
+
/**
|
|
58
|
+
* Creates encryption interface for chunk data
|
|
59
|
+
*/
|
|
60
|
+
export declare function newDataEncryption(key: Key): EncryptionInterface;
|
|
61
|
+
export interface ChunkEncrypter {
|
|
62
|
+
encryptChunk(chunkData: Uint8Array, key?: Key): {
|
|
63
|
+
key: Key;
|
|
64
|
+
encryptedSpan: Uint8Array;
|
|
65
|
+
encryptedData: Uint8Array;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Default chunk encrypter implementation
|
|
70
|
+
*/
|
|
71
|
+
export declare class DefaultChunkEncrypter implements ChunkEncrypter {
|
|
72
|
+
encryptChunk(chunkData: Uint8Array, key?: Key): {
|
|
73
|
+
key: Key;
|
|
74
|
+
encryptedSpan: Uint8Array;
|
|
75
|
+
encryptedData: Uint8Array;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a new chunk encrypter
|
|
80
|
+
*/
|
|
81
|
+
export declare function newChunkEncrypter(): ChunkEncrypter;
|
|
82
|
+
/**
|
|
83
|
+
* Decrypts encrypted chunk data using the provided encryption key
|
|
84
|
+
*/
|
|
85
|
+
export declare function decryptChunkData(key: Key, encryptedChunkData: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE, SPAN_SIZE, UNENCRYPTED_REF_SIZE, ENCRYPTED_REF_SIZE, IDENTIFIER_SIZE, SIGNATURE_SIZE, SOC_HEADER_SIZE, DEFAULT_DOWNLOAD_CONCURRENCY, DEFAULT_UPLOAD_CONCURRENCY, } from "./constants";
|
|
2
|
+
export { type Key, type ChunkEncrypter, type Encrypter, type Decrypter, type EncryptionInterface, KEY_LENGTH, REFERENCE_SIZE, newChunkEncrypter, decryptChunkData, generateRandomKey, newSpanEncryption, newDataEncryption, Encryption, DefaultChunkEncrypter, } from "./encryption";
|
|
3
|
+
export { calculateChunkAddress } from "./bmt";
|
|
4
|
+
export { type ContentAddressedChunk, makeContentAddressedChunk } from "./cac";
|
|
5
|
+
export { type EncryptedChunk, makeEncryptedContentAddressedChunk, decryptEncryptedChunk, extractEncryptionKey, extractChunkAddress, } from "./encrypted-cac";
|