@tinycloud/share-sdk 1.2.0-beta.5 → 1.2.0-beta.6

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.
@@ -185,6 +185,16 @@ declare class ShareRoot {
185
185
  toRecord(): ShareRootRecord;
186
186
  /** True while the root is live, its key is held, and it lasts at least until `expiresAt`. Revocation is not visible here. */
187
187
  covers(expiresAt: Date, now?: number): boolean;
188
+ /**
189
+ * Sign a 60-second `POST /revoke` invocation, by the share key, for one
190
+ * delegation this root issued (a bearer link). Zero approvals. The Node
191
+ * authorizes it because the share key is that delegation's delegator. The
192
+ * root itself is never revoked this way: only the owner does that.
193
+ */
194
+ signLinkRevocation(input: {
195
+ readonly delegationCid: string;
196
+ readonly nodeDid: string;
197
+ }): Promise<string>;
188
198
  /**
189
199
  * Sign a bearer link: `kv/get` on one exact key under the prefix, to the
190
200
  * link's own key, with this root as its only proof. It never outlives the
@@ -266,7 +276,7 @@ declare function signShareKeyDelegation(input: ShareKeyDelegationInput): Promise
266
276
  type ShareGrantAccess = "view" | "edit";
267
277
  /** P2(a): a read-back key lives at most one hour. */
268
278
  declare const EDIT_SHARE_READER_MAX_LIFETIME_MS: number;
269
- type FetchFn$1 = (input: URL, init?: RequestInit) => Promise<Response>;
279
+ type FetchFn$2 = (input: URL, init?: RequestInit) => Promise<Response>;
270
280
  /**
271
281
  * The share's one content key (I1): `xyz.tinycloud.share/shares/<shareId>/<file>`,
272
282
  * or, for `view` only, a TC-1428 version file
@@ -322,11 +332,14 @@ interface ShareGrantRequest {
322
332
  * exists and is `active` (a half-published share whose key was lost), and
323
333
  * grant a new key on it. Without it, `prepare` refuses an existing network
324
334
  * (`share-id-in-use`) before the prompt. A retired network is always
325
- * refused.
335
+ * refused. After resuming, revoke the lost key's grant with an owner SIWE
336
+ * on its CID (`POST /revoke`; not a kill-all control, which would also
337
+ * retire this network): it stays live, with decrypt on this share, until
338
+ * revoked or expired.
326
339
  */
327
340
  readonly resumeExistingNetwork?: boolean;
328
341
  readonly keyFactory?: () => Promise<DelegatedShareKey>;
329
- readonly fetch?: FetchFn$1;
342
+ readonly fetch?: FetchFn$2;
330
343
  readonly now?: () => number;
331
344
  }
332
345
  /** A per-share grant whose key exists but which the owner has not approved yet. Persist `row` first, then `approve()`. */
@@ -412,7 +425,7 @@ declare class ShareGrant {
412
425
  readonly record: ShareGrantRecord;
413
426
  readonly key: DelegatedShareKey;
414
427
  readonly now?: () => number;
415
- readonly fetch?: FetchFn$1;
428
+ readonly fetch?: FetchFn$2;
416
429
  }): Promise<ShareGrant>;
417
430
  /**
418
431
  * This grant's CACAO as base64url, UNPADDED (the record form). As a Node
@@ -475,7 +488,7 @@ declare function revokeShareGrant(input: {
475
488
  readonly owner: {
476
489
  signOwnerRevocation(cid: string): Promise<string>;
477
490
  };
478
- readonly fetch?: FetchFn$1;
491
+ readonly fetch?: FetchFn$2;
479
492
  }): Promise<{
480
493
  readonly network: "retired" | "absent";
481
494
  readonly grant: "revoked";
@@ -520,15 +533,17 @@ declare function perShareNetworkIdFor(ownerDid: string, shareId: string): string
520
533
  * production ingress (nginx 1.27.4, `large_client_header_buffers 4 32k`,
521
534
  * HTTP/1.1): 32,751 bytes pass, 32,752 bytes get `400 Request Header Or
522
535
  * Cookie Too Large`. A direct Node accepts ~483 KB and Cloudflare 128 KB.
523
- * 30,000 bytes leave 8.4% for header-name and proxy overhead. Each share
524
- * (network URN with `delegation/revoke` + `network.revoke`) costs ~845
525
- * bytes: 34 shares per control with the space rows (29,800 bytes measured
526
- * with the `applications` row, imported by the Node), 35 without.
536
+ * 30,000 bytes leave 8.4% for header-name and proxy overhead. A control's
537
+ * capacity depends on the shareId length; with the `applications` and
538
+ * `default` rows in every control: 33 shares for UUID shareIds, 34 for
539
+ * 32-hex ids, 37 for 16-character ids (34 UUID shares + the `applications`
540
+ * row alone measured 29,800 bytes, imported by the Node). L shares take
541
+ * ceil(L/n) controls, n from the plan.
527
542
  */
528
543
  declare const SHARE_CONTROL_HEADER_BUDGET_BYTES = 30000;
529
544
  /** A control lives ten minutes: long enough to retire and revoke one batch. */
530
545
  declare const SHARE_CONTROL_LIFETIME_MS: number;
531
- type FetchFn = (input: URL, init?: RequestInit) => Promise<Response>;
546
+ type FetchFn$1 = (input: URL, init?: RequestInit) => Promise<Response>;
532
547
  /** The owner wallet, as a SIWE signer: one call = one approval. */
533
548
  interface ShareControlOwner {
534
549
  /** `did:pkh:eip155:<chain>:<address>`. */
@@ -617,9 +632,56 @@ declare function runShareControlBatch(input: {
617
632
  readonly revokeFallback?: {
618
633
  signOwnerRevocation(cid: string): Promise<string>;
619
634
  };
620
- readonly fetch?: FetchFn;
635
+ readonly fetch?: FetchFn$1;
621
636
  readonly now?: () => number;
622
637
  readonly keyFactory?: () => Promise<DelegatedShareKey>;
623
638
  }): Promise<ShareControlResult>;
624
639
 
625
- export { AGENT_SHARE_ROOT_MAX_LIFETIME_MS, type DecodedOwnerRootCacao, EDIT_SHARE_READER_MAX_LIFETIME_MS, type EditShareReader, type PendingShareGrant, SHARE_CONTROL_HEADER_BUDGET_BYTES, SHARE_CONTROL_LIFETIME_MS, SHARE_NETWORK_NAME_PREFIX, SHARE_ROOT_CLOCK_SKEW_MS, SHARE_ROOT_LINK_MAX_LIFETIME_MS, SHARE_ROOT_MAX_LIFETIME_MS, SHARE_ROOT_PREFIX, SHARE_ROOT_REQUEST_MARGIN_MS, type ShareControlBatch, type ShareControlOwner, type ShareControlResult, type ShareControlTarget, ShareGrant, type ShareGrantAccess, type ShareGrantRecord, type ShareGrantRequest, type ShareGrantRow, type ShareGrantRowState, type ShareKeyDelegationInput, type ShareKeySigner, type SharePolicySigner, ShareRoot, ShareRootError, type ShareRootErrorCode, type ShareRootErrorDetails, type ShareRootKind, type ShareRootLink, type ShareRootOptions, type ShareRootOwnerAuthority, type ShareRootRecord, assertNetworkSafeShareId, assertNonExtractableShareKey, assertShareGrantPath, decodeOwnerRootCacao, ensureShareRoot, openEditShare, ownerRootCacaoCid, perShareNetworkIdFor, planShareControlBatches, revokeShareGrant, runShareControlBatch, signShareKeyDelegation };
640
+ type FetchFn = (input: URL, init?: RequestInit) => Promise<Response>;
641
+ /** A bearer link minted under a share root, in today's `/viewer#tc1=` shape. */
642
+ interface RootBearerLink {
643
+ /** `https://<viewer>/viewer#tc1=<token>`: the token carries the link key and its delegation, as SharingService links do. */
644
+ readonly url: string;
645
+ readonly delegationCid: string;
646
+ readonly rootCid: string;
647
+ readonly spaceId: string;
648
+ readonly path: string;
649
+ readonly expiresAt: Date;
650
+ }
651
+ /**
652
+ * Mint a view-only bearer link under a share root (TC-1423 D3, slice 3).
653
+ * Zero approvals: the share key signs `kv/get` on one exact key under the
654
+ * share prefix (`ShareRoot.signBearerLink`: never beyond the root, never
655
+ * more than 30 days, exactly `expiresAt` to the second), the Node imports it
656
+ * (`POST /delegate`), and the link is encoded exactly like a SharingService
657
+ * link: a fresh per-link Ed25519 key and its delegation in a `tc1:` token in
658
+ * the `/viewer` fragment, so viewers and `openNativeShare` read it unchanged.
659
+ *
660
+ * The per-link key travels in the fragment by design (anyone with the link
661
+ * holds it). Links need no decrypt (share roots never have it). Use
662
+ * `ensureShareRoot` first; a `delegation-revoked` or expired root fails here
663
+ * and needs a re-root (one approval).
664
+ */
665
+ declare function publishRootBearerLink(input: {
666
+ readonly root: ShareRoot;
667
+ readonly path: string;
668
+ readonly expiresAt: Date;
669
+ readonly viewerOrigin: string;
670
+ readonly fetch?: FetchFn;
671
+ }): Promise<RootBearerLink>;
672
+ /**
673
+ * Revoke one bearer link with the share key that issued it (zero
674
+ * approvals): a share-key-signed `POST /revoke` on the link's delegation CID.
675
+ * Only a `{ revoked: true, cid }` receipt counts as done. The owner can
676
+ * always revoke the same CID with an owner SIWE (one approval).
677
+ */
678
+ declare function revokeRootBearerLink(input: {
679
+ readonly root: ShareRoot;
680
+ readonly delegationCid: string;
681
+ readonly fetch?: FetchFn;
682
+ }): Promise<{
683
+ readonly revoked: true;
684
+ readonly cid: string;
685
+ }>;
686
+
687
+ export { AGENT_SHARE_ROOT_MAX_LIFETIME_MS, type DecodedOwnerRootCacao, EDIT_SHARE_READER_MAX_LIFETIME_MS, type EditShareReader, type PendingShareGrant, type RootBearerLink, SHARE_CONTROL_HEADER_BUDGET_BYTES, SHARE_CONTROL_LIFETIME_MS, SHARE_NETWORK_NAME_PREFIX, SHARE_ROOT_CLOCK_SKEW_MS, SHARE_ROOT_LINK_MAX_LIFETIME_MS, SHARE_ROOT_MAX_LIFETIME_MS, SHARE_ROOT_PREFIX, SHARE_ROOT_REQUEST_MARGIN_MS, type ShareControlBatch, type ShareControlOwner, type ShareControlResult, type ShareControlTarget, ShareGrant, type ShareGrantAccess, type ShareGrantRecord, type ShareGrantRequest, type ShareGrantRow, type ShareGrantRowState, type ShareKeyDelegationInput, type ShareKeySigner, type SharePolicySigner, ShareRoot, ShareRootError, type ShareRootErrorCode, type ShareRootErrorDetails, type ShareRootKind, type ShareRootLink, type ShareRootOptions, type ShareRootOwnerAuthority, type ShareRootRecord, assertNetworkSafeShareId, assertNonExtractableShareKey, assertShareGrantPath, decodeOwnerRootCacao, ensureShareRoot, openEditShare, ownerRootCacaoCid, perShareNetworkIdFor, planShareControlBatches, publishRootBearerLink, revokeRootBearerLink, revokeShareGrant, runShareControlBatch, signShareKeyDelegation };
@@ -185,6 +185,16 @@ declare class ShareRoot {
185
185
  toRecord(): ShareRootRecord;
186
186
  /** True while the root is live, its key is held, and it lasts at least until `expiresAt`. Revocation is not visible here. */
187
187
  covers(expiresAt: Date, now?: number): boolean;
188
+ /**
189
+ * Sign a 60-second `POST /revoke` invocation, by the share key, for one
190
+ * delegation this root issued (a bearer link). Zero approvals. The Node
191
+ * authorizes it because the share key is that delegation's delegator. The
192
+ * root itself is never revoked this way: only the owner does that.
193
+ */
194
+ signLinkRevocation(input: {
195
+ readonly delegationCid: string;
196
+ readonly nodeDid: string;
197
+ }): Promise<string>;
188
198
  /**
189
199
  * Sign a bearer link: `kv/get` on one exact key under the prefix, to the
190
200
  * link's own key, with this root as its only proof. It never outlives the
@@ -266,7 +276,7 @@ declare function signShareKeyDelegation(input: ShareKeyDelegationInput): Promise
266
276
  type ShareGrantAccess = "view" | "edit";
267
277
  /** P2(a): a read-back key lives at most one hour. */
268
278
  declare const EDIT_SHARE_READER_MAX_LIFETIME_MS: number;
269
- type FetchFn$1 = (input: URL, init?: RequestInit) => Promise<Response>;
279
+ type FetchFn$2 = (input: URL, init?: RequestInit) => Promise<Response>;
270
280
  /**
271
281
  * The share's one content key (I1): `xyz.tinycloud.share/shares/<shareId>/<file>`,
272
282
  * or, for `view` only, a TC-1428 version file
@@ -322,11 +332,14 @@ interface ShareGrantRequest {
322
332
  * exists and is `active` (a half-published share whose key was lost), and
323
333
  * grant a new key on it. Without it, `prepare` refuses an existing network
324
334
  * (`share-id-in-use`) before the prompt. A retired network is always
325
- * refused.
335
+ * refused. After resuming, revoke the lost key's grant with an owner SIWE
336
+ * on its CID (`POST /revoke`; not a kill-all control, which would also
337
+ * retire this network): it stays live, with decrypt on this share, until
338
+ * revoked or expired.
326
339
  */
327
340
  readonly resumeExistingNetwork?: boolean;
328
341
  readonly keyFactory?: () => Promise<DelegatedShareKey>;
329
- readonly fetch?: FetchFn$1;
342
+ readonly fetch?: FetchFn$2;
330
343
  readonly now?: () => number;
331
344
  }
332
345
  /** A per-share grant whose key exists but which the owner has not approved yet. Persist `row` first, then `approve()`. */
@@ -412,7 +425,7 @@ declare class ShareGrant {
412
425
  readonly record: ShareGrantRecord;
413
426
  readonly key: DelegatedShareKey;
414
427
  readonly now?: () => number;
415
- readonly fetch?: FetchFn$1;
428
+ readonly fetch?: FetchFn$2;
416
429
  }): Promise<ShareGrant>;
417
430
  /**
418
431
  * This grant's CACAO as base64url, UNPADDED (the record form). As a Node
@@ -475,7 +488,7 @@ declare function revokeShareGrant(input: {
475
488
  readonly owner: {
476
489
  signOwnerRevocation(cid: string): Promise<string>;
477
490
  };
478
- readonly fetch?: FetchFn$1;
491
+ readonly fetch?: FetchFn$2;
479
492
  }): Promise<{
480
493
  readonly network: "retired" | "absent";
481
494
  readonly grant: "revoked";
@@ -520,15 +533,17 @@ declare function perShareNetworkIdFor(ownerDid: string, shareId: string): string
520
533
  * production ingress (nginx 1.27.4, `large_client_header_buffers 4 32k`,
521
534
  * HTTP/1.1): 32,751 bytes pass, 32,752 bytes get `400 Request Header Or
522
535
  * Cookie Too Large`. A direct Node accepts ~483 KB and Cloudflare 128 KB.
523
- * 30,000 bytes leave 8.4% for header-name and proxy overhead. Each share
524
- * (network URN with `delegation/revoke` + `network.revoke`) costs ~845
525
- * bytes: 34 shares per control with the space rows (29,800 bytes measured
526
- * with the `applications` row, imported by the Node), 35 without.
536
+ * 30,000 bytes leave 8.4% for header-name and proxy overhead. A control's
537
+ * capacity depends on the shareId length; with the `applications` and
538
+ * `default` rows in every control: 33 shares for UUID shareIds, 34 for
539
+ * 32-hex ids, 37 for 16-character ids (34 UUID shares + the `applications`
540
+ * row alone measured 29,800 bytes, imported by the Node). L shares take
541
+ * ceil(L/n) controls, n from the plan.
527
542
  */
528
543
  declare const SHARE_CONTROL_HEADER_BUDGET_BYTES = 30000;
529
544
  /** A control lives ten minutes: long enough to retire and revoke one batch. */
530
545
  declare const SHARE_CONTROL_LIFETIME_MS: number;
531
- type FetchFn = (input: URL, init?: RequestInit) => Promise<Response>;
546
+ type FetchFn$1 = (input: URL, init?: RequestInit) => Promise<Response>;
532
547
  /** The owner wallet, as a SIWE signer: one call = one approval. */
533
548
  interface ShareControlOwner {
534
549
  /** `did:pkh:eip155:<chain>:<address>`. */
@@ -617,9 +632,56 @@ declare function runShareControlBatch(input: {
617
632
  readonly revokeFallback?: {
618
633
  signOwnerRevocation(cid: string): Promise<string>;
619
634
  };
620
- readonly fetch?: FetchFn;
635
+ readonly fetch?: FetchFn$1;
621
636
  readonly now?: () => number;
622
637
  readonly keyFactory?: () => Promise<DelegatedShareKey>;
623
638
  }): Promise<ShareControlResult>;
624
639
 
625
- export { AGENT_SHARE_ROOT_MAX_LIFETIME_MS, type DecodedOwnerRootCacao, EDIT_SHARE_READER_MAX_LIFETIME_MS, type EditShareReader, type PendingShareGrant, SHARE_CONTROL_HEADER_BUDGET_BYTES, SHARE_CONTROL_LIFETIME_MS, SHARE_NETWORK_NAME_PREFIX, SHARE_ROOT_CLOCK_SKEW_MS, SHARE_ROOT_LINK_MAX_LIFETIME_MS, SHARE_ROOT_MAX_LIFETIME_MS, SHARE_ROOT_PREFIX, SHARE_ROOT_REQUEST_MARGIN_MS, type ShareControlBatch, type ShareControlOwner, type ShareControlResult, type ShareControlTarget, ShareGrant, type ShareGrantAccess, type ShareGrantRecord, type ShareGrantRequest, type ShareGrantRow, type ShareGrantRowState, type ShareKeyDelegationInput, type ShareKeySigner, type SharePolicySigner, ShareRoot, ShareRootError, type ShareRootErrorCode, type ShareRootErrorDetails, type ShareRootKind, type ShareRootLink, type ShareRootOptions, type ShareRootOwnerAuthority, type ShareRootRecord, assertNetworkSafeShareId, assertNonExtractableShareKey, assertShareGrantPath, decodeOwnerRootCacao, ensureShareRoot, openEditShare, ownerRootCacaoCid, perShareNetworkIdFor, planShareControlBatches, revokeShareGrant, runShareControlBatch, signShareKeyDelegation };
640
+ type FetchFn = (input: URL, init?: RequestInit) => Promise<Response>;
641
+ /** A bearer link minted under a share root, in today's `/viewer#tc1=` shape. */
642
+ interface RootBearerLink {
643
+ /** `https://<viewer>/viewer#tc1=<token>`: the token carries the link key and its delegation, as SharingService links do. */
644
+ readonly url: string;
645
+ readonly delegationCid: string;
646
+ readonly rootCid: string;
647
+ readonly spaceId: string;
648
+ readonly path: string;
649
+ readonly expiresAt: Date;
650
+ }
651
+ /**
652
+ * Mint a view-only bearer link under a share root (TC-1423 D3, slice 3).
653
+ * Zero approvals: the share key signs `kv/get` on one exact key under the
654
+ * share prefix (`ShareRoot.signBearerLink`: never beyond the root, never
655
+ * more than 30 days, exactly `expiresAt` to the second), the Node imports it
656
+ * (`POST /delegate`), and the link is encoded exactly like a SharingService
657
+ * link: a fresh per-link Ed25519 key and its delegation in a `tc1:` token in
658
+ * the `/viewer` fragment, so viewers and `openNativeShare` read it unchanged.
659
+ *
660
+ * The per-link key travels in the fragment by design (anyone with the link
661
+ * holds it). Links need no decrypt (share roots never have it). Use
662
+ * `ensureShareRoot` first; a `delegation-revoked` or expired root fails here
663
+ * and needs a re-root (one approval).
664
+ */
665
+ declare function publishRootBearerLink(input: {
666
+ readonly root: ShareRoot;
667
+ readonly path: string;
668
+ readonly expiresAt: Date;
669
+ readonly viewerOrigin: string;
670
+ readonly fetch?: FetchFn;
671
+ }): Promise<RootBearerLink>;
672
+ /**
673
+ * Revoke one bearer link with the share key that issued it (zero
674
+ * approvals): a share-key-signed `POST /revoke` on the link's delegation CID.
675
+ * Only a `{ revoked: true, cid }` receipt counts as done. The owner can
676
+ * always revoke the same CID with an owner SIWE (one approval).
677
+ */
678
+ declare function revokeRootBearerLink(input: {
679
+ readonly root: ShareRoot;
680
+ readonly delegationCid: string;
681
+ readonly fetch?: FetchFn;
682
+ }): Promise<{
683
+ readonly revoked: true;
684
+ readonly cid: string;
685
+ }>;
686
+
687
+ export { AGENT_SHARE_ROOT_MAX_LIFETIME_MS, type DecodedOwnerRootCacao, EDIT_SHARE_READER_MAX_LIFETIME_MS, type EditShareReader, type PendingShareGrant, type RootBearerLink, SHARE_CONTROL_HEADER_BUDGET_BYTES, SHARE_CONTROL_LIFETIME_MS, SHARE_NETWORK_NAME_PREFIX, SHARE_ROOT_CLOCK_SKEW_MS, SHARE_ROOT_LINK_MAX_LIFETIME_MS, SHARE_ROOT_MAX_LIFETIME_MS, SHARE_ROOT_PREFIX, SHARE_ROOT_REQUEST_MARGIN_MS, type ShareControlBatch, type ShareControlOwner, type ShareControlResult, type ShareControlTarget, ShareGrant, type ShareGrantAccess, type ShareGrantRecord, type ShareGrantRequest, type ShareGrantRow, type ShareGrantRowState, type ShareKeyDelegationInput, type ShareKeySigner, type SharePolicySigner, ShareRoot, ShareRootError, type ShareRootErrorCode, type ShareRootErrorDetails, type ShareRootKind, type ShareRootLink, type ShareRootOptions, type ShareRootOwnerAuthority, type ShareRootRecord, assertNetworkSafeShareId, assertNonExtractableShareKey, assertShareGrantPath, decodeOwnerRootCacao, ensureShareRoot, openEditShare, ownerRootCacaoCid, perShareNetworkIdFor, planShareControlBatches, publishRootBearerLink, revokeRootBearerLink, revokeShareGrant, runShareControlBatch, signShareKeyDelegation };
@@ -6932,6 +6932,35 @@ var ShareRoot = class _ShareRoot {
6932
6932
  covers(expiresAt, now = this.#now()) {
6933
6933
  return this.#key !== void 0 && now < this.expiresAt.getTime() && expiresAt.getTime() <= this.expiresAt.getTime();
6934
6934
  }
6935
+ /**
6936
+ * Sign a 60-second `POST /revoke` invocation, by the share key, for one
6937
+ * delegation this root issued (a bearer link). Zero approvals. The Node
6938
+ * authorizes it because the share key is that delegation's delegator. The
6939
+ * root itself is never revoked this way: only the owner does that.
6940
+ */
6941
+ async signLinkRevocation(input) {
6942
+ const key = this.#activeKey();
6943
+ if (typeof input.delegationCid !== "string" || !/^b[a-z2-7]+$/.test(input.delegationCid)) throw new ShareRootError("invalid-request", "delegation CID is invalid");
6944
+ if (input.delegationCid === this.cid) throw new ShareRootError("invalid-request", "the share key does not revoke its own root; the owner does");
6945
+ try {
6946
+ ed25519PublicKeyFromDidKey(input.nodeDid);
6947
+ } catch {
6948
+ throw new ShareRootError("invalid-request", "node DID must be an Ed25519 did:key");
6949
+ }
6950
+ const now = seconds(this.#now());
6951
+ const revocation = await signCompactUcanAuthorization({
6952
+ issuerDid: key.did,
6953
+ audienceDid: input.nodeDid,
6954
+ attenuation: { [`urn:cid:${input.delegationCid}`]: { "tinycloud.delegation/revoke": [{}] } },
6955
+ facts: [{}],
6956
+ proofs: [],
6957
+ notBefore: now,
6958
+ expiresAt: now + 60,
6959
+ nonce: `urn:uuid:${crypto.randomUUID()}`,
6960
+ sign: (bytes2) => key.sign(bytes2)
6961
+ });
6962
+ return revocation.authorization;
6963
+ }
6935
6964
  /**
6936
6965
  * Sign a bearer link: `kv/get` on one exact key under the prefix, to the
6937
6966
  * link's own key, with this root as its only proof. It never outlives the
@@ -7714,6 +7743,69 @@ async function runShareControlBatch(input) {
7714
7743
  key.clear();
7715
7744
  }
7716
7745
  }
7746
+
7747
+ // src/share-root-link.ts
7748
+ import { ed25519 as ed255196 } from "@noble/curves/ed25519";
7749
+
7750
+ // src/native.ts
7751
+ var NATIVE_SHARE_FRAGMENT_PARAMETER = "tc1";
7752
+ function canonicalViewerUrl(viewerOrigin) {
7753
+ const url = new URL(viewerOrigin);
7754
+ if (url.protocol !== "https:" || url.origin !== viewerOrigin || url.pathname !== "/" || url.search || url.hash) throw new TypeError("viewer origin must be a canonical HTTPS origin");
7755
+ url.pathname = "/viewer";
7756
+ return url;
7757
+ }
7758
+
7759
+ // src/share-root-link.ts
7760
+ async function publishRootBearerLink(input) {
7761
+ const fetchFn = input.fetch ?? ((url, init) => globalThis.fetch(url, init));
7762
+ const viewerUrl = canonicalViewerUrl(input.viewerOrigin);
7763
+ const secret = ed255196.utils.randomPrivateKey();
7764
+ try {
7765
+ const publicKey = ed255196.getPublicKey(secret);
7766
+ const linkDid = didKeyFromEd25519PublicKey(publicKey);
7767
+ const link = await input.root.signBearerLink({ linkDid, path: input.path, expiresAt: input.expiresAt });
7768
+ const response = await fetchFn(new URL("/delegate", input.root.nodeOrigin), { method: "POST", redirect: "error", headers: { Authorization: link.delegation.authorization } });
7769
+ if (!response.ok) throw new ShareRootError("node-refused", `the Node refused the link delegation: HTTP ${response.status} ${(await response.text()).slice(0, 120)}`, { rootCid: input.root.cid });
7770
+ const createdAt = /* @__PURE__ */ new Date();
7771
+ const shareData = {
7772
+ key: { kty: "OKP", crv: "Ed25519", x: toBase64Url(publicKey), d: toBase64Url(secret) },
7773
+ keyDid: `${linkDid}#${linkDid.slice("did:key:".length)}`,
7774
+ delegation: {
7775
+ cid: link.delegation.cid,
7776
+ delegateDID: linkDid,
7777
+ delegatorDID: input.root.keyDid,
7778
+ spaceId: input.root.spaceId,
7779
+ path: input.path,
7780
+ actions: ["tinycloud.kv/get"],
7781
+ expiry: link.expiresAt,
7782
+ isRevoked: false,
7783
+ allowSubDelegation: true,
7784
+ createdAt,
7785
+ parentCid: input.root.cid,
7786
+ authHeader: link.delegation.authorization
7787
+ },
7788
+ path: input.path,
7789
+ host: input.root.nodeOrigin,
7790
+ spaceId: input.root.spaceId,
7791
+ version: 1
7792
+ };
7793
+ const token = `tc1:${toBase64Url(new TextEncoder().encode(JSON.stringify(shareData)))}`;
7794
+ viewerUrl.hash = `${NATIVE_SHARE_FRAGMENT_PARAMETER}=${encodeURIComponent(token)}`;
7795
+ return { url: viewerUrl.toString(), delegationCid: link.delegation.cid, rootCid: input.root.cid, spaceId: input.root.spaceId, path: input.path, expiresAt: link.expiresAt };
7796
+ } finally {
7797
+ secret.fill(0);
7798
+ }
7799
+ }
7800
+ async function revokeRootBearerLink(input) {
7801
+ const fetchFn = input.fetch ?? ((url, init) => globalThis.fetch(url, init));
7802
+ const nodeDid = await nodeDidOf(input.root.nodeOrigin, fetchFn);
7803
+ const authorization = await input.root.signLinkRevocation({ delegationCid: input.delegationCid, nodeDid });
7804
+ const response = await fetchFn(new URL("/revoke", input.root.nodeOrigin), { method: "POST", redirect: "error", headers: { Authorization: authorization } });
7805
+ if (!response.ok) throw new ShareRootError("node-refused", `the Node refused the link revocation: HTTP ${response.status} ${(await response.text()).slice(0, 120)}`, { rootCid: input.root.cid });
7806
+ if (!await isRevokeReceiptFor(response, input.delegationCid)) throw new ShareRootError("invalid-request", "link revocation receipt does not confirm this delegation");
7807
+ return { revoked: true, cid: input.delegationCid };
7808
+ }
7717
7809
  export {
7718
7810
  AGENT_SHARE_ROOT_MAX_LIFETIME_MS,
7719
7811
  EDIT_SHARE_READER_MAX_LIFETIME_MS,
@@ -7737,6 +7829,8 @@ export {
7737
7829
  ownerRootCacaoCid,
7738
7830
  perShareNetworkIdFor,
7739
7831
  planShareControlBatches,
7832
+ publishRootBearerLink,
7833
+ revokeRootBearerLink,
7740
7834
  revokeShareGrant,
7741
7835
  runShareControlBatch,
7742
7836
  signShareKeyDelegation