uxnan-bridge 0.0.1-alpha.20260621

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.
Files changed (255) hide show
  1. package/README.md +150 -0
  2. package/dist/src/account-status.d.ts +13 -0
  3. package/dist/src/account-status.js +78 -0
  4. package/dist/src/account-status.js.map +1 -0
  5. package/dist/src/adapters/base-adapter.d.ts +18 -0
  6. package/dist/src/adapters/base-adapter.js +15 -0
  7. package/dist/src/adapters/base-adapter.js.map +1 -0
  8. package/dist/src/adapters/claude-adapter.d.ts +102 -0
  9. package/dist/src/adapters/claude-adapter.js +486 -0
  10. package/dist/src/adapters/claude-adapter.js.map +1 -0
  11. package/dist/src/adapters/claude-tools.d.ts +25 -0
  12. package/dist/src/adapters/claude-tools.js +146 -0
  13. package/dist/src/adapters/claude-tools.js.map +1 -0
  14. package/dist/src/adapters/codex-adapter.d.ts +116 -0
  15. package/dist/src/adapters/codex-adapter.js +912 -0
  16. package/dist/src/adapters/codex-adapter.js.map +1 -0
  17. package/dist/src/adapters/codex-app-server.d.ts +74 -0
  18. package/dist/src/adapters/codex-app-server.js +225 -0
  19. package/dist/src/adapters/codex-app-server.js.map +1 -0
  20. package/dist/src/adapters/codex-approval.d.ts +88 -0
  21. package/dist/src/adapters/codex-approval.js +160 -0
  22. package/dist/src/adapters/codex-approval.js.map +1 -0
  23. package/dist/src/adapters/codex-tools.d.ts +18 -0
  24. package/dist/src/adapters/codex-tools.js +106 -0
  25. package/dist/src/adapters/codex-tools.js.map +1 -0
  26. package/dist/src/adapters/content-blocks.d.ts +68 -0
  27. package/dist/src/adapters/content-blocks.js +205 -0
  28. package/dist/src/adapters/content-blocks.js.map +1 -0
  29. package/dist/src/adapters/echo-agent-adapter.d.ts +23 -0
  30. package/dist/src/adapters/echo-agent-adapter.js +72 -0
  31. package/dist/src/adapters/echo-agent-adapter.js.map +1 -0
  32. package/dist/src/adapters/gemini-adapter.d.ts +87 -0
  33. package/dist/src/adapters/gemini-adapter.js +594 -0
  34. package/dist/src/adapters/gemini-adapter.js.map +1 -0
  35. package/dist/src/adapters/gemini-tools.d.ts +4 -0
  36. package/dist/src/adapters/gemini-tools.js +48 -0
  37. package/dist/src/adapters/gemini-tools.js.map +1 -0
  38. package/dist/src/adapters/opencode-adapter.d.ts +74 -0
  39. package/dist/src/adapters/opencode-adapter.js +418 -0
  40. package/dist/src/adapters/opencode-adapter.js.map +1 -0
  41. package/dist/src/adapters/opencode-tools.d.ts +2 -0
  42. package/dist/src/adapters/opencode-tools.js +41 -0
  43. package/dist/src/adapters/opencode-tools.js.map +1 -0
  44. package/dist/src/adapters/pi-adapter.d.ts +92 -0
  45. package/dist/src/adapters/pi-adapter.js +467 -0
  46. package/dist/src/adapters/pi-adapter.js.map +1 -0
  47. package/dist/src/adapters/pi-tools.d.ts +10 -0
  48. package/dist/src/adapters/pi-tools.js +72 -0
  49. package/dist/src/adapters/pi-tools.js.map +1 -0
  50. package/dist/src/adapters/process-agent-adapter.d.ts +24 -0
  51. package/dist/src/adapters/process-agent-adapter.js +111 -0
  52. package/dist/src/adapters/process-agent-adapter.js.map +1 -0
  53. package/dist/src/adapters/resolve-claude.d.ts +13 -0
  54. package/dist/src/adapters/resolve-claude.js +57 -0
  55. package/dist/src/adapters/resolve-claude.js.map +1 -0
  56. package/dist/src/adapters/resolve-codex.d.ts +13 -0
  57. package/dist/src/adapters/resolve-codex.js +48 -0
  58. package/dist/src/adapters/resolve-codex.js.map +1 -0
  59. package/dist/src/adapters/resolve-gemini.d.ts +13 -0
  60. package/dist/src/adapters/resolve-gemini.js +47 -0
  61. package/dist/src/adapters/resolve-gemini.js.map +1 -0
  62. package/dist/src/adapters/resolve-opencode.d.ts +11 -0
  63. package/dist/src/adapters/resolve-opencode.js +49 -0
  64. package/dist/src/adapters/resolve-opencode.js.map +1 -0
  65. package/dist/src/adapters/resolve-pi.d.ts +13 -0
  66. package/dist/src/adapters/resolve-pi.js +46 -0
  67. package/dist/src/adapters/resolve-pi.js.map +1 -0
  68. package/dist/src/adapters/run-options.d.ts +22 -0
  69. package/dist/src/adapters/run-options.js +48 -0
  70. package/dist/src/adapters/run-options.js.map +1 -0
  71. package/dist/src/adapters/spawn.d.ts +20 -0
  72. package/dist/src/adapters/spawn.js +16 -0
  73. package/dist/src/adapters/spawn.js.map +1 -0
  74. package/dist/src/agents/agent-manager.d.ts +98 -0
  75. package/dist/src/agents/agent-manager.js +433 -0
  76. package/dist/src/agents/agent-manager.js.map +1 -0
  77. package/dist/src/agents/attachments.d.ts +28 -0
  78. package/dist/src/agents/attachments.js +121 -0
  79. package/dist/src/agents/attachments.js.map +1 -0
  80. package/dist/src/bridge-context.d.ts +45 -0
  81. package/dist/src/bridge-context.js +2 -0
  82. package/dist/src/bridge-context.js.map +1 -0
  83. package/dist/src/bridge-status.d.ts +12 -0
  84. package/dist/src/bridge-status.js +17 -0
  85. package/dist/src/bridge-status.js.map +1 -0
  86. package/dist/src/bridge.d.ts +37 -0
  87. package/dist/src/bridge.js +446 -0
  88. package/dist/src/bridge.js.map +1 -0
  89. package/dist/src/cli.d.ts +2 -0
  90. package/dist/src/cli.js +194 -0
  91. package/dist/src/cli.js.map +1 -0
  92. package/dist/src/conversation/session-history.d.ts +27 -0
  93. package/dist/src/conversation/session-history.js +1082 -0
  94. package/dist/src/conversation/session-history.js.map +1 -0
  95. package/dist/src/conversation/thread-store.d.ts +74 -0
  96. package/dist/src/conversation/thread-store.js +366 -0
  97. package/dist/src/conversation/thread-store.js.map +1 -0
  98. package/dist/src/daemon-config.d.ts +123 -0
  99. package/dist/src/daemon-config.js +64 -0
  100. package/dist/src/daemon-config.js.map +1 -0
  101. package/dist/src/daemon-state.d.ts +27 -0
  102. package/dist/src/daemon-state.js +76 -0
  103. package/dist/src/daemon-state.js.map +1 -0
  104. package/dist/src/git/git-runner.d.ts +24 -0
  105. package/dist/src/git/git-runner.js +63 -0
  106. package/dist/src/git/git-runner.js.map +1 -0
  107. package/dist/src/git/git-service.d.ts +76 -0
  108. package/dist/src/git/git-service.js +435 -0
  109. package/dist/src/git/git-service.js.map +1 -0
  110. package/dist/src/handler-router.d.ts +34 -0
  111. package/dist/src/handler-router.js +67 -0
  112. package/dist/src/handler-router.js.map +1 -0
  113. package/dist/src/handlers/account-handler.d.ts +4 -0
  114. package/dist/src/handlers/account-handler.js +27 -0
  115. package/dist/src/handlers/account-handler.js.map +1 -0
  116. package/dist/src/handlers/agent-handler.d.ts +2 -0
  117. package/dist/src/handlers/agent-handler.js +8 -0
  118. package/dist/src/handlers/agent-handler.js.map +1 -0
  119. package/dist/src/handlers/bridge-control-handler.d.ts +2 -0
  120. package/dist/src/handlers/bridge-control-handler.js +64 -0
  121. package/dist/src/handlers/bridge-control-handler.js.map +1 -0
  122. package/dist/src/handlers/desktop-handler.d.ts +12 -0
  123. package/dist/src/handlers/desktop-handler.js +5 -0
  124. package/dist/src/handlers/desktop-handler.js.map +1 -0
  125. package/dist/src/handlers/git-handler.d.ts +2 -0
  126. package/dist/src/handlers/git-handler.js +82 -0
  127. package/dist/src/handlers/git-handler.js.map +1 -0
  128. package/dist/src/handlers/index.d.ts +8 -0
  129. package/dist/src/handlers/index.js +22 -0
  130. package/dist/src/handlers/index.js.map +1 -0
  131. package/dist/src/handlers/not-implemented.d.ts +10 -0
  132. package/dist/src/handlers/not-implemented.js +21 -0
  133. package/dist/src/handlers/not-implemented.js.map +1 -0
  134. package/dist/src/handlers/notifications-handler.d.ts +2 -0
  135. package/dist/src/handlers/notifications-handler.js +62 -0
  136. package/dist/src/handlers/notifications-handler.js.map +1 -0
  137. package/dist/src/handlers/params.d.ts +11 -0
  138. package/dist/src/handlers/params.js +72 -0
  139. package/dist/src/handlers/params.js.map +1 -0
  140. package/dist/src/handlers/project-handler.d.ts +2 -0
  141. package/dist/src/handlers/project-handler.js +6 -0
  142. package/dist/src/handlers/project-handler.js.map +1 -0
  143. package/dist/src/handlers/thread-context-handler.d.ts +2 -0
  144. package/dist/src/handlers/thread-context-handler.js +211 -0
  145. package/dist/src/handlers/thread-context-handler.js.map +1 -0
  146. package/dist/src/handlers/workspace-handler.d.ts +2 -0
  147. package/dist/src/handlers/workspace-handler.js +101 -0
  148. package/dist/src/handlers/workspace-handler.js.map +1 -0
  149. package/dist/src/hooks/claude-approval-hook.d.ts +7 -0
  150. package/dist/src/hooks/claude-approval-hook.js +95 -0
  151. package/dist/src/hooks/claude-approval-hook.js.map +1 -0
  152. package/dist/src/hooks/gemini-approval-hook.d.ts +7 -0
  153. package/dist/src/hooks/gemini-approval-hook.js +113 -0
  154. package/dist/src/hooks/gemini-approval-hook.js.map +1 -0
  155. package/dist/src/index.d.ts +62 -0
  156. package/dist/src/index.js +65 -0
  157. package/dist/src/index.js.map +1 -0
  158. package/dist/src/keyring-secret-store.d.ts +36 -0
  159. package/dist/src/keyring-secret-store.js +70 -0
  160. package/dist/src/keyring-secret-store.js.map +1 -0
  161. package/dist/src/lock-file.d.ts +18 -0
  162. package/dist/src/lock-file.js +60 -0
  163. package/dist/src/lock-file.js.map +1 -0
  164. package/dist/src/logger.d.ts +28 -0
  165. package/dist/src/logger.js +99 -0
  166. package/dist/src/logger.js.map +1 -0
  167. package/dist/src/pairing/pairing-code-service.d.ts +45 -0
  168. package/dist/src/pairing/pairing-code-service.js +183 -0
  169. package/dist/src/pairing/pairing-code-service.js.map +1 -0
  170. package/dist/src/projects/project-registry.d.ts +14 -0
  171. package/dist/src/projects/project-registry.js +60 -0
  172. package/dist/src/projects/project-registry.js.map +1 -0
  173. package/dist/src/push/push-sender.d.ts +21 -0
  174. package/dist/src/push/push-sender.js +96 -0
  175. package/dist/src/push/push-sender.js.map +1 -0
  176. package/dist/src/push/push-service.d.ts +122 -0
  177. package/dist/src/push/push-service.js +260 -0
  178. package/dist/src/push/push-service.js.map +1 -0
  179. package/dist/src/qr.d.ts +17 -0
  180. package/dist/src/qr.js +31 -0
  181. package/dist/src/qr.js.map +1 -0
  182. package/dist/src/secret-store.d.ts +23 -0
  183. package/dist/src/secret-store.js +27 -0
  184. package/dist/src/secret-store.js.map +1 -0
  185. package/dist/src/secure-device-state.d.ts +16 -0
  186. package/dist/src/secure-device-state.js +63 -0
  187. package/dist/src/secure-device-state.js.map +1 -0
  188. package/dist/src/service-installer.d.ts +57 -0
  189. package/dist/src/service-installer.js +254 -0
  190. package/dist/src/service-installer.js.map +1 -0
  191. package/dist/src/session-state.d.ts +14 -0
  192. package/dist/src/session-state.js +19 -0
  193. package/dist/src/session-state.js.map +1 -0
  194. package/dist/src/transport/crypto.d.ts +43 -0
  195. package/dist/src/transport/crypto.js +78 -0
  196. package/dist/src/transport/crypto.js.map +1 -0
  197. package/dist/src/transport/lan-server.d.ts +33 -0
  198. package/dist/src/transport/lan-server.js +105 -0
  199. package/dist/src/transport/lan-server.js.map +1 -0
  200. package/dist/src/transport/local-hosts.d.ts +17 -0
  201. package/dist/src/transport/local-hosts.js +30 -0
  202. package/dist/src/transport/local-hosts.js.map +1 -0
  203. package/dist/src/transport/mdns-advertiser.d.ts +83 -0
  204. package/dist/src/transport/mdns-advertiser.js +282 -0
  205. package/dist/src/transport/mdns-advertiser.js.map +1 -0
  206. package/dist/src/transport/message-io.d.ts +33 -0
  207. package/dist/src/transport/message-io.js +87 -0
  208. package/dist/src/transport/message-io.js.map +1 -0
  209. package/dist/src/transport/outbound-log.d.ts +24 -0
  210. package/dist/src/transport/outbound-log.js +78 -0
  211. package/dist/src/transport/outbound-log.js.map +1 -0
  212. package/dist/src/transport/relay-client.d.ts +19 -0
  213. package/dist/src/transport/relay-client.js +27 -0
  214. package/dist/src/transport/relay-client.js.map +1 -0
  215. package/dist/src/transport/secure-channel.d.ts +33 -0
  216. package/dist/src/transport/secure-channel.js +81 -0
  217. package/dist/src/transport/secure-channel.js.map +1 -0
  218. package/dist/src/transport/server-handshake.d.ts +49 -0
  219. package/dist/src/transport/server-handshake.js +137 -0
  220. package/dist/src/transport/server-handshake.js.map +1 -0
  221. package/dist/src/transport/session-handler.d.ts +19 -0
  222. package/dist/src/transport/session-handler.js +134 -0
  223. package/dist/src/transport/session-handler.js.map +1 -0
  224. package/dist/src/transport/session-registry.d.ts +58 -0
  225. package/dist/src/transport/session-registry.js +91 -0
  226. package/dist/src/transport/session-registry.js.map +1 -0
  227. package/dist/src/transport/trust-store.d.ts +23 -0
  228. package/dist/src/transport/trust-store.js +33 -0
  229. package/dist/src/transport/trust-store.js.map +1 -0
  230. package/dist/src/transport/ws-adapter.d.ts +7 -0
  231. package/dist/src/transport/ws-adapter.js +16 -0
  232. package/dist/src/transport/ws-adapter.js.map +1 -0
  233. package/dist/src/version.d.ts +1 -0
  234. package/dist/src/version.js +13 -0
  235. package/dist/src/version.js.map +1 -0
  236. package/dist/src/workspace/browse-service.d.ts +10 -0
  237. package/dist/src/workspace/browse-service.js +97 -0
  238. package/dist/src/workspace/browse-service.js.map +1 -0
  239. package/dist/src/workspace/checkpoint-service.d.ts +21 -0
  240. package/dist/src/workspace/checkpoint-service.js +219 -0
  241. package/dist/src/workspace/checkpoint-service.js.map +1 -0
  242. package/dist/src/workspace/path-guard.d.ts +7 -0
  243. package/dist/src/workspace/path-guard.js +51 -0
  244. package/dist/src/workspace/path-guard.js.map +1 -0
  245. package/dist/src/workspace/workspace-service.d.ts +8 -0
  246. package/dist/src/workspace/workspace-service.js +111 -0
  247. package/dist/src/workspace/workspace-service.js.map +1 -0
  248. package/package.json +46 -0
  249. package/scripts/extract-gemini-hook.mjs +16 -0
  250. package/scripts/fake-approval-bridge.mjs +23 -0
  251. package/scripts/install-service-linux.sh +38 -0
  252. package/scripts/install-service-macos.sh +38 -0
  253. package/scripts/install-service-windows.ps1 +26 -0
  254. package/scripts/test-gemini-hook-e2e.mjs +168 -0
  255. package/scripts/write-gemini-settings.mjs +31 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.js","sourceRoot":"","sources":["../../src/qr.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,MAAM,MAAM,iBAAiB,CAAC;AACrC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,GAEhB,MAAM,eAAe,CAAC;AAgBvB,MAAM,UAAU,sBAAsB,CAAC,OAA+B;IACpE,MAAM,OAAO,GAAmB;QAC9B,CAAC,EAAE,kBAAkB;QACrB,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,UAAU,EAAE;QAC5C,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,SAAS,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5C,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;IACF,IAAI,OAAO,CAAC,QAAQ;QAAE,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC;IACvD,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,OAAuB;IACrD,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Abstraction over secret storage. The bridge's Ed25519 private identity is a
3
+ * secret and (per AGENTS.md) must never be written to disk in plaintext.
4
+ *
5
+ * This module ships an in-memory implementation only. A persistent, OS-keychain
6
+ * backed implementation is required before real pairing.
7
+ *
8
+ * FOR-DEV: implement an OS-keychain-backed SecretStore (Windows Credential
9
+ * Manager / macOS Keychain / libsecret) so the bridge identity survives restarts
10
+ * (src/secret-store.ts). Unblocks: real pairing & trusted reconnect.
11
+ */
12
+ export interface SecretStore {
13
+ get(key: string): Promise<string | null>;
14
+ set(key: string, value: string): Promise<void>;
15
+ delete(key: string): Promise<void>;
16
+ }
17
+ /** Process-lifetime secret store. Secrets are lost when the process exits. */
18
+ export declare class InMemorySecretStore implements SecretStore {
19
+ #private;
20
+ get(key: string): Promise<string | null>;
21
+ set(key: string, value: string): Promise<void>;
22
+ delete(key: string): Promise<void>;
23
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Abstraction over secret storage. The bridge's Ed25519 private identity is a
3
+ * secret and (per AGENTS.md) must never be written to disk in plaintext.
4
+ *
5
+ * This module ships an in-memory implementation only. A persistent, OS-keychain
6
+ * backed implementation is required before real pairing.
7
+ *
8
+ * FOR-DEV: implement an OS-keychain-backed SecretStore (Windows Credential
9
+ * Manager / macOS Keychain / libsecret) so the bridge identity survives restarts
10
+ * (src/secret-store.ts). Unblocks: real pairing & trusted reconnect.
11
+ */
12
+ /** Process-lifetime secret store. Secrets are lost when the process exits. */
13
+ export class InMemorySecretStore {
14
+ #secrets = new Map();
15
+ get(key) {
16
+ return Promise.resolve(this.#secrets.get(key) ?? null);
17
+ }
18
+ set(key, value) {
19
+ this.#secrets.set(key, value);
20
+ return Promise.resolve();
21
+ }
22
+ delete(key) {
23
+ this.#secrets.delete(key);
24
+ return Promise.resolve();
25
+ }
26
+ }
27
+ //# sourceMappingURL=secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.js","sourceRoot":"","sources":["../../src/secret-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,8EAA8E;AAC9E,MAAM,OAAO,mBAAmB;IACrB,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE9C,GAAG,CAAC,GAAW;QACb,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAa;QAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import type { SecretStore } from './secret-store.js';
2
+ /** Non-secret identity safe to share (e.g. inside the pairing QR). */
3
+ export interface PublicIdentity {
4
+ macDeviceId: string;
5
+ /** Ed25519 identity public key as lowercase hex (32 bytes). */
6
+ macIdentityPublicKey: string;
7
+ }
8
+ export declare class SecureDeviceState {
9
+ #private;
10
+ constructor(store: SecretStore);
11
+ /** Load the persisted identity, or generate and persist a new one. */
12
+ loadOrCreate(): Promise<PublicIdentity>;
13
+ get identity(): PublicIdentity;
14
+ /** Sign a message with the Ed25519 identity key; returns lowercase hex. */
15
+ sign(message: Buffer | string): string;
16
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bridge cryptographic identity (Ed25519) — generation, persistence (via a
3
+ * {@link SecretStore}) and signing.
4
+ *
5
+ * Source: architecture/02a-system-architecture.md §5.8.3 (secure-device-state)
6
+ * and §5.9.1 (handshake signatures).
7
+ */
8
+ import { createPrivateKey, createPublicKey, generateKeyPairSync, randomUUID, sign as edSign, } from 'node:crypto';
9
+ const STORE_KEY = 'secure-device-state';
10
+ function publicKeyHexFromPrivate(privateKey) {
11
+ const publicJwk = createPublicKey(privateKey).export({ format: 'jwk' });
12
+ if (typeof publicJwk.x !== 'string') {
13
+ throw new Error('failed to derive Ed25519 public key');
14
+ }
15
+ return Buffer.from(publicJwk.x, 'base64url').toString('hex');
16
+ }
17
+ export class SecureDeviceState {
18
+ #store;
19
+ #privateKey;
20
+ #identity;
21
+ constructor(store) {
22
+ this.#store = store;
23
+ }
24
+ /** Load the persisted identity, or generate and persist a new one. */
25
+ async loadOrCreate() {
26
+ const raw = await this.#store.get(STORE_KEY);
27
+ if (raw) {
28
+ const stored = JSON.parse(raw);
29
+ this.#privateKey = createPrivateKey({ key: stored.privateKeyJwk, format: 'jwk' });
30
+ this.#identity = {
31
+ macDeviceId: stored.macDeviceId,
32
+ macIdentityPublicKey: publicKeyHexFromPrivate(this.#privateKey),
33
+ };
34
+ return this.#identity;
35
+ }
36
+ const { privateKey } = generateKeyPairSync('ed25519');
37
+ const macDeviceId = randomUUID();
38
+ const privateKeyJwk = privateKey.export({ format: 'jwk' });
39
+ const stored = { macDeviceId, privateKeyJwk };
40
+ await this.#store.set(STORE_KEY, JSON.stringify(stored));
41
+ this.#privateKey = privateKey;
42
+ this.#identity = {
43
+ macDeviceId,
44
+ macIdentityPublicKey: publicKeyHexFromPrivate(privateKey),
45
+ };
46
+ return this.#identity;
47
+ }
48
+ get identity() {
49
+ if (!this.#identity) {
50
+ throw new Error('SecureDeviceState.loadOrCreate() must be called first');
51
+ }
52
+ return this.#identity;
53
+ }
54
+ /** Sign a message with the Ed25519 identity key; returns lowercase hex. */
55
+ sign(message) {
56
+ if (!this.#privateKey) {
57
+ throw new Error('SecureDeviceState.loadOrCreate() must be called first');
58
+ }
59
+ const data = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message;
60
+ return edSign(null, data, this.#privateKey).toString('hex');
61
+ }
62
+ }
63
+ //# sourceMappingURL=secure-device-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secure-device-state.js","sourceRoot":"","sources":["../../src/secure-device-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,IAAI,IAAI,MAAM,GAEf,MAAM,aAAa,CAAC;AAGrB,MAAM,SAAS,GAAG,qBAAqB,CAAC;AAexC,SAAS,uBAAuB,CAAC,UAAqB;IACpD,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAmB,CAAC;IAC1F,IAAI,OAAO,SAAS,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,OAAO,iBAAiB;IACnB,MAAM,CAAc;IAC7B,WAAW,CAAwB;IACnC,SAAS,CAA6B;IAEtC,YAAY,KAAkB;QAC5B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;YACjD,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YAClF,IAAI,CAAC,SAAS,GAAG;gBACf,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,oBAAoB,EAAE,uBAAuB,CAAC,IAAI,CAAC,WAAW,CAAC;aAChE,CAAC;YACF,OAAO,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,UAAU,EAAE,CAAC;QACjC,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAA4B,CAAC;QACtF,MAAM,MAAM,GAAmB,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;QAC9D,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAEzD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG;YACf,WAAW;YACX,oBAAoB,EAAE,uBAAuB,CAAC,UAAU,CAAC;SAC1D,CAAC;QACF,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,IAAI,QAAQ;QACV,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,2EAA2E;IAC3E,IAAI,CAAC,OAAwB;QAC3B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACnF,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,57 @@
1
+ export type ServicePlatform = 'win32' | 'darwin' | 'linux';
2
+ export interface ServiceEnv {
3
+ platform: ServicePlatform;
4
+ /** Absolute node executable (e.g. `process.execPath`). */
5
+ execPath: string;
6
+ /** Absolute path to the bridge CLI entry (`cli.js`). */
7
+ cliPath: string;
8
+ /** User home directory. */
9
+ home: string;
10
+ /** `XDG_CONFIG_HOME` when set (Linux only). */
11
+ xdgConfigHome?: string;
12
+ /** `%APPDATA%` (Windows only) — used for the Startup-folder fallback. */
13
+ appData?: string;
14
+ }
15
+ export interface ServiceCommand {
16
+ argv: string[];
17
+ /** When true, a non-zero exit is tolerated (e.g. unloading a not-loaded agent). */
18
+ ignoreFailure?: boolean;
19
+ }
20
+ export interface ServicePlan {
21
+ platform: ServicePlatform;
22
+ /** Human-facing task/agent/unit identifier. */
23
+ label: string;
24
+ /** Directories to ensure before writing the file / running commands. */
25
+ dirs: string[];
26
+ /** A config file to write (plist / unit), if any. */
27
+ file?: {
28
+ path: string;
29
+ content: string;
30
+ };
31
+ install: ServiceCommand[];
32
+ uninstall: ServiceCommand[];
33
+ /** File removed on uninstall. */
34
+ removeFile?: string;
35
+ note: string;
36
+ uninstallNote: string;
37
+ }
38
+ /** Build the platform-specific autostart plan. Pure — no side effects. */
39
+ export declare function buildServicePlan(env: ServiceEnv): ServicePlan;
40
+ /**
41
+ * Windows fallback when Task Scheduler is unavailable (e.g. policy/restricted
42
+ * accounts return "Access denied" on `schtasks /Create`): a hidden launcher in the
43
+ * user's Startup folder. Writing there needs no special permission, and the `.vbs`
44
+ * launches the bridge with no console window. Pure — no side effects.
45
+ */
46
+ export declare function buildWindowsStartupPlan(env: ServiceEnv): ServicePlan;
47
+ /** Resolve the autostart environment for the current process. */
48
+ export declare function currentServiceEnv(cliPath: string): ServiceEnv;
49
+ /** True for platforms with a supported autostart mechanism. */
50
+ export declare function isServicePlatformSupported(platform: NodeJS.Platform): platform is ServicePlatform;
51
+ /**
52
+ * Execute the install plan: ensure dirs, write the config file, run the commands.
53
+ * On Windows, falls back to the Startup-folder launcher if Task Scheduler fails.
54
+ */
55
+ export declare function installService(env: ServiceEnv): Promise<ServicePlan>;
56
+ /** Execute the uninstall plan: run the commands, then remove the file(s). */
57
+ export declare function uninstallService(env: ServiceEnv): Promise<ServicePlan>;
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Autostart installer: registers the bridge to start at user logon, **as the
3
+ * logged-in user and never elevated** (the Ed25519 identity already lives in the
4
+ * per-user OS keychain, so no root/SYSTEM is needed).
5
+ *
6
+ * - Windows: a Task Scheduler task `At log on` with run level LIMITED.
7
+ * - macOS: a per-user LaunchAgent (`RunAtLoad` + `KeepAlive`).
8
+ * - Linux: a systemd `--user` unit (pair with `loginctl enable-linger`).
9
+ *
10
+ * The bridge is launched as `<node> <cli.js> start`, which works both for a global
11
+ * npm install and a dev checkout. {@link buildServicePlan} is pure (no side
12
+ * effects) so it can be unit-tested; {@link installService}/{@link uninstallService}
13
+ * execute the plan with `execFile` (no shell).
14
+ *
15
+ * This supersedes the manual `scripts/install-service-*` files (kept as reference).
16
+ */
17
+ import { execFile } from 'node:child_process';
18
+ import { mkdir, writeFile, rm } from 'node:fs/promises';
19
+ import { dirname } from 'node:path';
20
+ import { promisify } from 'node:util';
21
+ import { join } from 'node:path';
22
+ const run = promisify(execFile);
23
+ const TASK_NAME = 'UxnanBridge';
24
+ const LAUNCH_LABEL = 'dev.luisgamas.bridge';
25
+ const UNIT_NAME = 'uxnan-bridge.service';
26
+ /** Build the platform-specific autostart plan. Pure — no side effects. */
27
+ export function buildServicePlan(env) {
28
+ const { platform, execPath, cliPath, home } = env;
29
+ if (platform === 'win32') {
30
+ // schtasks `/TR` takes the whole command as ONE string; quote each path.
31
+ const tr = `"${execPath}" "${cliPath}" start`;
32
+ return {
33
+ platform,
34
+ label: TASK_NAME,
35
+ dirs: [],
36
+ install: [
37
+ {
38
+ argv: [
39
+ 'schtasks',
40
+ '/Create',
41
+ '/TN',
42
+ TASK_NAME,
43
+ '/TR',
44
+ tr,
45
+ '/SC',
46
+ 'ONLOGON',
47
+ '/RL',
48
+ 'LIMITED',
49
+ '/F',
50
+ ],
51
+ },
52
+ ],
53
+ uninstall: [{ argv: ['schtasks', '/Delete', '/TN', TASK_NAME, '/F'] }],
54
+ note: `Registered scheduled task '${TASK_NAME}' (starts at logon). Start now: schtasks /Run /TN ${TASK_NAME}`,
55
+ uninstallNote: `Removed scheduled task '${TASK_NAME}'.`,
56
+ };
57
+ }
58
+ if (platform === 'darwin') {
59
+ const plistPath = join(home, 'Library', 'LaunchAgents', `${LAUNCH_LABEL}.plist`);
60
+ const logDir = join(home, '.uxnan', 'logs');
61
+ return {
62
+ platform,
63
+ label: LAUNCH_LABEL,
64
+ dirs: [dirname(plistPath), logDir],
65
+ file: { path: plistPath, content: plistContent([execPath, cliPath, 'start'], logDir) },
66
+ install: [
67
+ { argv: ['launchctl', 'unload', plistPath], ignoreFailure: true },
68
+ { argv: ['launchctl', 'load', plistPath] },
69
+ ],
70
+ uninstall: [{ argv: ['launchctl', 'unload', plistPath], ignoreFailure: true }],
71
+ removeFile: plistPath,
72
+ note: `Loaded LaunchAgent ${LAUNCH_LABEL} (starts at login).`,
73
+ uninstallNote: `Unloaded and removed LaunchAgent ${LAUNCH_LABEL}.`,
74
+ };
75
+ }
76
+ // linux — systemd --user
77
+ const unitDir = join(env.xdgConfigHome ?? join(home, '.config'), 'systemd', 'user');
78
+ const unitPath = join(unitDir, UNIT_NAME);
79
+ return {
80
+ platform,
81
+ label: UNIT_NAME,
82
+ dirs: [unitDir],
83
+ file: { path: unitPath, content: systemdUnit(`${execPath} ${cliPath} start`) },
84
+ install: [
85
+ { argv: ['systemctl', '--user', 'daemon-reload'] },
86
+ { argv: ['systemctl', '--user', 'enable', '--now', UNIT_NAME] },
87
+ ],
88
+ uninstall: [
89
+ { argv: ['systemctl', '--user', 'disable', '--now', UNIT_NAME], ignoreFailure: true },
90
+ ],
91
+ removeFile: unitPath,
92
+ note: `Enabled systemd user unit ${UNIT_NAME}. Tip: 'loginctl enable-linger $USER' keeps it running after logout.`,
93
+ uninstallNote: `Disabled and removed systemd user unit ${UNIT_NAME}.`,
94
+ };
95
+ }
96
+ /**
97
+ * Windows fallback when Task Scheduler is unavailable (e.g. policy/restricted
98
+ * accounts return "Access denied" on `schtasks /Create`): a hidden launcher in the
99
+ * user's Startup folder. Writing there needs no special permission, and the `.vbs`
100
+ * launches the bridge with no console window. Pure — no side effects.
101
+ */
102
+ export function buildWindowsStartupPlan(env) {
103
+ const appData = env.appData ?? join(env.home, 'AppData', 'Roaming');
104
+ const startupDir = join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
105
+ const vbsPath = join(startupDir, 'uxnan-bridge.vbs');
106
+ return {
107
+ platform: 'win32',
108
+ label: 'uxnan-bridge.vbs',
109
+ dirs: [startupDir],
110
+ file: { path: vbsPath, content: vbsLauncher(env.execPath, env.cliPath) },
111
+ install: [],
112
+ uninstall: [],
113
+ removeFile: vbsPath,
114
+ note: `Added a hidden Startup launcher (${vbsPath}); starts at next logon.`,
115
+ uninstallNote: `Removed the Startup launcher ${vbsPath}.`,
116
+ };
117
+ }
118
+ /** Resolve the autostart environment for the current process. */
119
+ export function currentServiceEnv(cliPath) {
120
+ const env = {
121
+ platform: process.platform,
122
+ execPath: process.execPath,
123
+ cliPath,
124
+ home: requireHome(),
125
+ };
126
+ const xdg = process.env['XDG_CONFIG_HOME'];
127
+ if (xdg)
128
+ env.xdgConfigHome = xdg;
129
+ const appData = process.env['APPDATA'];
130
+ if (appData)
131
+ env.appData = appData;
132
+ return env;
133
+ }
134
+ /** True for platforms with a supported autostart mechanism. */
135
+ export function isServicePlatformSupported(platform) {
136
+ return platform === 'win32' || platform === 'darwin' || platform === 'linux';
137
+ }
138
+ /**
139
+ * Execute the install plan: ensure dirs, write the config file, run the commands.
140
+ * On Windows, falls back to the Startup-folder launcher if Task Scheduler fails.
141
+ */
142
+ export async function installService(env) {
143
+ if (env.platform === 'win32') {
144
+ const taskPlan = buildServicePlan(env);
145
+ try {
146
+ await runCommands(taskPlan.install);
147
+ return taskPlan;
148
+ }
149
+ catch {
150
+ return applyFilePlan(buildWindowsStartupPlan(env));
151
+ }
152
+ }
153
+ const plan = buildServicePlan(env);
154
+ for (const dir of plan.dirs)
155
+ await mkdir(dir, { recursive: true });
156
+ if (plan.file)
157
+ await writeFile(plan.file.path, plan.file.content, 'utf-8');
158
+ await runCommands(plan.install);
159
+ return plan;
160
+ }
161
+ /** Execute the uninstall plan: run the commands, then remove the file(s). */
162
+ export async function uninstallService(env) {
163
+ if (env.platform === 'win32') {
164
+ // Remove whichever method was used: the scheduled task and/or the Startup launcher.
165
+ await runCommands(buildServicePlan(env).uninstall.map((c) => ({ ...c, ignoreFailure: true })));
166
+ const startup = buildWindowsStartupPlan(env);
167
+ if (startup.removeFile)
168
+ await rm(startup.removeFile, { force: true });
169
+ return {
170
+ ...startup,
171
+ uninstallNote: 'Removed the autostart entry (scheduled task and/or Startup launcher).',
172
+ };
173
+ }
174
+ const plan = buildServicePlan(env);
175
+ await runCommands(plan.uninstall);
176
+ if (plan.removeFile)
177
+ await rm(plan.removeFile, { force: true });
178
+ return plan;
179
+ }
180
+ async function applyFilePlan(plan) {
181
+ for (const dir of plan.dirs)
182
+ await mkdir(dir, { recursive: true });
183
+ if (plan.file)
184
+ await writeFile(plan.file.path, plan.file.content, 'utf-8');
185
+ await runCommands(plan.install);
186
+ return plan;
187
+ }
188
+ async function runCommands(commands) {
189
+ for (const { argv, ignoreFailure } of commands) {
190
+ try {
191
+ await run(argv[0], argv.slice(1), { windowsHide: true });
192
+ }
193
+ catch (err) {
194
+ if (!ignoreFailure)
195
+ throw err;
196
+ }
197
+ }
198
+ }
199
+ function requireHome() {
200
+ const home = process.env['HOME'] ?? process.env['USERPROFILE'];
201
+ if (!home)
202
+ throw new Error('cannot resolve the user home directory');
203
+ return home;
204
+ }
205
+ function plistContent(programArgs, logDir) {
206
+ const args = programArgs.map((a) => ` <string>${xmlEscape(a)}</string>`).join('\n');
207
+ return `<?xml version="1.0" encoding="UTF-8"?>
208
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
209
+ <plist version="1.0">
210
+ <dict>
211
+ <key>Label</key><string>${LAUNCH_LABEL}</string>
212
+ <key>ProgramArguments</key>
213
+ <array>
214
+ ${args}
215
+ </array>
216
+ <key>RunAtLoad</key><true/>
217
+ <key>KeepAlive</key><true/>
218
+ <key>StandardOutPath</key><string>${xmlEscape(join(logDir, 'launchd.out.log'))}</string>
219
+ <key>StandardErrorPath</key><string>${xmlEscape(join(logDir, 'launchd.err.log'))}</string>
220
+ </dict>
221
+ </plist>
222
+ `;
223
+ }
224
+ function systemdUnit(execStart) {
225
+ return `[Unit]
226
+ Description=Uxnan Bridge daemon
227
+ After=network-online.target
228
+ Wants=network-online.target
229
+
230
+ [Service]
231
+ ExecStart=${execStart}
232
+ Restart=on-failure
233
+ RestartSec=5
234
+
235
+ [Install]
236
+ WantedBy=default.target
237
+ `;
238
+ }
239
+ /** A `.vbs` that launches the bridge with NO console window (`WScript.Shell.Run`). */
240
+ function vbsLauncher(execPath, cliPath) {
241
+ const runtimeCmd = `"${execPath}" "${cliPath}" start`;
242
+ // VBScript string literal: each `"` is doubled.
243
+ const literal = `"${runtimeCmd.replace(/"/g, '""')}"`;
244
+ // window style 0 = hidden, waitOnReturn = False (don't block).
245
+ return `Set sh = CreateObject("WScript.Shell")\r\nsh.Run ${literal}, 0, False\r\n`;
246
+ }
247
+ function xmlEscape(value) {
248
+ return value
249
+ .replace(/&/g, '&amp;')
250
+ .replace(/</g, '&lt;')
251
+ .replace(/>/g, '&gt;')
252
+ .replace(/"/g, '&quot;');
253
+ }
254
+ //# sourceMappingURL=service-installer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-installer.js","sourceRoot":"","sources":["../../src/service-installer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * In-memory registry of active mobile sessions.
3
+ *
4
+ * Source: architecture/02a-system-architecture.md §5.8.2 (session-state).
5
+ */
6
+ import type { ConnectedPhone } from '@uxnan/shared';
7
+ export declare class SessionState {
8
+ #private;
9
+ add(phone: ConnectedPhone): void;
10
+ remove(deviceId: string): boolean;
11
+ get(deviceId: string): ConnectedPhone | undefined;
12
+ list(): ConnectedPhone[];
13
+ get count(): number;
14
+ }
@@ -0,0 +1,19 @@
1
+ export class SessionState {
2
+ #sessions = new Map();
3
+ add(phone) {
4
+ this.#sessions.set(phone.deviceId, phone);
5
+ }
6
+ remove(deviceId) {
7
+ return this.#sessions.delete(deviceId);
8
+ }
9
+ get(deviceId) {
10
+ return this.#sessions.get(deviceId);
11
+ }
12
+ list() {
13
+ return [...this.#sessions.values()];
14
+ }
15
+ get count() {
16
+ return this.#sessions.size;
17
+ }
18
+ }
19
+ //# sourceMappingURL=session-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../src/session-state.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,YAAY;IACd,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEvD,GAAG,CAAC,KAAqB;QACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,CAAC,QAAgB;QACrB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,GAAG,CAAC,QAAgB;QAClB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Bridge-side E2EE primitives, mirroring the mobile app's audited
3
+ * `infrastructure/crypto/` exactly (architecture/02a §5.9.1). No cryptographic
4
+ * variants: X25519 + HKDF-SHA256 → AES-256-GCM; Ed25519 transcript signatures.
5
+ *
6
+ * All byte fields cross the wire as lowercase hex, except AES ciphertext/tag
7
+ * which are base64 (see {@link SecureEnvelope}).
8
+ */
9
+ import { type KeyObject } from 'node:crypto';
10
+ export interface EphemeralKeyPair {
11
+ /** X25519 public key as lowercase hex (32 bytes). */
12
+ publicKeyHex: string;
13
+ privateKey: KeyObject;
14
+ }
15
+ /** Generate a fresh X25519 ephemeral key pair for a single handshake. */
16
+ export declare function generateEphemeralKeyPair(): EphemeralKeyPair;
17
+ export interface DeriveSessionKeyOptions {
18
+ /** Our X25519 ephemeral private key. */
19
+ privateKey: KeyObject;
20
+ /** Peer X25519 ephemeral public key (hex). */
21
+ peerPublicHex: string;
22
+ clientNonceHex: string;
23
+ serverNonceHex: string;
24
+ }
25
+ /**
26
+ * Derive the 32-byte AES-256 session key.
27
+ * `salt = clientNonce || serverNonce` (raw bytes), `info = "uxnan-e2ee-v1"`.
28
+ */
29
+ export declare function deriveSessionKey(options: DeriveSessionKeyOptions): Buffer;
30
+ /** Cryptographically secure random bytes as lowercase hex. */
31
+ export declare function randomHex(byteLength: number): string;
32
+ /** Verify an Ed25519 signature (hex) over `message` against a public key (hex). */
33
+ export declare function verifyEd25519(message: Buffer, signatureHex: string, publicKeyHex: string): boolean;
34
+ export interface AesGcmParts {
35
+ /** Per-message nonce (hex, 12 bytes). */
36
+ nonceHex: string;
37
+ /** Ciphertext (base64). */
38
+ ciphertextB64: string;
39
+ /** GCM auth tag (base64, 16 bytes). */
40
+ tagB64: string;
41
+ }
42
+ export declare function aesGcmEncrypt(key: Buffer, plaintext: Buffer): AesGcmParts;
43
+ export declare function aesGcmDecrypt(key: Buffer, parts: AesGcmParts): Buffer;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Bridge-side E2EE primitives, mirroring the mobile app's audited
3
+ * `infrastructure/crypto/` exactly (architecture/02a §5.9.1). No cryptographic
4
+ * variants: X25519 + HKDF-SHA256 → AES-256-GCM; Ed25519 transcript signatures.
5
+ *
6
+ * All byte fields cross the wire as lowercase hex, except AES ciphertext/tag
7
+ * which are base64 (see {@link SecureEnvelope}).
8
+ */
9
+ import { createCipheriv, createDecipheriv, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, verify, } from 'node:crypto';
10
+ import { HKDF_INFO_TAG } from '@uxnan/shared';
11
+ /** Generate a fresh X25519 ephemeral key pair for a single handshake. */
12
+ export function generateEphemeralKeyPair() {
13
+ const { publicKey, privateKey } = generateKeyPairSync('x25519');
14
+ const jwk = publicKey.export({ format: 'jwk' });
15
+ if (typeof jwk.x !== 'string')
16
+ throw new Error('failed to export X25519 public key');
17
+ return { publicKeyHex: Buffer.from(jwk.x, 'base64url').toString('hex'), privateKey };
18
+ }
19
+ /**
20
+ * Derive the 32-byte AES-256 session key.
21
+ * `salt = clientNonce || serverNonce` (raw bytes), `info = "uxnan-e2ee-v1"`.
22
+ */
23
+ export function deriveSessionKey(options) {
24
+ const peerPublic = createPublicKey({
25
+ key: {
26
+ kty: 'OKP',
27
+ crv: 'X25519',
28
+ x: Buffer.from(options.peerPublicHex, 'hex').toString('base64url'),
29
+ },
30
+ format: 'jwk',
31
+ });
32
+ const shared = diffieHellman({ privateKey: options.privateKey, publicKey: peerPublic });
33
+ const salt = Buffer.concat([
34
+ Buffer.from(options.clientNonceHex, 'hex'),
35
+ Buffer.from(options.serverNonceHex, 'hex'),
36
+ ]);
37
+ return Buffer.from(hkdfSync('sha256', shared, salt, Buffer.from(HKDF_INFO_TAG, 'utf-8'), 32));
38
+ }
39
+ /** Cryptographically secure random bytes as lowercase hex. */
40
+ export function randomHex(byteLength) {
41
+ return randomBytes(byteLength).toString('hex');
42
+ }
43
+ /** Verify an Ed25519 signature (hex) over `message` against a public key (hex). */
44
+ export function verifyEd25519(message, signatureHex, publicKeyHex) {
45
+ try {
46
+ const publicKey = createPublicKey({
47
+ key: {
48
+ kty: 'OKP',
49
+ crv: 'Ed25519',
50
+ x: Buffer.from(publicKeyHex, 'hex').toString('base64url'),
51
+ },
52
+ format: 'jwk',
53
+ });
54
+ return verify(null, message, publicKey, Buffer.from(signatureHex, 'hex'));
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ export function aesGcmEncrypt(key, plaintext) {
61
+ const nonce = randomBytes(12);
62
+ const cipher = createCipheriv('aes-256-gcm', key, nonce);
63
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
64
+ return {
65
+ nonceHex: nonce.toString('hex'),
66
+ ciphertextB64: ciphertext.toString('base64'),
67
+ tagB64: cipher.getAuthTag().toString('base64'),
68
+ };
69
+ }
70
+ export function aesGcmDecrypt(key, parts) {
71
+ const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(parts.nonceHex, 'hex'));
72
+ decipher.setAuthTag(Buffer.from(parts.tagB64, 'base64'));
73
+ return Buffer.concat([
74
+ decipher.update(Buffer.from(parts.ciphertextB64, 'base64')),
75
+ decipher.final(),
76
+ ]);
77
+ }
78
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/transport/crypto.ts"],"names":[],"mappings":"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"}