@tinycloud/share-sdk 1.4.4 → 1.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,25 +23,22 @@ import {
23
23
  // 1. Plan, before anything is written. The network must be the owner's default network with the EIP-55 checksummed address.
24
24
  const plan = planPathBoundShare({ shareId, encryptionNetwork: defaultEncryptionNetworkForOwner(ownerDid) });
25
25
 
26
- // 2. Hold two capabilities. (a) decrypt under `<network>/share/` (`plan.registrationPermissions`): the node refuses to register the share
27
- // unless the owner holds it; a bare-network decrypt never covers a share key. (b) `key.create` for the share's key, by ONE of:
28
- // - `plan.sessionPermissions` in the manifest the owner signs in with (decrypt + key.create on `<network>/share/`, no prompt per
29
- // share; needs a sign-in route that signs these, for example `signMessage` from a first-party origin), or
30
- // - `await tcw.requestPermissions(plan.requiredPermissions)` per share (one consent per share).
31
- // Without it, `encryptAddressedShareContent` throws `key-not-authorized`. Every created key permanently uses one of the node's 1000 key slots per
32
- // network (deleted keys count, and the count is shared with `apps/` keys): each shared file uses one slot, so validate everything first
33
- // (`planPathBoundShare` refuses a bad share id or network before any key exists). A failure after the key exists still costs the slot and there
34
- // is no key delete: retry with a NEW shareId.
26
+ // 2. Hold decrypt under `<network>/share/` (`plan.registrationPermissions`, or `pathBoundSessionPermissions({ encryptionNetwork })` in the
27
+ // sign-in manifest, before any share exists): the node refuses to register the share unless the owner
28
+ // holds it; a bare-network decrypt never covers a share key. No capability is needed to encrypt: the share's key is deterministic and
29
+ // `keys.encrypt` wraps to its public key, which the node serves without authentication. The node records the key when the share policy
30
+ // is registered (step 5), in the same transaction. Each published share adds one key record, which counts toward the network's key limit
31
+ // (shared with `apps/` keys); `planPathBoundShare` refuses a bad share id or network before anything is encrypted.
35
32
  // The share id is ONE grammar everywhere (plan, size bound, publisher): `[a-z0-9][a-z0-9_-]{15,63}` (16 to 64 characters; a lowercase `randomUUID()`,
36
33
  // with or without its hyphens, is valid). Uppercase, `.`, a leading `_` or `-`, and anything longer than 64 characters are refused.
37
34
 
38
- // 3. Create the key and encrypt under it (`keys` is a named-key client: `tcw.keys`, `node.keys` or `NamedKeyClient`).
35
+ // 3. Encrypt under the share's key (`keys` is a named-key client: `tcw.keys`, `node.keys` or `NamedKeyClient`).
39
36
  const encrypted = await encryptAddressedShareContent({ plan, keys, plaintext, metadata: { contentType } });
40
37
 
41
38
  // 4. Store EXACTLY encrypted.storedBytes (the canonical v2 envelope) at the share's path.
42
39
  await put(path, encrypted.storedBytes);
43
40
 
44
- // 5. Bind and publish.
41
+ // 5. Bind and publish. Registering the policy records the share's key.
45
42
  const contentSource = bindPathBoundContentSource({
46
43
  plan, envelope: encrypted.envelope, storedBytes: encrypted.storedBytes, kvResource: `${spaceId}/kv/${path}`, selector: "exact",
47
44
  });
@@ -50,4 +47,4 @@ await publishAddressedShare({ /* shareId, target, resource, actions, ... */ cont
50
47
 
51
48
  `publishAddressedShare` refuses a content source that is not bound to its own share's key (`AddressedContentSourceError`, match on `code`: `key-path-required`, `key-path-mismatch`, `digest-invalid`, `network-invalid`) before it signs, registers or writes anything; there is no network-wide fallback. Do not call `encryptToNetwork` with a `keyPath` for a share: it returns a v1 envelope wrapped to the network key, which neither the node nor the recipient opens. `assertAddressedShareFits` and `addressedShareBodyUpperBound` derive the key path from `shareId`. Opening an older share that has no key path is unchanged.
52
49
 
53
- `pathBoundSessionPermissions({ encryptionNetwork })` returns the one manifest entry for the sign-in route (`decrypt` + `key.create` on `<network>/share/`) without needing a share id; build both your manifest and any scope guard of your own from it. Hand-written content sources are not supported: the publisher checks the path, the digest's format and the network, but only `bindPathBoundContentSource` derives the raw-byte digest the recipient and the node compare.
50
+ Hand-written content sources are not supported: the publisher checks the path, the digest's format and the network, but only `bindPathBoundContentSource` derives the raw-byte digest the recipient and the node compare.
@@ -9,11 +9,11 @@ import { S as ShareAuthorizationRequired, a as ShareAuthorizationMethod } from '
9
9
  * opens only that one key, never the network. This module is the single place that derives the path, validates the
10
10
  * network, serializes the stored envelope and computes the digests, so no consumer hand-writes any of them.
11
11
  *
12
- * Call sequence: `planPathBoundShare` (before anything is written) -> the caller holds the plan's `requiredPermissions`
13
- * -> `encryptAddressedShareContent` (or the caller's own `keys.createKey` + `keys.encrypt`) -> upload `storedBytes` ->
14
- * `bindPathBoundContentSource` -> `publishAddressedShare`.
12
+ * Call sequence: `planPathBoundShare` (before anything is written) -> `encryptAddressedShareContent` (or the caller's own
13
+ * `keys.encrypt`, which encrypts to the key's public key) -> upload `storedBytes` -> `bindPathBoundContentSource` ->
14
+ * `publishAddressedShare`. The node records the share's key when the policy is registered; nothing creates it beforehand.
15
15
  */
16
- type AddressedContentSourceErrorCode = "share-id-invalid" | "network-invalid" | "network-not-checksummed" | "key-path-required" | "key-path-mismatch" | "digest-invalid" | "key-not-authorized" | "key-receipt-mismatch" | "envelope-invalid" | "stored-bytes-mismatch";
16
+ type AddressedContentSourceErrorCode = "share-id-invalid" | "network-invalid" | "network-not-checksummed" | "key-path-required" | "key-path-mismatch" | "digest-invalid" | "envelope-invalid" | "stored-bytes-mismatch";
17
17
  /** A path-bound source could not be planned, built or accepted. Match on `code`, not the message or `instanceof`. */
18
18
  declare class AddressedContentSourceError extends TypeError {
19
19
  readonly code: AddressedContentSourceErrorCode;
@@ -56,18 +56,7 @@ interface PathBoundSharePlan {
56
56
  readonly keyPath: string;
57
57
  /** `<network>/share/<shareId>`: the one decrypt resource the share grants. */
58
58
  readonly keyResource: string;
59
- /**
60
- * ROUTE A, per share at runtime: `key.create` on exactly this share's key, requested with `requestPermissions` before the key is created
61
- * (one extra consent per share). For apps that cannot use the sign-in route (for example apps whose sign-in goes through the OpenKey widget
62
- * `authorizeTinyCloud` route, which refuses decrypt and key.create outside `apps/`).
63
- */
64
- readonly requiredPermissions: PathBoundPermissionEntry[];
65
- /**
66
- * ROUTE B, once at sign-in (manifest entries): `key.create` and `decrypt` on the default network's `<network>/share/` prefix, so every share's
67
- * key can be created without a further prompt. Put these in the `permissions` of the manifest the owner signs in with.
68
- */
69
- readonly sessionPermissions: PathBoundPermissionEntry[];
70
- /** The owner session must hold these for the node to register the policy: decrypt under `<network>/share/` (also part of `sessionPermissions`). */
59
+ /** The owner session must hold these for the node to register the policy: decrypt under `<network>/share/`. */
71
60
  readonly registrationPermissions: PathBoundPermissionEntry[];
72
61
  /** Options for `keys.encrypt(plan.keyPath, plaintext, plan.encryptOptions({ metadata }))`. */
73
62
  encryptOptions(extra?: {
@@ -78,8 +67,8 @@ interface PathBoundSharePlan {
78
67
  };
79
68
  }
80
69
  /**
81
- * The manifest entries (route B, sign-in) that let one session create every share's key: `decrypt` and `key.create` on the default network's
82
- * `<network>/share/` prefix. Needs no share id, so an app builds its manifest before any share exists. Put the result in `manifest.permissions`.
70
+ * The manifest entries that let one session register every share's policy: `decrypt` on the default network's `<network>/share/` prefix.
71
+ * Needs no share id, so an app builds its sign-in manifest before any share exists. Put the result in `manifest.permissions`.
83
72
  */
84
73
  declare function pathBoundSessionPermissions(input: {
85
74
  readonly encryptionNetwork: string;
@@ -114,14 +103,6 @@ declare function bindPathBoundContentSource(input: {
114
103
  }): PathBoundContentSource;
115
104
  /** The part of a named-key client (`NamedKeyClient`, web `tcw.keys`, node `node.keys`) this module calls. */
116
105
  interface AddressedShareKeyClient {
117
- createKey(path: string, options: {
118
- networkId: string;
119
- }): Promise<{
120
- readonly networkId: string;
121
- readonly keyPath: string;
122
- readonly state?: string;
123
- readonly keyVersion?: number;
124
- }>;
125
106
  encrypt(path: string, plaintext: Uint8Array, options: {
126
107
  networkId: string;
127
108
  metadata?: Record<string, string>;
@@ -134,9 +115,8 @@ interface EncryptedAddressedContent {
134
115
  readonly byteLength: number;
135
116
  }
136
117
  /**
137
- * Create the share's named key and encrypt the content under it. Built from the same steps a consumer may run itself
138
- * (`keys.createKey`, `keys.encrypt`, then `bindPathBoundContentSource`). The key-create capability in
139
- * `plan.requiredPermissions` must already be held; an unauthorized answer is reported as `key-not-authorized` naming it.
118
+ * Encrypt the content under the share's named key. `keys.encrypt` wraps to the key's public key, which the node serves without
119
+ * authentication; the key needs no record yet (the node records it when the share policy is registered).
140
120
  */
141
121
  declare function encryptAddressedShareContent(input: {
142
122
  readonly plan: PathBoundSharePlan;
@@ -9,11 +9,11 @@ import { S as ShareAuthorizationRequired, a as ShareAuthorizationMethod } from '
9
9
  * opens only that one key, never the network. This module is the single place that derives the path, validates the
10
10
  * network, serializes the stored envelope and computes the digests, so no consumer hand-writes any of them.
11
11
  *
12
- * Call sequence: `planPathBoundShare` (before anything is written) -> the caller holds the plan's `requiredPermissions`
13
- * -> `encryptAddressedShareContent` (or the caller's own `keys.createKey` + `keys.encrypt`) -> upload `storedBytes` ->
14
- * `bindPathBoundContentSource` -> `publishAddressedShare`.
12
+ * Call sequence: `planPathBoundShare` (before anything is written) -> `encryptAddressedShareContent` (or the caller's own
13
+ * `keys.encrypt`, which encrypts to the key's public key) -> upload `storedBytes` -> `bindPathBoundContentSource` ->
14
+ * `publishAddressedShare`. The node records the share's key when the policy is registered; nothing creates it beforehand.
15
15
  */
16
- type AddressedContentSourceErrorCode = "share-id-invalid" | "network-invalid" | "network-not-checksummed" | "key-path-required" | "key-path-mismatch" | "digest-invalid" | "key-not-authorized" | "key-receipt-mismatch" | "envelope-invalid" | "stored-bytes-mismatch";
16
+ type AddressedContentSourceErrorCode = "share-id-invalid" | "network-invalid" | "network-not-checksummed" | "key-path-required" | "key-path-mismatch" | "digest-invalid" | "envelope-invalid" | "stored-bytes-mismatch";
17
17
  /** A path-bound source could not be planned, built or accepted. Match on `code`, not the message or `instanceof`. */
18
18
  declare class AddressedContentSourceError extends TypeError {
19
19
  readonly code: AddressedContentSourceErrorCode;
@@ -56,18 +56,7 @@ interface PathBoundSharePlan {
56
56
  readonly keyPath: string;
57
57
  /** `<network>/share/<shareId>`: the one decrypt resource the share grants. */
58
58
  readonly keyResource: string;
59
- /**
60
- * ROUTE A, per share at runtime: `key.create` on exactly this share's key, requested with `requestPermissions` before the key is created
61
- * (one extra consent per share). For apps that cannot use the sign-in route (for example apps whose sign-in goes through the OpenKey widget
62
- * `authorizeTinyCloud` route, which refuses decrypt and key.create outside `apps/`).
63
- */
64
- readonly requiredPermissions: PathBoundPermissionEntry[];
65
- /**
66
- * ROUTE B, once at sign-in (manifest entries): `key.create` and `decrypt` on the default network's `<network>/share/` prefix, so every share's
67
- * key can be created without a further prompt. Put these in the `permissions` of the manifest the owner signs in with.
68
- */
69
- readonly sessionPermissions: PathBoundPermissionEntry[];
70
- /** The owner session must hold these for the node to register the policy: decrypt under `<network>/share/` (also part of `sessionPermissions`). */
59
+ /** The owner session must hold these for the node to register the policy: decrypt under `<network>/share/`. */
71
60
  readonly registrationPermissions: PathBoundPermissionEntry[];
72
61
  /** Options for `keys.encrypt(plan.keyPath, plaintext, plan.encryptOptions({ metadata }))`. */
73
62
  encryptOptions(extra?: {
@@ -78,8 +67,8 @@ interface PathBoundSharePlan {
78
67
  };
79
68
  }
80
69
  /**
81
- * The manifest entries (route B, sign-in) that let one session create every share's key: `decrypt` and `key.create` on the default network's
82
- * `<network>/share/` prefix. Needs no share id, so an app builds its manifest before any share exists. Put the result in `manifest.permissions`.
70
+ * The manifest entries that let one session register every share's policy: `decrypt` on the default network's `<network>/share/` prefix.
71
+ * Needs no share id, so an app builds its sign-in manifest before any share exists. Put the result in `manifest.permissions`.
83
72
  */
84
73
  declare function pathBoundSessionPermissions(input: {
85
74
  readonly encryptionNetwork: string;
@@ -114,14 +103,6 @@ declare function bindPathBoundContentSource(input: {
114
103
  }): PathBoundContentSource;
115
104
  /** The part of a named-key client (`NamedKeyClient`, web `tcw.keys`, node `node.keys`) this module calls. */
116
105
  interface AddressedShareKeyClient {
117
- createKey(path: string, options: {
118
- networkId: string;
119
- }): Promise<{
120
- readonly networkId: string;
121
- readonly keyPath: string;
122
- readonly state?: string;
123
- readonly keyVersion?: number;
124
- }>;
125
106
  encrypt(path: string, plaintext: Uint8Array, options: {
126
107
  networkId: string;
127
108
  metadata?: Record<string, string>;
@@ -134,9 +115,8 @@ interface EncryptedAddressedContent {
134
115
  readonly byteLength: number;
135
116
  }
136
117
  /**
137
- * Create the share's named key and encrypt the content under it. Built from the same steps a consumer may run itself
138
- * (`keys.createKey`, `keys.encrypt`, then `bindPathBoundContentSource`). The key-create capability in
139
- * `plan.requiredPermissions` must already be held; an unauthorized answer is reported as `key-not-authorized` naming it.
118
+ * Encrypt the content under the share's named key. `keys.encrypt` wraps to the key's public key, which the node serves without
119
+ * authentication; the key needs no record yet (the node records it when the share policy is registered).
140
120
  */
141
121
  declare function encryptAddressedShareContent(input: {
142
122
  readonly plan: PathBoundSharePlan;
package/dist/artifact.cjs CHANGED
@@ -5484,6 +5484,7 @@ function serialize(value) {
5484
5484
  function canonicalize(value) {
5485
5485
  return serialize(value);
5486
5486
  }
5487
+ var POLICY_ROOT_MAX_LIFETIME_SECONDS = 31 * 24 * 60 * 60;
5487
5488
 
5488
5489
  // src/artifact.ts
5489
5490
  var import_sha2562 = require("@noble/hashes/sha256");