@ours.network/messenger-server 0.1.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 (165) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +225 -0
  3. package/dist/chunks/chunk-UW6DWUXJ.js +91 -0
  4. package/dist/chunks/server-BXJKKJP6.js +13854 -0
  5. package/dist/cli.js +142 -0
  6. package/dist/web/assets/abnfDiagram-N423BO3Z-Ct0MgGTC.js +2 -0
  7. package/dist/web/assets/abnfDiagram-N423BO3Z-Ct0MgGTC.js.map +1 -0
  8. package/dist/web/assets/arc-DkAjpDRH.js +2 -0
  9. package/dist/web/assets/arc-DkAjpDRH.js.map +1 -0
  10. package/dist/web/assets/architectureDiagram-T3A2C74G-CQbNIUjF.js +37 -0
  11. package/dist/web/assets/architectureDiagram-T3A2C74G-CQbNIUjF.js.map +1 -0
  12. package/dist/web/assets/blockDiagram-VBNYF7ZC-CZeLN8UY.js +133 -0
  13. package/dist/web/assets/blockDiagram-VBNYF7ZC-CZeLN8UY.js.map +1 -0
  14. package/dist/web/assets/browser-BE8bTUfE.js +9 -0
  15. package/dist/web/assets/browser-BE8bTUfE.js.map +1 -0
  16. package/dist/web/assets/c4Diagram-5PPSVZJV-CIhfUxeH.js +11 -0
  17. package/dist/web/assets/c4Diagram-5PPSVZJV-CIhfUxeH.js.map +1 -0
  18. package/dist/web/assets/channel-BZKtZ9zx.js +2 -0
  19. package/dist/web/assets/channel-BZKtZ9zx.js.map +1 -0
  20. package/dist/web/assets/chunk-2GRJ4B5K-D3TYelbp.js +2 -0
  21. package/dist/web/assets/chunk-2GRJ4B5K-D3TYelbp.js.map +1 -0
  22. package/dist/web/assets/chunk-2Q5K7J3B-BiZeOFrG.js +2 -0
  23. package/dist/web/assets/chunk-2Q5K7J3B-BiZeOFrG.js.map +1 -0
  24. package/dist/web/assets/chunk-5RXB4S5H-CJfZ1dPC.js +232 -0
  25. package/dist/web/assets/chunk-5RXB4S5H-CJfZ1dPC.js.map +1 -0
  26. package/dist/web/assets/chunk-5VM5RSS4-Vmzi7LBN.js +16 -0
  27. package/dist/web/assets/chunk-5VM5RSS4-Vmzi7LBN.js.map +1 -0
  28. package/dist/web/assets/chunk-6Q2QTUOP-B2h9pd0z.js +89 -0
  29. package/dist/web/assets/chunk-6Q2QTUOP-B2h9pd0z.js.map +1 -0
  30. package/dist/web/assets/chunk-GF5L2VYU-C4KJO4_l.js +207 -0
  31. package/dist/web/assets/chunk-GF5L2VYU-C4KJO4_l.js.map +1 -0
  32. package/dist/web/assets/chunk-JWPE2WC7-CVR7-I8U.js +2 -0
  33. package/dist/web/assets/chunk-JWPE2WC7-CVR7-I8U.js.map +1 -0
  34. package/dist/web/assets/chunk-KBJHAD2P-DJiYvgrH.js +2 -0
  35. package/dist/web/assets/chunk-KBJHAD2P-DJiYvgrH.js.map +1 -0
  36. package/dist/web/assets/chunk-RYQCIY6F-B9pd0kEE.js +2 -0
  37. package/dist/web/assets/chunk-RYQCIY6F-B9pd0kEE.js.map +1 -0
  38. package/dist/web/assets/chunk-XXDRQBXY-LhL86vIO.js +2 -0
  39. package/dist/web/assets/chunk-XXDRQBXY-LhL86vIO.js.map +1 -0
  40. package/dist/web/assets/classDiagram-JCYQIIEL-DXETV2D8.js +2 -0
  41. package/dist/web/assets/classDiagram-JCYQIIEL-DXETV2D8.js.map +1 -0
  42. package/dist/web/assets/classDiagram-v2-OCEON4UE-DXETV2D8.js +2 -0
  43. package/dist/web/assets/classDiagram-v2-OCEON4UE-DXETV2D8.js.map +1 -0
  44. package/dist/web/assets/cose-bilkent-JH36ORCC-BvO6F98R.js +2 -0
  45. package/dist/web/assets/cose-bilkent-JH36ORCC-BvO6F98R.js.map +1 -0
  46. package/dist/web/assets/cynefin-VYW2F7L2-B-e6msie.js +179 -0
  47. package/dist/web/assets/cynefin-VYW2F7L2-B-e6msie.js.map +1 -0
  48. package/dist/web/assets/cynefinDiagram-MW4NZA55-dEt_pguc.js +63 -0
  49. package/dist/web/assets/cynefinDiagram-MW4NZA55-dEt_pguc.js.map +1 -0
  50. package/dist/web/assets/cytoscape.esm-BvX2Lq_V.js +332 -0
  51. package/dist/web/assets/cytoscape.esm-BvX2Lq_V.js.map +1 -0
  52. package/dist/web/assets/dagre-VZM6K2ZE-CAXYH_6z.js +5 -0
  53. package/dist/web/assets/dagre-VZM6K2ZE-CAXYH_6z.js.map +1 -0
  54. package/dist/web/assets/defaultLocale-DX6XiGOO.js +2 -0
  55. package/dist/web/assets/defaultLocale-DX6XiGOO.js.map +1 -0
  56. package/dist/web/assets/diagram-7IWD3JNH-DS6SYGQO.js +31 -0
  57. package/dist/web/assets/diagram-7IWD3JNH-DS6SYGQO.js.map +1 -0
  58. package/dist/web/assets/diagram-B4RE2ZJO-2nKd6p2f.js +4 -0
  59. package/dist/web/assets/diagram-B4RE2ZJO-2nKd6p2f.js.map +1 -0
  60. package/dist/web/assets/diagram-LBJQPF4R-Brh5t1Yq.js +25 -0
  61. package/dist/web/assets/diagram-LBJQPF4R-Brh5t1Yq.js.map +1 -0
  62. package/dist/web/assets/diagram-Q27KOJAE-YOwnQ8kk.js +25 -0
  63. package/dist/web/assets/diagram-Q27KOJAE-YOwnQ8kk.js.map +1 -0
  64. package/dist/web/assets/diagram-UB23O5K3-BLSDWDQP.js +42 -0
  65. package/dist/web/assets/diagram-UB23O5K3-BLSDWDQP.js.map +1 -0
  66. package/dist/web/assets/ebnfDiagram-BXEA7PRR-CxT-sneC.js +2 -0
  67. package/dist/web/assets/ebnfDiagram-BXEA7PRR-CxT-sneC.js.map +1 -0
  68. package/dist/web/assets/erDiagram-JOGREHBK-CEYu1DWL.js +86 -0
  69. package/dist/web/assets/erDiagram-JOGREHBK-CEYu1DWL.js.map +1 -0
  70. package/dist/web/assets/flowDiagram-UKHOOZJN-CGCtZwTp.js +157 -0
  71. package/dist/web/assets/flowDiagram-UKHOOZJN-CGCtZwTp.js.map +1 -0
  72. package/dist/web/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  73. package/dist/web/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  74. package/dist/web/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  75. package/dist/web/assets/ganttDiagram-PKOTCBZU-B9YOo1yU.js +293 -0
  76. package/dist/web/assets/ganttDiagram-PKOTCBZU-B9YOo1yU.js.map +1 -0
  77. package/dist/web/assets/gitGraphDiagram-DS77QQ5N-32_lkB3m.js +107 -0
  78. package/dist/web/assets/gitGraphDiagram-DS77QQ5N-32_lkB3m.js.map +1 -0
  79. package/dist/web/assets/graph-gxHQN9-b.js +2 -0
  80. package/dist/web/assets/graph-gxHQN9-b.js.map +1 -0
  81. package/dist/web/assets/index-Cce6sBBo.js +317 -0
  82. package/dist/web/assets/index-Cce6sBBo.js.map +1 -0
  83. package/dist/web/assets/index-DukxAC__.css +1 -0
  84. package/dist/web/assets/infoDiagram-6WML65LV-Diwcibc2.js +3 -0
  85. package/dist/web/assets/infoDiagram-6WML65LV-Diwcibc2.js.map +1 -0
  86. package/dist/web/assets/init-Gi6I4Gst.js +2 -0
  87. package/dist/web/assets/init-Gi6I4Gst.js.map +1 -0
  88. package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  89. package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  90. package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  91. package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  92. package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  93. package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  94. package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  95. package/dist/web/assets/ishikawaDiagram-WSZJBQD7-DUghzz6A.js +71 -0
  96. package/dist/web/assets/ishikawaDiagram-WSZJBQD7-DUghzz6A.js.map +1 -0
  97. package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  98. package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  99. package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  100. package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  101. package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  102. package/dist/web/assets/journeyDiagram-NVQOT4AX-CEMc0cFh.js +140 -0
  103. package/dist/web/assets/journeyDiagram-NVQOT4AX-CEMc0cFh.js.map +1 -0
  104. package/dist/web/assets/jsQR-DNCbBmIv.js +2 -0
  105. package/dist/web/assets/jsQR-DNCbBmIv.js.map +1 -0
  106. package/dist/web/assets/kanban-definition-27J2QSJJ-C60H4z4j.js +90 -0
  107. package/dist/web/assets/kanban-definition-27J2QSJJ-C60H4z4j.js.map +1 -0
  108. package/dist/web/assets/katex-HP8lGamR.js +258 -0
  109. package/dist/web/assets/katex-HP8lGamR.js.map +1 -0
  110. package/dist/web/assets/layout-CpcJW2by.js +2 -0
  111. package/dist/web/assets/layout-CpcJW2by.js.map +1 -0
  112. package/dist/web/assets/linear-BRdw4K7J.js +2 -0
  113. package/dist/web/assets/linear-BRdw4K7J.js.map +1 -0
  114. package/dist/web/assets/map-CKPHyBGT.js +2 -0
  115. package/dist/web/assets/map-CKPHyBGT.js.map +1 -0
  116. package/dist/web/assets/mermaid.core-DTdyJUyZ.js +315 -0
  117. package/dist/web/assets/mermaid.core-DTdyJUyZ.js.map +1 -0
  118. package/dist/web/assets/mindmap-definition-FAOFIHXS-Ctd1Avoh.js +97 -0
  119. package/dist/web/assets/mindmap-definition-FAOFIHXS-Ctd1Avoh.js.map +1 -0
  120. package/dist/web/assets/ordinal-Cboi1Yqb.js +2 -0
  121. package/dist/web/assets/ordinal-Cboi1Yqb.js.map +1 -0
  122. package/dist/web/assets/pegDiagram-VL7TDLO6-CIJ0TF6e.js +2 -0
  123. package/dist/web/assets/pegDiagram-VL7TDLO6-CIJ0TF6e.js.map +1 -0
  124. package/dist/web/assets/pieDiagram-7S7Q4E2Y-PFIMn7nc.js +40 -0
  125. package/dist/web/assets/pieDiagram-7S7Q4E2Y-PFIMn7nc.js.map +1 -0
  126. package/dist/web/assets/quadrantDiagram-CIZ2JOQS-CX4UzzKN.js +8 -0
  127. package/dist/web/assets/quadrantDiagram-CIZ2JOQS-CX4UzzKN.js.map +1 -0
  128. package/dist/web/assets/railroadDiagram-AXF67PYL-DOjwFK86.js +2 -0
  129. package/dist/web/assets/railroadDiagram-AXF67PYL-DOjwFK86.js.map +1 -0
  130. package/dist/web/assets/requirementDiagram-LRYGKXZP-BmoMDpuG.js +85 -0
  131. package/dist/web/assets/requirementDiagram-LRYGKXZP-BmoMDpuG.js.map +1 -0
  132. package/dist/web/assets/sankeyDiagram-W5VNT64P-BxsyY7C0.js +41 -0
  133. package/dist/web/assets/sankeyDiagram-W5VNT64P-BxsyY7C0.js.map +1 -0
  134. package/dist/web/assets/sequenceDiagram-SI44F4Z6-Dvv3O0Rc.js +163 -0
  135. package/dist/web/assets/sequenceDiagram-SI44F4Z6-Dvv3O0Rc.js.map +1 -0
  136. package/dist/web/assets/sizeCapture-X5ZJPWSS-CS4OfTKV.js +2 -0
  137. package/dist/web/assets/sizeCapture-X5ZJPWSS-CS4OfTKV.js.map +1 -0
  138. package/dist/web/assets/stateDiagram-OKZ733FA-DLx4cUCx.js +2 -0
  139. package/dist/web/assets/stateDiagram-OKZ733FA-DLx4cUCx.js.map +1 -0
  140. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CGTS_71O.js +2 -0
  141. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CGTS_71O.js.map +1 -0
  142. package/dist/web/assets/swimlanes-SLNWSIFB-CfboUePX.js +3 -0
  143. package/dist/web/assets/swimlanes-SLNWSIFB-CfboUePX.js.map +1 -0
  144. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DC39ekbl.js +9 -0
  145. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DC39ekbl.js.map +1 -0
  146. package/dist/web/assets/timeline-definition-Z64GVDOM-BEMGCQrU.js +121 -0
  147. package/dist/web/assets/timeline-definition-Z64GVDOM-BEMGCQrU.js.map +1 -0
  148. package/dist/web/assets/vennDiagram-T6HMQDX7-y2pz9Wz-.js +35 -0
  149. package/dist/web/assets/vennDiagram-T6HMQDX7-y2pz9Wz-.js.map +1 -0
  150. package/dist/web/assets/wardleyDiagram-T6FBY63Y-CQ-4d_jJ.js +79 -0
  151. package/dist/web/assets/wardleyDiagram-T6FBY63Y-CQ-4d_jJ.js.map +1 -0
  152. package/dist/web/assets/xychartDiagram-ELKLHX3M-BDr2YKTM.js +8 -0
  153. package/dist/web/assets/xychartDiagram-ELKLHX3M-BDr2YKTM.js.map +1 -0
  154. package/dist/web/icons/apple-touch-icon.png +0 -0
  155. package/dist/web/icons/favicon-16.png +0 -0
  156. package/dist/web/icons/favicon-32.png +0 -0
  157. package/dist/web/icons/icon-192-maskable.png +0 -0
  158. package/dist/web/icons/icon-192.png +0 -0
  159. package/dist/web/icons/icon-512-maskable.png +0 -0
  160. package/dist/web/icons/icon-512.png +0 -0
  161. package/dist/web/index.html +19 -0
  162. package/dist/web/manifest.webmanifest +17 -0
  163. package/dist/web/sw.js +181 -0
  164. package/dist/web/version.json +1 -0
  165. package/package.json +78 -0
package/LICENSE ADDED
@@ -0,0 +1,98 @@
1
+ # Functional Source License, Version 1.1, Apache 2.0 Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-Apache-2.0
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Adapt Framework Solutions Ltd
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to use,
27
+ copy, modify, create derivative works, publicly perform, publicly display and
28
+ redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or service
34
+ that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software that
39
+ exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee using
52
+ the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to the
59
+ infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software, you
68
+ must include a copy of or a link to these Terms and Conditions and not remove any
69
+ copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, INCLUDING
74
+ WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
75
+ PURPOSE, NON-INFRINGEMENT, OR THAT THE SOFTWARE IS FREE OF DEFECTS. IN NO EVENT
76
+ WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE,
77
+ INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE
78
+ BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
79
+
80
+ ### Grant of Future License
81
+
82
+ We hereby irrevocably grant you an additional license to use the Software under
83
+ the Apache License, Version 2.0 that is effective on the second anniversary of
84
+ the date we make the Software available. On or after that date, you may use the
85
+ Software under the Apache License, Version 2.0, in which case the following will
86
+ apply:
87
+
88
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
89
+ this file except in compliance with the License.
90
+
91
+ You may obtain a copy of the License at
92
+
93
+ http://www.apache.org/licenses/LICENSE-2.0
94
+
95
+ Unless required by applicable law or agreed to in writing, software distributed
96
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
97
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
98
+ specific language governing permissions and limitations under the License.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # @ours.network/messenger-server
2
+
3
+ A self-hosted web messenger for ours.network. It attaches to the same shared
4
+ ours daemon used by the CLI and peer services, leases one configured identity,
5
+ and exposes a same-origin REST/SSE web application. Messenger never starts,
6
+ stops, embeds, or owns the daemon, and `/mcp` always returns 404.
7
+
8
+ Message and file history is persisted by the shared daemon outside MUFL packets.
9
+ Messenger reads the daemon's external history API and selectively consumes only
10
+ the unread messages for the active conversation. SSE carries metadata-only
11
+ invalidations; the browser rebuilds durable truth from REST snapshots.
12
+
13
+ ## Shared daemon and identity
14
+
15
+ `start()` calls the SDK's client-only `attachOursClient`, using the standard
16
+ `OURS_CONFIG`, `OURS_STATE_DIR`, `OURS_PORT`, endpoint, and API-token selection.
17
+ The SDK verifies that the endpoint belongs to the expected state directory
18
+ before sending credentials. Messenger then calls `chooseIdentity` for exactly
19
+ `OURS_MESSENGER_IDENTITY`; it never creates an identity or chooses one
20
+ implicitly.
21
+
22
+ The shared daemon owns identity keys, MUFL protocol state, message/file history,
23
+ and file blobs. Messenger owns only its public HTTP server and application state
24
+ under `OURS_MESSENGER_STATE_DIR`, including WebPush subscriptions, delivery
25
+ queue metadata, and VAPID keys. Shutting down messenger releases its SDK lease
26
+ but does not stop the daemon.
27
+
28
+ Startup is transactional: a daemon-attach, identity-bind, application-store,
29
+ watcher, or listener failure closes the public server if present, stops the
30
+ watcher, releases the lease, and preserves existing state. Normal `close()` is
31
+ idempotent and follows the same ordered path.
32
+
33
+ ## Running
34
+
35
+ Install and start the shared daemon with `@ours.network/cli`, create the identity
36
+ there, then start messenger with the same daemon selection:
37
+
38
+ ```bash
39
+ npm install
40
+ npm run build
41
+
42
+ # One-time host setup. These commands come from @ours.network/cli.
43
+ ours config setup --port 3070 --state-dir /srv/ours
44
+ ours daemon start
45
+ ours identity create-root --name 'Ada@server'
46
+
47
+ OURS_MESSENGER_STATE_DIR=/srv/ours-messenger \
48
+ OURS_STATE_DIR=/srv/ours \
49
+ OURS_PORT=3070 \
50
+ OURS_MESSENGER_IDENTITY='Ada@server' \
51
+ OURS_MESSENGER_PUBLIC_ORIGIN=http://127.0.0.1:8420 \
52
+ node dist/cli.js serve
53
+ ```
54
+
55
+ `serve` is the only messenger command. Daemon lifecycle, identity provisioning,
56
+ and daemon-state reset belong to the ours CLI. This storage epoch intentionally
57
+ has no migration: remove old daemon state before installing it, as documented by
58
+ the SDK/CLI release. Messenger-specific `push.json` remains separate and is not
59
+ daemon identity storage.
60
+
61
+ Open `http://127.0.0.1:8420/`. Useful configuration:
62
+
63
+ ```text
64
+ OURS_MESSENGER_HOST default 127.0.0.1
65
+ OURS_MESSENGER_PORT default 8420; 0 selects a dynamic public port
66
+ OURS_MESSENGER_PUBLIC_ORIGIN required exact external http(s) origin
67
+ OURS_MESSENGER_STATE_DIR default ~/.ours-messenger
68
+ OURS_MESSENGER_FORCE default false
69
+ OURS_MESSENGER_VAPID_PUBLIC_KEY optional; must be paired with the private key
70
+ OURS_MESSENGER_VAPID_PRIVATE_KEY optional secret; never expose to the browser
71
+ OURS_MESSENGER_VAPID_SUBJECT default mailto:admin@localhost
72
+ ```
73
+
74
+ The production build is an installable React 18 + TypeScript PWA emitted by Vite under
75
+ `dist/web`: `index.html` is no-cache, while content-hashed `/assets/*` are served
76
+ immutable with explicit MIME types and `nosniff`. During frontend-only work,
77
+ `npm run dev` starts Vite on loopback and proxies `/api` and `/mcp` to the
78
+ messenger server; use `npm run dev:server` for the backend process.
79
+
80
+ Every state-changing HTTP request must carry `Content-Type: application/json`,
81
+ an exact single `Origin` equal to `OURS_MESSENGER_PUBLIC_ORIGIN`, and
82
+ `X-Ours-Messenger-CSRF: 1`. The check runs before body parsing and route handlers;
83
+ the server supplies no permissive CORS or successful preflight path. Browser file
84
+ sends accept bounded inline base64 only and reject the `path` key.
85
+
86
+ The public messenger REST surface has no built-in user authentication. Keep the
87
+ default loopback bind or put an authenticated reverse proxy in front of a
88
+ non-loopback bind. The shared daemon token protects only its private SDK HTTP
89
+ surface and is never returned by `/api/state` or written to messenger logs.
90
+
91
+ ## Receipts and live updates
92
+
93
+ Delivered and read are distinct. The daemon records delivered after accepted
94
+ storage; browser snapshot reads are non-consuming. The web read gate calls
95
+ `POST /api/conversations/:contact/read` only when that exact dialog is visible.
96
+ That route filters the non-consuming unread index by the selected peer CID and
97
+ selectively consumes only those wire IDs. The general consuming `getMessages`
98
+ operation is intentionally absent from the REST surface. Duplicate calls are
99
+ harmless because only unread-to-read transitions produce receipt wire IDs.
100
+
101
+ `GET /api/events` is a bounded SSE invalidation stream:
102
+
103
+ - every connection begins with `sync_required(reason=connected)`;
104
+ - message and receipt events carry authenticated IDs and wire IDs, never bodies;
105
+ - watcher reconnect emits `sync_required(reason=daemon_reconnected)`;
106
+ - backpressure overflow collapses details to one `sync_required(reason=overflow)`;
107
+ - there is no replay ID; REST snapshots recover all durable state.
108
+
109
+ The focused client does not persist messages, identities, receipts, or API
110
+ responses in browser storage. Its service worker caches only the application
111
+ shell and static assets for offline launch; all `/api/*` requests bypass it.
112
+
113
+ The web UI is the messenger-only port of the canonical ours-control-plane
114
+ interface at commit `bc0183c80e9ee0ea2dd5adecb58460b0564e90d5`; its exact source inventory and
115
+ transport exclusions are recorded in `web/src/CANONICAL_UI_PROVENANCE.md`. It
116
+ includes grouped identity-root contacts; contact add, approval, rename and
117
+ removal; one-time/public invite creation and revocation; public bio editing;
118
+ message and attachment replies; drag/drop/paste and picker uploads with bounded
119
+ progress/error states; per-dialog file and version history; photo/audio/Markdown
120
+ previews; voice recording and transcript display. Conversations open on the newest 50 messages and load older
121
+ history with an exclusive cursor while preserving the visible scroll position.
122
+ Incoming files are fetched only after an explicit user action. HTML previews run
123
+ in a sandboxed frame with a deny-by-default CSP, and Markdown is rendered to
124
+ React nodes without raw HTML execution. Direct media responses allowlist only
125
+ raster image/audio/video MIME types for inline use; HTML, SVG, XML, PDF, scripts,
126
+ and unknown formats are opaque attachments with `nosniff` and a sandboxing CSP.
127
+
128
+ ## WebPush
129
+
130
+ Web Push is an explicit per-browser opt-in in Settings. The default `Full`
131
+ preview sends the canonical sender and message text or filename; `Private`
132
+ sends only a generic message/file/photo/voice label. The payload and safe
133
+ same-origin dialog path are encrypted to the browser with the standard Web Push
134
+ content-encoding contract and signed with VAPID. The upstream watcher and SSE
135
+ stream remain non-consuming and metadata-only, so push delivery cannot mark a
136
+ message read. Web Push is separate from the ours end-to-end channel: the push
137
+ provider observes endpoint, timing, and payload-size metadata, and the device
138
+ may display the decrypted preview on its lock screen. The UI states this before
139
+ permission is requested.
140
+
141
+ The browser state is server-acknowledged: `Off`, `Needs permission`,
142
+ `Repairing`, or `On`. A browser is not shown as On until its current
143
+ subscription and VAPID generation have been acknowledged by the server. VAPID
144
+ rotation makes old bindings repair-required; Repair performs one controlled
145
+ unsubscribe/resubscribe. Disable converges both the server binding and browser
146
+ subscription. On iOS, install the site to the Home Screen before enabling push.
147
+
148
+ Delivery jobs are durable, identity-scoped metadata keyed by authenticated
149
+ sender CID, wire ID, and event kind. They never persist message text or file
150
+ contents. Each attempt re-reads the canonical SDK projection; restart resumes
151
+ pending work, duplicate watcher events converge to one job, 404/410 responses
152
+ prune dead bindings, and transient network/429/5xx responses use bounded
153
+ backoff with jitter and expiry. Push failures do not stop the watcher.
154
+
155
+ An absent `push.json` is initialized on first run. An existing file that is
156
+ unreadable, malformed, or schema-invalid aborts startup without rewriting keys,
157
+ bindings, or jobs; the error identifies the preserved path and requires the
158
+ operator to restore it or explicitly move it aside before a new state is made.
159
+ The file is written atomically with mode `0600`. Back up the stable state root;
160
+ losing or rotating its VAPID private key requires every browser to repair its
161
+ subscription.
162
+
163
+ Production prerequisites are an accurate system clock, outbound HTTPS access to
164
+ browser push services, a stable writable owner-only state directory, an exact
165
+ `OURS_MESSENGER_PUBLIC_ORIGIN`, and HTTPS at the public browser origin (localhost
166
+ is the development exception). This application enforces same-origin, CSRF,
167
+ input bounds, identity isolation, and secret redaction. It does not authenticate
168
+ public users: any non-loopback deployment still requires an authenticated
169
+ reverse proxy. No nginx or other proxy-specific configuration is assumed.
170
+
171
+ ## REST surface
172
+
173
+ Routes live under `/api/`; `src/api.ts` is the executable route list. Principal
174
+ routes include identity/contact/invite operations, conversation snapshots,
175
+ explicit read, text/file mutations, per-dialog media inventory and explicit
176
+ incoming fetch, owner-only media download, WebPush routes, `/api/state`,
177
+ `/api/healthz`, `/api/build-info`, and `/api/events`. During startup, build
178
+ metadata is available while health returns a fixed `status=starting` 503. After
179
+ the readiness transition, health returns 200 only when the shared daemon responds
180
+ before its deadline with the startup-bound identity CID; failures use one fixed
181
+ 503 shape. State excludes the daemon endpoint, token, and broker configuration.
182
+ Build metadata is injected by `build.mjs` as
183
+ the full Git SHA plus clean/dirty provenance; `OURS_MESSENGER_RELEASE_BUILD=1`
184
+ refuses dirty tracked or untracked source. Unknown `/api/*` routes remain JSON
185
+ 404 responses. `/mcp` is a
186
+ plain 404 and never falls through to the SPA.
187
+
188
+ Legacy browser naming maps as follows:
189
+
190
+ | old name | messenger route / SDK operation |
191
+ | --- | --- |
192
+ | `listPendingInvites` | `GET /api/invites` → `listInvites` |
193
+ | `listContactRoots` | `GET /api/contacts/roots` → `listContacts().roots` |
194
+ | `getProfileName` | `GET /api/identity` → `currentIdentity()` |
195
+ | `introduce` | responder-only `respondToIntroduction`; no initiator operation exists |
196
+
197
+ ## Verification
198
+
199
+ ```bash
200
+ npm run typecheck
201
+ npm run build
202
+ npm run test:offline
203
+ npm run test:loopback
204
+ npm run test:browser
205
+ npm test
206
+ ```
207
+
208
+ The suite covers mutation intent gates, safe inline-file bounds, health identity
209
+ and timeout behavior, response/log redaction, immutable build identity, shared
210
+ daemon attachment, exact identity leasing, external history paging, selective
211
+ conversation reads, graceful lease release, bundle execution, token redaction,
212
+ `/mcp` 404, programmatic shutdown and partial-start rollback, receipt semantics,
213
+ REST/WebPush encryption and full payloads, reply correlation, immutable media and
214
+ version round-trips, hostile top-level media navigation, exact voice MIME/bytes,
215
+ sandboxed previews, corrupt push-state preservation/recovery, PWA cache
216
+ isolation/installability/offline launch in Chromium, SSE backpressure and
217
+ reconnect, cursor paging with stable scroll anchoring, invite-dialog reopen,
218
+ focused-client contracts and the exact-dialog read gate.
219
+
220
+ ## SDK lifecycle boundary
221
+
222
+ Messenger imports only the public client surface of `@ours.network/sdk`.
223
+ Bundle-contract tests reject daemon/native/MUFL artifacts and embedded-runtime
224
+ imports. Process signals stop the messenger HTTP application and release its
225
+ identity lease; daemon lifecycle remains exclusively under the ours CLI.
@@ -0,0 +1,91 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
16
+ var __commonJS = (cb, mod) => function __require2() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
28
+ // If the importer is in node compatibility mode or this is not an ESM
29
+ // file that has been converted to a CommonJS file using a Babel-
30
+ // compatible transform (i.e. "__esModule" has not been set), then set
31
+ // "default" to the CommonJS "module.exports" for node compatibility.
32
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
33
+ mod
34
+ ));
35
+
36
+ // <define:__MESSENGER_BUILD_INFO__>
37
+ var define_MESSENGER_BUILD_INFO_default;
38
+ var init_define_MESSENGER_BUILD_INFO = __esm({
39
+ "<define:__MESSENGER_BUILD_INFO__>"() {
40
+ define_MESSENGER_BUILD_INFO_default = { name: "@ours.network/messenger-server", version: "0.1.1", sha: "603ac4d57d82f845d7cf4d4fdfeb026908ca8a62", dirty: false };
41
+ }
42
+ });
43
+
44
+ // src/security.ts
45
+ init_define_MESSENGER_BUILD_INFO();
46
+ import { randomUUID } from "node:crypto";
47
+ var ConfigurationError = class extends Error {
48
+ name = "ConfigurationError";
49
+ };
50
+ function operatorError(error, context, write) {
51
+ if (error instanceof ConfigurationError) {
52
+ write(error.message);
53
+ return;
54
+ }
55
+ reportFailure(write, context, error);
56
+ }
57
+ function reportFailure(write, context, error) {
58
+ void error;
59
+ const correlationId = randomUUID();
60
+ write(`${context} failed (correlation ${correlationId})`);
61
+ return correlationId;
62
+ }
63
+ function publicInternalError(error, context, write = (message) => console.warn(`[messenger] ${message}`)) {
64
+ const correlationId = reportFailure(write, context, error);
65
+ return { code: "INTERNAL", message: "Internal server error", correlationId };
66
+ }
67
+ var SAFE_ENGINE_ERRORS = Object.freeze({
68
+ NO_SUCH_CONTACT: "Contact not found",
69
+ NO_SUCH_IDENTITY: "Identity not found",
70
+ IDENTITY_IN_USE: "Identity is in use",
71
+ INVALID_INVITE: "Invite is invalid",
72
+ INVITE_ALREADY_USED: "Invite is no longer available",
73
+ INVITE_REVOKED: "Invite is no longer available"
74
+ });
75
+ function publicEngineError(code) {
76
+ const message = SAFE_ENGINE_ERRORS[code];
77
+ return message ? { code, message } : { code: "REQUEST_REJECTED", message: "Request rejected" };
78
+ }
79
+
80
+ export {
81
+ __require,
82
+ __commonJS,
83
+ __toESM,
84
+ init_define_MESSENGER_BUILD_INFO,
85
+ define_MESSENGER_BUILD_INFO_default,
86
+ ConfigurationError,
87
+ operatorError,
88
+ reportFailure,
89
+ publicInternalError,
90
+ publicEngineError
91
+ };