@membox-cloud/membox 0.1.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 +169 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +159 -0
- package/dist/src/api/account.d.ts +3 -0
- package/dist/src/api/account.js +3 -0
- package/dist/src/api/client.d.ts +21 -0
- package/dist/src/api/client.js +107 -0
- package/dist/src/api/device-flow.d.ts +9 -0
- package/dist/src/api/device-flow.js +55 -0
- package/dist/src/api/devices.d.ts +10 -0
- package/dist/src/api/devices.js +24 -0
- package/dist/src/api/recovery.d.ts +5 -0
- package/dist/src/api/recovery.js +9 -0
- package/dist/src/api/sync.d.ts +9 -0
- package/dist/src/api/sync.js +22 -0
- package/dist/src/cli/bootstrap.d.ts +37 -0
- package/dist/src/cli/bootstrap.js +326 -0
- package/dist/src/cli/grants.d.ts +8 -0
- package/dist/src/cli/grants.js +76 -0
- package/dist/src/cli/helpers.d.ts +6 -0
- package/dist/src/cli/helpers.js +13 -0
- package/dist/src/cli/passphrase-input.d.ts +11 -0
- package/dist/src/cli/passphrase-input.js +94 -0
- package/dist/src/cli/pause.d.ts +2 -0
- package/dist/src/cli/pause.js +29 -0
- package/dist/src/cli/provisioning.d.ts +3 -0
- package/dist/src/cli/provisioning.js +30 -0
- package/dist/src/cli/pull.d.ts +31 -0
- package/dist/src/cli/pull.js +142 -0
- package/dist/src/cli/restore.d.ts +12 -0
- package/dist/src/cli/restore.js +90 -0
- package/dist/src/cli/setup.d.ts +17 -0
- package/dist/src/cli/setup.js +209 -0
- package/dist/src/cli/status.d.ts +1 -0
- package/dist/src/cli/status.js +31 -0
- package/dist/src/cli/sync.d.ts +4 -0
- package/dist/src/cli/sync.js +124 -0
- package/dist/src/cli/unlock.d.ts +19 -0
- package/dist/src/cli/unlock.js +153 -0
- package/dist/src/config.d.ts +4 -0
- package/dist/src/config.js +12 -0
- package/dist/src/constants.d.ts +23 -0
- package/dist/src/constants.js +27 -0
- package/dist/src/contract-types.d.ts +301 -0
- package/dist/src/contract-types.js +52 -0
- package/dist/src/crypto/aes-gcm.d.ts +29 -0
- package/dist/src/crypto/aes-gcm.js +44 -0
- package/dist/src/crypto/device-keys.d.ts +18 -0
- package/dist/src/crypto/device-keys.js +25 -0
- package/dist/src/crypto/grant.d.ts +29 -0
- package/dist/src/crypto/grant.js +87 -0
- package/dist/src/crypto/kdf.d.ts +16 -0
- package/dist/src/crypto/kdf.js +24 -0
- package/dist/src/crypto/keys.d.ts +14 -0
- package/dist/src/crypto/keys.js +35 -0
- package/dist/src/crypto/manifest.d.ts +25 -0
- package/dist/src/crypto/manifest.js +41 -0
- package/dist/src/crypto/recovery.d.ts +16 -0
- package/dist/src/crypto/recovery.js +94 -0
- package/dist/src/crypto/types.d.ts +34 -0
- package/dist/src/crypto/types.js +1 -0
- package/dist/src/debug-logger.d.ts +32 -0
- package/dist/src/debug-logger.js +108 -0
- package/dist/src/hooks/gateway-lifecycle.d.ts +6 -0
- package/dist/src/hooks/gateway-lifecycle.js +40 -0
- package/dist/src/hooks/prompt-inject.d.ts +7 -0
- package/dist/src/hooks/prompt-inject.js +18 -0
- package/dist/src/store/keychain.d.ts +26 -0
- package/dist/src/store/keychain.js +151 -0
- package/dist/src/store/local-state.d.ts +27 -0
- package/dist/src/store/local-state.js +47 -0
- package/dist/src/store/managed-unlock.d.ts +8 -0
- package/dist/src/store/managed-unlock.js +46 -0
- package/dist/src/store/pending-setup.d.ts +23 -0
- package/dist/src/store/pending-setup.js +32 -0
- package/dist/src/store/session.d.ts +13 -0
- package/dist/src/store/session.js +28 -0
- package/dist/src/sync/auto-sync.d.ts +12 -0
- package/dist/src/sync/auto-sync.js +82 -0
- package/dist/src/sync/conflict.d.ts +24 -0
- package/dist/src/sync/conflict.js +92 -0
- package/dist/src/sync/diff.d.ts +25 -0
- package/dist/src/sync/diff.js +75 -0
- package/dist/src/sync/downloader.d.ts +16 -0
- package/dist/src/sync/downloader.js +73 -0
- package/dist/src/sync/scanner.d.ts +12 -0
- package/dist/src/sync/scanner.js +52 -0
- package/dist/src/sync/state.d.ts +4 -0
- package/dist/src/sync/state.js +22 -0
- package/dist/src/sync/uploader.d.ts +20 -0
- package/dist/src/sync/uploader.js +86 -0
- package/dist/src/tools/grants-approve-pending.d.ts +17 -0
- package/dist/src/tools/grants-approve-pending.js +50 -0
- package/dist/src/tools/pull.d.ts +31 -0
- package/dist/src/tools/pull.js +71 -0
- package/dist/src/tools/restore.d.ts +31 -0
- package/dist/src/tools/restore.js +96 -0
- package/dist/src/tools/result.d.ts +7 -0
- package/dist/src/tools/result.js +6 -0
- package/dist/src/tools/secret-file.d.ts +10 -0
- package/dist/src/tools/secret-file.js +37 -0
- package/dist/src/tools/setup-finish.d.ts +36 -0
- package/dist/src/tools/setup-finish.js +108 -0
- package/dist/src/tools/setup-poll.d.ts +27 -0
- package/dist/src/tools/setup-poll.js +83 -0
- package/dist/src/tools/setup-start.d.ts +18 -0
- package/dist/src/tools/setup-start.js +49 -0
- package/dist/src/tools/status.d.ts +17 -0
- package/dist/src/tools/status.js +40 -0
- package/dist/src/tools/sync.d.ts +17 -0
- package/dist/src/tools/sync.js +49 -0
- package/dist/src/tools/unlock-secret.d.ts +42 -0
- package/dist/src/tools/unlock-secret.js +87 -0
- package/dist/src/tools/unlock.d.ts +25 -0
- package/dist/src/tools/unlock.js +72 -0
- package/openclaw.plugin.json +16 -0
- package/package.json +35 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { readState } from "../store/local-state.js";
|
|
2
|
+
import { getBytes, ACCOUNTS } from "../store/keychain.js";
|
|
3
|
+
import { deriveURK } from "../crypto/kdf.js";
|
|
4
|
+
import { unwrapAMK } from "../crypto/keys.js";
|
|
5
|
+
import { vaultSession } from "../store/session.js";
|
|
6
|
+
import { disableManagedUnlock, enableManagedUnlock, getManagedUnlockPassphrase, getManagedUnlockStatus, } from "../store/managed-unlock.js";
|
|
7
|
+
import { readPassphrase } from "./passphrase-input.js";
|
|
8
|
+
async function canUnlockVault() {
|
|
9
|
+
const state = await readState();
|
|
10
|
+
if (!state?.setup_complete) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const wrappedAmk = await getBytes(ACCOUNTS.WRAPPED_AMK);
|
|
14
|
+
const salt = await getBytes(ACCOUNTS.WRAPPED_AMK_SALT);
|
|
15
|
+
const iv = await getBytes(ACCOUNTS.WRAPPED_AMK_IV);
|
|
16
|
+
const tag = await getBytes(ACCOUNTS.WRAPPED_AMK_TAG);
|
|
17
|
+
if (!wrappedAmk || !salt || !iv || !tag) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
export async function unlockWithPassphrase(passphrase, options) {
|
|
23
|
+
const state = await readState();
|
|
24
|
+
if (!state?.setup_complete) {
|
|
25
|
+
if (options?.announceFailure) {
|
|
26
|
+
console.log("Vault not set up. Run `openclaw membox setup`.");
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const wrappedAmk = await getBytes(ACCOUNTS.WRAPPED_AMK);
|
|
31
|
+
const salt = await getBytes(ACCOUNTS.WRAPPED_AMK_SALT);
|
|
32
|
+
const iv = await getBytes(ACCOUNTS.WRAPPED_AMK_IV);
|
|
33
|
+
const tag = await getBytes(ACCOUNTS.WRAPPED_AMK_TAG);
|
|
34
|
+
if (!wrappedAmk || !salt || !iv || !tag) {
|
|
35
|
+
if (options?.announceFailure) {
|
|
36
|
+
console.error("Keychain data missing. Re-run setup or use recovery.");
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (options?.announceDeriving) {
|
|
41
|
+
console.log("Deriving key...");
|
|
42
|
+
}
|
|
43
|
+
const { urk } = await deriveURK(passphrase, salt);
|
|
44
|
+
try {
|
|
45
|
+
const amk = unwrapAMK(urk, { encrypted_amk: wrappedAmk, iv, tag });
|
|
46
|
+
vaultSession.unlock(amk);
|
|
47
|
+
if (options?.announceSuccess) {
|
|
48
|
+
console.log("Vault unlocked.");
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
if (options?.announceFailure) {
|
|
54
|
+
console.error("Wrong passphrase.");
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
urk.fill(0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function tryManagedUnlock(options) {
|
|
63
|
+
const passphrase = await getManagedUnlockPassphrase();
|
|
64
|
+
if (!passphrase) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const unlocked = await unlockWithPassphrase(passphrase, {
|
|
68
|
+
announceDeriving: options?.announceDeriving,
|
|
69
|
+
announceSuccess: false,
|
|
70
|
+
announceFailure: false,
|
|
71
|
+
});
|
|
72
|
+
if (unlocked) {
|
|
73
|
+
if (options?.announceSuccess) {
|
|
74
|
+
console.log("Vault unlocked from the managed local unlock secret.");
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (options?.announceFailure) {
|
|
79
|
+
console.error("Managed unlock secret failed. Disable it and re-enable it with the current passphrase.");
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
export async function ensureVaultUnlocked(options) {
|
|
84
|
+
if (vaultSession.isUnlocked()) {
|
|
85
|
+
if (options?.announceAlreadyUnlocked) {
|
|
86
|
+
console.log("Vault is already unlocked.");
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
if (!(await canUnlockVault())) {
|
|
91
|
+
const state = await readState();
|
|
92
|
+
if (!state?.setup_complete) {
|
|
93
|
+
console.log("Vault not set up. Run `openclaw membox setup`.");
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.error("Keychain data missing. Re-run setup or use recovery.");
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (options?.allowManagedUnlock !== false) {
|
|
101
|
+
const managedStatus = await getManagedUnlockStatus();
|
|
102
|
+
if (managedStatus.enabled) {
|
|
103
|
+
const unlocked = await tryManagedUnlock({
|
|
104
|
+
announceDeriving: true,
|
|
105
|
+
announceSuccess: options?.announceSuccess,
|
|
106
|
+
announceFailure: options?.allowPrompt !== false,
|
|
107
|
+
});
|
|
108
|
+
if (unlocked) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (options?.allowPrompt === false) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (!managedStatus.secret_present) {
|
|
115
|
+
console.error("Managed unlock is enabled but no local unlock secret is stored.");
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log("Falling back to a manual passphrase prompt.");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (options?.allowPrompt === false) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const passphrase = await readPassphrase("Vault passphrase: ");
|
|
126
|
+
return unlockWithPassphrase(passphrase, {
|
|
127
|
+
announceDeriving: true,
|
|
128
|
+
announceFailure: true,
|
|
129
|
+
announceSuccess: options?.announceSuccess,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export async function unlockAction() {
|
|
133
|
+
await ensureVaultUnlocked({
|
|
134
|
+
announceAlreadyUnlocked: true,
|
|
135
|
+
announceSuccess: true,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
export async function enableManagedUnlockAction(passphrase) {
|
|
139
|
+
const unlocked = await unlockWithPassphrase(passphrase, {
|
|
140
|
+
announceDeriving: true,
|
|
141
|
+
announceFailure: true,
|
|
142
|
+
announceSuccess: false,
|
|
143
|
+
});
|
|
144
|
+
if (!unlocked) {
|
|
145
|
+
throw new Error("Wrong passphrase. Managed unlock secret was not enabled.");
|
|
146
|
+
}
|
|
147
|
+
await enableManagedUnlock(passphrase);
|
|
148
|
+
console.log("Managed unlock secret enabled on this machine.");
|
|
149
|
+
}
|
|
150
|
+
export async function disableManagedUnlockAction() {
|
|
151
|
+
await disableManagedUnlock();
|
|
152
|
+
console.log("Managed unlock secret disabled on this machine.");
|
|
153
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DEFAULT_SERVER_URL } from "./constants.js";
|
|
2
|
+
export function resolveConfig(raw) {
|
|
3
|
+
const obj = raw && typeof raw === "object" ? raw : {};
|
|
4
|
+
// Priority: MEMBOX_SERVER_URL env var > plugin config > default
|
|
5
|
+
const envUrl = process.env.MEMBOX_SERVER_URL;
|
|
6
|
+
const serverUrl = typeof envUrl === "string" && envUrl.length > 0
|
|
7
|
+
? envUrl
|
|
8
|
+
: typeof obj.serverUrl === "string"
|
|
9
|
+
? obj.serverUrl
|
|
10
|
+
: DEFAULT_SERVER_URL;
|
|
11
|
+
return { serverUrl };
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const PLUGIN_ID = "membox";
|
|
2
|
+
export declare const PLUGIN_VERSION = "0.1.0";
|
|
3
|
+
export declare const DEFAULT_SERVER_URL = "https://membox.cloud";
|
|
4
|
+
export declare const API_PREFIX = "/api/v1";
|
|
5
|
+
export declare const KEYCHAIN_SERVICE: string;
|
|
6
|
+
export declare const STATE_DIR = ".membox";
|
|
7
|
+
export declare const STATE_FILE = "state.json";
|
|
8
|
+
export declare const PENDING_SETUP_FILE = "pending-setup.json";
|
|
9
|
+
export declare const STATE_ROOT_ENV = "MEMBOX_STATE_ROOT";
|
|
10
|
+
export declare function resolveStateRoot(): string;
|
|
11
|
+
export declare const MANIFEST_VERSION = 1;
|
|
12
|
+
export declare const CIPHER_ALGORITHM: "aes-256-gcm";
|
|
13
|
+
export declare const WRAP_ALGORITHM: "aes-256-gcm";
|
|
14
|
+
export declare const CRYPTO_VERSION = 1;
|
|
15
|
+
export declare const ARGON2_PARAMS: {
|
|
16
|
+
readonly memoryKib: 65536;
|
|
17
|
+
readonly iterations: 3;
|
|
18
|
+
readonly parallelism: 4;
|
|
19
|
+
readonly saltLength: 16;
|
|
20
|
+
readonly outputLength: 32;
|
|
21
|
+
};
|
|
22
|
+
export declare const MEMORY_ROOT_PATH = "MEMORY.md";
|
|
23
|
+
export declare const MEMORY_DAILY_PATTERN: RegExp;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const PLUGIN_ID = "membox";
|
|
4
|
+
export const PLUGIN_VERSION = "0.1.0";
|
|
5
|
+
export const DEFAULT_SERVER_URL = "https://membox.cloud";
|
|
6
|
+
export const API_PREFIX = "/api/v1";
|
|
7
|
+
export const KEYCHAIN_SERVICE = process.env.MEMBOX_KEYCHAIN_SERVICE || "membox.cloud";
|
|
8
|
+
export const STATE_DIR = ".membox";
|
|
9
|
+
export const STATE_FILE = "state.json";
|
|
10
|
+
export const PENDING_SETUP_FILE = "pending-setup.json";
|
|
11
|
+
export const STATE_ROOT_ENV = "MEMBOX_STATE_ROOT";
|
|
12
|
+
export function resolveStateRoot() {
|
|
13
|
+
return process.env[STATE_ROOT_ENV] || join(homedir(), STATE_DIR);
|
|
14
|
+
}
|
|
15
|
+
export const MANIFEST_VERSION = 1;
|
|
16
|
+
export const CIPHER_ALGORITHM = "aes-256-gcm";
|
|
17
|
+
export const WRAP_ALGORITHM = "aes-256-gcm";
|
|
18
|
+
export const CRYPTO_VERSION = 1;
|
|
19
|
+
export const ARGON2_PARAMS = {
|
|
20
|
+
memoryKib: 65536,
|
|
21
|
+
iterations: 3,
|
|
22
|
+
parallelism: 4,
|
|
23
|
+
saltLength: 16,
|
|
24
|
+
outputLength: 32,
|
|
25
|
+
};
|
|
26
|
+
export const MEMORY_ROOT_PATH = "MEMORY.md";
|
|
27
|
+
export const MEMORY_DAILY_PATTERN = /^memory\/\d{4}-\d{2}-\d{2}\.md$/;
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
export interface AadDescriptor {
|
|
2
|
+
user_id: string;
|
|
3
|
+
object_id: string;
|
|
4
|
+
object_version: number;
|
|
5
|
+
created_by_device_id: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AccountMe {
|
|
8
|
+
user_id: string;
|
|
9
|
+
display_name: string | null;
|
|
10
|
+
primary_email: string | null;
|
|
11
|
+
providers: LinkedProviderOut[];
|
|
12
|
+
current_device_id?: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface BinaryDownloadResponse {
|
|
15
|
+
payload_b64: string;
|
|
16
|
+
content_sha256: string;
|
|
17
|
+
}
|
|
18
|
+
export interface BinaryUploadInput {
|
|
19
|
+
payload_b64: string;
|
|
20
|
+
content_sha256: string;
|
|
21
|
+
}
|
|
22
|
+
export interface BinaryUploadResponse {
|
|
23
|
+
blob_key: string;
|
|
24
|
+
content_sha256: string;
|
|
25
|
+
payload_size: number;
|
|
26
|
+
}
|
|
27
|
+
export interface BlobRef {
|
|
28
|
+
blob_key: string;
|
|
29
|
+
iv_b64: string;
|
|
30
|
+
tag_b64: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ChangeItem {
|
|
33
|
+
seq: number;
|
|
34
|
+
object_id: string;
|
|
35
|
+
version: number;
|
|
36
|
+
change_type: string;
|
|
37
|
+
created_by_device_id: string;
|
|
38
|
+
created_at: string;
|
|
39
|
+
}
|
|
40
|
+
export interface DekEnvelope {
|
|
41
|
+
version?: number;
|
|
42
|
+
algorithm?: "aes-256-gcm";
|
|
43
|
+
wrapped_for?: "amk";
|
|
44
|
+
encrypted_dek_b64: string;
|
|
45
|
+
iv_b64: string;
|
|
46
|
+
tag_b64: string;
|
|
47
|
+
}
|
|
48
|
+
export interface DeviceGrantApproveInput {
|
|
49
|
+
encrypted_grant_payload_b64: string;
|
|
50
|
+
signature: DeviceGrantSignature;
|
|
51
|
+
payload_checksum_sha256: string;
|
|
52
|
+
}
|
|
53
|
+
export interface DeviceGrantOut {
|
|
54
|
+
grant_id: string;
|
|
55
|
+
source_device_id?: string | null;
|
|
56
|
+
target_device_id: string;
|
|
57
|
+
target_device_name?: string;
|
|
58
|
+
target_platform?: string;
|
|
59
|
+
status: "pending" | "approved" | "rejected" | "expired";
|
|
60
|
+
expires_at: string;
|
|
61
|
+
crypto_version?: number;
|
|
62
|
+
target_device_kex_public_key_b64?: string | null;
|
|
63
|
+
encrypted_grant_payload_b64?: string | null;
|
|
64
|
+
grant_signature_b64?: string | null;
|
|
65
|
+
signature_algorithm?: string | null;
|
|
66
|
+
source_device_sign_public_key_b64?: string | null;
|
|
67
|
+
source_device_kex_public_key_b64?: string | null;
|
|
68
|
+
}
|
|
69
|
+
export interface DeviceGrantSignature {
|
|
70
|
+
signature_algorithm?: "ed25519";
|
|
71
|
+
signature_b64: string;
|
|
72
|
+
}
|
|
73
|
+
export interface DevicePollResponse {
|
|
74
|
+
status: "authorization_pending" | "access_granted" | "slow_down" | "expired_token" | "denied";
|
|
75
|
+
access_token?: string | null;
|
|
76
|
+
refresh_token?: string | null;
|
|
77
|
+
token_type?: string | null;
|
|
78
|
+
expires_in?: number | null;
|
|
79
|
+
device_id?: string | null;
|
|
80
|
+
}
|
|
81
|
+
export interface DeviceStartRequest {
|
|
82
|
+
device_name: string;
|
|
83
|
+
platform: Platform;
|
|
84
|
+
plugin_version?: string | null;
|
|
85
|
+
sign_public_key_b64?: string | null;
|
|
86
|
+
kex_public_key_b64?: string | null;
|
|
87
|
+
}
|
|
88
|
+
export interface DeviceStartResponse {
|
|
89
|
+
device_code: string;
|
|
90
|
+
user_code: string;
|
|
91
|
+
verification_uri: string;
|
|
92
|
+
verification_uri_complete: string;
|
|
93
|
+
expires_in: number;
|
|
94
|
+
interval: number;
|
|
95
|
+
}
|
|
96
|
+
export interface DeviceSummary {
|
|
97
|
+
device_id: string;
|
|
98
|
+
device_name: string;
|
|
99
|
+
platform: Platform;
|
|
100
|
+
plugin_version?: string | null;
|
|
101
|
+
status: "active" | "revoked" | "pending";
|
|
102
|
+
created_at: string;
|
|
103
|
+
last_seen_at: string | null;
|
|
104
|
+
has_device_keys?: boolean;
|
|
105
|
+
grant_capable?: boolean;
|
|
106
|
+
is_current: boolean;
|
|
107
|
+
}
|
|
108
|
+
export interface DeviceVerifyDetail {
|
|
109
|
+
user_code: string;
|
|
110
|
+
device_name: string;
|
|
111
|
+
platform: Platform;
|
|
112
|
+
plugin_version: string | null;
|
|
113
|
+
status: "pending" | "confirmed" | "denied" | "expired" | "issued";
|
|
114
|
+
expires_at: string;
|
|
115
|
+
}
|
|
116
|
+
export interface EmailRequestInput {
|
|
117
|
+
email: string;
|
|
118
|
+
}
|
|
119
|
+
export interface EmailRequestResponse {
|
|
120
|
+
sent?: boolean;
|
|
121
|
+
debug_code?: string | null;
|
|
122
|
+
}
|
|
123
|
+
export interface EmailVerifyInput {
|
|
124
|
+
email: string;
|
|
125
|
+
code: string;
|
|
126
|
+
}
|
|
127
|
+
export interface EncryptedObjectManifest {
|
|
128
|
+
manifest_version?: number;
|
|
129
|
+
cipher_algorithm?: "aes-256-gcm" | "AES-256-GCM";
|
|
130
|
+
wrap_algorithm?: "aes-256-gcm" | "aes-256-kw" | "AES-256-GCM" | "AES-256-KW";
|
|
131
|
+
object_id: string;
|
|
132
|
+
object_version: number;
|
|
133
|
+
ciphertext_size: number;
|
|
134
|
+
content_sha256: string;
|
|
135
|
+
metadata_sha256: string;
|
|
136
|
+
blob: BlobRef;
|
|
137
|
+
metadata: BlobRef;
|
|
138
|
+
dek_envelope: DekEnvelope;
|
|
139
|
+
aad: AadDescriptor;
|
|
140
|
+
}
|
|
141
|
+
export interface ExportStatusResponse {
|
|
142
|
+
export_id: string;
|
|
143
|
+
status: "queued" | "running" | "done" | "failed" | "expired";
|
|
144
|
+
artifact_key: string | null;
|
|
145
|
+
expires_at: string | null;
|
|
146
|
+
created_at?: string | null;
|
|
147
|
+
completed_at?: string | null;
|
|
148
|
+
download_url?: string | null;
|
|
149
|
+
}
|
|
150
|
+
export interface GenericMessage {
|
|
151
|
+
ok?: boolean;
|
|
152
|
+
message?: string | null;
|
|
153
|
+
}
|
|
154
|
+
export interface HealthResponse {
|
|
155
|
+
status: string;
|
|
156
|
+
}
|
|
157
|
+
export interface LinkedProviderOut {
|
|
158
|
+
provider: Provider;
|
|
159
|
+
provider_email: string | null;
|
|
160
|
+
linked_at: string;
|
|
161
|
+
is_primary: boolean;
|
|
162
|
+
}
|
|
163
|
+
export interface ManifestResponse {
|
|
164
|
+
manifest: EncryptedObjectManifest;
|
|
165
|
+
}
|
|
166
|
+
export interface PendingDeviceAuthorization {
|
|
167
|
+
authorization_id: string;
|
|
168
|
+
user_code: string;
|
|
169
|
+
device_name: string;
|
|
170
|
+
platform: Platform;
|
|
171
|
+
plugin_version: string | null;
|
|
172
|
+
status: "pending";
|
|
173
|
+
created_at: string;
|
|
174
|
+
expires_at: string;
|
|
175
|
+
}
|
|
176
|
+
export type Platform = "macos" | "windows" | "linux" | "unknown";
|
|
177
|
+
export type Provider = "github" | "google" | "email";
|
|
178
|
+
export interface ProviderLinkInput {
|
|
179
|
+
code: string;
|
|
180
|
+
redirect_uri: string;
|
|
181
|
+
}
|
|
182
|
+
export interface ProviderStartResponse {
|
|
183
|
+
authorization_url: string;
|
|
184
|
+
provider: Provider;
|
|
185
|
+
flow: string;
|
|
186
|
+
}
|
|
187
|
+
export interface RecoveryBundleDownload {
|
|
188
|
+
material_type: string;
|
|
189
|
+
encrypted_payload_b64: string;
|
|
190
|
+
payload_checksum: string;
|
|
191
|
+
algorithm: string;
|
|
192
|
+
crypto_version: number;
|
|
193
|
+
}
|
|
194
|
+
export interface RecoveryDownloadInput {
|
|
195
|
+
material_type?: "recovery_bundle" | "recovery_code";
|
|
196
|
+
}
|
|
197
|
+
export interface RecoveryMaterialStatus {
|
|
198
|
+
has_recovery_code: boolean;
|
|
199
|
+
has_recovery_bundle: boolean;
|
|
200
|
+
updated_at: string | null;
|
|
201
|
+
}
|
|
202
|
+
export interface RecoveryMaterialUpload {
|
|
203
|
+
material_type: "recovery_bundle" | "recovery_code";
|
|
204
|
+
encrypted_payload_b64: string;
|
|
205
|
+
payload_checksum: string;
|
|
206
|
+
algorithm?: "aes-256-gcm";
|
|
207
|
+
crypto_version?: number;
|
|
208
|
+
}
|
|
209
|
+
export interface RefreshTokenInput {
|
|
210
|
+
refresh_token: string;
|
|
211
|
+
}
|
|
212
|
+
export interface SyncChangesResponse {
|
|
213
|
+
cursor: number;
|
|
214
|
+
changes: ChangeItem[];
|
|
215
|
+
}
|
|
216
|
+
export interface SyncCommitInput {
|
|
217
|
+
manifest: EncryptedObjectManifest;
|
|
218
|
+
}
|
|
219
|
+
export interface SyncCommitResponse {
|
|
220
|
+
object_id: string;
|
|
221
|
+
version: number;
|
|
222
|
+
seq: number;
|
|
223
|
+
}
|
|
224
|
+
export interface SyncStatusResponse {
|
|
225
|
+
cursor: number;
|
|
226
|
+
object_count: number;
|
|
227
|
+
current_device_last_seen_at: string | null;
|
|
228
|
+
}
|
|
229
|
+
export interface TokenResponse {
|
|
230
|
+
access_token: string;
|
|
231
|
+
refresh_token: string;
|
|
232
|
+
token_type?: string;
|
|
233
|
+
expires_in: number;
|
|
234
|
+
device_id: string;
|
|
235
|
+
}
|
|
236
|
+
export type LinkedProvider = LinkedProviderOut;
|
|
237
|
+
export type DeviceGrant = DeviceGrantOut;
|
|
238
|
+
export type ExportTask = ExportStatusResponse;
|
|
239
|
+
export type ExportStatus = ExportTask["status"];
|
|
240
|
+
export type SyncChange = ChangeItem;
|
|
241
|
+
export type TokenPair = TokenResponse;
|
|
242
|
+
export type EmailRequestPayload = EmailRequestInput;
|
|
243
|
+
export type EmailVerifyPayload = EmailVerifyInput;
|
|
244
|
+
export declare const ERROR_CODES: {
|
|
245
|
+
readonly UNAUTHORIZED: "unauthorized";
|
|
246
|
+
readonly FORBIDDEN: "forbidden";
|
|
247
|
+
readonly TOKEN_EXPIRED: "token_expired";
|
|
248
|
+
readonly REFRESH_TOKEN_INVALID: "refresh_token_invalid";
|
|
249
|
+
readonly EMAIL_RATE_LIMITED: "email_rate_limited";
|
|
250
|
+
readonly EMAIL_CODE_INVALID: "email_code_invalid";
|
|
251
|
+
readonly EMAIL_CODE_EXPIRED: "email_code_expired";
|
|
252
|
+
readonly DEVICE_CODE_EXPIRED: "device_code_expired";
|
|
253
|
+
readonly DEVICE_CODE_NOT_FOUND: "device_code_not_found";
|
|
254
|
+
readonly DEVICE_CODE_ALREADY_CONFIRMED: "device_code_already_confirmed";
|
|
255
|
+
readonly PROVIDER_ALREADY_LINKED: "provider_already_linked";
|
|
256
|
+
readonly PROVIDER_IS_PRIMARY: "provider_is_primary";
|
|
257
|
+
readonly LAST_PROVIDER: "last_provider";
|
|
258
|
+
readonly DEVICE_ALREADY_REVOKED: "device_already_revoked";
|
|
259
|
+
readonly CANNOT_REVOKE_CURRENT: "cannot_revoke_current";
|
|
260
|
+
readonly EXPORT_IN_PROGRESS: "export_in_progress";
|
|
261
|
+
readonly RECOVERY_REGENERATE_IN_PLUGIN: "recovery_regenerate_in_plugin";
|
|
262
|
+
readonly EXPIRED: "expired";
|
|
263
|
+
readonly PAYLOAD_TOO_LARGE: "payload_too_large";
|
|
264
|
+
readonly VALIDATION_ERROR: "validation_error";
|
|
265
|
+
readonly NOT_FOUND: "not_found";
|
|
266
|
+
readonly INTERNAL_ERROR: "internal_error";
|
|
267
|
+
readonly AAD_MISMATCH: "aad_mismatch";
|
|
268
|
+
readonly ACCESS_DENIED: "access_denied";
|
|
269
|
+
readonly ALREADY_DELETED: "already_deleted";
|
|
270
|
+
readonly BLOB_NOT_FOUND: "blob_not_found";
|
|
271
|
+
readonly CHECKSUM_MISMATCH: "checksum_mismatch";
|
|
272
|
+
readonly DEVICE_INACTIVE: "device_inactive";
|
|
273
|
+
readonly INVALID_AUTH: "invalid_auth";
|
|
274
|
+
readonly INVALID_DEVICE: "invalid_device";
|
|
275
|
+
readonly INVALID_FLOW: "invalid_flow";
|
|
276
|
+
readonly INVALID_OBJECT_ID: "invalid_object_id";
|
|
277
|
+
readonly INVALID_PAYLOAD: "invalid_payload";
|
|
278
|
+
readonly INVALID_SESSION: "invalid_session";
|
|
279
|
+
readonly INVALID_STATE: "invalid_state";
|
|
280
|
+
readonly INVALID_TOKEN: "invalid_token";
|
|
281
|
+
readonly OAUTH_FAILED: "oauth_failed";
|
|
282
|
+
readonly OBJECT_DELETED: "object_deleted";
|
|
283
|
+
readonly PROVIDER_NOT_CONFIGURED: "provider_not_configured";
|
|
284
|
+
readonly SESSION_EXPIRED: "session_expired";
|
|
285
|
+
readonly SESSION_MISMATCH: "session_mismatch";
|
|
286
|
+
readonly SESSION_REVOKED: "session_revoked";
|
|
287
|
+
readonly UNSUPPORTED_PROVIDER: "unsupported_provider";
|
|
288
|
+
readonly USER_INACTIVE: "user_inactive";
|
|
289
|
+
readonly VERSION_CONFLICT: "version_conflict";
|
|
290
|
+
readonly NO_SESSION: "no_session";
|
|
291
|
+
readonly EXPORT_NOT_READY: "export_not_ready";
|
|
292
|
+
};
|
|
293
|
+
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
294
|
+
export interface ApiErrorBody {
|
|
295
|
+
error: {
|
|
296
|
+
code: ErrorCode | string;
|
|
297
|
+
message: string;
|
|
298
|
+
request_id: string;
|
|
299
|
+
details?: Record<string, unknown>;
|
|
300
|
+
};
|
|
301
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Inlined from @membox/contract for standalone npm publishing.
|
|
2
|
+
// Source: packages/contract/src/generated/types.ts + packages/contract/src/types.ts
|
|
3
|
+
// ErrorCode and ApiErrorBody
|
|
4
|
+
export const ERROR_CODES = {
|
|
5
|
+
UNAUTHORIZED: "unauthorized",
|
|
6
|
+
FORBIDDEN: "forbidden",
|
|
7
|
+
TOKEN_EXPIRED: "token_expired",
|
|
8
|
+
REFRESH_TOKEN_INVALID: "refresh_token_invalid",
|
|
9
|
+
EMAIL_RATE_LIMITED: "email_rate_limited",
|
|
10
|
+
EMAIL_CODE_INVALID: "email_code_invalid",
|
|
11
|
+
EMAIL_CODE_EXPIRED: "email_code_expired",
|
|
12
|
+
DEVICE_CODE_EXPIRED: "device_code_expired",
|
|
13
|
+
DEVICE_CODE_NOT_FOUND: "device_code_not_found",
|
|
14
|
+
DEVICE_CODE_ALREADY_CONFIRMED: "device_code_already_confirmed",
|
|
15
|
+
PROVIDER_ALREADY_LINKED: "provider_already_linked",
|
|
16
|
+
PROVIDER_IS_PRIMARY: "provider_is_primary",
|
|
17
|
+
LAST_PROVIDER: "last_provider",
|
|
18
|
+
DEVICE_ALREADY_REVOKED: "device_already_revoked",
|
|
19
|
+
CANNOT_REVOKE_CURRENT: "cannot_revoke_current",
|
|
20
|
+
EXPORT_IN_PROGRESS: "export_in_progress",
|
|
21
|
+
RECOVERY_REGENERATE_IN_PLUGIN: "recovery_regenerate_in_plugin",
|
|
22
|
+
EXPIRED: "expired",
|
|
23
|
+
PAYLOAD_TOO_LARGE: "payload_too_large",
|
|
24
|
+
VALIDATION_ERROR: "validation_error",
|
|
25
|
+
NOT_FOUND: "not_found",
|
|
26
|
+
INTERNAL_ERROR: "internal_error",
|
|
27
|
+
AAD_MISMATCH: "aad_mismatch",
|
|
28
|
+
ACCESS_DENIED: "access_denied",
|
|
29
|
+
ALREADY_DELETED: "already_deleted",
|
|
30
|
+
BLOB_NOT_FOUND: "blob_not_found",
|
|
31
|
+
CHECKSUM_MISMATCH: "checksum_mismatch",
|
|
32
|
+
DEVICE_INACTIVE: "device_inactive",
|
|
33
|
+
INVALID_AUTH: "invalid_auth",
|
|
34
|
+
INVALID_DEVICE: "invalid_device",
|
|
35
|
+
INVALID_FLOW: "invalid_flow",
|
|
36
|
+
INVALID_OBJECT_ID: "invalid_object_id",
|
|
37
|
+
INVALID_PAYLOAD: "invalid_payload",
|
|
38
|
+
INVALID_SESSION: "invalid_session",
|
|
39
|
+
INVALID_STATE: "invalid_state",
|
|
40
|
+
INVALID_TOKEN: "invalid_token",
|
|
41
|
+
OAUTH_FAILED: "oauth_failed",
|
|
42
|
+
OBJECT_DELETED: "object_deleted",
|
|
43
|
+
PROVIDER_NOT_CONFIGURED: "provider_not_configured",
|
|
44
|
+
SESSION_EXPIRED: "session_expired",
|
|
45
|
+
SESSION_MISMATCH: "session_mismatch",
|
|
46
|
+
SESSION_REVOKED: "session_revoked",
|
|
47
|
+
UNSUPPORTED_PROVIDER: "unsupported_provider",
|
|
48
|
+
USER_INACTIVE: "user_inactive",
|
|
49
|
+
VERSION_CONFLICT: "version_conflict",
|
|
50
|
+
NO_SESSION: "no_session",
|
|
51
|
+
EXPORT_NOT_READY: "export_not_ready",
|
|
52
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface EncryptResult {
|
|
2
|
+
ciphertext: Uint8Array;
|
|
3
|
+
iv: Uint8Array;
|
|
4
|
+
tag: Uint8Array;
|
|
5
|
+
}
|
|
6
|
+
export interface KeyEnvelope {
|
|
7
|
+
encrypted_key: Uint8Array;
|
|
8
|
+
iv: Uint8Array;
|
|
9
|
+
tag: Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Encrypt plaintext with AES-256-GCM.
|
|
13
|
+
* Returns separate ciphertext, IV, and authentication tag.
|
|
14
|
+
*/
|
|
15
|
+
export declare function encrypt(key: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): EncryptResult;
|
|
16
|
+
/**
|
|
17
|
+
* Decrypt ciphertext with AES-256-GCM.
|
|
18
|
+
* Throws on authentication failure (wrong key, tampered data).
|
|
19
|
+
*/
|
|
20
|
+
export declare function decrypt(key: Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a key using AES-256-GCM (key wrapping via encryption).
|
|
23
|
+
*/
|
|
24
|
+
export declare function wrapKey(wrappingKey: Uint8Array, targetKey: Uint8Array, aad?: Uint8Array): KeyEnvelope;
|
|
25
|
+
/**
|
|
26
|
+
* Unwrap a key using AES-256-GCM.
|
|
27
|
+
* Throws on authentication failure.
|
|
28
|
+
*/
|
|
29
|
+
export declare function unwrapKey(wrappingKey: Uint8Array, envelope: KeyEnvelope, aad?: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { gcm } from "@noble/ciphers/aes";
|
|
2
|
+
import { randomBytes } from "@noble/hashes/utils";
|
|
3
|
+
const IV_LENGTH = 12;
|
|
4
|
+
const TAG_LENGTH = 16;
|
|
5
|
+
/**
|
|
6
|
+
* Encrypt plaintext with AES-256-GCM.
|
|
7
|
+
* Returns separate ciphertext, IV, and authentication tag.
|
|
8
|
+
*/
|
|
9
|
+
export function encrypt(key, plaintext, aad) {
|
|
10
|
+
const iv = randomBytes(IV_LENGTH);
|
|
11
|
+
const cipher = gcm(key, iv, aad);
|
|
12
|
+
const sealed = cipher.encrypt(plaintext);
|
|
13
|
+
// @noble/ciphers appends the 16-byte tag to the ciphertext
|
|
14
|
+
return {
|
|
15
|
+
ciphertext: sealed.subarray(0, sealed.length - TAG_LENGTH),
|
|
16
|
+
iv,
|
|
17
|
+
tag: sealed.subarray(sealed.length - TAG_LENGTH),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Decrypt ciphertext with AES-256-GCM.
|
|
22
|
+
* Throws on authentication failure (wrong key, tampered data).
|
|
23
|
+
*/
|
|
24
|
+
export function decrypt(key, ciphertext, iv, tag, aad) {
|
|
25
|
+
const sealed = new Uint8Array(ciphertext.length + tag.length);
|
|
26
|
+
sealed.set(ciphertext);
|
|
27
|
+
sealed.set(tag, ciphertext.length);
|
|
28
|
+
const cipher = gcm(key, iv, aad);
|
|
29
|
+
return cipher.decrypt(sealed);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wrap a key using AES-256-GCM (key wrapping via encryption).
|
|
33
|
+
*/
|
|
34
|
+
export function wrapKey(wrappingKey, targetKey, aad) {
|
|
35
|
+
const r = encrypt(wrappingKey, targetKey, aad);
|
|
36
|
+
return { encrypted_key: r.ciphertext, iv: r.iv, tag: r.tag };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Unwrap a key using AES-256-GCM.
|
|
40
|
+
* Throws on authentication failure.
|
|
41
|
+
*/
|
|
42
|
+
export function unwrapKey(wrappingKey, envelope, aad) {
|
|
43
|
+
return decrypt(wrappingKey, envelope.encrypted_key, envelope.iv, envelope.tag, aad);
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface DeviceKeyPair {
|
|
2
|
+
ed25519: {
|
|
3
|
+
publicKey: Uint8Array;
|
|
4
|
+
privateKey: Uint8Array;
|
|
5
|
+
};
|
|
6
|
+
x25519: {
|
|
7
|
+
publicKey: Uint8Array;
|
|
8
|
+
privateKey: Uint8Array;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Generate a fresh device key pair (Ed25519 for signing, X25519 for KEX). */
|
|
12
|
+
export declare function generateDeviceKeyPair(): DeviceKeyPair;
|
|
13
|
+
/** Sign a message with Ed25519. */
|
|
14
|
+
export declare function sign(privateKey: Uint8Array, message: Uint8Array): Uint8Array;
|
|
15
|
+
/** Verify an Ed25519 signature. */
|
|
16
|
+
export declare function verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): boolean;
|
|
17
|
+
/** Compute X25519 shared secret for key exchange. */
|
|
18
|
+
export declare function computeSharedSecret(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
|