@ours.network/notifications 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 (107) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +231 -0
  3. package/dist/cli.js +446 -0
  4. package/dist/config.js +104 -0
  5. package/dist/daemon.js +353 -0
  6. package/dist/http.js +31 -0
  7. package/dist/log-store.js +48 -0
  8. package/dist/presence.js +157 -0
  9. package/dist/provision.js +29 -0
  10. package/dist/push.js +6 -0
  11. package/dist/web-push-sender.js +41 -0
  12. package/mufl_code/20E39399689579D5AFA5132C59DEDDD328C3BCA1A74D546F2193B36585DDD283.muflo +0 -0
  13. package/mufl_code/config.mufl +21 -0
  14. package/mufl_code/core/.github/FUNDING.yml +3 -0
  15. package/mufl_code/core/.github/workflows/docs.yml +39 -0
  16. package/mufl_code/core/.github/workflows/tests.yml +66 -0
  17. package/mufl_code/core/COMMERCIAL-LICENCE.md +25 -0
  18. package/mufl_code/core/COMPATIBILITY.md +221 -0
  19. package/mufl_code/core/CONTRIBUTING.md +37 -0
  20. package/mufl_code/core/LICENSE +98 -0
  21. package/mufl_code/core/NOTIFICATIONS_CONTRACT.md +290 -0
  22. package/mufl_code/core/README.md +91 -0
  23. package/mufl_code/core/SECURITY.md +40 -0
  24. package/mufl_code/core/TRADEMARKS.md +24 -0
  25. package/mufl_code/core/a2a_capabilities.mm +449 -0
  26. package/mufl_code/core/a2a_cluster.mm +601 -0
  27. package/mufl_code/core/a2a_control.mm +90 -0
  28. package/mufl_code/core/a2a_messaging.mm +4817 -0
  29. package/mufl_code/core/a2a_monitoring.mm +94 -0
  30. package/mufl_code/core/a2a_notification_integration.mm +144 -0
  31. package/mufl_code/core/a2a_notifications.mm +1127 -0
  32. package/mufl_code/core/a2a_protocol.mm +246 -0
  33. package/mufl_code/core/a2a_versions.mm +801 -0
  34. package/mufl_code/core/config.mufl +29 -0
  35. package/mufl_code/core/docs/.vitepress/config.mts +58 -0
  36. package/mufl_code/core/docs/citations.json +688 -0
  37. package/mufl_code/core/docs/guide/01-vendor-the-core.md +50 -0
  38. package/mufl_code/core/docs/guide/02-configure-and-compile.md +100 -0
  39. package/mufl_code/core/docs/guide/03-wire-the-host.md +154 -0
  40. package/mufl_code/core/docs/guide/04-connect-and-message.md +156 -0
  41. package/mufl_code/core/docs/guide/05-test-your-app.md +75 -0
  42. package/mufl_code/core/docs/guide/index.md +63 -0
  43. package/mufl_code/core/docs/how-it-works/capabilities-and-control.md +77 -0
  44. package/mufl_code/core/docs/how-it-works/cluster.md +65 -0
  45. package/mufl_code/core/docs/how-it-works/identity.md +56 -0
  46. package/mufl_code/core/docs/how-it-works/invites-and-contacts.md +76 -0
  47. package/mufl_code/core/docs/how-it-works/messaging.md +55 -0
  48. package/mufl_code/core/docs/how-it-works/monitoring-and-config.md +72 -0
  49. package/mufl_code/core/docs/how-it-works/overview.md +45 -0
  50. package/mufl_code/core/docs/how-it-works/versioning.md +116 -0
  51. package/mufl_code/core/docs/index.md +27 -0
  52. package/mufl_code/core/docs/package-lock.json +3467 -0
  53. package/mufl_code/core/docs/package.json +15 -0
  54. package/mufl_code/core/docs/reference/contributing.md +26 -0
  55. package/mufl_code/core/docs/reference/glossary.md +108 -0
  56. package/mufl_code/core/docs/reference/implementations.md +23 -0
  57. package/mufl_code/core/docs/reference/modules.md +19 -0
  58. package/mufl_code/core/docs/scripts/check-citations.mjs +36 -0
  59. package/mufl_code/core/docs/scripts/check-denylist.mjs +30 -0
  60. package/mufl_code/core/docs/scripts/gen-llms.mjs +32 -0
  61. package/mufl_code/core/docs/workflows/cluster.md +89 -0
  62. package/mufl_code/core/docs/workflows/contact-exchange.md +60 -0
  63. package/mufl_code/core/docs/workflows/contact-restore.md +60 -0
  64. package/mufl_code/core/docs/workflows/control-verbs.md +70 -0
  65. package/mufl_code/core/docs/workflows/index.md +57 -0
  66. package/mufl_code/core/docs/workflows/introductions.md +60 -0
  67. package/mufl_code/core/docs/workflows/messaging.md +75 -0
  68. package/mufl_code/core/docs/workflows/monitoring.md +84 -0
  69. package/mufl_code/core/docs/workflows/notifications.md +109 -0
  70. package/mufl_code/core/release-notes/0.1.md +60 -0
  71. package/mufl_code/core/release-notes/0.3.md +22 -0
  72. package/mufl_code/core/release-notes/0.4.md +97 -0
  73. package/mufl_code/core/tests/README.md +83 -0
  74. package/mufl_code/core/tests/compat/README.md +82 -0
  75. package/mufl_code/core/tests/compat/compat.mjs +245 -0
  76. package/mufl_code/core/tests/compat/compat_peer.mjs +146 -0
  77. package/mufl_code/core/tests/compat/run_compat.sh +96 -0
  78. package/mufl_code/core/tests/corpus.mjs +139 -0
  79. package/mufl_code/core/tests/corpus_actor.mu +242 -0
  80. package/mufl_code/core/tests/fatcompile_actor.mu +52 -0
  81. package/mufl_code/core/tests/mig.mjs +805 -0
  82. package/mufl_code/core/tests/mig_actor.mu +518 -0
  83. package/mufl_code/core/tests/migapp.mjs +431 -0
  84. package/mufl_code/core/tests/migapp_actor.mu +294 -0
  85. package/mufl_code/core/tests/mufl_semantics/run.sh +69 -0
  86. package/mufl_code/core/tests/mufl_semantics/t11_three_version.mufl +47 -0
  87. package/mufl_code/core/tests/mufl_semantics/t2_extra_fields.mufl +11 -0
  88. package/mufl_code/core/tests/mufl_semantics/t7_runtime.mufl +30 -0
  89. package/mufl_code/core/tests/mufl_semantics/t9_passthrough.mufl +19 -0
  90. package/mufl_code/core/tests/mufl_semantics/t_str_order.mufl +19 -0
  91. package/mufl_code/core/tests/notif.mjs +755 -0
  92. package/mufl_code/core/tests/notif_actor.mu +450 -0
  93. package/mufl_code/core/tests/protocol_container.mm +18 -0
  94. package/mufl_code/core/tests/run.sh +65 -0
  95. package/mufl_code/core/tests/run_corpus.sh +51 -0
  96. package/mufl_code/core/tests/run_fatcompile.sh +40 -0
  97. package/mufl_code/core/tests/run_mig.sh +51 -0
  98. package/mufl_code/core/tests/run_migapp.sh +51 -0
  99. package/mufl_code/core/tests/run_notif.sh +57 -0
  100. package/mufl_code/core/tests/test.mjs +750 -0
  101. package/mufl_code/core/tests/test_actor.mu +564 -0
  102. package/mufl_code/core/tests/test_common.mjs +93 -0
  103. package/mufl_code/core/version.mm +24 -0
  104. package/mufl_code/packet.mu +170 -0
  105. package/mufl_code/protocol_container.mm +18 -0
  106. package/package.json +43 -0
  107. package/scripts/gen-vapid.mjs +16 -0
@@ -0,0 +1,94 @@
1
+ // Shared ours monitoring library — CONTROL-PLANE (receiver) side.
2
+ //
3
+ // Monitoring is split across two libraries by WHERE the state lives:
4
+ //
5
+ // - NODE side (a2a_messaging): the forced copy GENERATION
6
+ // (monitor_copy_actions in the send/receive chokepoint) AND the gate state
7
+ // it reads (monitoring_proxy / proxy_pending) plus that state's mutators —
8
+ // the 6-digit bind ceremony (set_proxy_pending / verify_proxy_code), the
9
+ // CP-authenticated disable_monitoring, and get_monitoring_status. They MUST
10
+ // live in a2a_messaging because the gate state is `hidden` there, and hidden
11
+ // state is mutable only by its declaring library — that is precisely what
12
+ // stops an app (or any other library) from assigning monitoring_proxy -> NIL
13
+ // to switch monitoring off without the ceremony.
14
+ //
15
+ // - CONTROL-PLANE side (HERE): the receiver for the copies a monitored node
16
+ // forwards. It touches NO gate state — it only validates the sender is a
17
+ // known contact and hands the copy to the app's storage hook. This is the
18
+ // only piece that can safely live outside a2a_messaging.
19
+ //
20
+ // Security model (founder directive, the ours monitoring design's accepted self-assertion limitation): monitoring is secured by the
21
+ // bind ceremony + open source + eviction; a node self-asserts monitoring_status,
22
+ // it is NOT cert-enforced. The "app can't override" guarantee is the narrower,
23
+ // real one: copy generation is unconditional core code and the gate state is
24
+ // non-app-writable (hidden), so an honest app cannot silently disable monitoring.
25
+ library a2a_monitoring loads libraries
26
+ current_transaction_info,
27
+ encrypted_channel,
28
+ a2a_messaging
29
+ uses transactions
30
+ {
31
+ hidden
32
+ {
33
+ // App-injected: store one received monitoring copy. Storage stays app-side
34
+ // (the messenger persists the feed), like a2a_messaging's message hooks.
35
+ // Receives ($source_id, $copy). Fires only for a known-contact sender.
36
+ on_monitoring_copy_received is (any -> transaction::action::type[]) = fn (_: any) -> transaction::action::type[] { abort "on_monitoring_copy_received hook is unset in a2a_monitoring (call a2a_monitoring::init)." when TRUE. return []. }
37
+ // core 3.2: store one pushed roster snapshot. Fires only for a known-contact
38
+ // sender (the node this control plane monitors). Receives ($source_id, $version, $members).
39
+ on_roster_update is (any -> transaction::action::type[]) = fn (_: any) -> transaction::action::type[] { abort "on_roster_update hook is unset in a2a_monitoring (call a2a_monitoring::init)." when TRUE. return []. }
40
+ }
41
+
42
+ init = fn (_:(
43
+ $on_monitoring_copy_received -> cb: (any -> transaction::action::type[]),
44
+ $on_roster_update -> rcb: (any -> transaction::action::type[])
45
+ ))
46
+ {
47
+ on_monitoring_copy_received -> cb.
48
+ on_roster_update -> rcb.
49
+ }
50
+
51
+ // Receive a forced copy from a node this control plane monitors. Storage is
52
+ // the app's (the on_monitoring_copy_received hook); the source is the channel-
53
+ // authenticated envelope sender, which must be a known contact. Touches no
54
+ // gate state — purely the CP-side ingest.
55
+ fn handle_receive_monitoring_copy (args: any) -> transaction::results::type
56
+ {
57
+ current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::external,).
58
+ encrypted_channel::check_encrypted_or_abort().
59
+
60
+ sender_id = current_transaction_info::get_external_envelope_or_abort() $from.
61
+ abort "Monitoring copy from an unknown sender was rejected." when (a2a_messaging::contacts sender_id) == NIL.
62
+ copy = (args $copy) safe a2a_messaging::monitoring_copy_t.
63
+ abort "Unsupported monitoring copy version." when (copy $version) != 1.
64
+
65
+ return transaction::success (on_monitoring_copy_received (
66
+ $source_id -> sender_id,
67
+ $copy -> copy
68
+ )).
69
+ }
70
+
71
+ trn receive_monitoring_copy args: any
72
+ {
73
+ return handle_receive_monitoring_copy args.
74
+ }
75
+
76
+ // core 3.2: receive a roster snapshot pushed by a node we control. Same CP-side
77
+ // ingest contract as receive_monitoring_copy — known-contact sender only, no gate
78
+ // state touched; the app stores it (on_roster_update). $members is opaque (the app
79
+ // interprets member_t); $version lets the app dedup/order.
80
+ fn handle_receive_roster_update (args: any) -> transaction::results::type
81
+ {
82
+ current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::external,).
83
+ encrypted_channel::check_encrypted_or_abort().
84
+ sender_id = current_transaction_info::get_external_envelope_or_abort() $from.
85
+ abort "Roster update from an unknown sender was rejected." when (a2a_messaging::contacts sender_id) == NIL.
86
+ return transaction::success (on_roster_update (
87
+ $source_id -> sender_id,
88
+ $version -> (args $version),
89
+ $members -> (args $members)
90
+ )).
91
+ }
92
+
93
+ trn receive_roster_update args: any { return handle_receive_roster_update args. }
94
+ }
@@ -0,0 +1,144 @@
1
+ // Core-owned notification integration for ordinary message sends.
2
+ //
3
+ // Recipients hand each sender a scoped notify_address_t over their established
4
+ // encrypted channel. This library owns those opaque handouts, persists them,
5
+ // and installs the optional a2a_messaging successful-send middleware. Missing
6
+ // or deleted handouts are no-ops; notification delivery is fire-and-forget.
7
+ library a2a_notification_integration loads libraries
8
+ current_transaction_info,
9
+ encrypted_channel,
10
+ a2a_messaging,
11
+ a2a_notifications
12
+ uses transactions
13
+ {
14
+ receive_notify_address_tx = "::a2a_notification_integration::receive_notify_address".
15
+
16
+ // Peer cid -> validated serialized notify_address_t. Opaque serialized
17
+ // storage composes cleanly beside a2a_messaging::export_core_state.
18
+ received_notify_addresses is (global_id ->> bin) = (,).
19
+
20
+ metadef integration_state_t: (
21
+ $version -> int,
22
+ $received_notify_addresses -> (global_id ->> bin)
23
+ ).
24
+
25
+ fn _save_state (_) = (transaction::action::return_data ($kind -> $save_state)).
26
+
27
+ // Relationship binding is checked at the handout boundary. The service
28
+ // later validates the token against its signed live exact-token store; the
29
+ // current protocol intentionally requires no sender<->service contact.
30
+ fn validate_address_for (recipient: global_id, blob: bin) -> a2a_notifications::notify_address_t
31
+ {
32
+ addr = a2a_notifications::decode_notify_address blob.
33
+ abort "Notify address token recipient does not match its sender." when (addr $token $c $recipient_cid) != recipient.
34
+ abort "Notify address token is not scoped to this sender." when (addr $token $c $scope) != _str (_get_container_id()).
35
+ return addr.
36
+ }
37
+
38
+ // Ordinary consumers are notification clients, not notification services.
39
+ // Install safe no-op service/UI callbacks here so they only need to init
40
+ // this integration. A service host may call a2a_notifications::init after
41
+ // this function to replace the defaults with durable/WebPush hooks.
42
+ init = fn (_:($_read_or_abort -> read: (bin->any)))
43
+ {
44
+ a2a_notifications::init (
45
+ $_read_or_abort -> read,
46
+ $on_notification_posted -> fn (_: any) -> transaction::action::type[] { return []. },
47
+ $on_notifications_marked_read -> fn (_: any) -> transaction::action::type[] { return []. },
48
+ $on_unregistered -> fn (_: any) -> transaction::action::type[] { return []. },
49
+ $on_notify_registration -> fn (_: any) -> transaction::action::type[] { return []. }
50
+ ).
51
+ a2a_messaging::set_post_send_middleware (fn (arg: any) -> transaction::action::type[]
52
+ {
53
+ target_id = (arg $target_id) safe global_id.
54
+ address = received_notify_addresses target_id.
55
+ if address == NIL { return []. }
56
+
57
+ wire_id = (arg $wire_id) safe str.
58
+ // Built only from stringified global ids and wire ids, whose hex
59
+ // alphabets require no JSON escaping.
60
+ payload = "{\"v\":1,\"kind\":\"message\",\"sender\":\"" +
61
+ (_str (_get_container_id())) + "\",\"wire_id\":\"" + wire_id + "\"}".
62
+ return [ a2a_notifications::notification_send_action address? payload wire_id ].
63
+ }).
64
+ }
65
+
66
+ fn handle_receive_notify_address (args: any) -> transaction::results::type
67
+ {
68
+ current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::external,).
69
+ encrypted_channel::check_encrypted_or_abort ().
70
+ from = current_transaction_info::get_external_envelope_or_abort() $from.
71
+
72
+ if (args $address) == NIL
73
+ {
74
+ if (received_notify_addresses from) != NIL { delete received_notify_addresses from. }
75
+ return transaction::success [ _save_state NIL ].
76
+ }
77
+
78
+ blob = (args $address) safe bin.
79
+ validate_address_for from blob.
80
+ received_notify_addresses from -> blob.
81
+ return transaction::success [ _save_state NIL ].
82
+ }
83
+
84
+ trn receive_notify_address args: any
85
+ {
86
+ return handle_receive_notify_address args.
87
+ }
88
+
89
+ // Recipient-side distribution: build the sender-scoped handout from the
90
+ // notification client mirror and deliver it over the existing encrypted
91
+ // contact channel. Consumers retain only UI policy, not protocol state.
92
+ trn send_notify_address _:($service -> service_ref: str, $contact -> contact_ref: str)
93
+ {
94
+ current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
95
+ target_id = a2a_messaging::resolve_contact contact_ref.
96
+ blob = a2a_notifications::build_notify_address service_ref contact_ref.
97
+ return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
98
+ return transaction::success [
99
+ encrypted_channel::send_encrypted_tx target_id (
100
+ $name -> receive_notify_address_tx,
101
+ $targ -> ($address -> blob)
102
+ ),
103
+ transaction::action::return_data ($kind -> $data, $payload -> ($sent_to -> target_id)),
104
+ _save_state NIL
105
+ ].
106
+ }).
107
+ }
108
+
109
+ // NIL is the wire-level mute/removal signal and intentionally leaves the
110
+ // ordinary messaging relationship untouched.
111
+ trn delete_notify_address _:($contact -> contact_ref: str)
112
+ {
113
+ current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
114
+ target_id = a2a_messaging::resolve_contact contact_ref.
115
+ return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
116
+ return transaction::success [
117
+ encrypted_channel::send_encrypted_tx target_id (
118
+ $name -> receive_notify_address_tx,
119
+ $targ -> ($address -> NIL)
120
+ ),
121
+ transaction::action::return_data ($kind -> $data, $payload -> ($sent_to -> target_id)),
122
+ _save_state NIL
123
+ ].
124
+ }).
125
+ }
126
+
127
+ fn export_state (_) -> integration_state_t
128
+ {
129
+ return (
130
+ $version -> 1,
131
+ $received_notify_addresses -> received_notify_addresses
132
+ ).
133
+ }
134
+
135
+ fn import_state (data: any) -> nil
136
+ {
137
+ abort "Unsupported notification integration state version." when ((data $version) safe int) != 1.
138
+ imported = (data $received_notify_addresses) safe (global_id ->> bin).
139
+ // Validate now so a corrupt local snapshot cannot turn a later accepted
140
+ // send_message into a middleware parse abort.
141
+ sc imported -- (peer -> blob) { validate_address_for peer blob. }
142
+ received_notify_addresses -> imported.
143
+ }
144
+ }