@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.
- package/LICENSE +98 -0
- package/README.md +225 -0
- package/dist/chunks/chunk-UW6DWUXJ.js +91 -0
- package/dist/chunks/server-BXJKKJP6.js +13854 -0
- package/dist/cli.js +142 -0
- package/dist/web/assets/abnfDiagram-N423BO3Z-Ct0MgGTC.js +2 -0
- package/dist/web/assets/abnfDiagram-N423BO3Z-Ct0MgGTC.js.map +1 -0
- package/dist/web/assets/arc-DkAjpDRH.js +2 -0
- package/dist/web/assets/arc-DkAjpDRH.js.map +1 -0
- package/dist/web/assets/architectureDiagram-T3A2C74G-CQbNIUjF.js +37 -0
- package/dist/web/assets/architectureDiagram-T3A2C74G-CQbNIUjF.js.map +1 -0
- package/dist/web/assets/blockDiagram-VBNYF7ZC-CZeLN8UY.js +133 -0
- package/dist/web/assets/blockDiagram-VBNYF7ZC-CZeLN8UY.js.map +1 -0
- package/dist/web/assets/browser-BE8bTUfE.js +9 -0
- package/dist/web/assets/browser-BE8bTUfE.js.map +1 -0
- package/dist/web/assets/c4Diagram-5PPSVZJV-CIhfUxeH.js +11 -0
- package/dist/web/assets/c4Diagram-5PPSVZJV-CIhfUxeH.js.map +1 -0
- package/dist/web/assets/channel-BZKtZ9zx.js +2 -0
- package/dist/web/assets/channel-BZKtZ9zx.js.map +1 -0
- package/dist/web/assets/chunk-2GRJ4B5K-D3TYelbp.js +2 -0
- package/dist/web/assets/chunk-2GRJ4B5K-D3TYelbp.js.map +1 -0
- package/dist/web/assets/chunk-2Q5K7J3B-BiZeOFrG.js +2 -0
- package/dist/web/assets/chunk-2Q5K7J3B-BiZeOFrG.js.map +1 -0
- package/dist/web/assets/chunk-5RXB4S5H-CJfZ1dPC.js +232 -0
- package/dist/web/assets/chunk-5RXB4S5H-CJfZ1dPC.js.map +1 -0
- package/dist/web/assets/chunk-5VM5RSS4-Vmzi7LBN.js +16 -0
- package/dist/web/assets/chunk-5VM5RSS4-Vmzi7LBN.js.map +1 -0
- package/dist/web/assets/chunk-6Q2QTUOP-B2h9pd0z.js +89 -0
- package/dist/web/assets/chunk-6Q2QTUOP-B2h9pd0z.js.map +1 -0
- package/dist/web/assets/chunk-GF5L2VYU-C4KJO4_l.js +207 -0
- package/dist/web/assets/chunk-GF5L2VYU-C4KJO4_l.js.map +1 -0
- package/dist/web/assets/chunk-JWPE2WC7-CVR7-I8U.js +2 -0
- package/dist/web/assets/chunk-JWPE2WC7-CVR7-I8U.js.map +1 -0
- package/dist/web/assets/chunk-KBJHAD2P-DJiYvgrH.js +2 -0
- package/dist/web/assets/chunk-KBJHAD2P-DJiYvgrH.js.map +1 -0
- package/dist/web/assets/chunk-RYQCIY6F-B9pd0kEE.js +2 -0
- package/dist/web/assets/chunk-RYQCIY6F-B9pd0kEE.js.map +1 -0
- package/dist/web/assets/chunk-XXDRQBXY-LhL86vIO.js +2 -0
- package/dist/web/assets/chunk-XXDRQBXY-LhL86vIO.js.map +1 -0
- package/dist/web/assets/classDiagram-JCYQIIEL-DXETV2D8.js +2 -0
- package/dist/web/assets/classDiagram-JCYQIIEL-DXETV2D8.js.map +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-DXETV2D8.js +2 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-DXETV2D8.js.map +1 -0
- package/dist/web/assets/cose-bilkent-JH36ORCC-BvO6F98R.js +2 -0
- package/dist/web/assets/cose-bilkent-JH36ORCC-BvO6F98R.js.map +1 -0
- package/dist/web/assets/cynefin-VYW2F7L2-B-e6msie.js +179 -0
- package/dist/web/assets/cynefin-VYW2F7L2-B-e6msie.js.map +1 -0
- package/dist/web/assets/cynefinDiagram-MW4NZA55-dEt_pguc.js +63 -0
- package/dist/web/assets/cynefinDiagram-MW4NZA55-dEt_pguc.js.map +1 -0
- package/dist/web/assets/cytoscape.esm-BvX2Lq_V.js +332 -0
- package/dist/web/assets/cytoscape.esm-BvX2Lq_V.js.map +1 -0
- package/dist/web/assets/dagre-VZM6K2ZE-CAXYH_6z.js +5 -0
- package/dist/web/assets/dagre-VZM6K2ZE-CAXYH_6z.js.map +1 -0
- package/dist/web/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/dist/web/assets/defaultLocale-DX6XiGOO.js.map +1 -0
- package/dist/web/assets/diagram-7IWD3JNH-DS6SYGQO.js +31 -0
- package/dist/web/assets/diagram-7IWD3JNH-DS6SYGQO.js.map +1 -0
- package/dist/web/assets/diagram-B4RE2ZJO-2nKd6p2f.js +4 -0
- package/dist/web/assets/diagram-B4RE2ZJO-2nKd6p2f.js.map +1 -0
- package/dist/web/assets/diagram-LBJQPF4R-Brh5t1Yq.js +25 -0
- package/dist/web/assets/diagram-LBJQPF4R-Brh5t1Yq.js.map +1 -0
- package/dist/web/assets/diagram-Q27KOJAE-YOwnQ8kk.js +25 -0
- package/dist/web/assets/diagram-Q27KOJAE-YOwnQ8kk.js.map +1 -0
- package/dist/web/assets/diagram-UB23O5K3-BLSDWDQP.js +42 -0
- package/dist/web/assets/diagram-UB23O5K3-BLSDWDQP.js.map +1 -0
- package/dist/web/assets/ebnfDiagram-BXEA7PRR-CxT-sneC.js +2 -0
- package/dist/web/assets/ebnfDiagram-BXEA7PRR-CxT-sneC.js.map +1 -0
- package/dist/web/assets/erDiagram-JOGREHBK-CEYu1DWL.js +86 -0
- package/dist/web/assets/erDiagram-JOGREHBK-CEYu1DWL.js.map +1 -0
- package/dist/web/assets/flowDiagram-UKHOOZJN-CGCtZwTp.js +157 -0
- package/dist/web/assets/flowDiagram-UKHOOZJN-CGCtZwTp.js.map +1 -0
- package/dist/web/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
- package/dist/web/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
- package/dist/web/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
- package/dist/web/assets/ganttDiagram-PKOTCBZU-B9YOo1yU.js +293 -0
- package/dist/web/assets/ganttDiagram-PKOTCBZU-B9YOo1yU.js.map +1 -0
- package/dist/web/assets/gitGraphDiagram-DS77QQ5N-32_lkB3m.js +107 -0
- package/dist/web/assets/gitGraphDiagram-DS77QQ5N-32_lkB3m.js.map +1 -0
- package/dist/web/assets/graph-gxHQN9-b.js +2 -0
- package/dist/web/assets/graph-gxHQN9-b.js.map +1 -0
- package/dist/web/assets/index-Cce6sBBo.js +317 -0
- package/dist/web/assets/index-Cce6sBBo.js.map +1 -0
- package/dist/web/assets/index-DukxAC__.css +1 -0
- package/dist/web/assets/infoDiagram-6WML65LV-Diwcibc2.js +3 -0
- package/dist/web/assets/infoDiagram-6WML65LV-Diwcibc2.js.map +1 -0
- package/dist/web/assets/init-Gi6I4Gst.js +2 -0
- package/dist/web/assets/init-Gi6I4Gst.js.map +1 -0
- package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/web/assets/ishikawaDiagram-WSZJBQD7-DUghzz6A.js +71 -0
- package/dist/web/assets/ishikawaDiagram-WSZJBQD7-DUghzz6A.js.map +1 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/assets/journeyDiagram-NVQOT4AX-CEMc0cFh.js +140 -0
- package/dist/web/assets/journeyDiagram-NVQOT4AX-CEMc0cFh.js.map +1 -0
- package/dist/web/assets/jsQR-DNCbBmIv.js +2 -0
- package/dist/web/assets/jsQR-DNCbBmIv.js.map +1 -0
- package/dist/web/assets/kanban-definition-27J2QSJJ-C60H4z4j.js +90 -0
- package/dist/web/assets/kanban-definition-27J2QSJJ-C60H4z4j.js.map +1 -0
- package/dist/web/assets/katex-HP8lGamR.js +258 -0
- package/dist/web/assets/katex-HP8lGamR.js.map +1 -0
- package/dist/web/assets/layout-CpcJW2by.js +2 -0
- package/dist/web/assets/layout-CpcJW2by.js.map +1 -0
- package/dist/web/assets/linear-BRdw4K7J.js +2 -0
- package/dist/web/assets/linear-BRdw4K7J.js.map +1 -0
- package/dist/web/assets/map-CKPHyBGT.js +2 -0
- package/dist/web/assets/map-CKPHyBGT.js.map +1 -0
- package/dist/web/assets/mermaid.core-DTdyJUyZ.js +315 -0
- package/dist/web/assets/mermaid.core-DTdyJUyZ.js.map +1 -0
- package/dist/web/assets/mindmap-definition-FAOFIHXS-Ctd1Avoh.js +97 -0
- package/dist/web/assets/mindmap-definition-FAOFIHXS-Ctd1Avoh.js.map +1 -0
- package/dist/web/assets/ordinal-Cboi1Yqb.js +2 -0
- package/dist/web/assets/ordinal-Cboi1Yqb.js.map +1 -0
- package/dist/web/assets/pegDiagram-VL7TDLO6-CIJ0TF6e.js +2 -0
- package/dist/web/assets/pegDiagram-VL7TDLO6-CIJ0TF6e.js.map +1 -0
- package/dist/web/assets/pieDiagram-7S7Q4E2Y-PFIMn7nc.js +40 -0
- package/dist/web/assets/pieDiagram-7S7Q4E2Y-PFIMn7nc.js.map +1 -0
- package/dist/web/assets/quadrantDiagram-CIZ2JOQS-CX4UzzKN.js +8 -0
- package/dist/web/assets/quadrantDiagram-CIZ2JOQS-CX4UzzKN.js.map +1 -0
- package/dist/web/assets/railroadDiagram-AXF67PYL-DOjwFK86.js +2 -0
- package/dist/web/assets/railroadDiagram-AXF67PYL-DOjwFK86.js.map +1 -0
- package/dist/web/assets/requirementDiagram-LRYGKXZP-BmoMDpuG.js +85 -0
- package/dist/web/assets/requirementDiagram-LRYGKXZP-BmoMDpuG.js.map +1 -0
- package/dist/web/assets/sankeyDiagram-W5VNT64P-BxsyY7C0.js +41 -0
- package/dist/web/assets/sankeyDiagram-W5VNT64P-BxsyY7C0.js.map +1 -0
- package/dist/web/assets/sequenceDiagram-SI44F4Z6-Dvv3O0Rc.js +163 -0
- package/dist/web/assets/sequenceDiagram-SI44F4Z6-Dvv3O0Rc.js.map +1 -0
- package/dist/web/assets/sizeCapture-X5ZJPWSS-CS4OfTKV.js +2 -0
- package/dist/web/assets/sizeCapture-X5ZJPWSS-CS4OfTKV.js.map +1 -0
- package/dist/web/assets/stateDiagram-OKZ733FA-DLx4cUCx.js +2 -0
- package/dist/web/assets/stateDiagram-OKZ733FA-DLx4cUCx.js.map +1 -0
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CGTS_71O.js +2 -0
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CGTS_71O.js.map +1 -0
- package/dist/web/assets/swimlanes-SLNWSIFB-CfboUePX.js +3 -0
- package/dist/web/assets/swimlanes-SLNWSIFB-CfboUePX.js.map +1 -0
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DC39ekbl.js +9 -0
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DC39ekbl.js.map +1 -0
- package/dist/web/assets/timeline-definition-Z64GVDOM-BEMGCQrU.js +121 -0
- package/dist/web/assets/timeline-definition-Z64GVDOM-BEMGCQrU.js.map +1 -0
- package/dist/web/assets/vennDiagram-T6HMQDX7-y2pz9Wz-.js +35 -0
- package/dist/web/assets/vennDiagram-T6HMQDX7-y2pz9Wz-.js.map +1 -0
- package/dist/web/assets/wardleyDiagram-T6FBY63Y-CQ-4d_jJ.js +79 -0
- package/dist/web/assets/wardleyDiagram-T6FBY63Y-CQ-4d_jJ.js.map +1 -0
- package/dist/web/assets/xychartDiagram-ELKLHX3M-BDr2YKTM.js +8 -0
- package/dist/web/assets/xychartDiagram-ELKLHX3M-BDr2YKTM.js.map +1 -0
- package/dist/web/icons/apple-touch-icon.png +0 -0
- package/dist/web/icons/favicon-16.png +0 -0
- package/dist/web/icons/favicon-32.png +0 -0
- package/dist/web/icons/icon-192-maskable.png +0 -0
- package/dist/web/icons/icon-192.png +0 -0
- package/dist/web/icons/icon-512-maskable.png +0 -0
- package/dist/web/icons/icon-512.png +0 -0
- package/dist/web/index.html +19 -0
- package/dist/web/manifest.webmanifest +17 -0
- package/dist/web/sw.js +181 -0
- package/dist/web/version.json +1 -0
- 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
|
+
};
|