@telecrypt-io/storage 0.7.2 → 0.9.0
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 +37 -3
- package/dist/TeleCryptIOStorage.d.ts +43 -3
- package/dist/TeleCryptIOStorage.js +365 -49
- package/dist/core/operations.js +234 -147
- package/dist/core/types.d.ts +1 -0
- package/dist/key-safe.d.ts +62 -0
- package/dist/key-safe.js +349 -0
- package/package.json +2 -2
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.
|
|
@@ -62,14 +69,41 @@ The SDK maps the file tree to Matrix primitives defined by MSC3089:
|
|
|
62
69
|
Vault, folder, and file IDs are opaque Matrix room and event IDs. Names are display labels: two
|
|
63
70
|
objects with the same name are still distinct, and a name is not an idempotency key.
|
|
64
71
|
|
|
72
|
+
Vault and folder names, and file names, are stored in encrypted room messages. Matrix room names
|
|
73
|
+
remain the generic `Encrypted storage`; file-listing state stores only an event pointer, and media
|
|
74
|
+
uploads omit the original filename. Use the asynchronous operations in `@telecrypt-io/storage/core`
|
|
75
|
+
to read names. The synchronous Matrix SDK tree helper cannot decrypt on demand and exposes a generic
|
|
76
|
+
file label until an async operation resolves the name. This is a TeleCrypt metadata format: standard
|
|
77
|
+
MSC3089 clients will not display the private names or understand these file indexes. SDK 0.8 starts
|
|
78
|
+
this format without a reader or migration for trees written by older SDK versions; those old trees
|
|
79
|
+
may be ignored or removed.
|
|
80
|
+
|
|
65
81
|
File bytes are encrypted in the client before upload with Matrix attachment encryption (AES-CTR
|
|
66
82
|
and a per-file key distributed through the room's Megolm session). The server stores encrypted
|
|
67
83
|
media and Matrix events, but encryption does not hide all room, event, or other metadata from the
|
|
68
84
|
server or users who can read the room.
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
The Decryption Key Safe is mandatory before the public core storage operations. Inspect
|
|
87
|
+
`storage.keySafe.getStatus(signal?)`. For `setup-required`, call `setup(signal?)`, offer the returned
|
|
88
|
+
`recoveryKey` for copying/saving, explain that losing both this key and local decryption keys can
|
|
89
|
+
make files permanently unreadable, and require an explicit saved-key acknowledgement before
|
|
90
|
+
`confirmSaved(signal?)`. `confirmation-required` returns the same pending key after interruption.
|
|
91
|
+
For `restore-required`, call `restore(recoveryKey, signal?)` with the existing key; this restores
|
|
92
|
+
room keys and signs the current login using the existing account keys. Normal use requires `ready`.
|
|
93
|
+
Setup and restore never replace an existing safe or account signing identity. Resetting the account
|
|
94
|
+
password cannot recover encrypted files.
|
|
95
|
+
|
|
96
|
+
Pending setup state is kept beside the login's IndexedDB crypto data. A platform that snapshots
|
|
97
|
+
IndexedDB to disk, such as the CLI, must supply `onKeySafeStateChanged` at creation and durably flush
|
|
98
|
+
its snapshot before resolving that callback. Key Safe methods accept an optional `AbortSignal`.
|
|
99
|
+
The safe, signing keys, and saved-key acknowledgement are shared SDK behavior rather than separate
|
|
100
|
+
Web/CLI implementations.
|
|
101
|
+
|
|
102
|
+
Sharing uses Matrix's native encrypted-history bundle exchange. Both users must have completed
|
|
103
|
+
their account signing setup. An invitation alone does not establish successful history sharing:
|
|
104
|
+
the SDK checks native recipient-device eligibility afterward and reports partial completion if
|
|
105
|
+
keys could not be shared. An upload restriction can reject the encrypted history bundle upload;
|
|
106
|
+
the error is preserved, including when retrying an existing invitation.
|
|
73
107
|
|
|
74
108
|
Deletion has strict ordering. Delete files before their folders or vaults because folders and
|
|
75
109
|
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: {
|
|
@@ -26,8 +27,9 @@ export interface TreeSpace {
|
|
|
26
27
|
}
|
|
27
28
|
export interface FileBranch {
|
|
28
29
|
readonly id: string;
|
|
30
|
+
readonly roomId: string;
|
|
29
31
|
/** Current MSC3089 branch/listing event. Its event ID is distinct from `id` (the state key). */
|
|
30
|
-
readonly indexEvent?: Pick<MatrixEvent, "getId">;
|
|
32
|
+
readonly indexEvent?: Pick<MatrixEvent, "getId" | "getContent" | "getSender" | "isRedacted">;
|
|
31
33
|
/** Matrix SDK keeps a redacted branch in state as an inactive object. */
|
|
32
34
|
readonly isActive?: boolean;
|
|
33
35
|
getName(): string;
|
|
@@ -63,8 +65,12 @@ export interface CreateTeleCryptIOStorageOptions {
|
|
|
63
65
|
* Node/tests, where `fake-indexeddb` is process-global).
|
|
64
66
|
*/
|
|
65
67
|
cryptoDatabasePrefix?: string;
|
|
68
|
+
/** Persist local Key Safe state before server-side setup continues (CLI snapshots). */
|
|
69
|
+
onKeySafeStateChanged?: () => Promise<void>;
|
|
66
70
|
/** initialSyncLimit passed to startClient(); default 10. */
|
|
67
71
|
initialSyncLimit?: number;
|
|
72
|
+
/** Start Matrix sync after crypto initializes; default true. */
|
|
73
|
+
startClient?: boolean;
|
|
68
74
|
/** How long to wait for the first sync before giving up; default 15000ms. */
|
|
69
75
|
syncTimeoutMs?: number;
|
|
70
76
|
/** How long to wait for rust-crypto WASM + IndexedDB init; default 60000ms. */
|
|
@@ -106,7 +112,10 @@ export interface CreateFromOidcOptions {
|
|
|
106
112
|
tokenRefreshFunction?: TokenRefreshFunction;
|
|
107
113
|
persistentCryptoStore?: boolean;
|
|
108
114
|
cryptoDatabasePrefix?: string;
|
|
115
|
+
/** Persist local Key Safe state before server-side setup continues (CLI snapshots). */
|
|
116
|
+
onKeySafeStateChanged?: () => Promise<void>;
|
|
109
117
|
initialSyncLimit?: number;
|
|
118
|
+
startClient?: boolean;
|
|
110
119
|
syncTimeoutMs?: number;
|
|
111
120
|
initTimeoutMs?: number;
|
|
112
121
|
onProgress?: (message: string) => void;
|
|
@@ -143,10 +152,40 @@ export declare function readFileEventMetadata(content: unknown): FileEventMetada
|
|
|
143
152
|
export declare function withTreeMutation<T>(client: MatrixClient, operation: (signal?: AbortSignal) => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
144
153
|
export declare class TeleCryptIOStorage {
|
|
145
154
|
private client;
|
|
155
|
+
private readonly syncOptions;
|
|
146
156
|
private readonly decoratedTreeSpaces;
|
|
147
|
-
|
|
157
|
+
private readonly decoratedFileBranches;
|
|
158
|
+
private readonly treeNames;
|
|
159
|
+
private readonly fileNames;
|
|
160
|
+
readonly keySafe: DecryptionKeySafe;
|
|
161
|
+
private syncStarted;
|
|
162
|
+
private syncInFlight;
|
|
163
|
+
constructor(client: MatrixClient, options?: KeySafePersistenceOptions, syncOptions?: Pick<CreateTeleCryptIOStorageOptions, "initialSyncLimit" | "syncTimeoutMs" | "onProgress">);
|
|
148
164
|
/** The underlying matrix-js-sdk client (e.g. to stop it, or for advanced/interop use). */
|
|
149
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>;
|
|
169
|
+
private fileNameKey;
|
|
170
|
+
private getStateEvent;
|
|
171
|
+
private getTreeOwnerId;
|
|
172
|
+
private sendEncryptedMetadataMessage;
|
|
173
|
+
private readEncryptedMetadataMessage;
|
|
174
|
+
private writeTreeName;
|
|
175
|
+
/** Resolves an encrypted vault/folder name through its pointer state event. */
|
|
176
|
+
getTreeName(roomId: string, options?: MatrixRequestOptions): Promise<string>;
|
|
177
|
+
private writeFileName;
|
|
178
|
+
/** Resolves an encrypted file name from the MSC3089 index pointer. */
|
|
179
|
+
getFileName(treeId: string, fileId: string, options?: MatrixRequestOptions & {
|
|
180
|
+
refreshState?: boolean;
|
|
181
|
+
}): Promise<string>;
|
|
182
|
+
/** Returns the current file metadata pointer, including on inactive branches. */
|
|
183
|
+
getFileMetadataEventId(treeId: string, fileId: string, options?: MatrixRequestOptions): Promise<string | null>;
|
|
184
|
+
/** Fetches and validates a current encrypted rename event, allowing retries after redaction. */
|
|
185
|
+
getFileRenameMetadataEvent(treeId: string, fileId: string, metadataEventId: string): Promise<MatrixEvent>;
|
|
186
|
+
/** Fetches the original encrypted file message directly by event ID. */
|
|
187
|
+
getOriginalFileEvent(treeId: string, fileId: string, allowRedacted?: boolean): Promise<MatrixEvent>;
|
|
188
|
+
private createEncryptedFile;
|
|
150
189
|
/**
|
|
151
190
|
* Refreshes a room's current state from the homeserver. The background sync
|
|
152
191
|
* loop is intentionally asynchronous, so a short-lived CLI process can
|
|
@@ -177,7 +216,7 @@ export declare class TeleCryptIOStorage {
|
|
|
177
216
|
*/
|
|
178
217
|
static createFromOidc(opts: CreateFromOidcOptions): Promise<TeleCryptIOStorage>;
|
|
179
218
|
/** Shared post-construction bootstrap for `create()`/`createFromOidc()`:
|
|
180
|
-
* persistent crypto store, first sync, wrap in a `TeleCryptIOStorage`. */
|
|
219
|
+
* persistent crypto store, optional first sync, wrap in a `TeleCryptIOStorage`. */
|
|
181
220
|
private static bootstrap;
|
|
182
221
|
private static throwIfAborted;
|
|
183
222
|
private static withTimeout;
|
|
@@ -249,6 +288,7 @@ export declare class TeleCryptIOStorage {
|
|
|
249
288
|
* which refreshes the exact parent/child state before reporting success.
|
|
250
289
|
*/
|
|
251
290
|
private decorateTreeSpace;
|
|
291
|
+
private decorateFileBranch;
|
|
252
292
|
/** Cleans up only a room created by this operation and reports incomplete cleanup. */
|
|
253
293
|
private cleanupCreatedRoom;
|
|
254
294
|
private removeRelationLink;
|