@telecrypt-io/storage 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +57 -3
- package/dist/TeleCryptIOStorage.d.ts +32 -3
- package/dist/TeleCryptIOStorage.js +387 -70
- package/dist/core/errors.d.ts +6 -2
- package/dist/core/errors.js +4 -1
- package/dist/core/operations.d.ts +2 -0
- package/dist/core/operations.js +178 -79
- package/dist/core/types.d.ts +1 -0
- package/dist/key-safe.d.ts +62 -0
- package/dist/key-safe.js +349 -0
- package/dist/media.d.ts +3 -0
- package/dist/media.js +10 -0
- package/dist/sharing.d.ts +3 -0
- package/dist/sharing.js +18 -0
- package/package.json +2 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -46,6 +46,13 @@ by default. Browser persistence uses IndexedDB and the Matrix Rust crypto WASM r
|
|
|
46
46
|
example assumes a browser environment. OIDC discovery, login, refresh, and the corresponding
|
|
47
47
|
`createFromOidc` inputs are exported from `@telecrypt-io/storage/core`.
|
|
48
48
|
|
|
49
|
+
Applications that require the Decryption Key Safe before downloading room history can create the
|
|
50
|
+
client with `startClient: false`. Check its status and restore room keys first; `restore()` starts
|
|
51
|
+
sync after importing them so Matrix can publish this login's device keys and complete account
|
|
52
|
+
signing. For an already-ready safe, call `await storage.startSync()` after the status check. Keep
|
|
53
|
+
Storage operations locked until the Safe reports ready. The default is to start sync during
|
|
54
|
+
creation.
|
|
55
|
+
|
|
49
56
|
The example creates retained Matrix rooms and encrypted media. Delete its file and then its empty
|
|
50
57
|
vault when finished; cleanup is part of the operation, not an automatic consequence of stopping
|
|
51
58
|
the client.
|
|
@@ -76,9 +83,56 @@ and a per-file key distributed through the room's Megolm session). The server st
|
|
|
76
83
|
media and Matrix events, but encryption does not hide all room, event, or other metadata from the
|
|
77
84
|
server or users who can read the room.
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
If an upload is rejected after media was accepted, the SDK removes that media and any acknowledged
|
|
87
|
+
attachment. A transport failure leaves accepted work intact. An upload `MutationPartialError`
|
|
88
|
+
retains `treeId` and `completedIds` in the order `[mediaId, attachmentEventId?]`. Keep those IDs;
|
|
89
|
+
do not repeat the upload blindly. To discard the partial upload, call
|
|
90
|
+
`core.cleanupFileUpload(storage, error.treeId, error.completedIds[0], error.completedIds[1])`.
|
|
91
|
+
Retry cleanup with the same IDs if it fails. A known attachment ID can also be passed to ordinary
|
|
92
|
+
`core.deleteFile` even when its listing was never committed. An initial media response lost before
|
|
93
|
+
the server returns any identifier still has an unknown outcome and cannot provide cleanup IDs.
|
|
94
|
+
Upload cancellation stops before the next write and waits for the current request's bounded
|
|
95
|
+
settlement so returned media/event IDs are retained. Keep observing the upload promise after
|
|
96
|
+
cancelling a UI wait or closing a view; persist any partial receipt for the originating account.
|
|
97
|
+
|
|
98
|
+
The Decryption Key Safe is mandatory before the public core storage operations. Inspect
|
|
99
|
+
`storage.keySafe.getStatus(signal?)`. For `setup-required`, call `setup(signal?)`, offer the returned
|
|
100
|
+
`recoveryKey` for copying/saving, explain that losing both this key and local decryption keys can
|
|
101
|
+
make files permanently unreadable, and require an explicit saved-key acknowledgement before
|
|
102
|
+
`confirmSaved(signal?)`. `confirmation-required` returns the same pending key after interruption.
|
|
103
|
+
For `restore-required`, call `restore(recoveryKey, signal?)` with the existing key; this restores
|
|
104
|
+
room keys and signs the current login using the existing account keys. Normal use requires `ready`.
|
|
105
|
+
Setup and restore never replace an existing safe or account signing identity. Resetting the account
|
|
106
|
+
password cannot recover encrypted files.
|
|
107
|
+
|
|
108
|
+
Short-lived clients should call `storage.waitForKeyBackup(treeId, fileId?, options?)` after creating
|
|
109
|
+
or renaming encrypted metadata and before closing. Omit `fileId` for a vault or folder. This checks
|
|
110
|
+
the exact encryption session in the active server backup, including reused keys that produce no
|
|
111
|
+
new backup-progress event. If verification fails, retain the local profile and retry backup;
|
|
112
|
+
the metadata mutation has already committed.
|
|
113
|
+
|
|
114
|
+
Pending setup state is kept beside the login's IndexedDB crypto data. A platform that snapshots
|
|
115
|
+
IndexedDB to disk, such as the CLI, must supply `onKeySafeStateChanged` at creation and durably flush
|
|
116
|
+
its snapshot before resolving that callback. Key Safe methods accept an optional `AbortSignal`.
|
|
117
|
+
The safe, signing keys, and saved-key acknowledgement are shared SDK behavior rather than separate
|
|
118
|
+
Web/CLI implementations.
|
|
119
|
+
|
|
120
|
+
Sharing uses Matrix's native encrypted-history bundle exchange. Both users must have completed
|
|
121
|
+
their account signing setup. An invitation alone does not establish successful history sharing:
|
|
122
|
+
the SDK checks native recipient-device eligibility afterward and reports partial completion if
|
|
123
|
+
keys could not be shared. An upload restriction can reject the encrypted history bundle upload;
|
|
124
|
+
the error is preserved, including when retrying an existing invitation.
|
|
125
|
+
|
|
126
|
+
`joinVault` accepts the vault and its linked folder invitations, including their history keys.
|
|
127
|
+
New folders inherit the parent's joined and invited readers; browsing accepts their folder
|
|
128
|
+
invitations. If reader access fails after folder creation, the partial-completion error retains
|
|
129
|
+
the created folder ID. Retry sharing the parent vault to complete access to that folder.
|
|
130
|
+
|
|
131
|
+
Storage spaces are private and only their creator changes room state. Loading owned trees or
|
|
132
|
+
refreshing their state repairs the permissive defaults emitted by older SDKs and restores
|
|
133
|
+
invite-only joining. The repair preserves membership, unrelated power settings and encryption
|
|
134
|
+
keys; readers do not write it. Owners must use an updated client for their existing trees to be
|
|
135
|
+
repaired. Disabled Storage markers and deleted trees are not restored automatically.
|
|
82
136
|
|
|
83
137
|
Deletion has strict ordering. Delete files before their folders or vaults because folders and
|
|
84
138
|
vaults must be empty. File deletion removes the encrypted media object before redacting its Matrix
|
|
@@ -2,6 +2,7 @@ import { MatrixClient, MatrixEvent } from "matrix-js-sdk";
|
|
|
2
2
|
import type { CryptoCallbacks } from "matrix-js-sdk/lib/crypto-api/index.js";
|
|
3
3
|
import type { TokenRefreshFunction } from "matrix-js-sdk/lib/http-api/index.js";
|
|
4
4
|
import type { RecoveryStatus } from "./core/types.js";
|
|
5
|
+
import { DecryptionKeySafe, type KeySafePersistenceOptions } from "./key-safe.js";
|
|
5
6
|
export interface TreeSpace {
|
|
6
7
|
readonly id: string;
|
|
7
8
|
readonly room: {
|
|
@@ -20,7 +21,7 @@ export interface TreeSpace {
|
|
|
20
21
|
setPermissions(userId: string, role: string): Promise<void>;
|
|
21
22
|
getFile(fileEventId: string): FileBranch | null;
|
|
22
23
|
listFiles(): FileBranch[];
|
|
23
|
-
createFile(name: string, encryptedContents: ArrayBuffer | Uint8Array, info: Record<string, unknown>, additionalContent?: Record<string, unknown
|
|
24
|
+
createFile(name: string, encryptedContents: ArrayBuffer | Uint8Array, info: Record<string, unknown>, additionalContent?: Record<string, unknown>, signal?: AbortSignal): Promise<{
|
|
24
25
|
event_id: string;
|
|
25
26
|
}>;
|
|
26
27
|
}
|
|
@@ -64,8 +65,12 @@ export interface CreateTeleCryptIOStorageOptions {
|
|
|
64
65
|
* Node/tests, where `fake-indexeddb` is process-global).
|
|
65
66
|
*/
|
|
66
67
|
cryptoDatabasePrefix?: string;
|
|
68
|
+
/** Persist local Key Safe state before server-side setup continues (CLI snapshots). */
|
|
69
|
+
onKeySafeStateChanged?: () => Promise<void>;
|
|
67
70
|
/** initialSyncLimit passed to startClient(); default 10. */
|
|
68
71
|
initialSyncLimit?: number;
|
|
72
|
+
/** Start Matrix sync after crypto initializes; default true. */
|
|
73
|
+
startClient?: boolean;
|
|
69
74
|
/** How long to wait for the first sync before giving up; default 15000ms. */
|
|
70
75
|
syncTimeoutMs?: number;
|
|
71
76
|
/** How long to wait for rust-crypto WASM + IndexedDB init; default 60000ms. */
|
|
@@ -107,7 +112,10 @@ export interface CreateFromOidcOptions {
|
|
|
107
112
|
tokenRefreshFunction?: TokenRefreshFunction;
|
|
108
113
|
persistentCryptoStore?: boolean;
|
|
109
114
|
cryptoDatabasePrefix?: string;
|
|
115
|
+
/** Persist local Key Safe state before server-side setup continues (CLI snapshots). */
|
|
116
|
+
onKeySafeStateChanged?: () => Promise<void>;
|
|
110
117
|
initialSyncLimit?: number;
|
|
118
|
+
startClient?: boolean;
|
|
111
119
|
syncTimeoutMs?: number;
|
|
112
120
|
initTimeoutMs?: number;
|
|
113
121
|
onProgress?: (message: string) => void;
|
|
@@ -144,21 +152,35 @@ export declare function readFileEventMetadata(content: unknown): FileEventMetada
|
|
|
144
152
|
export declare function withTreeMutation<T>(client: MatrixClient, operation: (signal?: AbortSignal) => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
145
153
|
export declare class TeleCryptIOStorage {
|
|
146
154
|
private client;
|
|
155
|
+
private readonly syncOptions;
|
|
147
156
|
private readonly decoratedTreeSpaces;
|
|
148
157
|
private readonly decoratedFileBranches;
|
|
149
158
|
private readonly treeNames;
|
|
150
159
|
private readonly fileNames;
|
|
151
|
-
|
|
160
|
+
readonly keySafe: DecryptionKeySafe;
|
|
161
|
+
private syncStarted;
|
|
162
|
+
private syncInFlight;
|
|
163
|
+
constructor(client: MatrixClient, options?: KeySafePersistenceOptions, syncOptions?: Pick<CreateTeleCryptIOStorageOptions, "initialSyncLimit" | "syncTimeoutMs" | "onProgress">);
|
|
152
164
|
/** The underlying matrix-js-sdk client (e.g. to stop it, or for advanced/interop use). */
|
|
153
165
|
getClient(): MatrixClient;
|
|
166
|
+
/** Start Matrix room sync after crypto setup, for example after the current
|
|
167
|
+
* login has checked or restored its Decryption Key Safe. */
|
|
168
|
+
startSync(signal?: AbortSignal): Promise<void>;
|
|
154
169
|
private fileNameKey;
|
|
155
170
|
private getStateEvent;
|
|
156
171
|
private getTreeOwnerId;
|
|
157
172
|
private sendEncryptedMetadataMessage;
|
|
158
173
|
private readEncryptedMetadataMessage;
|
|
159
174
|
private writeTreeName;
|
|
175
|
+
private getTreeMetadataEvent;
|
|
160
176
|
/** Resolves an encrypted vault/folder name through its pointer state event. */
|
|
161
177
|
getTreeName(roomId: string, options?: MatrixRequestOptions): Promise<string>;
|
|
178
|
+
/**
|
|
179
|
+
* Waits for the current encrypted vault/folder name, or file metadata, to
|
|
180
|
+
* have its session in the active native key backup. Checks the exact session
|
|
181
|
+
* even when encryption reuses a key and the backup engine emits no new event.
|
|
182
|
+
*/
|
|
183
|
+
waitForKeyBackup(treeId: string, fileId?: string, options?: MatrixRequestOptions): Promise<void>;
|
|
162
184
|
private writeFileName;
|
|
163
185
|
/** Resolves an encrypted file name from the MSC3089 index pointer. */
|
|
164
186
|
getFileName(treeId: string, fileId: string, options?: MatrixRequestOptions & {
|
|
@@ -171,6 +193,12 @@ export declare class TeleCryptIOStorage {
|
|
|
171
193
|
/** Fetches the original encrypted file message directly by event ID. */
|
|
172
194
|
getOriginalFileEvent(treeId: string, fileId: string, allowRedacted?: boolean): Promise<MatrixEvent>;
|
|
173
195
|
private createEncryptedFile;
|
|
196
|
+
/**
|
|
197
|
+
* Discards a partial upload using its MutationPartialError.completedIds:
|
|
198
|
+
* [mediaId, fileId?]. Explicit cleanup also covers an unacknowledged listing.
|
|
199
|
+
* Keep the same IDs after failure and retry this operation, not the upload.
|
|
200
|
+
*/
|
|
201
|
+
cleanupFileUpload(treeId: string, mediaId: string, fileId?: string, options?: MatrixRequestOptions): Promise<void>;
|
|
174
202
|
/**
|
|
175
203
|
* Refreshes a room's current state from the homeserver. The background sync
|
|
176
204
|
* loop is intentionally asynchronous, so a short-lived CLI process can
|
|
@@ -178,6 +206,7 @@ export declare class TeleCryptIOStorage {
|
|
|
178
206
|
* changed a file name or branch.
|
|
179
207
|
*/
|
|
180
208
|
refreshRoomState(roomId: string, options?: MatrixRequestOptions): Promise<void>;
|
|
209
|
+
private repairOwnedTreePermissions;
|
|
181
210
|
/**
|
|
182
211
|
* Recommended entry point: builds the MatrixClient with a persistent crypto
|
|
183
212
|
* store and the secret-storage callback wiring `keys.*` needs, starts the
|
|
@@ -201,7 +230,7 @@ export declare class TeleCryptIOStorage {
|
|
|
201
230
|
*/
|
|
202
231
|
static createFromOidc(opts: CreateFromOidcOptions): Promise<TeleCryptIOStorage>;
|
|
203
232
|
/** Shared post-construction bootstrap for `create()`/`createFromOidc()`:
|
|
204
|
-
* persistent crypto store, first sync, wrap in a `TeleCryptIOStorage`. */
|
|
233
|
+
* persistent crypto store, optional first sync, wrap in a `TeleCryptIOStorage`. */
|
|
205
234
|
private static bootstrap;
|
|
206
235
|
private static throwIfAborted;
|
|
207
236
|
private static withTimeout;
|