@joe-broadhead/messaging 0.0.0-stage → 0.7.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.
Files changed (273) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +12 -0
  4. package/README.md +36 -2
  5. package/dist/bin/messaging-mcp.js +26524 -0
  6. package/dist/bin/messaging-relay.js +19768 -0
  7. package/dist/bin/messaging.js +15465 -0
  8. package/dist/bin/messagingd.js +29449 -0
  9. package/dist/lib/core-testing.js +1 -0
  10. package/dist/lib/core.js +1 -0
  11. package/dist/lib/ipc-testing.js +1 -0
  12. package/dist/lib/ipc.js +1 -0
  13. package/dist/lib/modules/core/address.js +42 -0
  14. package/dist/lib/modules/core/agent/operations.js +595 -0
  15. package/dist/lib/modules/core/agent/present.js +244 -0
  16. package/dist/lib/modules/core/agent/text.js +79 -0
  17. package/dist/lib/modules/core/agent-tools.js +477 -0
  18. package/dist/lib/modules/core/b64u.js +20 -0
  19. package/dist/lib/modules/core/cards.js +55 -0
  20. package/dist/lib/modules/core/check.js +20 -0
  21. package/dist/lib/modules/core/consent.js +199 -0
  22. package/dist/lib/modules/core/contact-card.js +74 -0
  23. package/dist/lib/modules/core/conversation/context.js +80 -0
  24. package/dist/lib/modules/core/conversation/event.js +100 -0
  25. package/dist/lib/modules/core/conversation/ids.js +38 -0
  26. package/dist/lib/modules/core/conversation/link.js +35 -0
  27. package/dist/lib/modules/core/conversation/log.js +766 -0
  28. package/dist/lib/modules/core/conversation/ratchet.js +243 -0
  29. package/dist/lib/modules/core/conversation/seal.js +198 -0
  30. package/dist/lib/modules/core/delegation.js +174 -0
  31. package/dist/lib/modules/core/directory/device-log.js +258 -0
  32. package/dist/lib/modules/core/directory/epoch.js +103 -0
  33. package/dist/lib/modules/core/directory/handle.js +29 -0
  34. package/dist/lib/modules/core/directory/jelly-device.js +204 -0
  35. package/dist/lib/modules/core/directory/jelly-issuer.js +12 -0
  36. package/dist/lib/modules/core/directory/jelly-token.js +305 -0
  37. package/dist/lib/modules/core/directory/keys-statement.js +43 -0
  38. package/dist/lib/modules/core/directory/link.js +81 -0
  39. package/dist/lib/modules/core/directory/merkle-log.js +250 -0
  40. package/dist/lib/modules/core/directory/person-view.js +197 -0
  41. package/dist/lib/modules/core/directory/person.js +40 -0
  42. package/dist/lib/modules/core/directory/realm.js +59 -0
  43. package/dist/lib/modules/core/directory/smt.js +266 -0
  44. package/dist/lib/modules/core/envelope.js +320 -0
  45. package/dist/lib/modules/core/hash.js +5 -0
  46. package/dist/lib/modules/core/head.js +27 -0
  47. package/dist/lib/modules/core/identity.js +71 -0
  48. package/dist/lib/modules/core/index.js +53 -0
  49. package/dist/lib/modules/core/jcs.js +96 -0
  50. package/dist/lib/modules/core/jelly-host-double.js +35 -0
  51. package/dist/lib/modules/core/jelly-host.js +211 -0
  52. package/dist/lib/modules/core/keys.js +79 -0
  53. package/dist/lib/modules/core/peer-text.js +563 -0
  54. package/dist/lib/modules/core/realm.js +1 -0
  55. package/dist/lib/modules/core/roster.js +454 -0
  56. package/dist/lib/modules/core/schema/a2a.js +85 -0
  57. package/dist/lib/modules/core/schema/common.js +310 -0
  58. package/dist/lib/modules/core/schema/consent.js +120 -0
  59. package/dist/lib/modules/core/schema/conversation.js +292 -0
  60. package/dist/lib/modules/core/schema/delegation.js +58 -0
  61. package/dist/lib/modules/core/schema/directory-frames.js +270 -0
  62. package/dist/lib/modules/core/schema/directory.js +169 -0
  63. package/dist/lib/modules/core/schema/envelope.js +211 -0
  64. package/dist/lib/modules/core/schema/errors.js +104 -0
  65. package/dist/lib/modules/core/schema/index.js +16 -0
  66. package/dist/lib/modules/core/schema/ipc/consent.js +181 -0
  67. package/dist/lib/modules/core/schema/ipc/device.js +276 -0
  68. package/dist/lib/modules/core/schema/ipc/jelly.js +189 -0
  69. package/dist/lib/modules/core/schema/ipc/shapes.js +185 -0
  70. package/dist/lib/modules/core/schema/ipc/surfaces.js +223 -0
  71. package/dist/lib/modules/core/schema/ipc.js +451 -0
  72. package/dist/lib/modules/core/schema/jelly.js +122 -0
  73. package/dist/lib/modules/core/schema/realm.js +27 -0
  74. package/dist/lib/modules/core/schema/relay.js +112 -0
  75. package/dist/lib/modules/core/schema/roster.js +126 -0
  76. package/dist/lib/modules/core/sign.js +43 -0
  77. package/dist/lib/modules/core/task.js +37 -0
  78. package/dist/lib/modules/core/testing.js +21 -0
  79. package/dist/lib/modules/core/ulid.js +36 -0
  80. package/dist/lib/modules/core/version.js +6 -0
  81. package/dist/lib/modules/core/wire.js +255 -0
  82. package/dist/lib/modules/ipc/client.js +289 -0
  83. package/dist/lib/modules/ipc/framing.js +33 -0
  84. package/dist/lib/modules/ipc/index.js +6 -0
  85. package/dist/lib/modules/ipc/paths.js +102 -0
  86. package/dist/lib/modules/ipc/pipes.js +65 -0
  87. package/dist/lib/modules/ipc/rpc.js +8 -0
  88. package/dist/lib/modules/ipc/server.js +317 -0
  89. package/dist/lib/modules/ipc/testing.js +145 -0
  90. package/dist/lib/modules/ipc/trust.js +166 -0
  91. package/dist/lib/modules/ipc/windows-acl.js +275 -0
  92. package/dist/lib/modules/vendor/zod/index.js +7 -0
  93. package/dist/lib/modules/vendor/zod/v4/classic/checks.js +1 -0
  94. package/dist/lib/modules/vendor/zod/v4/classic/coerce.js +17 -0
  95. package/dist/lib/modules/vendor/zod/v4/classic/compat.js +31 -0
  96. package/dist/lib/modules/vendor/zod/v4/classic/deep-partial.js +13 -0
  97. package/dist/lib/modules/vendor/zod/v4/classic/errors.js +57 -0
  98. package/dist/lib/modules/vendor/zod/v4/classic/external.js +17 -0
  99. package/dist/lib/modules/vendor/zod/v4/classic/from-json-schema.js +881 -0
  100. package/dist/lib/modules/vendor/zod/v4/classic/in-out.js +36 -0
  101. package/dist/lib/modules/vendor/zod/v4/classic/iso.js +15 -0
  102. package/dist/lib/modules/vendor/zod/v4/classic/parse.js +16 -0
  103. package/dist/lib/modules/vendor/zod/v4/classic/schemas.js +1535 -0
  104. package/dist/lib/modules/vendor/zod/v4/core/api.js +1107 -0
  105. package/dist/lib/modules/vendor/zod/v4/core/checks.js +516 -0
  106. package/dist/lib/modules/vendor/zod/v4/core/compile.js +1866 -0
  107. package/dist/lib/modules/vendor/zod/v4/core/core.js +141 -0
  108. package/dist/lib/modules/vendor/zod/v4/core/doc.js +38 -0
  109. package/dist/lib/modules/vendor/zod/v4/core/errors.js +267 -0
  110. package/dist/lib/modules/vendor/zod/v4/core/index.js +19 -0
  111. package/dist/lib/modules/vendor/zod/v4/core/json-schema-generator.js +99 -0
  112. package/dist/lib/modules/vendor/zod/v4/core/json-schema-processors.js +843 -0
  113. package/dist/lib/modules/vendor/zod/v4/core/json-schema.js +1 -0
  114. package/dist/lib/modules/vendor/zod/v4/core/memoizer.js +311 -0
  115. package/dist/lib/modules/vendor/zod/v4/core/parse.js +175 -0
  116. package/dist/lib/modules/vendor/zod/v4/core/regexes.js +156 -0
  117. package/dist/lib/modules/vendor/zod/v4/core/registries.js +51 -0
  118. package/dist/lib/modules/vendor/zod/v4/core/schemas.js +2636 -0
  119. package/dist/lib/modules/vendor/zod/v4/core/to-json-schema.js +651 -0
  120. package/dist/lib/modules/vendor/zod/v4/core/util.js +873 -0
  121. package/dist/lib/modules/vendor/zod/v4/core/versions.js +5 -0
  122. package/dist/lib/modules/vendor/zod/v4/core/visit.js +169 -0
  123. package/dist/lib/modules/vendor/zod/v4/locales/ar.js +111 -0
  124. package/dist/lib/modules/vendor/zod/v4/locales/az.js +110 -0
  125. package/dist/lib/modules/vendor/zod/v4/locales/be.js +168 -0
  126. package/dist/lib/modules/vendor/zod/v4/locales/bg.js +125 -0
  127. package/dist/lib/modules/vendor/zod/v4/locales/bn.js +113 -0
  128. package/dist/lib/modules/vendor/zod/v4/locales/ca.js +112 -0
  129. package/dist/lib/modules/vendor/zod/v4/locales/ckb.js +133 -0
  130. package/dist/lib/modules/vendor/zod/v4/locales/cs.js +116 -0
  131. package/dist/lib/modules/vendor/zod/v4/locales/da.js +120 -0
  132. package/dist/lib/modules/vendor/zod/v4/locales/de.js +113 -0
  133. package/dist/lib/modules/vendor/zod/v4/locales/el.js +112 -0
  134. package/dist/lib/modules/vendor/zod/v4/locales/en.js +125 -0
  135. package/dist/lib/modules/vendor/zod/v4/locales/eo.js +114 -0
  136. package/dist/lib/modules/vendor/zod/v4/locales/es.js +136 -0
  137. package/dist/lib/modules/vendor/zod/v4/locales/fa.js +119 -0
  138. package/dist/lib/modules/vendor/zod/v4/locales/fi.js +117 -0
  139. package/dist/lib/modules/vendor/zod/v4/locales/fr-CA.js +112 -0
  140. package/dist/lib/modules/vendor/zod/v4/locales/fr.js +129 -0
  141. package/dist/lib/modules/vendor/zod/v4/locales/gu.js +113 -0
  142. package/dist/lib/modules/vendor/zod/v4/locales/he.js +221 -0
  143. package/dist/lib/modules/vendor/zod/v4/locales/hi.js +111 -0
  144. package/dist/lib/modules/vendor/zod/v4/locales/hr.js +126 -0
  145. package/dist/lib/modules/vendor/zod/v4/locales/hu.js +113 -0
  146. package/dist/lib/modules/vendor/zod/v4/locales/hy.js +158 -0
  147. package/dist/lib/modules/vendor/zod/v4/locales/id.js +111 -0
  148. package/dist/lib/modules/vendor/zod/v4/locales/index.js +63 -0
  149. package/dist/lib/modules/vendor/zod/v4/locales/is.js +114 -0
  150. package/dist/lib/modules/vendor/zod/v4/locales/it.js +113 -0
  151. package/dist/lib/modules/vendor/zod/v4/locales/ja.js +112 -0
  152. package/dist/lib/modules/vendor/zod/v4/locales/ka.js +117 -0
  153. package/dist/lib/modules/vendor/zod/v4/locales/kh.js +5 -0
  154. package/dist/lib/modules/vendor/zod/v4/locales/km.js +115 -0
  155. package/dist/lib/modules/vendor/zod/v4/locales/kn.js +116 -0
  156. package/dist/lib/modules/vendor/zod/v4/locales/ko.js +116 -0
  157. package/dist/lib/modules/vendor/zod/v4/locales/lt.js +207 -0
  158. package/dist/lib/modules/vendor/zod/v4/locales/mk.js +114 -0
  159. package/dist/lib/modules/vendor/zod/v4/locales/ms.js +112 -0
  160. package/dist/lib/modules/vendor/zod/v4/locales/ne.js +111 -0
  161. package/dist/lib/modules/vendor/zod/v4/locales/nl.js +115 -0
  162. package/dist/lib/modules/vendor/zod/v4/locales/nn.js +113 -0
  163. package/dist/lib/modules/vendor/zod/v4/locales/no.js +113 -0
  164. package/dist/lib/modules/vendor/zod/v4/locales/ota.js +114 -0
  165. package/dist/lib/modules/vendor/zod/v4/locales/pl.js +114 -0
  166. package/dist/lib/modules/vendor/zod/v4/locales/ps.js +119 -0
  167. package/dist/lib/modules/vendor/zod/v4/locales/pt-BR.js +143 -0
  168. package/dist/lib/modules/vendor/zod/v4/locales/pt.js +142 -0
  169. package/dist/lib/modules/vendor/zod/v4/locales/ro.js +122 -0
  170. package/dist/lib/modules/vendor/zod/v4/locales/ru.js +168 -0
  171. package/dist/lib/modules/vendor/zod/v4/locales/sk.js +116 -0
  172. package/dist/lib/modules/vendor/zod/v4/locales/sl.js +114 -0
  173. package/dist/lib/modules/vendor/zod/v4/locales/sv.js +115 -0
  174. package/dist/lib/modules/vendor/zod/v4/locales/ta.js +115 -0
  175. package/dist/lib/modules/vendor/zod/v4/locales/tg.js +117 -0
  176. package/dist/lib/modules/vendor/zod/v4/locales/th.js +115 -0
  177. package/dist/lib/modules/vendor/zod/v4/locales/tk.js +107 -0
  178. package/dist/lib/modules/vendor/zod/v4/locales/tr.js +110 -0
  179. package/dist/lib/modules/vendor/zod/v4/locales/ua.js +5 -0
  180. package/dist/lib/modules/vendor/zod/v4/locales/uk.js +113 -0
  181. package/dist/lib/modules/vendor/zod/v4/locales/ur.js +115 -0
  182. package/dist/lib/modules/vendor/zod/v4/locales/uz.js +113 -0
  183. package/dist/lib/modules/vendor/zod/v4/locales/vi.js +113 -0
  184. package/dist/lib/modules/vendor/zod/v4/locales/yo.js +112 -0
  185. package/dist/lib/modules/vendor/zod/v4/locales/zh-CN.js +114 -0
  186. package/dist/lib/modules/vendor/zod/v4/locales/zh-TW.js +112 -0
  187. package/dist/lib/testing-jelly-id.js +1219 -0
  188. package/dist/lib/zod.js +1 -0
  189. package/dist/spec/messaging-v1.md +3744 -0
  190. package/dist/third-party-notices.md +239 -0
  191. package/dist/third-party.json +88 -0
  192. package/dist/types/core/address.d.ts +16 -0
  193. package/dist/types/core/agent/operations.d.ts +129 -0
  194. package/dist/types/core/agent/present.d.ts +57 -0
  195. package/dist/types/core/agent/text.d.ts +62 -0
  196. package/dist/types/core/agent-tools.d.ts +966 -0
  197. package/dist/types/core/b64u.d.ts +6 -0
  198. package/dist/types/core/cards.d.ts +21 -0
  199. package/dist/types/core/check.d.ts +10 -0
  200. package/dist/types/core/consent.d.ts +132 -0
  201. package/dist/types/core/contact-card.d.ts +34 -0
  202. package/dist/types/core/conversation/context.d.ts +56 -0
  203. package/dist/types/core/conversation/event.d.ts +36 -0
  204. package/dist/types/core/conversation/ids.d.ts +18 -0
  205. package/dist/types/core/conversation/link.d.ts +16 -0
  206. package/dist/types/core/conversation/log.d.ts +142 -0
  207. package/dist/types/core/conversation/ratchet.d.ts +95 -0
  208. package/dist/types/core/conversation/seal.d.ts +47 -0
  209. package/dist/types/core/delegation.d.ts +71 -0
  210. package/dist/types/core/directory/device-log.d.ts +119 -0
  211. package/dist/types/core/directory/epoch.d.ts +53 -0
  212. package/dist/types/core/directory/handle.d.ts +11 -0
  213. package/dist/types/core/directory/jelly-device.d.ts +88 -0
  214. package/dist/types/core/directory/jelly-issuer.d.ts +8 -0
  215. package/dist/types/core/directory/jelly-token.d.ts +116 -0
  216. package/dist/types/core/directory/keys-statement.d.ts +22 -0
  217. package/dist/types/core/directory/link.d.ts +36 -0
  218. package/dist/types/core/directory/merkle-log.d.ts +40 -0
  219. package/dist/types/core/directory/person-view.d.ts +90 -0
  220. package/dist/types/core/directory/person.d.ts +8 -0
  221. package/dist/types/core/directory/realm.d.ts +23 -0
  222. package/dist/types/core/directory/smt.d.ts +52 -0
  223. package/dist/types/core/envelope.d.ts +68 -0
  224. package/dist/types/core/hash.d.ts +2 -0
  225. package/dist/types/core/head.d.ts +34 -0
  226. package/dist/types/core/identity.d.ts +35 -0
  227. package/dist/types/core/index.d.ts +45 -0
  228. package/dist/types/core/jcs.d.ts +12 -0
  229. package/dist/types/core/jelly-host-double.d.ts +21 -0
  230. package/dist/types/core/jelly-host.d.ts +86 -0
  231. package/dist/types/core/keys.d.ts +16 -0
  232. package/dist/types/core/peer-text.d.ts +87 -0
  233. package/dist/types/core/realm.d.ts +36 -0
  234. package/dist/types/core/roster.d.ts +146 -0
  235. package/dist/types/core/schema/a2a.d.ts +251 -0
  236. package/dist/types/core/schema/common.d.ts +328 -0
  237. package/dist/types/core/schema/consent.d.ts +140 -0
  238. package/dist/types/core/schema/conversation.d.ts +1657 -0
  239. package/dist/types/core/schema/delegation.d.ts +58 -0
  240. package/dist/types/core/schema/directory-frames.d.ts +1301 -0
  241. package/dist/types/core/schema/directory.d.ts +375 -0
  242. package/dist/types/core/schema/envelope.d.ts +756 -0
  243. package/dist/types/core/schema/errors.d.ts +142 -0
  244. package/dist/types/core/schema/index.d.ts +16 -0
  245. package/dist/types/core/schema/ipc/consent.d.ts +555 -0
  246. package/dist/types/core/schema/ipc/device.d.ts +735 -0
  247. package/dist/types/core/schema/ipc/jelly.d.ts +501 -0
  248. package/dist/types/core/schema/ipc/shapes.d.ts +414 -0
  249. package/dist/types/core/schema/ipc/surfaces.d.ts +680 -0
  250. package/dist/types/core/schema/ipc.d.ts +3146 -0
  251. package/dist/types/core/schema/jelly.d.ts +160 -0
  252. package/dist/types/core/schema/realm.d.ts +38 -0
  253. package/dist/types/core/schema/relay.d.ts +1134 -0
  254. package/dist/types/core/schema/roster.d.ts +347 -0
  255. package/dist/types/core/sign.d.ts +13 -0
  256. package/dist/types/core/task.d.ts +6 -0
  257. package/dist/types/core/testing.d.ts +19 -0
  258. package/dist/types/core/ulid.d.ts +5 -0
  259. package/dist/types/core/version.d.ts +6 -0
  260. package/dist/types/core/wire.d.ts +14 -0
  261. package/dist/types/ipc/client.d.ts +85 -0
  262. package/dist/types/ipc/framing.d.ts +12 -0
  263. package/dist/types/ipc/index.d.ts +6 -0
  264. package/dist/types/ipc/paths.d.ts +39 -0
  265. package/dist/types/ipc/pipes.d.ts +16 -0
  266. package/dist/types/ipc/rpc.d.ts +13 -0
  267. package/dist/types/ipc/server.d.ts +94 -0
  268. package/dist/types/ipc/testing.d.ts +89 -0
  269. package/dist/types/ipc/trust.d.ts +49 -0
  270. package/dist/types/ipc/windows-acl.d.ts +56 -0
  271. package/dist/types/testing/jelly-id.d.ts +518 -0
  272. package/dist/types/zod.d.ts +1 -0
  273. package/package.json +77 -4
@@ -0,0 +1,94 @@
1
+ import type { z } from "zod";
2
+ import { IpcEvent, type IpcMethod, type IpcMethods, type IpcResult } from "../core/index.js";
3
+ import { type SocketTier } from "./paths.js";
4
+ export interface ConnectionContext {
5
+ /** Unique across every server in this process, so a daemon with two sockets can key state by it. */
6
+ readonly id: number;
7
+ /** The tier of the socket this connection came in on. */
8
+ readonly tier: SocketTier;
9
+ /** Session keys this connection subscribed to with `events.subscribe`. */
10
+ readonly subscriptions: ReadonlySet<string>;
11
+ /** Aborted when the connection closes. */
12
+ readonly signal: AbortSignal;
13
+ /** Sends an event to this connection; false when it is too large to send. */
14
+ notify(event: IpcEvent): boolean;
15
+ }
16
+ /**
17
+ * What `authorize` decided for one call: `scope` names the grant that allowed it (`deployment: "jelly"`, spec
18
+ * §8.11), and `expiresAt` (ms) when that grant ends. Undefined when the call needed no grant.
19
+ */
20
+ export interface CallAuthority {
21
+ readonly scope: "action" | "host";
22
+ readonly expiresAt: number;
23
+ }
24
+ /** One call as `authorize` sees it: before its params are parsed, with the request's `grant` member. */
25
+ export interface AuthorizeCall {
26
+ readonly method: IpcMethod;
27
+ /** The params as received (absent: undefined). */
28
+ readonly params: unknown;
29
+ /** The request's top-level `grant` member, when it is a string. */
30
+ readonly grant: string | undefined;
31
+ readonly ctx: ConnectionContext;
32
+ }
33
+ export type Handler<M extends IpcMethod> = (params: z.output<IpcMethods[M]["params"]>, ctx: ConnectionContext, authority: CallAuthority | undefined) => Promise<IpcResult<M>> | IpcResult<M>;
34
+ export type Handlers = {
35
+ [M in IpcMethod]?: Handler<M>;
36
+ };
37
+ export interface IpcServerOptions {
38
+ socketPath: string;
39
+ /**
40
+ * What this socket serves (spec §8.2, "Two sockets"). `agent`: only `AGENT_METHODS`; anything else is
41
+ * `forbidden` before its params are read. `admin`: every method.
42
+ */
43
+ tier: SocketTier;
44
+ /**
45
+ * A group that may connect: the socket is then 0660 and owned by this gid (the daemon's user must be
46
+ * in it). Without it the socket is 0600. On an `admin` socket only together with `authorize`
47
+ * (`deployment: "jelly"`, spec §8.11): a group may reach it only where every call there needs a grant.
48
+ */
49
+ group?: number;
50
+ /**
51
+ * Called for every call that passes the tier check, before its params are read. It throws a `MessagingError`
52
+ * to refuse the call (`grant_required`, `grant_invalid`), and returns what allowed it, which the handler gets.
53
+ */
54
+ authorize?: (call: AuthorizeCall) => CallAuthority | undefined;
55
+ /**
56
+ * Who may own the parent of the socket's directory (default: this user). A `run/` outside the home (`--run`,
57
+ * spec §8.11) may sit in a system directory that root owns: its parent must still be writable by nobody else.
58
+ */
59
+ runParentOwners?: readonly number[];
60
+ /** Named in the hello: `jelly` for a daemon under a Jelly host. */
61
+ deployment?: "jelly";
62
+ handlers: Handlers;
63
+ /** Called for unexpected handler failures. Never log params or results: they can hold message bodies. */
64
+ onError?: (method: string, error: unknown) => void;
65
+ /** Called after a connection closes (subscriptions are gone). */
66
+ onDisconnect?: (ctx: ConnectionContext) => void;
67
+ /** The release this server names in its hello; this build's `VERSION` unless a test says otherwise. */
68
+ version?: string;
69
+ }
70
+ export interface IpcServer {
71
+ /** Where clients find this endpoint: the socket, or on Windows the pipe pointer file. */
72
+ readonly socketPath: string;
73
+ /**
74
+ * Sends an event to every connection that called `events.subscribe` for its `key`; key-less events go
75
+ * to every such connection. So the daemon never publishes `chat` events here: it sends them itself,
76
+ * with `ctx.notify`, only to connections that called `chat.subscribe` (spec §6.8).
77
+ */
78
+ publish(event: IpcEvent): void;
79
+ connections(): number;
80
+ close(): Promise<void>;
81
+ }
82
+ /**
83
+ * Starts one socket of the daemon API. Each connection first gets the hello (`IpcHello`), then
84
+ * answers to its requests. The socket's directory and its parent (`run/` and `$MESSAGING_HOME`)
85
+ * must be private: real directories owned by this user that no group or other user can write
86
+ * (`forbidden` otherwise); a missing one is created 0700. The caller owns their modes beyond that, so
87
+ * a deployment can let a group search them (spec §8.2). Refuses to start when another server answers
88
+ * on the socket; removes a stale socket file otherwise. The socket is 0600, or 0660 to `group`.
89
+ *
90
+ * On Windows (spec §8.2, "Windows") it listens on a fresh named pipe and writes `socketPath` as the pointer
91
+ * file that names it (`pipes.ts`); the directories are private by ACL, and a missing one is created private.
92
+ * `group` is refused there: Node cannot give a pipe to a group.
93
+ */
94
+ export declare function createIpcServer(options: IpcServerOptions): Promise<IpcServer>;
@@ -0,0 +1,89 @@
1
+ import type net from "node:net";
2
+ import { type IpcEvent, type IpcMethod } from "../core/index.js";
3
+ import { MessagingClient } from "./client.js";
4
+ import { type SocketTier } from "./paths.js";
5
+ import { type Handler, type Handlers } from "./server.js";
6
+ /**
7
+ * `@messaging/ipc/testing`: helpers for tests only. Production code never imports this entry point.
8
+ *
9
+ * A fresh home whose `tier` socket path (`run/agent.sock` by default) is short enough for macOS
10
+ * (falls back to /tmp when the OS temp dir is too long). On Windows: a private (by ACL) home in the temp
11
+ * directory, and its `run\<tier>.pipe`.
12
+ */
13
+ export declare function tempSocketPath(prefix?: string, tier?: SocketTier): {
14
+ dir: string;
15
+ socketPath: string;
16
+ };
17
+ /**
18
+ * Makes a bare `net.Server` listen where a client of `socketPath` will find it: on the socket itself, or on Windows
19
+ * on a fresh pipe that a pointer file at `socketPath` names, with `pid` (default this process's) as the pid the
20
+ * client will require in the hello. For impostors and other raw servers in tests.
21
+ */
22
+ export declare function listenAt(server: net.Server, socketPath: string, { pid }?: {
23
+ pid?: number;
24
+ }): Promise<void>;
25
+ /** What `net.connect` takes to reach the endpoint at `socketPath`: the socket itself, or on Windows the pipe its pointer names. */
26
+ export declare function rawEndpoint(socketPath: string): string;
27
+ /** A deterministic ULID for test fixtures: `ulid(7)` is `01JC0000000000000000000007`. */
28
+ export declare function ulid(n: number): string;
29
+ /** Polls `predicate` every 10 ms until it holds; throws after `timeoutMs`. */
30
+ export declare function until(predicate: () => boolean, timeoutMs?: number, what?: string): Promise<void>;
31
+ /** One call the fake received: the method and a copy of its parsed params, taken before its handler ran. */
32
+ export interface Recorded {
33
+ method: IpcMethod;
34
+ params: Record<string, unknown>;
35
+ }
36
+ export type Params<M extends IpcMethod> = Parameters<Handler<M>>[0];
37
+ export interface RecordingDaemonOptions {
38
+ /** Handlers by method; `handle` adds or replaces one later. Every other method is refused with `forbidden`. */
39
+ handlers?: Handlers;
40
+ /** Prefix of the temp home the fake creates (and removes on `close`). */
41
+ prefix?: string;
42
+ /** Use this home instead of a temp home of its own; `close` then leaves the directory alone. */
43
+ at?: {
44
+ dir: string;
45
+ };
46
+ /** The release its hello names (spec §8.2); this build's own unless a test plays another. */
47
+ version?: string;
48
+ }
49
+ /**
50
+ * A daemon stand-in on real Unix sockets, built on the IPC package's own server, so params are
51
+ * validated as messagingd validates them. Like messagingd it listens on both sockets of its home:
52
+ * `run/agent.sock` (the agent tier: `AGENT_METHODS` only) and `run/admin.sock` (every method). Every
53
+ * method of the contract is registered: each call the tier lets through is recorded once, then answered
54
+ * by its handler, or refused with `forbidden` ("fake daemon: no handler for <method>") when there is
55
+ * none. A fake that falls behind the contract fails loudly.
56
+ */
57
+ export interface RecordingDaemon {
58
+ readonly dir: string;
59
+ /** The agent socket, `<dir>/run/agent.sock`. */
60
+ readonly socketPath: string;
61
+ /** The admin socket, `<dir>/run/admin.sock`. */
62
+ readonly adminSocketPath: string;
63
+ /** Every call, in order. */
64
+ readonly calls: Recorded[];
65
+ methods(): IpcMethod[];
66
+ callsTo<M extends IpcMethod>(method: M): Params<M>[];
67
+ last<M extends IpcMethod>(method: M): Params<M> | undefined;
68
+ /** Adds or replaces the handler for `method`, effective for the next call. */
69
+ handle<M extends IpcMethod>(method: M, handler: Handler<M>): void;
70
+ /** `IpcServer.publish`: to every connection that subscribed to the event's key (key-less: to all of them). */
71
+ publish(event: IpcEvent): void;
72
+ /** A `conv` event to the connections whose `conv.subscribe` succeeded, as messagingd sends it. */
73
+ conv(event: Extract<IpcEvent, {
74
+ type: "conv";
75
+ }>): void;
76
+ /** A client on the agent socket, or the `admin` one. */
77
+ connect(tier?: SocketTier): Promise<MessagingClient>;
78
+ /** Open connections on both sockets. */
79
+ connections(): number;
80
+ /** Listens (again) on the same socket; a no-op while listening. */
81
+ start(): Promise<void>;
82
+ /** Drops every connection and stops listening; the socket file goes away. */
83
+ stop(): Promise<void>;
84
+ /** `stop`, wait `downMs`, `start`: same path, same state. */
85
+ restart(downMs?: number): Promise<void>;
86
+ /** `stop`, and remove the temp home the fake created. */
87
+ close(): Promise<void>;
88
+ }
89
+ export declare function recordingDaemon(options?: RecordingDaemonOptions): Promise<RecordingDaemon>;
@@ -0,0 +1,49 @@
1
+ import { type Stats } from "node:fs";
2
+ /**
3
+ * Directory trust for `$MESSAGING_HOME` and `run/` (spec §8.1, §8.2). A directory is private when it is a
4
+ * real directory (not a symlink), owned by a trusted user, and writable by nobody else (no group or
5
+ * other write bit). Whoever can write a directory on the socket's path can replace the socket and
6
+ * capture every client, so the server refuses to listen and clients refuse to connect unless both hold.
7
+ *
8
+ * The trusted owners are this process's effective user, plus, for a client, the daemon's own uid when
9
+ * one is configured (`MESSAGING_DAEMON_UID`): a deployment can run `messagingd` under its own user, with the
10
+ * engine in the group of `run/agent.sock` (spec §8.2, "Two sockets"). A group may read and search a
11
+ * directory, never write it.
12
+ *
13
+ * On Windows there is no mode and no cheap owner: a directory is private when it is a real directory whose
14
+ * ACL passes `daclProblem` with a pinned owner (`windows-acl.ts`); `owners` does not apply.
15
+ */
16
+ export declare function checkPrivateDir(path: string, what: string, owners?: readonly number[]): void;
17
+ /**
18
+ * Creates `path` (mode 0700) when it is missing, then checks it as `checkPrivateDir` does. On Windows a
19
+ * directory it created that is not yet private (it inherited its parent's ACL) is made private
20
+ * (`securePrivate`); one that already existed is only checked.
21
+ */
22
+ export declare function ensurePrivateDir(path: string, what: string): void;
23
+ /** Windows: makes a directory this process just created private, unless it already is (it inherited a private ACL). */
24
+ export declare function secureCreated(path: string, what: string): void;
25
+ /**
26
+ * Windows: the daemon's `chmod 0700` of a home directory (spec §8.1). A directory it just created is made
27
+ * private. An existing one is refused (`forbidden`) when it is a symlink, not a directory, or writable by anyone
28
+ * but this user, SYSTEM or Administrators; otherwise it is tightened to this user alone with its owner pinned
29
+ * (`securePrivate`), unless it already is.
30
+ */
31
+ export declare function adoptPrivateDir(path: string, what: string, created: boolean): void;
32
+ /** Why `stats` is not a private directory owned by one of `owners` (default: this user), or undefined when it is. */
33
+ export declare function privacyProblem(stats: Stats, owners?: readonly number[]): string | undefined;
34
+ /**
35
+ * The socket must be a socket owned by a trusted user (no symlink to someone else's). On Windows `path` is the
36
+ * pipe pointer file (`pipes.ts`): a regular file, not a symlink, private by `daclProblem` with a pinned owner.
37
+ */
38
+ export declare function checkSocket(path: string, owners?: readonly number[]): void;
39
+ /**
40
+ * The users a client trusts to own the daemon's directories and socket: itself, and `daemonUid` when
41
+ * given. Empty (no owner check) without `geteuid`.
42
+ */
43
+ export declare function trustedOwners(daemonUid?: number): number[];
44
+ /**
45
+ * `openSync(path, flags | O_NOFOLLOW, mode)`: refuses a symbolic link at `path` with `ELOOP`. Windows has no
46
+ * `O_NOFOLLOW`, so there the path is checked with `lstat` first (a symlink or junction is `ELOOP`), then opened.
47
+ * In a private directory only its owner could swap the path in between.
48
+ */
49
+ export declare function openNoFollow(path: string, flags: number, mode?: number): number;
@@ -0,0 +1,56 @@
1
+ export declare const SID_SYSTEM = "S-1-5-18";
2
+ export declare const SID_ADMINISTRATORS = "S-1-5-32-544";
3
+ export declare const SID_OWNER_RIGHTS = "S-1-3-4";
4
+ /**
5
+ * Rights that let a principal change a file or directory, its entries, or who may: write data or add a file
6
+ * (0x2), append or add a subdirectory (0x4), write extended attributes (0x10), delete a child (0x40), write
7
+ * attributes (0x100), DELETE, WRITE_DAC, WRITE_OWNER, GENERIC_WRITE and GENERIC_ALL.
8
+ */
9
+ export declare const WRITE_RIGHTS: number;
10
+ export interface Ace {
11
+ /** SDDL ace type: `A` allow, `D` deny, `OA`, `XA`, … */
12
+ type: string;
13
+ flags: string[];
14
+ rights: number;
15
+ /** A full SID (`S-1-…`), or an alias this module does not map, as written. */
16
+ sid: string;
17
+ }
18
+ export interface Dacl {
19
+ /** `D:NO_ACCESS_CONTROL`: a NULL DACL. */
20
+ nullDacl: boolean;
21
+ /** `P`: inheritance from the parent is blocked. */
22
+ protected: boolean;
23
+ aces: Ace[];
24
+ }
25
+ /** The DACL part of an SDDL string, or undefined when it has none. Throws on text that is not SDDL. */
26
+ export declare function parseSddlDacl(sddl: string): Dacl | undefined;
27
+ /** `sid` as a full SID when it is a domain-relative alias of `self`'s domain; otherwise unchanged. */
28
+ export declare function resolveSid(sid: string, self: string): string;
29
+ /**
30
+ * Why `dacl` is not private to `self` (a SID), or undefined when it is (the rules in this module's comment).
31
+ * `pinnedOwner`: also require an OWNER RIGHTS entry that grants no write-class right.
32
+ */
33
+ export declare function daclProblem(dacl: Dacl | undefined, self: string, { pinnedOwner }: {
34
+ pinnedOwner: boolean;
35
+ }): string | undefined;
36
+ /** This process's user SID (`whoami /user`), read once. */
37
+ export declare function currentUserSid(): string;
38
+ /**
39
+ * The DACL of `path`, read with `icacls /save` (SDDL; locale-independent). A path that is gone by the time icacls
40
+ * reads it is `daemon_unavailable`, not `forbidden`: a daemon that stops or restarts removes its pipe file between a
41
+ * client's `lstat` and this read, and that client has found no daemon, not an unsafe one.
42
+ */
43
+ export declare function readDacl(path: string): Dacl | undefined;
44
+ /** The SDDL line of an `icacls /save` file: the object's name, then its SDDL (UTF-16LE, with or without a BOM). */
45
+ export declare function sddlLine(bytes: Buffer): string;
46
+ /** Why `path`'s ACL is not private to this user (see `daclProblem`), or undefined. */
47
+ export declare function aclProblem(path: string, options: {
48
+ pinnedOwner: boolean;
49
+ }): string | undefined;
50
+ /**
51
+ * Makes `path` (a directory) private to this user, as `chmod 0700` does on POSIX: inheritance from the parent
52
+ * removed, full control to this user and an OWNER RIGHTS entry that only reads the ACL, both inherited by
53
+ * everything created inside, and every other entry removed. Then reads the ACL back and checks it. Fails with
54
+ * `forbidden` when this user may not change the ACL.
55
+ */
56
+ export declare function securePrivate(path: string, what: string): void;