@tinycloud/share-sdk 1.2.0-beta.3 → 1.2.0-beta.5
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/dist/addressed-publish-BnSBJ-MX.d.ts +242 -0
- package/dist/addressed-publish-DtcLNG2m.d.cts +242 -0
- package/dist/artifact.cjs +88 -13
- package/dist/artifact.cjs.map +1 -1
- package/dist/artifact.d.cts +28 -8
- package/dist/artifact.d.ts +28 -8
- package/dist/artifact.js +84 -14
- package/dist/artifact.js.map +1 -1
- package/dist/history-CUWDK0lE.d.cts +180 -0
- package/dist/history-CyT6x8h4.d.ts +180 -0
- package/dist/index.cjs +128 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -154
- package/dist/index.d.ts +93 -154
- package/dist/index.js +123 -4
- package/dist/index.js.map +1 -1
- package/dist/owner-policy-BqzRlkb6.d.cts +297 -0
- package/dist/owner-policy-BqzRlkb6.d.ts +297 -0
- package/dist/share-index.cjs +8714 -0
- package/dist/share-index.cjs.map +1 -0
- package/dist/share-index.d.cts +1063 -0
- package/dist/share-index.d.ts +1063 -0
- package/dist/share-index.js +8642 -0
- package/dist/share-index.js.map +1 -0
- package/dist/share-root.cjs +23 -1
- package/dist/share-root.cjs.map +1 -1
- package/dist/share-root.d.cts +2 -1
- package/dist/share-root.d.ts +2 -1
- package/dist/share-root.js +23 -1
- package/dist/share-root.js.map +1 -1
- package/package.json +8 -2
- package/dist/addressed-publish-CsUS7-SI.d.cts +0 -535
- package/dist/addressed-publish-CsUS7-SI.d.ts +0 -535
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { d as OwnerShareAction, f as OwnerShareMatcher, j as PublishedShareDeliveryMaterial, q as SignedDelegation, h as OwnerSharePolicyRegistrationReceipt, a as DelegatedShareKey, P as PublishedShare } from './owner-policy-BqzRlkb6.cjs';
|
|
2
|
+
|
|
3
|
+
interface EncryptedShareHistoryStorage {
|
|
4
|
+
put(value: Uint8Array): Promise<void>;
|
|
5
|
+
list(): Promise<readonly Uint8Array[]>;
|
|
6
|
+
clear(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
declare class MemoryEncryptedShareHistoryStorage implements EncryptedShareHistoryStorage {
|
|
9
|
+
private readonly values;
|
|
10
|
+
put(value: Uint8Array): Promise<void>;
|
|
11
|
+
list(): Promise<readonly Uint8Array[]>;
|
|
12
|
+
clear(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The durable record a sender needs, after a reload, to display and manage
|
|
16
|
+
* a share it created. Every identity/CID here comes verbatim from the
|
|
17
|
+
* Node-verified {@link OwnerSharePolicyRegistrationReceipt}; nothing is
|
|
18
|
+
* re-derived or guessed.
|
|
19
|
+
*/
|
|
20
|
+
interface SenderShareRecord {
|
|
21
|
+
readonly shareId: string;
|
|
22
|
+
readonly registrationCid?: string;
|
|
23
|
+
readonly policyCid?: string;
|
|
24
|
+
readonly ownerDelegationCid?: string;
|
|
25
|
+
readonly enforcementDelegationCid?: string;
|
|
26
|
+
readonly shareKeyDid?: string;
|
|
27
|
+
readonly ownerDid?: string;
|
|
28
|
+
readonly enforcerDid?: string;
|
|
29
|
+
readonly target: {
|
|
30
|
+
readonly origin: string;
|
|
31
|
+
readonly nodeAudience: string;
|
|
32
|
+
readonly spaceId: string;
|
|
33
|
+
};
|
|
34
|
+
readonly resource: {
|
|
35
|
+
readonly kind: "exact" | "prefix";
|
|
36
|
+
readonly path: string;
|
|
37
|
+
};
|
|
38
|
+
readonly actions: readonly OwnerShareAction[];
|
|
39
|
+
readonly recipientMatcher: OwnerShareMatcher;
|
|
40
|
+
/** The signed target discriminator, retained separately from the email matcher. */
|
|
41
|
+
readonly targetKind?: "bearer" | "recipientDid" | "email" | "emailDomain";
|
|
42
|
+
readonly registeredAt: string;
|
|
43
|
+
readonly expiresAt: string;
|
|
44
|
+
readonly revokedAt?: string;
|
|
45
|
+
readonly envelopeCid?: string;
|
|
46
|
+
readonly shareCid?: string;
|
|
47
|
+
/** Complete bearer links are encrypted at rest and only revealed explicitly. */
|
|
48
|
+
readonly link?: string;
|
|
49
|
+
readonly filename?: string;
|
|
50
|
+
/** Policy/v3 delivery material. The containing history record must remain encrypted at rest. */
|
|
51
|
+
readonly deliveryMaterial?: PublishedShareDeliveryMaterial;
|
|
52
|
+
/** Canonical mailboxes whose invitation delivery was confirmed; history is encrypted at rest. */
|
|
53
|
+
readonly deliveredRecipients?: readonly string[];
|
|
54
|
+
/**
|
|
55
|
+
* `migrated`: imported once from a CLI's local history by the OWNER's
|
|
56
|
+
* session (slice 5), at an owner-layout key `records/<shareId>`. Such a
|
|
57
|
+
* record is exempt from the ±1-day `registeredAt` window and nothing else:
|
|
58
|
+
* its chain is verified like an agent record's, it never ranks as a web
|
|
59
|
+
* row, and it cannot override a revocation. A record claiming `migrated`
|
|
60
|
+
* inside an agent subtree is rejected.
|
|
61
|
+
*/
|
|
62
|
+
readonly provenance?: "migrated";
|
|
63
|
+
/**
|
|
64
|
+
* The signed chain behind every CID this record claims (TC-1432 C1), as
|
|
65
|
+
* exact bytes. Agent records carry it so the owner's browser can verify
|
|
66
|
+
* the chain without a node lookup. Policy/v3 records prove through
|
|
67
|
+
* `deliveryMaterial.envelope` plus `session`.
|
|
68
|
+
*/
|
|
69
|
+
readonly proofs?: SenderShareRecordProofs;
|
|
70
|
+
}
|
|
71
|
+
interface SenderShareRecordProofs {
|
|
72
|
+
/** Owner→session SIWE CACAO (base64url DAG-CBOR) of the issuing session; absent when it is gone. */
|
|
73
|
+
readonly session?: string;
|
|
74
|
+
/** Native bearer link delegation (compact UCAN); its CID is `enforcementDelegationCid`. */
|
|
75
|
+
readonly link?: string;
|
|
76
|
+
/** Policy/v2 owner-policy chain: policy bytes (base64url), owner→share-key SIWE CACAO (base64url), enforcement delegation and node registration receipt. */
|
|
77
|
+
readonly ownerPolicy?: string;
|
|
78
|
+
readonly ownerDelegation?: string;
|
|
79
|
+
readonly enforcementDelegation?: SignedDelegation;
|
|
80
|
+
readonly registration?: OwnerSharePolicyRegistrationReceipt;
|
|
81
|
+
}
|
|
82
|
+
/** Create the durable encrypted-history shape from canonical publication receipts. */
|
|
83
|
+
declare function historyRecordForPublishedShare(result: PublishedShare, now?: Date): SenderShareRecord;
|
|
84
|
+
/**
|
|
85
|
+
* "direct" selects the Policy/v3 enforcement root; "ancestor" selects its
|
|
86
|
+
* policy-authority sibling. Addressed shares revoke that signed root through
|
|
87
|
+
* the Node Policy/v3 endpoint, which cuts off existing sessions as well as
|
|
88
|
+
* fresh admission and delivery. Bearer shares retain ordinary delegation
|
|
89
|
+
* revocation.
|
|
90
|
+
*/
|
|
91
|
+
type SenderShareRevocationScope = "direct" | "ancestor";
|
|
92
|
+
interface SenderShareRecordStorage {
|
|
93
|
+
put(record: SenderShareRecord): Promise<void>;
|
|
94
|
+
/** Atomically transform the latest record, if present. Implementations that
|
|
95
|
+
* provide this must serialize the read and write with every other writer. */
|
|
96
|
+
update?(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
|
|
97
|
+
list(): Promise<readonly SenderShareRecord[]>;
|
|
98
|
+
get(shareId: string): Promise<SenderShareRecord | undefined>;
|
|
99
|
+
delete(shareId: string): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
interface StoredSenderShareKey {
|
|
102
|
+
readonly cryptoKey: CryptoKey;
|
|
103
|
+
readonly publicKey: Uint8Array;
|
|
104
|
+
readonly extractable: boolean;
|
|
105
|
+
}
|
|
106
|
+
interface SenderShareKeyStorage {
|
|
107
|
+
put(shareId: string, key: StoredSenderShareKey): Promise<void>;
|
|
108
|
+
get(shareId: string): Promise<StoredSenderShareKey | undefined>;
|
|
109
|
+
delete(shareId: string): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
/** In-memory reference implementation. Real deployments back these with IndexedDB so the non-extractable `CryptoKey` survives a reload via structured clone. */
|
|
112
|
+
declare class MemorySenderShareRecordStorage implements SenderShareRecordStorage {
|
|
113
|
+
private readonly records;
|
|
114
|
+
private operation;
|
|
115
|
+
private serialize;
|
|
116
|
+
put(record: SenderShareRecord): Promise<void>;
|
|
117
|
+
update(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
|
|
118
|
+
list(): Promise<readonly SenderShareRecord[]>;
|
|
119
|
+
get(shareId: string): Promise<SenderShareRecord | undefined>;
|
|
120
|
+
delete(shareId: string): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
declare class MemorySenderShareKeyStorage implements SenderShareKeyStorage {
|
|
123
|
+
private readonly keys;
|
|
124
|
+
put(shareId: string, key: StoredSenderShareKey): Promise<void>;
|
|
125
|
+
get(shareId: string): Promise<StoredSenderShareKey | undefined>;
|
|
126
|
+
delete(shareId: string): Promise<void>;
|
|
127
|
+
}
|
|
128
|
+
interface SenderShareStoreOptions {
|
|
129
|
+
readonly records: SenderShareRecordStorage;
|
|
130
|
+
readonly keys: SenderShareKeyStorage;
|
|
131
|
+
readonly now?: () => Date;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Reload-safe sender-side management for shares created through
|
|
135
|
+
* {@link canonicalOwnerSharePolicy} / `registerOwnerSharePolicy`. Persists
|
|
136
|
+
* the registration receipt's canonical identity tuple plus the ephemeral
|
|
137
|
+
* share key's non-extractable `CryptoKey` handle, so a sender can list,
|
|
138
|
+
* inspect, and revoke shares it created in an earlier session -- without
|
|
139
|
+
* ever re-exporting private key material.
|
|
140
|
+
*/
|
|
141
|
+
declare class SenderShareStore {
|
|
142
|
+
private readonly records;
|
|
143
|
+
private readonly keys;
|
|
144
|
+
private readonly now;
|
|
145
|
+
constructor(options: SenderShareStoreOptions);
|
|
146
|
+
/** Persist a Node-verified registration receipt and its share key handle. */
|
|
147
|
+
save(receipt: OwnerSharePolicyRegistrationReceipt, key: DelegatedShareKey): Promise<SenderShareRecord>;
|
|
148
|
+
/** List shares created in any session, most recent first. */
|
|
149
|
+
list(options?: {
|
|
150
|
+
readonly includeRevoked?: boolean;
|
|
151
|
+
readonly includeExpired?: boolean;
|
|
152
|
+
}): Promise<readonly SenderShareRecord[]>;
|
|
153
|
+
get(shareId: string): Promise<SenderShareRecord | undefined>;
|
|
154
|
+
/** Recover a signer for the persisted share key after a reload. */
|
|
155
|
+
loadKey(shareId: string): Promise<DelegatedShareKey | undefined>;
|
|
156
|
+
/**
|
|
157
|
+
* The delegation CID to pass to the node's existing generic
|
|
158
|
+
* `tinycloud.delegation/revoke` invocation to revoke this share at the
|
|
159
|
+
* requested scope. Does not itself call the node -- callers invoke
|
|
160
|
+
* their own `revokeDelegation`/`DelegationManager.revoke` with the
|
|
161
|
+
* returned CID, then call {@link markRevoked} once that succeeds.
|
|
162
|
+
*/
|
|
163
|
+
revocationTarget(shareId: string, scope: SenderShareRevocationScope): Promise<string>;
|
|
164
|
+
/** Mark a share revoked locally after the caller's node-side revoke call succeeds. */
|
|
165
|
+
markRevoked(shareId: string, revokedAt?: string): Promise<SenderShareRecord>;
|
|
166
|
+
/** Forget a share and clear its persisted key handle. */
|
|
167
|
+
remove(shareId: string): Promise<void>;
|
|
168
|
+
}
|
|
169
|
+
/** WebCrypto-backed persistence adapter; links and private material are absent. */
|
|
170
|
+
declare class EncryptedSenderShareHistory {
|
|
171
|
+
private readonly storage;
|
|
172
|
+
private readonly key;
|
|
173
|
+
private readonly now;
|
|
174
|
+
constructor(storage: EncryptedShareHistoryStorage, key: CryptoKey, now?: () => Date);
|
|
175
|
+
put(record: SenderShareRecord): Promise<void>;
|
|
176
|
+
list(): Promise<readonly SenderShareRecord[]>;
|
|
177
|
+
clear(): Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { EncryptedSenderShareHistory as E, MemoryEncryptedShareHistoryStorage as M, type SenderShareRecord as S, type SenderShareRecordStorage as a, type EncryptedShareHistoryStorage as b, MemorySenderShareKeyStorage as c, MemorySenderShareRecordStorage as d, type SenderShareKeyStorage as e, type SenderShareRecordProofs as f, type SenderShareRevocationScope as g, SenderShareStore as h, historyRecordForPublishedShare as i };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { d as OwnerShareAction, f as OwnerShareMatcher, j as PublishedShareDeliveryMaterial, q as SignedDelegation, h as OwnerSharePolicyRegistrationReceipt, a as DelegatedShareKey, P as PublishedShare } from './owner-policy-BqzRlkb6.js';
|
|
2
|
+
|
|
3
|
+
interface EncryptedShareHistoryStorage {
|
|
4
|
+
put(value: Uint8Array): Promise<void>;
|
|
5
|
+
list(): Promise<readonly Uint8Array[]>;
|
|
6
|
+
clear(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
declare class MemoryEncryptedShareHistoryStorage implements EncryptedShareHistoryStorage {
|
|
9
|
+
private readonly values;
|
|
10
|
+
put(value: Uint8Array): Promise<void>;
|
|
11
|
+
list(): Promise<readonly Uint8Array[]>;
|
|
12
|
+
clear(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The durable record a sender needs, after a reload, to display and manage
|
|
16
|
+
* a share it created. Every identity/CID here comes verbatim from the
|
|
17
|
+
* Node-verified {@link OwnerSharePolicyRegistrationReceipt}; nothing is
|
|
18
|
+
* re-derived or guessed.
|
|
19
|
+
*/
|
|
20
|
+
interface SenderShareRecord {
|
|
21
|
+
readonly shareId: string;
|
|
22
|
+
readonly registrationCid?: string;
|
|
23
|
+
readonly policyCid?: string;
|
|
24
|
+
readonly ownerDelegationCid?: string;
|
|
25
|
+
readonly enforcementDelegationCid?: string;
|
|
26
|
+
readonly shareKeyDid?: string;
|
|
27
|
+
readonly ownerDid?: string;
|
|
28
|
+
readonly enforcerDid?: string;
|
|
29
|
+
readonly target: {
|
|
30
|
+
readonly origin: string;
|
|
31
|
+
readonly nodeAudience: string;
|
|
32
|
+
readonly spaceId: string;
|
|
33
|
+
};
|
|
34
|
+
readonly resource: {
|
|
35
|
+
readonly kind: "exact" | "prefix";
|
|
36
|
+
readonly path: string;
|
|
37
|
+
};
|
|
38
|
+
readonly actions: readonly OwnerShareAction[];
|
|
39
|
+
readonly recipientMatcher: OwnerShareMatcher;
|
|
40
|
+
/** The signed target discriminator, retained separately from the email matcher. */
|
|
41
|
+
readonly targetKind?: "bearer" | "recipientDid" | "email" | "emailDomain";
|
|
42
|
+
readonly registeredAt: string;
|
|
43
|
+
readonly expiresAt: string;
|
|
44
|
+
readonly revokedAt?: string;
|
|
45
|
+
readonly envelopeCid?: string;
|
|
46
|
+
readonly shareCid?: string;
|
|
47
|
+
/** Complete bearer links are encrypted at rest and only revealed explicitly. */
|
|
48
|
+
readonly link?: string;
|
|
49
|
+
readonly filename?: string;
|
|
50
|
+
/** Policy/v3 delivery material. The containing history record must remain encrypted at rest. */
|
|
51
|
+
readonly deliveryMaterial?: PublishedShareDeliveryMaterial;
|
|
52
|
+
/** Canonical mailboxes whose invitation delivery was confirmed; history is encrypted at rest. */
|
|
53
|
+
readonly deliveredRecipients?: readonly string[];
|
|
54
|
+
/**
|
|
55
|
+
* `migrated`: imported once from a CLI's local history by the OWNER's
|
|
56
|
+
* session (slice 5), at an owner-layout key `records/<shareId>`. Such a
|
|
57
|
+
* record is exempt from the ±1-day `registeredAt` window and nothing else:
|
|
58
|
+
* its chain is verified like an agent record's, it never ranks as a web
|
|
59
|
+
* row, and it cannot override a revocation. A record claiming `migrated`
|
|
60
|
+
* inside an agent subtree is rejected.
|
|
61
|
+
*/
|
|
62
|
+
readonly provenance?: "migrated";
|
|
63
|
+
/**
|
|
64
|
+
* The signed chain behind every CID this record claims (TC-1432 C1), as
|
|
65
|
+
* exact bytes. Agent records carry it so the owner's browser can verify
|
|
66
|
+
* the chain without a node lookup. Policy/v3 records prove through
|
|
67
|
+
* `deliveryMaterial.envelope` plus `session`.
|
|
68
|
+
*/
|
|
69
|
+
readonly proofs?: SenderShareRecordProofs;
|
|
70
|
+
}
|
|
71
|
+
interface SenderShareRecordProofs {
|
|
72
|
+
/** Owner→session SIWE CACAO (base64url DAG-CBOR) of the issuing session; absent when it is gone. */
|
|
73
|
+
readonly session?: string;
|
|
74
|
+
/** Native bearer link delegation (compact UCAN); its CID is `enforcementDelegationCid`. */
|
|
75
|
+
readonly link?: string;
|
|
76
|
+
/** Policy/v2 owner-policy chain: policy bytes (base64url), owner→share-key SIWE CACAO (base64url), enforcement delegation and node registration receipt. */
|
|
77
|
+
readonly ownerPolicy?: string;
|
|
78
|
+
readonly ownerDelegation?: string;
|
|
79
|
+
readonly enforcementDelegation?: SignedDelegation;
|
|
80
|
+
readonly registration?: OwnerSharePolicyRegistrationReceipt;
|
|
81
|
+
}
|
|
82
|
+
/** Create the durable encrypted-history shape from canonical publication receipts. */
|
|
83
|
+
declare function historyRecordForPublishedShare(result: PublishedShare, now?: Date): SenderShareRecord;
|
|
84
|
+
/**
|
|
85
|
+
* "direct" selects the Policy/v3 enforcement root; "ancestor" selects its
|
|
86
|
+
* policy-authority sibling. Addressed shares revoke that signed root through
|
|
87
|
+
* the Node Policy/v3 endpoint, which cuts off existing sessions as well as
|
|
88
|
+
* fresh admission and delivery. Bearer shares retain ordinary delegation
|
|
89
|
+
* revocation.
|
|
90
|
+
*/
|
|
91
|
+
type SenderShareRevocationScope = "direct" | "ancestor";
|
|
92
|
+
interface SenderShareRecordStorage {
|
|
93
|
+
put(record: SenderShareRecord): Promise<void>;
|
|
94
|
+
/** Atomically transform the latest record, if present. Implementations that
|
|
95
|
+
* provide this must serialize the read and write with every other writer. */
|
|
96
|
+
update?(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
|
|
97
|
+
list(): Promise<readonly SenderShareRecord[]>;
|
|
98
|
+
get(shareId: string): Promise<SenderShareRecord | undefined>;
|
|
99
|
+
delete(shareId: string): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
interface StoredSenderShareKey {
|
|
102
|
+
readonly cryptoKey: CryptoKey;
|
|
103
|
+
readonly publicKey: Uint8Array;
|
|
104
|
+
readonly extractable: boolean;
|
|
105
|
+
}
|
|
106
|
+
interface SenderShareKeyStorage {
|
|
107
|
+
put(shareId: string, key: StoredSenderShareKey): Promise<void>;
|
|
108
|
+
get(shareId: string): Promise<StoredSenderShareKey | undefined>;
|
|
109
|
+
delete(shareId: string): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
/** In-memory reference implementation. Real deployments back these with IndexedDB so the non-extractable `CryptoKey` survives a reload via structured clone. */
|
|
112
|
+
declare class MemorySenderShareRecordStorage implements SenderShareRecordStorage {
|
|
113
|
+
private readonly records;
|
|
114
|
+
private operation;
|
|
115
|
+
private serialize;
|
|
116
|
+
put(record: SenderShareRecord): Promise<void>;
|
|
117
|
+
update(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
|
|
118
|
+
list(): Promise<readonly SenderShareRecord[]>;
|
|
119
|
+
get(shareId: string): Promise<SenderShareRecord | undefined>;
|
|
120
|
+
delete(shareId: string): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
declare class MemorySenderShareKeyStorage implements SenderShareKeyStorage {
|
|
123
|
+
private readonly keys;
|
|
124
|
+
put(shareId: string, key: StoredSenderShareKey): Promise<void>;
|
|
125
|
+
get(shareId: string): Promise<StoredSenderShareKey | undefined>;
|
|
126
|
+
delete(shareId: string): Promise<void>;
|
|
127
|
+
}
|
|
128
|
+
interface SenderShareStoreOptions {
|
|
129
|
+
readonly records: SenderShareRecordStorage;
|
|
130
|
+
readonly keys: SenderShareKeyStorage;
|
|
131
|
+
readonly now?: () => Date;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Reload-safe sender-side management for shares created through
|
|
135
|
+
* {@link canonicalOwnerSharePolicy} / `registerOwnerSharePolicy`. Persists
|
|
136
|
+
* the registration receipt's canonical identity tuple plus the ephemeral
|
|
137
|
+
* share key's non-extractable `CryptoKey` handle, so a sender can list,
|
|
138
|
+
* inspect, and revoke shares it created in an earlier session -- without
|
|
139
|
+
* ever re-exporting private key material.
|
|
140
|
+
*/
|
|
141
|
+
declare class SenderShareStore {
|
|
142
|
+
private readonly records;
|
|
143
|
+
private readonly keys;
|
|
144
|
+
private readonly now;
|
|
145
|
+
constructor(options: SenderShareStoreOptions);
|
|
146
|
+
/** Persist a Node-verified registration receipt and its share key handle. */
|
|
147
|
+
save(receipt: OwnerSharePolicyRegistrationReceipt, key: DelegatedShareKey): Promise<SenderShareRecord>;
|
|
148
|
+
/** List shares created in any session, most recent first. */
|
|
149
|
+
list(options?: {
|
|
150
|
+
readonly includeRevoked?: boolean;
|
|
151
|
+
readonly includeExpired?: boolean;
|
|
152
|
+
}): Promise<readonly SenderShareRecord[]>;
|
|
153
|
+
get(shareId: string): Promise<SenderShareRecord | undefined>;
|
|
154
|
+
/** Recover a signer for the persisted share key after a reload. */
|
|
155
|
+
loadKey(shareId: string): Promise<DelegatedShareKey | undefined>;
|
|
156
|
+
/**
|
|
157
|
+
* The delegation CID to pass to the node's existing generic
|
|
158
|
+
* `tinycloud.delegation/revoke` invocation to revoke this share at the
|
|
159
|
+
* requested scope. Does not itself call the node -- callers invoke
|
|
160
|
+
* their own `revokeDelegation`/`DelegationManager.revoke` with the
|
|
161
|
+
* returned CID, then call {@link markRevoked} once that succeeds.
|
|
162
|
+
*/
|
|
163
|
+
revocationTarget(shareId: string, scope: SenderShareRevocationScope): Promise<string>;
|
|
164
|
+
/** Mark a share revoked locally after the caller's node-side revoke call succeeds. */
|
|
165
|
+
markRevoked(shareId: string, revokedAt?: string): Promise<SenderShareRecord>;
|
|
166
|
+
/** Forget a share and clear its persisted key handle. */
|
|
167
|
+
remove(shareId: string): Promise<void>;
|
|
168
|
+
}
|
|
169
|
+
/** WebCrypto-backed persistence adapter; links and private material are absent. */
|
|
170
|
+
declare class EncryptedSenderShareHistory {
|
|
171
|
+
private readonly storage;
|
|
172
|
+
private readonly key;
|
|
173
|
+
private readonly now;
|
|
174
|
+
constructor(storage: EncryptedShareHistoryStorage, key: CryptoKey, now?: () => Date);
|
|
175
|
+
put(record: SenderShareRecord): Promise<void>;
|
|
176
|
+
list(): Promise<readonly SenderShareRecord[]>;
|
|
177
|
+
clear(): Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { EncryptedSenderShareHistory as E, MemoryEncryptedShareHistoryStorage as M, type SenderShareRecord as S, type SenderShareRecordStorage as a, type EncryptedShareHistoryStorage as b, MemorySenderShareKeyStorage as c, MemorySenderShareRecordStorage as d, type SenderShareKeyStorage as e, type SenderShareRecordProofs as f, type SenderShareRevocationScope as g, SenderShareStore as h, historyRecordForPublishedShare as i };
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
ArtifactPinError: () => ArtifactPinError,
|
|
23
24
|
CredentialInvitationError: () => CredentialInvitationError,
|
|
24
25
|
DEFAULT_MAX_CONTENT_BLOB_BYTES: () => DEFAULT_MAX_CONTENT_BLOB_BYTES,
|
|
25
26
|
DEFAULT_MAX_SEALED_BLOB_BYTES: () => DEFAULT_MAX_SEALED_BLOB_BYTES,
|
|
@@ -42,6 +43,8 @@ __export(src_exports, {
|
|
|
42
43
|
ShareReceiveError: () => ShareReceiveError,
|
|
43
44
|
ShareRecipientClient: () => ShareRecipientClient,
|
|
44
45
|
addressedCredentialRequirement: () => addressedCredentialRequirement,
|
|
46
|
+
artifactBindingView: () => artifactBindingView,
|
|
47
|
+
assertArtifactBinding: () => assertArtifactBinding,
|
|
45
48
|
authorizationMethodForTarget: () => authorizationMethodForTarget,
|
|
46
49
|
authorizeShare: () => authorizeShare,
|
|
47
50
|
canonicalOwnerSharePolicy: () => canonicalOwnerSharePolicy,
|
|
@@ -84,7 +87,8 @@ __export(src_exports, {
|
|
|
84
87
|
toBase64Url: () => toBase64Url,
|
|
85
88
|
toShareErrorInfo: () => toShareErrorInfo,
|
|
86
89
|
validateOwnerSharePolicyRegistration: () => validateOwnerSharePolicyRegistration,
|
|
87
|
-
validateOwnerSharePolicyRegistrationBytes: () => validateOwnerSharePolicyRegistrationBytes
|
|
90
|
+
validateOwnerSharePolicyRegistrationBytes: () => validateOwnerSharePolicyRegistrationBytes,
|
|
91
|
+
verifyArtifactShare: () => verifyArtifactShare
|
|
88
92
|
});
|
|
89
93
|
module.exports = __toCommonJS(src_exports);
|
|
90
94
|
|
|
@@ -5425,6 +5429,20 @@ var v3TargetSchema = external_exports.object({
|
|
|
5425
5429
|
nodeAudience: external_exports.string().min(1),
|
|
5426
5430
|
spaceId: external_exports.string().refine(isCanonicalPathSegment, { message: "expected a canonical space id" })
|
|
5427
5431
|
}).strict();
|
|
5432
|
+
var ARTIFACT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{2,62}$/;
|
|
5433
|
+
var artifactPinSchema = external_exports.object({
|
|
5434
|
+
v: external_exports.literal(1),
|
|
5435
|
+
artifactId: external_exports.string().regex(ARTIFACT_ID_PATTERN),
|
|
5436
|
+
version: external_exports.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
|
|
5437
|
+
/** SHA-256 (lowercase hex) of the exact `manifest.json` bytes. The manifest itself stays an owner-side record. */
|
|
5438
|
+
manifestSha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5439
|
+
/** An addressed share reads one exact file, so exactly one entry: the stored bytes (ciphertext) it will fetch. */
|
|
5440
|
+
files: external_exports.array(external_exports.object({
|
|
5441
|
+
path: external_exports.string().min(1).max(255),
|
|
5442
|
+
sha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5443
|
+
bytes: external_exports.number().int().nonnegative()
|
|
5444
|
+
}).strict()).length(1)
|
|
5445
|
+
}).strict();
|
|
5428
5446
|
var unsignedShareEnvelopeV3BaseSchema = external_exports.object({
|
|
5429
5447
|
version: external_exports.literal(3),
|
|
5430
5448
|
shareId: external_exports.string().min(1),
|
|
@@ -5444,7 +5462,13 @@ var unsignedShareEnvelopeV3BaseSchema = external_exports.object({
|
|
|
5444
5462
|
expiry: external_exports.string().datetime({ offset: true }),
|
|
5445
5463
|
display: displaySchema,
|
|
5446
5464
|
encrypted: external_exports.literal(true),
|
|
5447
|
-
metadata: contentMetadataSchema
|
|
5465
|
+
metadata: contentMetadataSchema,
|
|
5466
|
+
/**
|
|
5467
|
+
* TC-1428: an owner-signed statement that this share reads one write-once artifact version. Additive: absent on
|
|
5468
|
+
* every other share, and covered by the envelope signature because the signature spans the whole unsigned object.
|
|
5469
|
+
* The shape is checked here; its binding to the content source is `verifyArtifactShare` in share-sdk.
|
|
5470
|
+
*/
|
|
5471
|
+
artifactPin: artifactPinSchema.optional()
|
|
5448
5472
|
}).strict();
|
|
5449
5473
|
function validateV3Invariants(value, ctx) {
|
|
5450
5474
|
const actions = [...value.actions];
|
|
@@ -6036,6 +6060,84 @@ async function parseSealedInlineShareUrl(url, options = {}) {
|
|
|
6036
6060
|
return { kind: "inline", ciphertextCid: record.cid, ciphertext, key32 };
|
|
6037
6061
|
}
|
|
6038
6062
|
|
|
6063
|
+
// src/artifact-core.ts
|
|
6064
|
+
var ARTIFACT_ROOT = "artifacts";
|
|
6065
|
+
function parseArtifactVersionPath(path) {
|
|
6066
|
+
const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
|
|
6067
|
+
if (match === null) return void 0;
|
|
6068
|
+
return { artifactId: match[1], version: Number(match[2]), file: match[3] };
|
|
6069
|
+
}
|
|
6070
|
+
|
|
6071
|
+
// src/artifact-pin.ts
|
|
6072
|
+
var ArtifactPinError = class extends Error {
|
|
6073
|
+
code;
|
|
6074
|
+
/** The code is the message: stable, short, and what callers and tests match on. */
|
|
6075
|
+
constructor(code3) {
|
|
6076
|
+
super(code3);
|
|
6077
|
+
this.name = "ArtifactPinError";
|
|
6078
|
+
this.code = code3;
|
|
6079
|
+
}
|
|
6080
|
+
};
|
|
6081
|
+
function artifactBindingView(envelope) {
|
|
6082
|
+
return {
|
|
6083
|
+
...envelope.artifactPin === void 0 ? {} : { artifactPin: envelope.artifactPin },
|
|
6084
|
+
target: envelope.target,
|
|
6085
|
+
contentSource: envelope.contentSource,
|
|
6086
|
+
resource: envelope.resource,
|
|
6087
|
+
actions: envelope.actions,
|
|
6088
|
+
policyKvActions: envelope.policy.capabilityCeiling.flatMap((capability) => capability.kind === "kv" ? capability.actions : [])
|
|
6089
|
+
};
|
|
6090
|
+
}
|
|
6091
|
+
function sourcePath(view) {
|
|
6092
|
+
const resource = view.contentSource.kvResource;
|
|
6093
|
+
const marker = "/kv/";
|
|
6094
|
+
const at = resource.indexOf(marker);
|
|
6095
|
+
if (at <= 0 || resource.slice(0, at) !== view.target.spaceId) {
|
|
6096
|
+
throw new ArtifactPinError("pin-source-mismatch");
|
|
6097
|
+
}
|
|
6098
|
+
return resource.slice(at + marker.length);
|
|
6099
|
+
}
|
|
6100
|
+
function assertArtifactBinding(view, expected = {}) {
|
|
6101
|
+
const pin = view.artifactPin;
|
|
6102
|
+
const path = sourcePath(view);
|
|
6103
|
+
const inArtifacts = path === ARTIFACT_ROOT || path.startsWith(`${ARTIFACT_ROOT}/`);
|
|
6104
|
+
if (!inArtifacts) {
|
|
6105
|
+
if (pin !== void 0) throw new ArtifactPinError("pin-source-mismatch");
|
|
6106
|
+
return void 0;
|
|
6107
|
+
}
|
|
6108
|
+
if (pin === void 0) throw new ArtifactPinError("missing-pin");
|
|
6109
|
+
const parsed = parseArtifactVersionPath(path);
|
|
6110
|
+
if (parsed === void 0) throw new ArtifactPinError("unsupported-artifact-path");
|
|
6111
|
+
const file = pin.files[0];
|
|
6112
|
+
const source = view.contentSource;
|
|
6113
|
+
if (
|
|
6114
|
+
// The stored ciphertext of one file: bounded like content (100 MiB) plus the envelope's own framing.
|
|
6115
|
+
file.bytes > 128 * 1024 * 1024 || pin.artifactId !== parsed.artifactId || pin.version !== parsed.version || file.path !== parsed.file || view.resource.kind !== "exact" || view.resource.path !== path || source.selector !== "exact" || source.mode !== "immutable" || source.initialCiphertextDigestHex !== file.sha256
|
|
6116
|
+
) {
|
|
6117
|
+
throw new ArtifactPinError("pin-source-mismatch");
|
|
6118
|
+
}
|
|
6119
|
+
if (view.actions.includes("edit") || view.policyKvActions.includes("tinycloud.kv/put")) {
|
|
6120
|
+
throw new ArtifactPinError("pinned-share-writable");
|
|
6121
|
+
}
|
|
6122
|
+
if (expected.artifactId !== void 0 && expected.artifactId !== pin.artifactId || expected.version !== void 0 && expected.version !== pin.version) {
|
|
6123
|
+
throw new ArtifactPinError("expectation-mismatch");
|
|
6124
|
+
}
|
|
6125
|
+
return pin;
|
|
6126
|
+
}
|
|
6127
|
+
function verifyArtifactShare(envelope, options) {
|
|
6128
|
+
if (envelope.signature.signerDid !== options.expectedOwnerDid) {
|
|
6129
|
+
throw new ArtifactPinError("wrong-signer");
|
|
6130
|
+
}
|
|
6131
|
+
let valid;
|
|
6132
|
+
try {
|
|
6133
|
+
valid = verifyEnvelopeV3SignatureOnly(envelope) && envelope.policy.ownerDid === options.expectedOwnerDid;
|
|
6134
|
+
} catch {
|
|
6135
|
+
valid = false;
|
|
6136
|
+
}
|
|
6137
|
+
if (!valid) throw new ArtifactPinError("bad-signature");
|
|
6138
|
+
return assertArtifactBinding(artifactBindingView(envelope), options.expected);
|
|
6139
|
+
}
|
|
6140
|
+
|
|
6039
6141
|
// src/receive.ts
|
|
6040
6142
|
var SHARE_RESULT_VERSION = 1;
|
|
6041
6143
|
var DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
|
|
@@ -6072,7 +6174,8 @@ function metadataFor(envelope, origin) {
|
|
|
6072
6174
|
...envelope.display.senderName === void 0 ? {} : { senderName: envelope.display.senderName },
|
|
6073
6175
|
...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
|
|
6074
6176
|
...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
|
|
6075
|
-
}
|
|
6177
|
+
},
|
|
6178
|
+
...envelope.artifactPin === void 0 ? {} : { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } }
|
|
6076
6179
|
};
|
|
6077
6180
|
}
|
|
6078
6181
|
async function resolvePolicyShare(link, options) {
|
|
@@ -6103,6 +6206,12 @@ async function resolvePolicyShare(link, options) {
|
|
|
6103
6206
|
} catch {
|
|
6104
6207
|
throw new ShareReceiveError("signature-invalid", "share signature is invalid");
|
|
6105
6208
|
}
|
|
6209
|
+
try {
|
|
6210
|
+
verifyArtifactShare(envelope, { expectedOwnerDid: envelope.policy.ownerDid });
|
|
6211
|
+
} catch (error) {
|
|
6212
|
+
if (!(error instanceof ArtifactPinError)) throw error;
|
|
6213
|
+
throw new ShareReceiveError("envelope-invalid", `share artifact pin is invalid (${error.code})`);
|
|
6214
|
+
}
|
|
6106
6215
|
options.signal?.throwIfAborted();
|
|
6107
6216
|
const expiry = Date.parse(envelope.expiry);
|
|
6108
6217
|
if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "share expiry is invalid");
|
|
@@ -6821,6 +6930,14 @@ function assertMailboxCommitment(commitment, target) {
|
|
|
6821
6930
|
async function publishAddressedShare(input) {
|
|
6822
6931
|
assertSafeInput(input);
|
|
6823
6932
|
const { target } = prepareAddressedShare(input);
|
|
6933
|
+
assertArtifactBinding({
|
|
6934
|
+
...input.artifactPin === void 0 ? {} : { artifactPin: input.artifactPin },
|
|
6935
|
+
target: { spaceId: input.spaceId },
|
|
6936
|
+
contentSource: input.contentSource,
|
|
6937
|
+
resource: input.resource,
|
|
6938
|
+
actions: input.actions,
|
|
6939
|
+
policyKvActions: input.policyActions
|
|
6940
|
+
});
|
|
6824
6941
|
const options = { ...input, filename: canonicalShareFilename(input.filename) };
|
|
6825
6942
|
if (target.kind !== "recipientDid") assertMailboxCommitment(options.credentialRequirement, target);
|
|
6826
6943
|
const expiry = rfc3339Seconds(options.expiresAt);
|
|
@@ -6887,7 +7004,8 @@ async function publishAddressedShare(input) {
|
|
|
6887
7004
|
filename: options.filename,
|
|
6888
7005
|
...options.mediaType.startsWith("text/") ? { encoding: "utf-8" } : {},
|
|
6889
7006
|
...options.artifact === void 0 ? {} : { artifact: options.artifact }
|
|
6890
|
-
}
|
|
7007
|
+
},
|
|
7008
|
+
...options.artifactPin === void 0 ? {} : { artifactPin: options.artifactPin }
|
|
6891
7009
|
};
|
|
6892
7010
|
unsignedShareEnvelopeV3Schema.parse(unsigned);
|
|
6893
7011
|
const envelopeSignature = await options.authority.sign((0, import_sha2562.sha256)(textEncoder.encode(`${ENVELOPE_V3_DOMAIN}${canonicalize(unsigned)}`)));
|
|
@@ -8581,6 +8699,7 @@ var ShareRecipientClient = class {
|
|
|
8581
8699
|
const envelope = this.options.envelope;
|
|
8582
8700
|
const signer = this.nativeSigner ?? this.signer;
|
|
8583
8701
|
if (envelope.version !== 3 || this.session === void 0 || this.v3Authorization === void 0 || this.v3NodeAudience === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
8702
|
+
assertArtifactBinding(artifactBindingView(envelope));
|
|
8584
8703
|
if (!matchesInitialCiphertextDigest(bytes3, envelope.contentSource.initialCiphertextDigestHex)) throw new Error("v3 owner KV ciphertext digest mismatch");
|
|
8585
8704
|
const encrypted = parseV3InlineEncryptedEnvelope(bytes3, envelope);
|
|
8586
8705
|
const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
@@ -8770,6 +8889,7 @@ async function openNativeShare(sharing, link) {
|
|
|
8770
8889
|
}
|
|
8771
8890
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8772
8891
|
0 && (module.exports = {
|
|
8892
|
+
ArtifactPinError,
|
|
8773
8893
|
CredentialInvitationError,
|
|
8774
8894
|
DEFAULT_MAX_CONTENT_BLOB_BYTES,
|
|
8775
8895
|
DEFAULT_MAX_SEALED_BLOB_BYTES,
|
|
@@ -8792,6 +8912,8 @@ async function openNativeShare(sharing, link) {
|
|
|
8792
8912
|
ShareReceiveError,
|
|
8793
8913
|
ShareRecipientClient,
|
|
8794
8914
|
addressedCredentialRequirement,
|
|
8915
|
+
artifactBindingView,
|
|
8916
|
+
assertArtifactBinding,
|
|
8795
8917
|
authorizationMethodForTarget,
|
|
8796
8918
|
authorizeShare,
|
|
8797
8919
|
canonicalOwnerSharePolicy,
|
|
@@ -8834,6 +8956,7 @@ async function openNativeShare(sharing, link) {
|
|
|
8834
8956
|
toBase64Url,
|
|
8835
8957
|
toShareErrorInfo,
|
|
8836
8958
|
validateOwnerSharePolicyRegistration,
|
|
8837
|
-
validateOwnerSharePolicyRegistrationBytes
|
|
8959
|
+
validateOwnerSharePolicyRegistrationBytes,
|
|
8960
|
+
verifyArtifactShare
|
|
8838
8961
|
});
|
|
8839
8962
|
//# sourceMappingURL=index.cjs.map
|