@telecrypt-io/storage 0.1.3 → 0.2.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 +9 -15
- package/dist/TeleCryptIOStorage.d.ts +7 -0
- package/dist/TeleCryptIOStorage.js +14 -1
- package/dist/core/operations.js +9 -0
- package/package.json +7 -10
- package/dist/cli/cryptoSnapshot.d.ts +0 -37
- package/dist/cli/cryptoSnapshot.js +0 -187
- package/dist/cli/errors.d.ts +0 -1
- package/dist/cli/errors.js +0 -3
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -443
- package/dist/cli/oidc.d.ts +0 -19
- package/dist/cli/oidc.js +0 -97
- package/dist/cli/oidcWindowPolyfill.d.ts +0 -1
- package/dist/cli/oidcWindowPolyfill.js +0 -76
- package/dist/cli/output.d.ts +0 -16
- package/dist/cli/output.js +0 -24
- package/dist/cli/poll.d.ts +0 -1
- package/dist/cli/poll.js +0 -3
- package/dist/cli/profile.d.ts +0 -33
- package/dist/cli/profile.js +0 -46
- package/dist/cli/storage.d.ts +0 -36
- package/dist/cli/storage.js +0 -126
package/dist/cli/poll.js
DELETED
package/dist/cli/profile.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export interface Session {
|
|
2
|
-
homeserver: string;
|
|
3
|
-
userId: string;
|
|
4
|
-
deviceId: string;
|
|
5
|
-
accessToken: string;
|
|
6
|
-
/**
|
|
7
|
-
* Present only when logged in via `storage login --oidc` (device-code
|
|
8
|
-
* grant against MAS; see src/cli/oidc.ts).
|
|
9
|
-
* Absent for a plain password login. `oidcTokenEndpoint` is persisted
|
|
10
|
-
* directly (rather than re-discovered from `oidcIssuer` on every command)
|
|
11
|
-
* so reusing/refreshing the session never needs OIDC discovery again —
|
|
12
|
-
* discovery is Node-hostile (see src/cli/oidcWindowPolyfill.ts) and this
|
|
13
|
-
* avoids needing it more than once, at login time.
|
|
14
|
-
*/
|
|
15
|
-
refreshToken?: string;
|
|
16
|
-
oidcIssuer?: string;
|
|
17
|
-
oidcClientId?: string;
|
|
18
|
-
oidcTokenEndpoint?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Resolves the profile directory: everything this CLI persists (session,
|
|
22
|
-
* crypto store) lives here. Overridable via TELECRYPT_IO_STORAGE_HOME so
|
|
23
|
-
* tests can give each simulated user/device its own isolated profile.
|
|
24
|
-
* Defaults to ~/.telecrypt-io/storage for normal interactive use.
|
|
25
|
-
*/
|
|
26
|
-
export declare function profileDir(): string;
|
|
27
|
-
export declare function sessionPath(dir?: string): string;
|
|
28
|
-
export declare function cryptoSnapshotPath(dir?: string): string;
|
|
29
|
-
export declare function ensureProfileDir(dir?: string): void;
|
|
30
|
-
export declare function readSession(dir?: string): Session | null;
|
|
31
|
-
export declare function writeSession(session: Session, dir?: string): void;
|
|
32
|
-
/** Clears all local state for this profile (session + crypto store). */
|
|
33
|
-
export declare function clearProfile(dir?: string): void;
|
package/dist/cli/profile.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
/**
|
|
5
|
-
* Resolves the profile directory: everything this CLI persists (session,
|
|
6
|
-
* crypto store) lives here. Overridable via TELECRYPT_IO_STORAGE_HOME so
|
|
7
|
-
* tests can give each simulated user/device its own isolated profile.
|
|
8
|
-
* Defaults to ~/.telecrypt-io/storage for normal interactive use.
|
|
9
|
-
*/
|
|
10
|
-
export function profileDir() {
|
|
11
|
-
const home = process.env.TELECRYPT_IO_STORAGE_HOME;
|
|
12
|
-
if (home && home.trim() !== "")
|
|
13
|
-
return home;
|
|
14
|
-
return path.join(os.homedir(), ".telecrypt-io", "storage");
|
|
15
|
-
}
|
|
16
|
-
export function sessionPath(dir = profileDir()) {
|
|
17
|
-
return path.join(dir, "session.json");
|
|
18
|
-
}
|
|
19
|
-
export function cryptoSnapshotPath(dir = profileDir()) {
|
|
20
|
-
return path.join(dir, "crypto.snapshot");
|
|
21
|
-
}
|
|
22
|
-
export function ensureProfileDir(dir = profileDir()) {
|
|
23
|
-
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
24
|
-
}
|
|
25
|
-
export function readSession(dir = profileDir()) {
|
|
26
|
-
const p = sessionPath(dir);
|
|
27
|
-
if (!fs.existsSync(p))
|
|
28
|
-
return null;
|
|
29
|
-
const raw = fs.readFileSync(p, "utf8");
|
|
30
|
-
return JSON.parse(raw);
|
|
31
|
-
}
|
|
32
|
-
export function writeSession(session, dir = profileDir()) {
|
|
33
|
-
ensureProfileDir(dir);
|
|
34
|
-
fs.writeFileSync(sessionPath(dir), JSON.stringify(session, null, 2), {
|
|
35
|
-
mode: 0o600,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
/** Clears all local state for this profile (session + crypto store). */
|
|
39
|
-
export function clearProfile(dir = profileDir()) {
|
|
40
|
-
const sp = sessionPath(dir);
|
|
41
|
-
const cp = cryptoSnapshotPath(dir);
|
|
42
|
-
if (fs.existsSync(sp))
|
|
43
|
-
fs.rmSync(sp);
|
|
44
|
-
if (fs.existsSync(cp))
|
|
45
|
-
fs.rmSync(cp);
|
|
46
|
-
}
|
package/dist/cli/storage.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import "fake-indexeddb/auto";
|
|
2
|
-
import { TeleCryptIOStorage } from "../TeleCryptIOStorage.js";
|
|
3
|
-
import { Session } from "./profile.js";
|
|
4
|
-
export interface OpenedStorage {
|
|
5
|
-
storage: TeleCryptIOStorage;
|
|
6
|
-
session: Session;
|
|
7
|
-
/** Persists the crypto store back to disk and stops the client. Call this
|
|
8
|
-
* in a `finally` block around every command that opens storage, so the
|
|
9
|
-
* next CLI invocation sees whatever this one learned (new megolm sessions,
|
|
10
|
-
* device keys, etc.) — this is what makes decryption survive across
|
|
11
|
-
* separate processes. */
|
|
12
|
-
close: () => Promise<void>;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Opens a TeleCryptIOStorage bound to the current profile's session, having first
|
|
16
|
-
* restored the crypto store snapshot (if any) from a previous CLI
|
|
17
|
-
* invocation. Throws CliError("not logged in") if there is no session.
|
|
18
|
-
*/
|
|
19
|
-
export declare function openStorage(dir?: string): Promise<OpenedStorage>;
|
|
20
|
-
/**
|
|
21
|
-
* If server-side key backup is active for this account, waits (best-effort,
|
|
22
|
-
* bounded) for the SDK's backup engine to report zero sessions remaining to
|
|
23
|
-
* upload. This matters specifically because a CLI command is a *short-lived
|
|
24
|
-
* process*: the backup engine deliberately fire-and-forgets its upload loop
|
|
25
|
-
* with a randomised 0-10s startup jitter (to avoid a multi-device thundering
|
|
26
|
-
* herd), so a command that creates a new megolm session (recovery setup,
|
|
27
|
-
* or a file upload once recovery is already set up) and then exits
|
|
28
|
-
* immediately can easily outrun that loop, leaving the new key silently
|
|
29
|
-
* absent from the backup — recoverable-looking but not actually recoverable.
|
|
30
|
-
* Never throws: this is a best-effort settle grace period, not a
|
|
31
|
-
* correctness gate on the command's primary result.
|
|
32
|
-
*/
|
|
33
|
-
export declare function waitForBackupSettled(storage: TeleCryptIOStorage, timeoutMs?: number): Promise<void>;
|
|
34
|
-
/** Used by login/register: builds storage for a brand-new session and
|
|
35
|
-
* establishes the initial (empty) crypto store snapshot on disk. */
|
|
36
|
-
export declare function initStorageForNewSession(session: Session, dir?: string): Promise<OpenedStorage>;
|
package/dist/cli/storage.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import "fake-indexeddb/auto";
|
|
2
|
-
import { CryptoEvent } from "matrix-js-sdk/lib/crypto-api/index.js";
|
|
3
|
-
import { TeleCryptIOStorage } from "../TeleCryptIOStorage.js";
|
|
4
|
-
import { cryptoSnapshotPath, ensureProfileDir, profileDir, readSession, writeSession } from "./profile.js";
|
|
5
|
-
import { persistCryptoStore, restoreCryptoStore } from "./cryptoSnapshot.js";
|
|
6
|
-
import { CliError } from "./errors.js";
|
|
7
|
-
import { buildTokenRefreshFunction } from "../core/oidc.js";
|
|
8
|
-
/**
|
|
9
|
-
* Builds a TeleCryptIOStorage for the given session — plain password/access-
|
|
10
|
-
* token sessions go through `create()`; sessions with an OIDC token set
|
|
11
|
-
* (persisted by `storage login --oidc`, see src/cli/oidc.ts) go through
|
|
12
|
-
* `createFromOidc()` with a token refresh function wired to persist
|
|
13
|
-
* refreshed tokens straight back to this profile's session.json, so a later
|
|
14
|
-
* CLI invocation picks up the refreshed access token rather than the
|
|
15
|
-
* (possibly now-expired) one this process started with. Needs no OIDC
|
|
16
|
-
* discovery / `window` shim here — `oidcTokenEndpoint` was already resolved
|
|
17
|
-
* and persisted at login time (see src/cli/oidc.ts).
|
|
18
|
-
*/
|
|
19
|
-
async function buildStorageForSession(session, dir) {
|
|
20
|
-
if (!session.refreshToken || !session.oidcClientId || !session.oidcTokenEndpoint) {
|
|
21
|
-
return TeleCryptIOStorage.create({
|
|
22
|
-
baseUrl: session.homeserver,
|
|
23
|
-
userId: session.userId,
|
|
24
|
-
accessToken: session.accessToken,
|
|
25
|
-
deviceId: session.deviceId,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
const tokenRefreshFunction = buildTokenRefreshFunction(session.oidcTokenEndpoint, session.oidcClientId, async (tokens) => {
|
|
29
|
-
writeSession({
|
|
30
|
-
...session,
|
|
31
|
-
accessToken: tokens.accessToken,
|
|
32
|
-
refreshToken: tokens.refreshToken ?? session.refreshToken,
|
|
33
|
-
}, dir);
|
|
34
|
-
});
|
|
35
|
-
return TeleCryptIOStorage.createFromOidc({
|
|
36
|
-
baseUrl: session.homeserver,
|
|
37
|
-
userId: session.userId,
|
|
38
|
-
accessToken: session.accessToken,
|
|
39
|
-
deviceId: session.deviceId,
|
|
40
|
-
refreshToken: session.refreshToken,
|
|
41
|
-
tokenRefreshFunction,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Opens a TeleCryptIOStorage bound to the current profile's session, having first
|
|
46
|
-
* restored the crypto store snapshot (if any) from a previous CLI
|
|
47
|
-
* invocation. Throws CliError("not logged in") if there is no session.
|
|
48
|
-
*/
|
|
49
|
-
export async function openStorage(dir = profileDir()) {
|
|
50
|
-
const session = readSession(dir);
|
|
51
|
-
if (!session) {
|
|
52
|
-
throw new CliError("not logged in — run `telecrypt-io storage login` first");
|
|
53
|
-
}
|
|
54
|
-
const snapshotPath = cryptoSnapshotPath(dir);
|
|
55
|
-
await restoreCryptoStore(snapshotPath);
|
|
56
|
-
const storage = await buildStorageForSession(session, dir);
|
|
57
|
-
let closed = false;
|
|
58
|
-
const close = async () => {
|
|
59
|
-
if (closed)
|
|
60
|
-
return;
|
|
61
|
-
closed = true;
|
|
62
|
-
try {
|
|
63
|
-
await persistCryptoStore(snapshotPath);
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
storage.getClient().stopClient();
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
return { storage, session, close };
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* If server-side key backup is active for this account, waits (best-effort,
|
|
73
|
-
* bounded) for the SDK's backup engine to report zero sessions remaining to
|
|
74
|
-
* upload. This matters specifically because a CLI command is a *short-lived
|
|
75
|
-
* process*: the backup engine deliberately fire-and-forgets its upload loop
|
|
76
|
-
* with a randomised 0-10s startup jitter (to avoid a multi-device thundering
|
|
77
|
-
* herd), so a command that creates a new megolm session (recovery setup,
|
|
78
|
-
* or a file upload once recovery is already set up) and then exits
|
|
79
|
-
* immediately can easily outrun that loop, leaving the new key silently
|
|
80
|
-
* absent from the backup — recoverable-looking but not actually recoverable.
|
|
81
|
-
* Never throws: this is a best-effort settle grace period, not a
|
|
82
|
-
* correctness gate on the command's primary result.
|
|
83
|
-
*/
|
|
84
|
-
export async function waitForBackupSettled(storage, timeoutMs = 20000) {
|
|
85
|
-
const active = await storage.keys.isRecoverySetup();
|
|
86
|
-
if (!active)
|
|
87
|
-
return;
|
|
88
|
-
const client = storage.getClient();
|
|
89
|
-
await new Promise((resolve) => {
|
|
90
|
-
let settled = false;
|
|
91
|
-
const finish = () => {
|
|
92
|
-
if (settled)
|
|
93
|
-
return;
|
|
94
|
-
settled = true;
|
|
95
|
-
client.removeListener(CryptoEvent.KeyBackupSessionsRemaining, onRemaining);
|
|
96
|
-
clearTimeout(timer);
|
|
97
|
-
resolve();
|
|
98
|
-
};
|
|
99
|
-
const onRemaining = (remaining) => {
|
|
100
|
-
if (remaining === 0)
|
|
101
|
-
finish();
|
|
102
|
-
};
|
|
103
|
-
client.on(CryptoEvent.KeyBackupSessionsRemaining, onRemaining);
|
|
104
|
-
const timer = setTimeout(finish, timeoutMs);
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
/** Used by login/register: builds storage for a brand-new session and
|
|
108
|
-
* establishes the initial (empty) crypto store snapshot on disk. */
|
|
109
|
-
export async function initStorageForNewSession(session, dir = profileDir()) {
|
|
110
|
-
ensureProfileDir(dir);
|
|
111
|
-
const snapshotPath = cryptoSnapshotPath(dir);
|
|
112
|
-
const storage = await buildStorageForSession(session, dir);
|
|
113
|
-
let closed = false;
|
|
114
|
-
const close = async () => {
|
|
115
|
-
if (closed)
|
|
116
|
-
return;
|
|
117
|
-
closed = true;
|
|
118
|
-
try {
|
|
119
|
-
await persistCryptoStore(snapshotPath);
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
storage.getClient().stopClient();
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
return { storage, session, close };
|
|
126
|
-
}
|