@tinycloud/share-sdk 1.2.0-beta.3 → 1.2.0-beta.4

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.
@@ -0,0 +1,1063 @@
1
+ import { a as SenderShareRecordStorage, S as SenderShareRecord } from './history-CyT6x8h4.js';
2
+ export { f as SenderShareRecordProofs } from './history-CyT6x8h4.js';
3
+ import { j as PublishedShareDeliveryMaterial, q as SignedDelegation, g as OwnerSharePolicyRegistration } from './owner-policy-BqzRlkb6.js';
4
+ import { ShareEnvelopeV3 } from '@tinycloud/share-envelope';
5
+
6
+ /**
7
+ * Identifiers and the key classifier for the shared share index
8
+ * (TC-1432 design C §1). All keys here are relative to the vault prefix
9
+ * `sender-history/v2/records/`.
10
+ */
11
+ /** Vault key prefix of every share record and of the index documents. */
12
+ declare const SHARE_RECORDS_VAULT_PREFIX = "sender-history/v2/records/";
13
+ /** The main index document key. */
14
+ declare const INDEX_MAIN_KEY = "_index/main";
15
+ /** Length of a DID-derived agent id (132 bits of base64url). */
16
+ declare const AGENT_ID_LENGTH = 22;
17
+ /** `^[A-Za-z0-9_-]{1,512}$`, excluding the reserved first segments `_index` and `agents`. */
18
+ declare function isShareId(value: unknown): value is string;
19
+ declare function isAgentId(value: unknown): value is string;
20
+ /**
21
+ * The first 22 base64url characters of
22
+ * `sha256("xyz.tinycloud.share/agent-id/v1|" + agentDid)`. `agentDid` is the
23
+ * DID of the key that signs the agent's invocations and delegations, so a new
24
+ * key gets a new prefix.
25
+ */
26
+ declare function agentIdFromDid(agentDid: string): string;
27
+ declare function agentRecordKey(agentId: string, shareId: string): string;
28
+ /** Explicit list prefix (with the trailing slash) of one agent's records, relative to `records/`. */
29
+ declare function agentRecordPrefix(agentId: string): string;
30
+ type ClassifiedRecordKey = {
31
+ readonly kind: "index";
32
+ readonly name: string;
33
+ } | {
34
+ readonly kind: "web";
35
+ readonly shareId: string;
36
+ } | {
37
+ readonly kind: "agent";
38
+ readonly agentId: string;
39
+ readonly shareId: string;
40
+ }
41
+ /**
42
+ * Never fetched, never rendered. `agentId`/`suffix` are set when the key
43
+ * lies inside an agent prefix (`agents/<valid id>/…`), so it is counted
44
+ * once against that agent; any other key can only be written by owner
45
+ * clients and is skipped without counting.
46
+ */
47
+ | {
48
+ readonly kind: "rejected";
49
+ readonly agentId?: string;
50
+ readonly suffix?: string;
51
+ };
52
+ /**
53
+ * Classify one raw listed key (relative to `records/`), before any
54
+ * needs-attention logic. A key containing `%`, `\`, a `.`/`..` segment, an
55
+ * empty segment or a leading `/` is rejected first.
56
+ */
57
+ declare function classifyRecordKey(key: string): ClassifiedRecordKey;
58
+
59
+ /**
60
+ * Share link validator (design C §5 check 6, §6 Open/Copy). A link is
61
+ * accepted only when it is https on a configured share origin and has one of
62
+ * the share link shapes:
63
+ * - `/s/<cid>#k=<key>`: registry envelope link;
64
+ * - `/s/inline#v=2&p=<payload>`: Node Policy/v3 sealed-inline link (what
65
+ * `publishAddressedShare` emits);
66
+ * - `/viewer#tc1=<token>`: native bearer link (`createNativeShare`).
67
+ *
68
+ * The result names the shape only; the link itself is never copied into it.
69
+ */
70
+ type ShareLinkShape = "envelope" | "inline" | "native";
71
+ type ShareLinkCheck = {
72
+ readonly ok: true;
73
+ readonly shape: ShareLinkShape;
74
+ } | {
75
+ readonly ok: false;
76
+ };
77
+ declare function validateShareLink(link: unknown, shareOrigins: readonly string[]): ShareLinkCheck;
78
+
79
+ /**
80
+ * The agent's key-encryption key (design C §3, V3-6). Security-sensitive:
81
+ * keep this module small and free of logging.
82
+ *
83
+ * KEK_a = HKDF-SHA256(ikm = private key bytes,
84
+ * salt = "xyz.tinycloud.share/agent-kek/v1",
85
+ * info = agentDid)
86
+ * kid = HKDF-SHA256(same ikm and salt, info = "kid")[0..8]
87
+ *
88
+ * The private key is the one the agent signs its invocations and delegations
89
+ * with (the session key when it has a private part, otherwise the profile
90
+ * key), and `agentDid` is that key's DID. KEK_a is imported non-extractable,
91
+ * for AES-GCM wrapping only.
92
+ */
93
+ interface AgentKeyMaterial {
94
+ readonly agentDid: string;
95
+ /** base64url of the first 8 bytes of HKDF(…, info "kid"). */
96
+ readonly kid: string;
97
+ readonly kek: CryptoKey;
98
+ }
99
+ /**
100
+ * Derive KEK_a and kid. `privateKeyBytes` is read, never retained; the caller
101
+ * owns (and should zero) it.
102
+ */
103
+ declare function deriveAgentKeyMaterial(privateKeyBytes: Uint8Array, agentDid: string): Promise<AgentKeyMaterial>;
104
+
105
+ /**
106
+ * Agent record codec: the double-wrapped data key (design C §3, R1, V3-3..6).
107
+ * Security-sensitive: keep this module small; nothing here logs, and no
108
+ * error carries key bytes.
109
+ *
110
+ * One stored value per record, `vault/…/records/agents/<agentId>/<shareId>`:
111
+ *
112
+ * { hdr: { v: 1, agentDid, kid, alg: "A256GCM", ownerNetworkId, recordV },
113
+ * ct: b64url(iv ‖ AES-256-GCM(encKey, record, aad = B)),
114
+ * commit: b64url(HMAC-SHA256(commitKey, B)),
115
+ * wrapAgent: b64url(iv ‖ AES-256-GCM(KEK_a, DEK, aad = B)) | null,
116
+ * wrapOwner: owner-network envelope of DEK with aad = B }
117
+ *
118
+ * B = "xyz.tinycloud.share/agent-record/v1|" + spaceId + "|" + vaultKey
119
+ * + "|" + agentDid + "|" + ownerNetworkId (EVM addresses lowercased)
120
+ * encKey = HKDF-SHA256(DEK, info "enc|" + B)
121
+ * commitKey = HKDF-SHA256(DEK, info "commit|" + B)
122
+ *
123
+ * Every seal takes a fresh random 32-byte DEK and fresh random 96-bit nonces.
124
+ * The DEK itself is never used as a key. Opening requires the unwrapped DEK
125
+ * to be exactly 32 bytes, checks `commit` in constant time, and zeroes the
126
+ * DEK on every path.
127
+ */
128
+ declare const AGENT_RECORD_VERSION = 1;
129
+ /** Record schema versions this build understands. */
130
+ declare const SUPPORTED_AGENT_RECORD_V = 1;
131
+ /**
132
+ * Stored agent records larger than this are refused before any crypto.
133
+ * Design C proposed 16 KiB (open decision 1); a real CLI bearer record that
134
+ * carries its link and its full chain (C1: link UCAN + owner→session CACAO)
135
+ * measured 16,918 B on a local node, so the cap is 32 KiB.
136
+ */
137
+ declare const MAX_AGENT_RECORD_BYTES: number;
138
+ type AgentRecordFailure = "size" | "shape" | "header" | "newer" | "decrypt" | "commit";
139
+ /** A refused agent record. The message is fixed per reason. */
140
+ declare class AgentRecordError extends Error {
141
+ readonly reason: AgentRecordFailure;
142
+ constructor(reason: AgentRecordFailure);
143
+ }
144
+ /** Thrown by an {@link OwnerNetworkUnwrap} when the decrypt service is unreachable (not a refusal). */
145
+ declare class OwnerUnwrapUnavailableError extends Error {
146
+ constructor();
147
+ }
148
+ interface AgentRecordHeader {
149
+ readonly v: 1;
150
+ readonly agentDid: string;
151
+ readonly kid: string | null;
152
+ readonly alg: "A256GCM";
153
+ readonly ownerNetworkId: string;
154
+ readonly recordV: number;
155
+ }
156
+ interface StoredAgentRecord {
157
+ readonly hdr: AgentRecordHeader;
158
+ readonly ct: string;
159
+ readonly commit: string;
160
+ readonly wrapAgent: string | null;
161
+ readonly wrapOwner: Readonly<Record<string, unknown>>;
162
+ }
163
+ /** Encrypts the DEK to the owner network (needs no capability). */
164
+ interface OwnerNetworkWrap {
165
+ wrap(dek: Uint8Array, aad: Uint8Array): Promise<Readonly<Record<string, unknown>>>;
166
+ }
167
+ /**
168
+ * Network-decrypts the owner wrap. Implementations must pass `aad` to the
169
+ * decrypt call so the envelope's declared AAD is compared before any POST.
170
+ */
171
+ interface OwnerNetworkUnwrap {
172
+ unwrap(envelope: Readonly<Record<string, unknown>>, aad: Uint8Array): Promise<Uint8Array>;
173
+ }
174
+ interface AgentRecordBindingInput {
175
+ readonly spaceId: string;
176
+ /** Full vault key: `sender-history/v2/records/agents/<agentId>/<shareId>`. */
177
+ readonly vaultKey: string;
178
+ readonly agentDid: string;
179
+ readonly ownerNetworkId: string;
180
+ }
181
+ declare function agentRecordBinding(input: AgentRecordBindingInput): Uint8Array;
182
+ interface SealAgentRecordInput extends AgentRecordBindingInput {
183
+ /** UTF-8 JSON of the full `SenderShareRecord` (link and `proofs` included). */
184
+ readonly plaintext: Uint8Array;
185
+ /** Absent for a remote-signer-only profile: the record is then owner-readable only (`kid: null`). */
186
+ readonly agentKey?: AgentKeyMaterial;
187
+ readonly ownerWrap: OwnerNetworkWrap;
188
+ readonly recordV?: number;
189
+ }
190
+ /** Seal one agent record. Each call draws a fresh DEK and fresh nonces. */
191
+ declare function sealAgentRecord(input: SealAgentRecordInput): Promise<StoredAgentRecord>;
192
+ /**
193
+ * Parse the stored bytes and check the header against the path (§5 checks
194
+ * 2–3), before any crypto. `newer` means a higher `recordV` than this build
195
+ * knows: skip and re-check after an upgrade, do not reject.
196
+ */
197
+ declare function parseStoredAgentRecord(bytes: Uint8Array, expected: {
198
+ readonly agentDid?: string;
199
+ readonly ownerNetworkId: string;
200
+ }): StoredAgentRecord;
201
+ /**
202
+ * Owner path (§3 steps 2–4): refuse locally, before any network call, an
203
+ * owner wrap whose declared AAD is not exactly B or whose network is not the
204
+ * owner's; then network-unwrap, and open.
205
+ */
206
+ declare function openAgentRecordAsOwner(record: StoredAgentRecord, location: {
207
+ readonly spaceId: string;
208
+ readonly vaultKey: string;
209
+ }, unwrap: OwnerNetworkUnwrap): Promise<Uint8Array>;
210
+ /** Agent read-back: `wrapAgent` under KEK_a; no owner-network decrypt anywhere. */
211
+ declare function openAgentRecordAsAgent(record: StoredAgentRecord, location: {
212
+ readonly spaceId: string;
213
+ readonly vaultKey: string;
214
+ }, agentKey: AgentKeyMaterial): Promise<Uint8Array>;
215
+
216
+ /**
217
+ * Storage port of the share index (design C §1, §4). Every key is relative to
218
+ * `sender-history/v2/records/`. The port is structural so share-sdk does not
219
+ * depend on the TinyCloud SDK packages; {@link createTinyCloudShareIndexBackend}
220
+ * adapts a TinyCloud client (node or web SDK) to it.
221
+ */
222
+ type ShareDocumentRead = {
223
+ readonly status: "ok";
224
+ readonly value: unknown;
225
+ readonly etag: string;
226
+ } | {
227
+ readonly status: "not_found";
228
+ }
229
+ /** Deterministic corruption (bad envelope, AAD bound elsewhere, undecodable payload). */
230
+ | {
231
+ readonly status: "corrupt";
232
+ readonly etag?: string;
233
+ }
234
+ /** Outage or transient failure: never a reason to rebuild. */
235
+ | {
236
+ readonly status: "unavailable";
237
+ };
238
+ type ShareRawRead = {
239
+ readonly status: "ok";
240
+ readonly bytes: Uint8Array;
241
+ readonly etag: string;
242
+ } | {
243
+ readonly status: "not_found";
244
+ }
245
+ /** Larger than the `maxBytes` the caller passed: the node refused to send the body, or its length was over. */
246
+ | {
247
+ readonly status: "too_large";
248
+ } | {
249
+ readonly status: "unavailable";
250
+ };
251
+ type ShareWriteCondition = {
252
+ readonly ifMatch: string;
253
+ } | {
254
+ readonly ifNoneMatch: "*";
255
+ };
256
+ type ShareWriteResult = {
257
+ readonly ok: true;
258
+ } | {
259
+ readonly ok: false;
260
+ readonly code: "conflict" | "outcome_unknown" | "busy" | "unauthorized" | "failed";
261
+ }
262
+ /** The etag was refused as malformed (for example weakened to `W/"…"` in transit). Never retried; `message` is the SDK's own. */
263
+ | {
264
+ readonly ok: false;
265
+ readonly code: "invalid_etag";
266
+ readonly message: string;
267
+ };
268
+ type ShareListResult = {
269
+ readonly status: "ok";
270
+ readonly keys: readonly string[];
271
+ readonly cursor?: string;
272
+ } | {
273
+ readonly status: "unavailable";
274
+ };
275
+ interface ShareIndexBackend {
276
+ readonly spaceId: string;
277
+ readonly ownerNetworkId: string;
278
+ /** Owner-network vault read with the slice-1 AAD check, from the network (never a replica). */
279
+ readVault(key: string): Promise<ShareDocumentRead>;
280
+ /** Owner-network vault write: a fresh envelope each time; conditional when `condition` is set. */
281
+ writeVault(key: string, value: unknown, condition?: ShareWriteCondition): Promise<ShareWriteResult>;
282
+ /** Delete a record; an absent key counts as success. */
283
+ deleteRecord(key: string): Promise<ShareWriteResult>;
284
+ /** One page of keys under `prefix` (relative, with its trailing slash, or "" for all records). */
285
+ listKeys(prefix: string, cursor?: string): Promise<ShareListResult>;
286
+ /**
287
+ * Raw stored bytes and etag (agent records), from the network. With
288
+ * `maxBytes`, a larger value is reported `too_large` and its body is not
289
+ * downloaded (see {@link createTinyCloudShareIndexBackend}).
290
+ */
291
+ readRaw(key: string, maxBytes?: number): Promise<ShareRawRead>;
292
+ writeRaw(key: string, value: Readonly<Record<string, unknown>>, condition: ShareWriteCondition): Promise<ShareWriteResult>;
293
+ readonly ownerWrap: OwnerNetworkWrap;
294
+ /** Owner-network decrypt; absent for Tier W agents, which have no decrypt grant. */
295
+ readonly ownerUnwrap?: OwnerNetworkUnwrap;
296
+ }
297
+ /** The vault's classified network read (`readNetworkEncrypted`), as this adapter consumes it. */
298
+ interface VaultNetworkRead {
299
+ readonly status: string;
300
+ readonly entry?: {
301
+ readonly value: unknown;
302
+ readonly etag?: string;
303
+ };
304
+ }
305
+ type SdkResult<T> = {
306
+ readonly ok: true;
307
+ readonly data: T;
308
+ } | {
309
+ readonly ok: false;
310
+ readonly error: {
311
+ readonly code: string;
312
+ readonly message?: string;
313
+ readonly meta?: Record<string, unknown>;
314
+ };
315
+ };
316
+ /** The subset of a TinyCloud client the adapter uses (node-sdk `TinyCloudNode` or the web SDK). */
317
+ interface TinyCloudShareIndexClient {
318
+ readonly vault: {
319
+ readNetworkEncrypted(key: string, options: {
320
+ readonly expectAad: true;
321
+ }): Promise<VaultNetworkRead>;
322
+ put(key: string, value: unknown, options?: {
323
+ readonly ifMatch?: string;
324
+ readonly ifNoneMatch?: "*";
325
+ }): Promise<SdkResult<unknown>>;
326
+ delete(key: string): Promise<SdkResult<unknown>>;
327
+ };
328
+ readonly kv: {
329
+ /** `maxResponseBytes`: the node refuses (413, `KV_RESPONSE_TOO_LARGE`) a larger value before sending its body. */
330
+ get(key: string, options: {
331
+ readonly prefix: "";
332
+ readonly raw: true;
333
+ readonly source: "network";
334
+ readonly maxResponseBytes?: number;
335
+ }): Promise<SdkResult<{
336
+ readonly data: unknown;
337
+ readonly headers: {
338
+ readonly etag?: string;
339
+ };
340
+ }>>;
341
+ put(key: string, value: unknown, options: {
342
+ readonly prefix: "";
343
+ readonly ifMatch?: string;
344
+ readonly ifNoneMatch?: "*";
345
+ }): Promise<SdkResult<unknown>>;
346
+ list(options: {
347
+ readonly prefix: string;
348
+ readonly removePrefix: true;
349
+ readonly limit: number;
350
+ readonly cursor?: string;
351
+ readonly source: "network";
352
+ }): Promise<SdkResult<{
353
+ readonly keys: readonly string[];
354
+ readonly nextCursor?: string;
355
+ readonly truncated?: boolean;
356
+ }>>;
357
+ };
358
+ readonly encryption: {
359
+ encryptToNetwork(networkId: string, plaintext: Uint8Array, options: {
360
+ readonly aad: Uint8Array;
361
+ }): Promise<SdkResult<object>>;
362
+ decryptEnvelope?(envelope: never, proof: {
363
+ readonly proofs: readonly string[];
364
+ }, options: {
365
+ readonly aad: Uint8Array;
366
+ }): Promise<SdkResult<Uint8Array>>;
367
+ };
368
+ }
369
+ /** Shown when the SDK reports `INVALID_ETAG` without a message of its own. */
370
+ declare const INVALID_ETAG_MESSAGE = "etag was weakened in transit; compare-and-swap unavailable through this path";
371
+ interface TinyCloudShareIndexBackendOptions {
372
+ readonly client: TinyCloudShareIndexClient;
373
+ readonly spaceId: string;
374
+ readonly ownerNetworkId: string;
375
+ /** The decrypt capability proof of the owner session (`{ proofs: [session.delegationCid] }`). Omit for Tier W agents. */
376
+ readonly decryptProof?: () => {
377
+ readonly proofs: readonly string[];
378
+ };
379
+ /**
380
+ * The KV prefix every key here sits under: `vault/sender-history/v2/records/`
381
+ * for a client whose KV service has no prefix of its own.
382
+ */
383
+ readonly kvRecordsPrefix?: string;
384
+ }
385
+ /** Adapt a TinyCloud client (owner web, owner-key CLI, Tier 2 or Tier W agent) to {@link ShareIndexBackend}. */
386
+ declare function createTinyCloudShareIndexBackend(options: TinyCloudShareIndexBackendOptions): ShareIndexBackend;
387
+
388
+ /**
389
+ * The compare-and-swap loop policy shared by the index and the record stores
390
+ * (design C §4, plus the lead's rule for slice 2):
391
+ * - every attempt starts with a fresh network read and re-applies the change,
392
+ * so a lost race (`ETAG_MISMATCH`) and an unknown outcome (`OUTCOME_UNKNOWN`)
393
+ * are both decided from what the node now holds, never by a blind re-put;
394
+ * - `ETAG_MISMATCH` and `NODE_BUSY` back off 50 ms·2ⁿ with jitter, capped at
395
+ * 1 s, for at most 6 attempts;
396
+ * - `INVALID_ETAG` is never retried: the first one throws
397
+ * {@link ShareIndexCasError} with the SDK's own message (an ETag weakened
398
+ * in transit, for example by a CDN, cannot be used for compare-and-swap).
399
+ */
400
+ interface CasPolicy {
401
+ readonly maxAttempts: number;
402
+ readonly backoffBaseMs: number;
403
+ readonly backoffCapMs: number;
404
+ readonly sleep: (ms: number) => Promise<void>;
405
+ readonly random: () => number;
406
+ }
407
+ declare const DEFAULT_CAS_POLICY: CasPolicy;
408
+ type ShareIndexCasErrorCode =
409
+ /** The etag was refused as malformed; compare-and-swap is unavailable through this path. Never retried. */
410
+ "INVALID_ETAG"
411
+ /** Every attempt lost a race or found the node busy. */
412
+ | "EXHAUSTED"
413
+ /** The write was refused (authorization, storage) or the read failed. */
414
+ | "FAILED";
415
+ declare class ShareIndexCasError extends Error {
416
+ readonly code: ShareIndexCasErrorCode;
417
+ readonly key: string;
418
+ readonly attempts: number;
419
+ constructor(code: ShareIndexCasErrorCode, message: string, key: string, attempts: number);
420
+ }
421
+
422
+ declare class WebShareRecordStore implements SenderShareRecordStorage {
423
+ private readonly backend;
424
+ private readonly cas;
425
+ constructor(backend: ShareIndexBackend, cas?: CasPolicy);
426
+ put(record: SenderShareRecord): Promise<void>;
427
+ update(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
428
+ get(shareId: string): Promise<SenderShareRecord | undefined>;
429
+ list(): Promise<readonly SenderShareRecord[]>;
430
+ delete(shareId: string): Promise<void>;
431
+ }
432
+ interface AgentShareRecordStoreOptions {
433
+ readonly backend: ShareIndexBackend;
434
+ /** DID of the key that signs the agent's invocations and delegations. */
435
+ readonly agentDid: string;
436
+ /** KEK_a from the same key; absent for a remote-signer-only profile (records are then owner-readable only). */
437
+ readonly agentKey?: AgentKeyMaterial;
438
+ readonly cas?: CasPolicy;
439
+ /**
440
+ * The share origin on which records read back get their Policy/v3
441
+ * `/s/inline` link rebuilt (the stored form keeps no link; see
442
+ * `delivery.ts`). Absent: records read back carry no v3 link.
443
+ */
444
+ readonly shareOrigin?: string;
445
+ }
446
+ declare class AgentShareRecordStore implements SenderShareRecordStorage {
447
+ private readonly options;
448
+ readonly agentId: string;
449
+ private readonly cas;
450
+ constructor(options: AgentShareRecordStoreOptions);
451
+ /** Whether this profile can read its own records back (`kid` is set). */
452
+ get canReadBack(): boolean;
453
+ private location;
454
+ private seal;
455
+ private open;
456
+ /** Create or replace; each write is a fresh seal under CAS (If-None-Match `*` for a new key). */
457
+ put(record: SenderShareRecord): Promise<void>;
458
+ /** CAS update with the etag from the agent's own read (for example its own `revokedAt`). */
459
+ update(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
460
+ get(shareId: string): Promise<SenderShareRecord | undefined>;
461
+ /** Lists the explicit `agents/<agentId>/` prefix (trailing slash: `list({})` would be refused). */
462
+ list(): Promise<readonly SenderShareRecord[]>;
463
+ /** Tier W has no delete grant on its prefix; the owner deletes agent records. */
464
+ delete(_shareId: string): Promise<void>;
465
+ }
466
+
467
+ /**
468
+ * The stored form of a share record in the space (TC-1432 slice 2, cap
469
+ * ruling): each blob is stored once.
470
+ *
471
+ * A Policy/v3 record's `deliveryMaterial.envelope` is exactly what
472
+ * `sealedEnvelope` decrypts to under `envelopeKey` (`publishAddressedShare`
473
+ * seals `canonicalize(envelope)`), and its `/s/inline` link is
474
+ * `sealedEnvelope`, its CID and `envelopeKey` encoded again. Both are
475
+ * dropped before an agent record is sealed and rebuilt when needed:
476
+ * {@link openDeliveryEnvelope} for the verifier and a delivery retry,
477
+ * {@link shareLinkForRecord} at Open/Copy. `sealedEnvelope` stays (the node
478
+ * reopens that exact value and its nonce is random) and so does the session
479
+ * CACAO (it proves the chain). A bearer record keeps its `link`, which is
480
+ * not derivable.
481
+ *
482
+ * Security-sensitive: the sealed bytes are checked against `shareCid` before
483
+ * they are opened, and the plaintext must be the canonical JSON of the
484
+ * envelope, so a record cannot carry one envelope for display and another for
485
+ * the node.
486
+ */
487
+ type StoredDeliveryMaterial = Omit<PublishedShareDeliveryMaterial, "envelope">;
488
+ /** A share record as stored at an agent (or migrated) key: {@link SenderShareRecord} without derivable blobs. */
489
+ type StoredShareRecord = Omit<SenderShareRecord, "deliveryMaterial"> & {
490
+ readonly deliveryMaterial?: StoredDeliveryMaterial;
491
+ };
492
+ /** Keys of a stored `deliveryMaterial`, exactly. */
493
+ declare const STORED_DELIVERY_MATERIAL_KEYS: readonly string[];
494
+ declare class DeliveryMaterialError extends Error {
495
+ constructor(message: string);
496
+ }
497
+ /** The signed envelope inside `sealedEnvelope`; throws {@link DeliveryMaterialError} unless it is exactly that. */
498
+ declare function openDeliveryEnvelope(material: StoredDeliveryMaterial): Promise<Record<string, unknown>>;
499
+ /**
500
+ * The link to show at Open/Copy: a bearer record's stored `link`, or a
501
+ * Policy/v3 record's `/s/inline` link rebuilt on `shareOrigin` (the
502
+ * viewer's configured share origin) from `sealedEnvelope` and `envelopeKey`.
503
+ */
504
+ declare function shareLinkForRecord(record: Pick<StoredShareRecord, "link" | "deliveryMaterial">, shareOrigin: string): Promise<string | undefined>;
505
+ /**
506
+ * The stored form of `record`. For a Policy/v3 record (one with
507
+ * `deliveryMaterial`), drops `deliveryMaterial.envelope` and `link` after
508
+ * checking they are exactly what the sealed material rebuilds, so nothing is
509
+ * lost. Throws {@link DeliveryMaterialError} otherwise.
510
+ */
511
+ declare function compactShareRecord(record: SenderShareRecord | StoredShareRecord): Promise<StoredShareRecord>;
512
+ /**
513
+ * The full record again (for a delivery retry or a caller that wants the
514
+ * {@link SenderShareRecord} shape): the envelope reopened from the sealed
515
+ * material and, when `shareOrigin` is given, the `/s/inline` link rebuilt.
516
+ */
517
+ declare function expandShareRecord(record: StoredShareRecord, shareOrigin?: string): Promise<SenderShareRecord>;
518
+
519
+ /**
520
+ * Index document schema v1, rows, `project()`, and the versioned agent-record
521
+ * schema (design C §2, §5 check 5). The index holds display fields only:
522
+ * never `link`, `deliveryMaterial`, share keys, unmasked emails or `#k=`.
523
+ * Unknown fields on the header, on agent entries and on rows are kept.
524
+ */
525
+ declare const INDEX_SCHEMA_VERSION = 1;
526
+ type ShareProofState = "proven" | "chained" | "unproven";
527
+ interface ShareIndexRow {
528
+ /** Display name: ≤120 characters, bidi and control characters stripped. */
529
+ readonly n: string;
530
+ readonly k: "file" | "folder";
531
+ /** Content kind: the lowercase file extension, when there is a short one. */
532
+ readonly ck?: string;
533
+ readonly ak: "bearer" | "recipientDid" | "exactEmail" | "emailDomain";
534
+ /** Masked audience label. */
535
+ readonly al: string;
536
+ readonly a: readonly string[];
537
+ /** Created (`registeredAt`) and expires, ISO 8601. */
538
+ readonly c: string;
539
+ readonly x: string;
540
+ /** Revocation; only `confirmed` (node-confirmed) is sticky. */
541
+ readonly r?: {
542
+ readonly at: string;
543
+ readonly confirmed: boolean;
544
+ };
545
+ /**
546
+ * Revoke kind. For agent rows the revoke action is decided at click time
547
+ * from the freshly verified record (§6). Observed on a local node (C5,
548
+ * TC-1432 slice 2):
549
+ * - per share: the owner wallet can revoke an agent's v3 root (`policyRoot`,
550
+ * `enforcementRoot` CIDs from the signed envelope) through the generic
551
+ * `/revoke` route: 200, and an open recipient session's next read gets 403
552
+ * `root-revoked`. Only the JSON root-revocation route answers 403
553
+ * `root-revocation-issuer-invalid` to the owner.
554
+ * - session-wide: revoking the agent's owner→session delegation kills its
555
+ * bearer links and fails new v3 claims at once, but an already-open v3
556
+ * recipient session keeps reading for its lifetime (3,598 s observed).
557
+ * The node liveness check (N3, test B-1) closes that.
558
+ */
559
+ readonly rk: "policy-v3" | "delegation" | "none";
560
+ /** The share's revoke CID; unique across rows. */
561
+ readonly cid?: string;
562
+ /** Every CID this row claims (one row per CID, §5 check 7). */
563
+ readonly cc?: readonly string[];
564
+ /** Provenance, derived from the key path: `web` or `agent:<agentId>`. */
565
+ readonly src: string;
566
+ /** Agent and migrated rows: the proof state of the verified chain. */
567
+ readonly ps?: ShareProofState;
568
+ /** `migrated`: a one-time owner import (shown as "imported"); ranks by `ps`, never as a web row. */
569
+ readonly pv?: "migrated";
570
+ /** The verified chain has expired (shown as Expired, not rejected). */
571
+ readonly pe?: true;
572
+ /** Needs attention: unreadable record, or `shareId` ≠ key basename. */
573
+ readonly na?: true;
574
+ readonly [unknown: string]: unknown;
575
+ }
576
+ interface ShareIndexAgentEntry {
577
+ /** The agent DID, once a record with a valid header was seen. */
578
+ readonly did: string | null;
579
+ readonly rows: number;
580
+ readonly rejected: Readonly<Record<string, number>>;
581
+ /** Raw key suffixes (≤64 chars) refused on first sight: never refetched. At most 100. */
582
+ readonly rejectedKeys: readonly string[];
583
+ /** Keys whose `recordV` is newer than this build: skipped until an upgrade. At most 100. */
584
+ readonly newerKeys?: readonly string[];
585
+ readonly newerV?: number;
586
+ readonly paused: false | {
587
+ readonly reason: "rejections" | "row-cap" | "newer";
588
+ readonly at: string;
589
+ };
590
+ readonly firstSeen: string;
591
+ readonly [unknown: string]: unknown;
592
+ }
593
+ interface ShareIndexDocument {
594
+ readonly v: number;
595
+ readonly ownerDid: string;
596
+ readonly spaceId: string;
597
+ readonly updatedAt: string;
598
+ readonly updatedBy: string;
599
+ readonly archives: readonly {
600
+ readonly name: string;
601
+ readonly ids: readonly string[];
602
+ }[];
603
+ readonly tombstones: readonly {
604
+ readonly key: string;
605
+ readonly at: string;
606
+ }[];
607
+ readonly reconcile: {
608
+ readonly cursor?: string;
609
+ };
610
+ readonly agents: Readonly<Record<string, ShareIndexAgentEntry>>;
611
+ readonly rows: Readonly<Record<string, ShareIndexRow>>;
612
+ readonly [unknown: string]: unknown;
613
+ }
614
+ interface ShareIndexArchiveDocument {
615
+ readonly v: number;
616
+ readonly rows: Readonly<Record<string, ShareIndexRow>>;
617
+ readonly [unknown: string]: unknown;
618
+ }
619
+ type ParsedIndexDocument = {
620
+ readonly status: "ok";
621
+ readonly doc: ShareIndexDocument;
622
+ }
623
+ /** A higher `v`: render read-only, never write. */
624
+ | {
625
+ readonly status: "newer";
626
+ readonly v: number;
627
+ }
628
+ /** Decrypted, but not a valid document: deterministic corruption. */
629
+ | {
630
+ readonly status: "invalid";
631
+ };
632
+ declare function parseIndexDocument(value: unknown): ParsedIndexDocument;
633
+ /** Strip bidi/control/format characters and cap at 120 code points. */
634
+ declare function displayName(value: unknown, fallback: string): string;
635
+ /**
636
+ * The display-only projection of a record. `claims` overrides the CIDs the
637
+ * row may claim (agent rows claim only what their chain verified).
638
+ */
639
+ declare function project(record: StoredShareRecord, input: {
640
+ readonly key: string;
641
+ readonly src: string;
642
+ readonly claims?: readonly string[];
643
+ readonly proof?: {
644
+ readonly state: ShareProofState;
645
+ readonly expired: boolean;
646
+ };
647
+ }): ShareIndexRow;
648
+ /**
649
+ * Whether `spaceId` (`tinycloud:pkh:eip155:<chain>:<address>:<name>`) is a
650
+ * space of the account `ownerDid` (`did:pkh:eip155:<chain>:<address>`),
651
+ * whatever its name: the CLI keeps share content in `default` while the
652
+ * records live in `applications`. Address spelling is ignored.
653
+ */
654
+ declare function isSpaceOwnedBy(spaceId: string, ownerDid: string): boolean;
655
+ /** A record refused by the schema. `provenance`: an agent-subtree record claiming `migrated`. */
656
+ declare class ShareRecordSchemaError extends Error {
657
+ readonly reason: "schema" | "provenance";
658
+ constructor(reason: "schema" | "provenance");
659
+ }
660
+
661
+ declare function claimRank(row: Pick<ShareIndexRow, "src" | "ps" | "pv">): number;
662
+ type ClaimDecision = {
663
+ readonly admit: true;
664
+ readonly displaced: readonly string[];
665
+ } | {
666
+ readonly admit: false;
667
+ readonly heldBy: string;
668
+ };
669
+ /**
670
+ * Decide a new claim `row` at `key` against the rows already in `rows`.
671
+ * Collisions are rows (other than `key` itself) that claim any CID in common.
672
+ * Web rows never displace other web rows (both are owner-written).
673
+ */
674
+ declare function decideClaim(rows: Readonly<Record<string, ShareIndexRow>>, key: string, row: ShareIndexRow): ClaimDecision;
675
+
676
+ /**
677
+ * Attenuation containment (design C4): every (resource, ability) of the child
678
+ * must be granted by the parent on the same resource or on a parent resource
679
+ * under the segment-prefix rule `child == parent || child.startsWith(parent
680
+ * + "/")` (a parent already ending in `/` is a prefix itself), as
681
+ * `unified.ts:1225-1233` applies it. EVM address spelling is ignored. A
682
+ * parent ability whose caveats are all empty objects is unrestricted;
683
+ * otherwise the child's caveats must be identical. Fail-closed.
684
+ */
685
+ type Attenuation = Readonly<Record<string, Readonly<Record<string, readonly unknown[]>>>>;
686
+ declare function attenuationCovers(parent: Attenuation, child: Attenuation): boolean;
687
+
688
+ /**
689
+ * Owner→session SIWE CACAO checks (design C4). Security-sensitive.
690
+ *
691
+ * Signature, canonical DAG-CBOR, exact-bytes CID and the ReCap statement are
692
+ * verified by the injected parser: sdk-core `parseSignedSiweCacao`
693
+ * (`cacao.ts:205-276`; share-sdk cannot depend on sdk-core, which depends on
694
+ * share-sdk). That parser does not check validity times, so this module adds:
695
+ * `Not Before`/`Expiration Time` against `now`, issuer == the account owner,
696
+ * audience == the child's issuer, and child attenuation ⊆ the CACAO's. `Not
697
+ * Before` and `Issued At` are read from the verified bytes ({@link cacaoTimes}).
698
+ */
699
+ interface ParsedSiweCacao {
700
+ readonly cid: string;
701
+ /** `did:pkh:eip155:<chain>:<address>`. */
702
+ readonly issuer: string;
703
+ /** SIWE `uri`: the delegate DID. */
704
+ readonly audience: string;
705
+ readonly attenuation: Attenuation;
706
+ readonly expiresAt?: string;
707
+ }
708
+ /** Verifies signature, encoding and the exact-bytes CID; throws otherwise. */
709
+ type SiweCacaoParser = (authorization: string, expectedCid: string) => Promise<ParsedSiweCacao>;
710
+ type DelegationTime = "current" | "expired";
711
+ interface OwnerSessionCheck {
712
+ readonly authorization: string;
713
+ readonly expectedCid: string;
714
+ readonly accountOwnerDid: string;
715
+ /** The issuer of the delegation this CACAO must authorize (the leaf or root issuer). */
716
+ readonly childIssuer: string;
717
+ readonly childAttenuation: Attenuation;
718
+ readonly now: Date;
719
+ readonly parse: SiweCacaoParser;
720
+ }
721
+ interface VerifiedOwnerSession {
722
+ readonly cid: string;
723
+ /** The session principal (audience without fragment). */
724
+ readonly sessionDid: string;
725
+ readonly time: DelegationTime;
726
+ /** The signed `Issued At` (epoch ms), when the parser reports it. */
727
+ readonly issuedAt?: number;
728
+ }
729
+ /** Throws when the CACAO does not prove the owner→session hop; reports expiry separately. */
730
+ declare function verifyOwnerSessionDelegation(input: OwnerSessionCheck): Promise<VerifiedOwnerSession>;
731
+
732
+ /**
733
+ * CID ownership by proof (design C §5 check 7, C1–C4). Everything is checked
734
+ * from the signed bytes the record carries; nothing is fetched.
735
+ *
736
+ * - `proven`: the full chain verifies to the account owner and the leaf
737
+ * issuer (the policy owner for Policy/v3) is `hdr.agentDid`.
738
+ * - `chained`: the full chain verifies to the account owner through another key.
739
+ * - `unproven`: the leaf verifies but the owner→session CACAO is absent.
740
+ * - `expired` is reported next to the state: a chain that verifies but has
741
+ * expired is admitted as Expired, not rejected.
742
+ * - anything else: `{ ok: false }` (reason `proof`).
743
+ *
744
+ * Supported chains: native bearer links (`proofs.link` + `proofs.session`),
745
+ * Policy/v3 addressed shares (the envelope opened from
746
+ * `deliveryMaterial.sealedEnvelope`, bound to `shareCid`, + `proofs.session`)
747
+ * and Policy/v2 owner-policy shares (`proofs.ownerPolicy`, `ownerDelegation`,
748
+ * `enforcementDelegation`, optional `registration`).
749
+ *
750
+ * What revocation achieves today (local node, C5): the owner wallet's generic
751
+ * `/revoke` of a verified v3 root CID ends that share at once, open recipient
752
+ * sessions included (403 `root-revoked`); only the JSON root-revocation route
753
+ * refuses the owner (403 `root-revocation-issuer-invalid`). Revoking the
754
+ * verified session CACAO kills the session's bearer links and new v3 claims
755
+ * at once, but an already-open v3 recipient session keeps reading for its
756
+ * lifetime (3,598 s observed) until the node liveness check (N3, test B-1).
757
+ * Migrated records are verified with no agent binding, so they are never
758
+ * `proven`.
759
+ */
760
+ type ChainVerdict = {
761
+ readonly ok: true;
762
+ readonly state: ShareProofState;
763
+ readonly expired: boolean;
764
+ readonly claims: readonly string[];
765
+ /**
766
+ * Where `registeredAt` may lie, from signed times in the chain (epoch ms):
767
+ * a signed issuance time ± {@link REGISTERED_AT_SKEW_MS} when the chain
768
+ * has one (link `nbf`, the v3 binding's `issuedAt`, the node's v2
769
+ * `registeredAt`); otherwise the session's signed `Issued At` as the
770
+ * earliest and the leaf's expiry as the latest.
771
+ */
772
+ readonly issued: {
773
+ readonly earliest?: number;
774
+ readonly latest?: number;
775
+ };
776
+ } | {
777
+ readonly ok: false;
778
+ readonly detail: string;
779
+ };
780
+ /** The node registration key for a node audience, from the share public config. */
781
+ interface ShareRegistrationKey {
782
+ readonly kid: string;
783
+ /** Raw 32-byte Ed25519 key, or its 34-byte did:key multicodec form. */
784
+ readonly publicKey: Uint8Array;
785
+ }
786
+ interface ChainContext {
787
+ readonly accountOwnerDid: string;
788
+ /** `hdr.agentDid`, already bound to the path by `derive(agentDid) == agentId`. */
789
+ readonly agentDid: string;
790
+ readonly now: Date;
791
+ readonly parseSiweCacao: SiweCacaoParser;
792
+ /** Registration key by `target.nodeAudience`; absent ⇒ `registrationCid` is not claimed. */
793
+ readonly registrationKey?: (nodeAudience: string) => ShareRegistrationKey | undefined;
794
+ }
795
+ /** Verify the chain behind every CID a stored agent (or migrated) record claims. Never throws. */
796
+ declare function verifyAgentRecordChain(record: StoredShareRecord, context: ChainContext): Promise<ChainVerdict>;
797
+
798
+ /**
799
+ * Signed UCAN verifier for issued delegations (native bearer link
800
+ * delegations and Policy/v3 owner roots). Security-sensitive.
801
+ *
802
+ * Same acceptance as sdk-core `parseSignedCompactUcanAttenuation`
803
+ * (`unified.ts:1281-1314`): EdDSA/JWT/ucv 0.10.0 header whose JWK binds the
804
+ * did:key issuer, strict Ed25519 signature, CID = CIDv1(raw, blake3) of the
805
+ * exact Authorization bytes. It additionally returns the verified `prf`,
806
+ * `nbf` and `exp` (design C4), and requires `exp`: an expiry-less delegation
807
+ * is refused. Payload JSON need not be JCS: WASM-issued link delegations are
808
+ * not.
809
+ */
810
+ interface SignedUcanDelegation {
811
+ readonly cid: string;
812
+ /** Issuer principal without any `#fragment`. */
813
+ readonly issuer: string;
814
+ /** Audience principal without any `#fragment`. */
815
+ readonly audience: string;
816
+ readonly attenuation: Readonly<Record<string, Readonly<Record<string, readonly unknown[]>>>>;
817
+ readonly proofs: readonly string[];
818
+ readonly notBefore?: number;
819
+ readonly expiresAt: number;
820
+ readonly facts?: readonly unknown[];
821
+ }
822
+ declare function parseSignedUcan(authorization: string, expectedCid?: string): SignedUcanDelegation;
823
+
824
+ /**
825
+ * Verifier for a Policy/v2 enforcement delegation
826
+ * (`TinyCloudSharePolicyEnforcement`, signed by the share key;
827
+ * `createPolicyEnforcementDelegation`). No verifier existed before
828
+ * (design C §5 check 7, C4).
829
+ *
830
+ * Checks, all fail-closed:
831
+ * - the DAG-CBOR bytes are exactly the canonical encoding of
832
+ * `{ domain, unsigned: { type, version: 2, issuerDid, audienceDid, facts } }`
833
+ * built from the delegation's own fields (so every reported field is signed);
834
+ * - the Ed25519 signature over those bytes verifies against `issuerDid`
835
+ * (a did:key), strict RFC 8032;
836
+ * - the CID recomputed from the exact bytes equals `cid` (and `expectedCid`);
837
+ * - `facts.shareKeyDid` equals the issuer.
838
+ */
839
+ declare function verifyPolicyEnforcementDelegation(delegation: SignedDelegation, expectedCid?: string): Promise<SignedDelegation>;
840
+
841
+ /**
842
+ * `SenderShareIndex`: the encrypted, display-only share index at
843
+ * `records/_index/main` (design C §2, §4, §5, §7). Records stay canonical;
844
+ * the index is a projection that is only ever written with compare-and-swap.
845
+ */
846
+ interface ShareIndexLimits {
847
+ readonly listPagesPerPass: number;
848
+ readonly fetchesPerPass: number;
849
+ readonly newRowsPerAgentPerPass: number;
850
+ readonly liveRowsPerAgent: number;
851
+ readonly rejectionsBeforePause: number;
852
+ /** Agent rows together may use at most this share of `mainRowBytes`. */
853
+ readonly agentRowShare: number;
854
+ readonly mainRowBytes: number;
855
+ readonly headerBytes: number;
856
+ readonly maxAgentRecordBytes: number;
857
+ readonly tombstoneTtlMs: number;
858
+ readonly archiveAgeMs: number;
859
+ readonly statusChecksPerSync: number;
860
+ }
861
+ declare const DEFAULT_SHARE_INDEX_LIMITS: ShareIndexLimits;
862
+ /** Outbox ops carry rows and CIDs, never records. */
863
+ type ShareIndexOp = {
864
+ readonly id: string;
865
+ readonly type: "upsert";
866
+ readonly key: string;
867
+ readonly row: ShareIndexRow;
868
+ } | {
869
+ readonly id: string;
870
+ readonly type: "revoke";
871
+ readonly key: string;
872
+ readonly at: string;
873
+ readonly confirmed: boolean;
874
+ }
875
+ /** Only for revoked, expired or unreadable rows: tombstone first, then the caller deletes the record. */
876
+ | {
877
+ readonly id: string;
878
+ readonly type: "delete";
879
+ readonly key: string;
880
+ }
881
+ /** Owner agent controls: `resume` clears a pause; `recheck` also clears the agent's rejected lists. */
882
+ | {
883
+ readonly id: string;
884
+ readonly type: "agent";
885
+ readonly agentId: string;
886
+ readonly action: "resume" | "recheck";
887
+ };
888
+ /** Durable op queue: IndexedDB on the web, the profile cache for Tier 2. */
889
+ interface ShareIndexOutbox {
890
+ list(): Promise<readonly ShareIndexOp[]>;
891
+ put(op: ShareIndexOp): Promise<void>;
892
+ remove(ids: readonly string[]): Promise<void>;
893
+ }
894
+ declare class MemoryShareIndexOutbox implements ShareIndexOutbox {
895
+ private readonly ops;
896
+ list(): Promise<readonly ShareIndexOp[]>;
897
+ put(op: ShareIndexOp): Promise<void>;
898
+ remove(ids: readonly string[]): Promise<void>;
899
+ }
900
+ /** Node-side status of a row's share, for status sync. `unknown` (e.g. agent-issued CIDs, TC-1419) changes nothing. */
901
+ interface ShareStatusPort {
902
+ status(key: string, row: ShareIndexRow): Promise<"revoked" | "active" | "unknown">;
903
+ }
904
+ interface SenderShareIndexOptions {
905
+ readonly backend: ShareIndexBackend;
906
+ readonly outbox: ShareIndexOutbox;
907
+ /** The account owner (`did:pkh:…`). */
908
+ readonly ownerDid: string;
909
+ /** `updatedBy` label of this writer. */
910
+ readonly writer: string;
911
+ /** Configured share origins, for the link check. */
912
+ readonly shareOrigins: readonly string[];
913
+ /** sdk-core `parseSignedSiweCacao`. */
914
+ readonly parseSiweCacao: SiweCacaoParser;
915
+ /** Node registration keys by `nodeAudience`, from the share public config. */
916
+ readonly registrationKey?: (nodeAudience: string) => ShareRegistrationKey | undefined;
917
+ readonly limits?: Partial<ShareIndexLimits>;
918
+ readonly cas?: Partial<CasPolicy>;
919
+ readonly now?: () => Date;
920
+ }
921
+ type ShareIndexLoad = {
922
+ readonly status: "ok";
923
+ readonly doc: ShareIndexDocument;
924
+ }
925
+ /** A higher schema version: render read-only (option A path), never write. */
926
+ | {
927
+ readonly status: "read_only";
928
+ readonly v: number;
929
+ } | {
930
+ readonly status: "not_found";
931
+ }
932
+ /** Deterministic corruption: the next write rebuilds. */
933
+ | {
934
+ readonly status: "corrupt";
935
+ }
936
+ /** Outage: never a reason to rebuild. */
937
+ | {
938
+ readonly status: "unavailable";
939
+ };
940
+ type ShareIndexWriteStatus = "written" | "unchanged" | "read_only" | "unavailable";
941
+ interface ShareIndexWriteOutcome {
942
+ readonly status: ShareIndexWriteStatus;
943
+ /** The document as written, or as found when the change was already present. */
944
+ readonly doc?: ShareIndexDocument;
945
+ }
946
+ interface ShareIndexFlushResult {
947
+ readonly status: ShareIndexWriteStatus | "empty";
948
+ /** Op ids that can never apply (tombstoned upsert, delete of a live row, unknown row). They are dropped. */
949
+ readonly refused: readonly string[];
950
+ }
951
+ /**
952
+ * §5 rejection reasons; every check-4 failure (unwrap, DEK length, commit, GCM) is `decrypt`.
953
+ * `provenance`: an agent-subtree record claiming `migrated`, which only owner imports may carry.
954
+ */
955
+ type AgentRejectReason = "size" | "shape" | "header" | "decrypt" | "schema" | "provenance" | "link" | "proof";
956
+ interface ShareIndexReconcileReport {
957
+ readonly status: ShareIndexWriteStatus;
958
+ readonly pages: number;
959
+ readonly fetches: number;
960
+ readonly admitted: number;
961
+ readonly rejected: Readonly<Record<string, number>>;
962
+ readonly deferred: number;
963
+ /** True when this pass reached the end of the key listing. */
964
+ readonly cycleComplete: boolean;
965
+ /** The header reached its cap: new agent header state (entries, rejections) was not added. Show an alert; no row was evicted. */
966
+ readonly headerFull: boolean;
967
+ }
968
+ /** Encoded size of everything except the rows. */
969
+ declare function headerBytes(doc: ShareIndexDocument): number;
970
+ declare class SenderShareIndex {
971
+ private readonly options;
972
+ private readonly backend;
973
+ private readonly limits;
974
+ private readonly cas;
975
+ private readonly now;
976
+ constructor(options: SenderShareIndexOptions);
977
+ /** Library open: one GET + one decrypt. */
978
+ load(): Promise<ShareIndexLoad>;
979
+ enqueue(op: ShareIndexOp): Promise<void>;
980
+ /** Rebuild base after deterministic corruption: an empty document plus the archives found under `_index/`. */
981
+ private rebuildBase;
982
+ private emptyDoc;
983
+ /**
984
+ * Read → mutate → If-Match write, per {@link runCas}. A no-op change is never
985
+ * written. The header cap is enforced by the reconciler on agent-derived
986
+ * state only (see {@link reconcile}); the owner's own writes are never blocked by it.
987
+ */
988
+ private update;
989
+ /** When false (header at its cap), no new agent header state is added: no new entries, rejections or newer keys. */
990
+ private growHeader;
991
+ private ensureAgent;
992
+ /** Count a rejection once per key: a key already in `rejectedKeys` changes nothing. */
993
+ private recordRejection;
994
+ /** Remove a displaced row; a displaced agent claim counts as `proof` and is not refetched. */
995
+ private displace;
996
+ private isRetired;
997
+ private applyOp;
998
+ private displaceForDelete;
999
+ /**
1000
+ * Apply every pending outbox op in one CAS. Ops leave the outbox only after
1001
+ * the write is confirmed (or the change was already present). Any CAS error,
1002
+ * including `INVALID_ETAG`, propagates with the ops still pending.
1003
+ */
1004
+ flush(): Promise<ShareIndexFlushResult>;
1005
+ private examineAgentRecord;
1006
+ /** §5 checks 5–7 for a decrypted record, shared by agent records and one-time owner imports. */
1007
+ private checkRecord;
1008
+ private peekRecordV;
1009
+ private archivedClaims;
1010
+ /**
1011
+ * One budgeted pass: list keys from `reconcile.cursor`, fetch and check what
1012
+ * the index does not know, then apply the findings in one CAS. Never
1013
+ * deletes rows; tombstoned, archived and rejected keys are never refetched.
1014
+ */
1015
+ reconcile(): Promise<ShareIndexReconcileReport>;
1016
+ private applyFindings;
1017
+ syncStatus(port: ShareStatusPort): Promise<{
1018
+ readonly status: ShareIndexWriteStatus;
1019
+ readonly checked: number;
1020
+ }>;
1021
+ compact(): Promise<{
1022
+ readonly status: ShareIndexWriteStatus;
1023
+ readonly archived: number;
1024
+ }>;
1025
+ }
1026
+
1027
+ /**
1028
+ * Policy/v2 node registration receipt, with expiry reported apart from
1029
+ * invalidity (design C4). Security-sensitive.
1030
+ *
1031
+ * The package's `validateOwnerSharePolicyRegistration` refuses an expired
1032
+ * receipt before checking anything else, so an expired-but-genuine receipt
1033
+ * cannot be told from a forged one. This verifier makes the same checks
1034
+ * (exact shape; binding to the chain's CIDs; every field the original compares
1035
+ * against the canonical policy and the enforcement facts; canonical times;
1036
+ * the CID over the canonical core; the node's Ed25519 signature against the
1037
+ * registration key from the share public config) and then reports `expired`
1038
+ * separately. `registrationCid` is not inside the node-signed core, so the
1039
+ * CID-over-core check is what binds it.
1040
+ */
1041
+ interface RegistrationKey {
1042
+ readonly kid: string;
1043
+ /** Raw 32-byte Ed25519 key, or its 34-byte did:key multicodec form. */
1044
+ readonly publicKey: Uint8Array;
1045
+ }
1046
+ interface CheckedRegistration {
1047
+ readonly status: "valid" | "expired";
1048
+ readonly registration: OwnerSharePolicyRegistration;
1049
+ }
1050
+ /** Throws when the receipt does not verify; reports a verified receipt past its `expiresAt` as `expired`. */
1051
+ declare function checkRegistrationReceipt(value: unknown, expected: {
1052
+ readonly policyCid: string;
1053
+ readonly ownerDelegationCid: string;
1054
+ readonly enforcementDelegationCid: string;
1055
+ /** The `policy` object of the canonical Policy/v2 envelope whose bytes hash to `policyCid`. */
1056
+ readonly policy: Readonly<Record<string, unknown>>;
1057
+ /** The verified enforcement delegation's facts. */
1058
+ readonly enforcementFacts: Readonly<Record<string, unknown>>;
1059
+ }, key: RegistrationKey, now: Date): CheckedRegistration;
1060
+
1061
+ declare function verifyEnvelopeV3At(envelope: ShareEnvelopeV3, expectedSignerDid: string, now: number): Promise<boolean>;
1062
+
1063
+ export { AGENT_ID_LENGTH, AGENT_RECORD_VERSION, type AgentKeyMaterial, AgentRecordError, type AgentRecordFailure, type AgentRecordHeader, type AgentRejectReason, AgentShareRecordStore, type AgentShareRecordStoreOptions, type Attenuation, type CasPolicy, type ChainContext, type ChainVerdict, type CheckedRegistration, type ClaimDecision, type ClassifiedRecordKey, DEFAULT_CAS_POLICY, DEFAULT_SHARE_INDEX_LIMITS, DeliveryMaterialError, INDEX_MAIN_KEY, INDEX_SCHEMA_VERSION, INVALID_ETAG_MESSAGE, MAX_AGENT_RECORD_BYTES, MemoryShareIndexOutbox, type OwnerNetworkUnwrap, type OwnerNetworkWrap, OwnerUnwrapUnavailableError, type ParsedSiweCacao, type RegistrationKey, SHARE_RECORDS_VAULT_PREFIX, STORED_DELIVERY_MATERIAL_KEYS, SUPPORTED_AGENT_RECORD_V, SenderShareIndex, type SenderShareIndexOptions, SenderShareRecord, type ShareDocumentRead, type ShareIndexAgentEntry, type ShareIndexArchiveDocument, type ShareIndexBackend, ShareIndexCasError, type ShareIndexCasErrorCode, type ShareIndexDocument, type ShareIndexFlushResult, type ShareIndexLimits, type ShareIndexLoad, type ShareIndexOp, type ShareIndexOutbox, type ShareIndexReconcileReport, type ShareIndexRow, type ShareIndexWriteOutcome, type ShareIndexWriteStatus, type ShareLinkCheck, type ShareLinkShape, type ShareListResult, type ShareProofState, type ShareRawRead, ShareRecordSchemaError, type ShareRegistrationKey, type ShareStatusPort, type ShareWriteCondition, type ShareWriteResult, type SignedUcanDelegation, type SiweCacaoParser, type StoredAgentRecord, type StoredDeliveryMaterial, type StoredShareRecord, type TinyCloudShareIndexBackendOptions, type TinyCloudShareIndexClient, type VaultNetworkRead, WebShareRecordStore, agentIdFromDid, agentRecordBinding, agentRecordKey, agentRecordPrefix, attenuationCovers, checkRegistrationReceipt, claimRank, classifyRecordKey, compactShareRecord, createTinyCloudShareIndexBackend, decideClaim, deriveAgentKeyMaterial, displayName, expandShareRecord, headerBytes, isAgentId, isShareId, isSpaceOwnedBy, openAgentRecordAsAgent, openAgentRecordAsOwner, openDeliveryEnvelope, parseIndexDocument, parseSignedUcan, parseStoredAgentRecord, project, sealAgentRecord, shareLinkForRecord, validateShareLink, verifyAgentRecordChain, verifyEnvelopeV3At, verifyOwnerSessionDelegation, verifyPolicyEnforcementDelegation };