@zeph-to/mcp-server 1.15.2 → 2.0.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 CHANGED
@@ -138,15 +138,21 @@ priority: "normal"
138
138
 
139
139
  ### zeph_file
140
140
 
141
- Send a text file to the user's device.
141
+ Send a file to the user's device. Either `filePath` (a file already on disk) or
142
+ `content` (text you generated) is required.
142
143
 
143
144
  ```
144
- fileName: "report.json"
145
- content: "{\"status\": \"ok\"}"
146
- title: "Build Report" (optional, defaults to fileName)
147
- targetDeviceId: "dev_..." (optional)
145
+ filePath: "/abs/path/screenshot.png" (images, PDFs, logs — anything on disk)
146
+ content: "{\"status\": \"ok\"}" (text only; requires fileName)
147
+ fileName: "report.json" (required with content; defaults to basename of filePath)
148
+ title: "Build Report" (optional, defaults to fileName)
149
+ targetDeviceId: "dev_..." (optional)
148
150
  ```
149
151
 
152
+ Images are delivered with their real mime type and render inline on the device.
153
+ Never base64 a binary file into `content` — pass `filePath` and the server reads
154
+ the bytes off disk.
155
+
150
156
  Returns: `{ pushId: "...", fileKey: "...", fileSize: 42 }`
151
157
 
152
158
  ### zeph_session_rename
@@ -323,9 +329,16 @@ Create an API key with the **MCP** preset in Settings > API Keys for the correct
323
329
 
324
330
  ## Encryption
325
331
 
326
- Push bodies are encrypted with AES-256-GCM. The wrapping key is derived via ECDH P-256 and synced across your own devices on first server startup so every device can read the same push. Toggle encryption in the Zeph app (Settings Encryption); when disabled, the server sends plaintext. No configuration needed.
332
+ Push bodies and file attachments are encrypted with AES-256-GCM. This server holds its own ECDH P-256 keypair, generated on first use and stored in `~/.config/zeph/device-keys.json` the private half never leaves the machine. Each push is encrypted once, and its AES key is wrapped separately for every device on your account using ECDH against that device's public key.
333
+
334
+ Toggle encryption in the Zeph app (Settings → Encryption); when it is off, pushes go out as plaintext. No configuration needed. The opt-in is read once at startup, so **turning it on while this server is running takes effect only after a restart.**
335
+
336
+ **Threat model:** against a passive backend — a leaked snapshot, an operator reading the table — the stored ciphertext and wrapped keys are useless, so push contents stay private. Three limits worth knowing:
337
+ - **No protection from an active malicious operator.** Recipient public keys come from `GET /devices` on that same server, unsigned and unpinned. A backend that injects a device record carrying its own key gets the message key wrapped for it, and reads everything. Closing this needs out-of-band device verification (ADR-0007 Phase 4, not built).
338
+ - **No forward secrecy.** The ECDH secret for a given sender/device pair is static, so compromising either private key opens every past push wrapped for that pair.
339
+ - **`senderPublicKey` is unsigned**, so a swapped one makes a push undecryptable — that direction fails closed rather than leaking.
327
340
 
328
- **Threat model honesty:** keys are persisted on the Zeph backend to enable cross-device sync, so this is *device-shared* encryption — not true end-to-end. It protects push contents from passive network observers and from a leaked database snapshot taken without the key store, but it does **not** protect against the Zeph backend itself (it has the keys it serves to your devices). A true E2E mode (per-device keypairs, server stores only public keys, no key escrow) is on the roadmap.
341
+ A device that has not registered a per-device public key cannot be sent to; it is skipped, and if no device qualifies the push goes out in the clear rather than arriving as something nothing can open.
329
342
 
330
343
  ## License
331
344
 
@@ -20,8 +20,8 @@ export declare class ZephApiClient {
20
20
  sessionId?: string;
21
21
  files?: AttachedFile[];
22
22
  isEncrypted?: boolean;
23
- encryptedKey?: string;
24
23
  senderPublicKey?: string;
24
+ deviceKeyMap?: Record<string, string>;
25
25
  }): Promise<PushResponse>;
26
26
  triggerHook(hookId: string, params: {
27
27
  title: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
package/dist/crypto.d.ts CHANGED
@@ -1,57 +1,76 @@
1
1
  /**
2
- * Device-shared encryption for MCP server — self-contained ECDH P-256 +
2
+ * Per-device encryption for the MCP server — self-contained ECDH P-256 +
3
3
  * AES-256-GCM. Mirrors @zeph/crypto API but bundled inline (no external
4
4
  * dependency). Uses Web Crypto API via node:crypto webcrypto — Node.js 18+
5
5
  * (the `crypto` global only exists unflagged from Node 19, so we import it).
6
6
  *
7
- * Threat model honesty (do not call this "E2E" without a footnote):
7
+ * How it works (ADR-0007):
8
8
  *
9
- * The Zeph backend persists the per-user private key in plaintext so it
10
- * can be synced down to a fresh device (fetchServerKeys / uploadServerKeys
11
- * below). That means the backend can decrypt any push body this is NOT
12
- * end-to-end in the standard sense. What it gives you is:
13
- * Protection against passive network observers
14
- * • Protection against a leaked DB snapshot taken without the key store
15
- * • Cross-device readability (all your devices share one keypair)
16
- * What it does NOT give you:
17
- * • Protection against the Zeph backend itself
18
- * • Forward secrecy — encryptPushBodyForSelf / encryptFileForSelf do
19
- * ECDH(self, self), which collapses to a static derived key. A single
20
- * device compromise (since all your devices share the same keypair)
21
- * lets the attacker decrypt every past push for which they have the
22
- * ciphertext. The per-message AES key is random, but its wrap key is
23
- * static, so wrapped keys are decryptable forever.
9
+ * This process holds its own ECDH keypair. The private half is generated
10
+ * here, written to ~/.config/zeph/device-keys.json, and never leaves the
11
+ * host the server only ever sees public keys. A push is encrypted once
12
+ * with a random AES key, and that key is wrapped separately for each of the
13
+ * user's registered devices using ECDH(this host, that device).
24
14
  *
25
- * True E2E would require a per-device keypair (server stores only public
26
- * keys; senders wrap the message key once per recipient device public
27
- * key). That refactor is on the roadmap; until then, treat push bodies as
28
- * sensitive-but-not-secret.
15
+ * That makes it end-to-end in the standard sense: the backend stores
16
+ * ciphertext plus wrapped keys it cannot unwrap.
17
+ *
18
+ * What it still does not give you:
19
+ * • Forward secrecy — the ECDH secret for a given (sender, device) pair is
20
+ * static, so a compromise of either private key retroactively opens every
21
+ * push wrapped for that pair. The per-message AES key is random; its wrap
22
+ * key is not.
23
+ * • Authenticity beyond the key pairing — nothing signs `senderPublicKey`,
24
+ * so a server that swapped it could make a push undecryptable, though not
25
+ * readable.
26
+ *
27
+ * Superseded scheme: a single account-wide keypair whose private half the
28
+ * backend escrowed so it could sync to new devices. Key escrow was removed
29
+ * server-side (zeph@8a6d21b), which left this client waiting for a private key
30
+ * the API stopped returning — encryption was silently off for months. Nothing
31
+ * here asks for that key any more.
29
32
  */
33
+ /** One target device, as returned by `GET /devices`. */
34
+ export interface DeviceRecipient {
35
+ deviceId: string;
36
+ /** Base64 SPKI of that device's per-device public key. */
37
+ publicKey: string;
38
+ }
39
+ /** deviceId → JSON `{ encryptedKey, keyIv }`, the wire shape the clients parse. */
40
+ export type DeviceKeyMap = Record<string, string>;
30
41
  /**
31
42
  * Initialize crypto.
32
43
  *
33
- * The MCP server is a CONSUMER of encryption keys, not a generator. Keys
34
- * are created in the Zeph app where the user explicitly opts in (Settings
35
- * Encryption). This function only imports keys that the server already
36
- * has, and only when the server confirms encryption is enabled.
44
+ * Encryption turns on only when the account has explicitly opted in
45
+ * `encryptionEnabled` from `GET /users/me/keys` is the single authoritative
46
+ * signal (ADR-0008), set from the Zeph app. Server unreachable, flag off, or
47
+ * the hard opt-out below all leave the cache empty and every send plaintext.
37
48
  *
38
- * Any other state server says disabled, server has no keys, server is
39
- * unreachable leaves encryption OFF (cache empty, no fallback). A
40
- * previous version generated and uploaded a fresh keypair on the "no keys
41
- * anywhere" path; combined with a transient fetch failure, that silently
42
- * turned encryption on without user consent and locked the account into
43
- * an "encryption enabled" state on the server.
49
+ * When it is on, this host generates its own keypair on first use and keeps
50
+ * it. Unlike the superseded scheme this asks the server for nothing but the
51
+ * flag: the private key is created here and stays here.
44
52
  *
45
53
  * Opt-out: `ZEPH_DISABLE_ENCRYPTION=1` forces crypto off regardless of
46
- * server state — useful while cleaning up legacy state or for users who
47
- * never want encryption.
54
+ * server state.
48
55
  *
49
- * Safe to call concurrently — deduplicates to single init.
50
- * Returns the exported public key when encryption is active, '' otherwise.
56
+ * Safe to call concurrently — deduplicates to a single init.
57
+ * Returns this host's public key when encryption is active, '' otherwise.
51
58
  *
52
59
  * NOTE: when `apiKey` is provided, `baseUrl` is required.
53
60
  */
54
61
  export declare const initCrypto: (apiKey?: string, baseUrl?: string) => Promise<string>;
62
+ /**
63
+ * Keep only devices this host can actually encrypt for.
64
+ *
65
+ * A device without a public key has never run a build that registers one, and
66
+ * a device still advertising the account-wide key has not migrated to
67
+ * per-device E2E — wrapping for either produces a push it cannot open, which
68
+ * is worse than sending plaintext it can read.
69
+ */
70
+ export declare const selectRecipients: (devices: {
71
+ deviceId: string;
72
+ publicKey?: string;
73
+ }[]) => DeviceRecipient[];
55
74
  export declare const getKeyPair: () => CryptoKeyPair | null;
56
75
  export declare const getPublicKey: () => string | null;
57
76
  /**
@@ -65,24 +84,28 @@ export declare const getPublicKey: () => string | null;
65
84
  */
66
85
  export declare const disableCrypto: () => void;
67
86
  /**
68
- * Encrypt push body for self (all own devices).
87
+ * Encrypt a push body for the given recipient devices.
88
+ *
89
+ * Returns the wire fields the API expects: `body` carries the ciphertext and
90
+ * IV, `deviceKeyMap` the per-device wrapped keys, `senderPublicKey` the half
91
+ * recipients need to derive the same secret back.
69
92
  */
70
- export declare const encryptPushBodyForSelf: (input: {
93
+ export declare const encryptPushBodyForDevices: (input: {
71
94
  title?: string;
72
95
  body?: string;
73
96
  url?: string;
74
- }) => Promise<{
97
+ }, recipients: DeviceRecipient[]) => Promise<{
75
98
  body: string;
76
- encryptedKey: string;
99
+ deviceKeyMap: DeviceKeyMap;
77
100
  senderPublicKey: string;
78
101
  isEncrypted: true;
79
102
  }>;
80
103
  /**
81
- * Encrypt file content for self (all own devices).
104
+ * Encrypt file content for the given recipient devices.
82
105
  */
83
- export declare const encryptFileForSelf: (content: string) => Promise<{
106
+ export declare const encryptFileForDevices: (content: string | Buffer, recipients: DeviceRecipient[]) => Promise<{
84
107
  ciphertext: Buffer;
85
108
  iv: string;
86
- encryptedKey: string;
109
+ deviceKeyMap: DeviceKeyMap;
87
110
  }>;
88
111
  //# sourceMappingURL=crypto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA0JH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAsE5E,CAAC;AAmCF,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAAO,IAIhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KACrD,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAaA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,KACd,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAQlE,CAAC"}
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAuEH,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mFAAmF;AACnF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA6ElD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CA2D5E,CAAC;AAuCF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAClD,eAAe,EAG8C,CAAC;AAEjE,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAAO,IAGhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACpC,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,YAAY,eAAe,EAAE,KAC5B,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAkBA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,GAAG,MAAM,EACxB,YAAY,eAAe,EAAE,KAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAE,CAsBxE,CAAC"}