@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,65 @@
1
+ # Cluster
2
+
3
+ The `core.cluster` capability, defined in `a2a_cluster.mm`, gives a root node a
4
+ uniform surface for child/subagent lifecycle, per-child monitoring authorization,
5
+ the host-local contact book, and introductions between children and contacts.
6
+ The authz chokepoint in `a2a_capabilities::dispatch` gates every verb — handlers
7
+ run only after it clears.
8
+
9
+ Source: [`a2a_cluster.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_cluster.mm).
10
+
11
+ ## Registry
12
+
13
+ The root packet holds a `cluster_members` map (keyed by container id) of
14
+ `member_t` rows. Each row tracks the child's name, role id, bio, persona,
15
+ current monitoring state, and advertised capability ids. The registry is a
16
+ projection of host truth: `reconcile` runs on boot and on a periodic schedule to
17
+ add children the host knows about but the registry doesn't, and to drop members
18
+ the host has removed.
19
+
20
+ ```mufl
21
+ metadef member_t: ($cid -> global_id, $role_id -> str, $name -> str,
22
+ $bio -> str, $persona -> str, $monitoring -> str, $caps -> str[]).
23
+ ```
24
+
25
+ ## Lifecycle verbs
26
+
27
+ The `cluster_handler` function switches on the `$verb` field of each inbound
28
+ `control_envelope_t` and dispatches to the appropriate handler. The authorized
29
+ bare verb names (as they appear on the wire and in `control_auth_class`) are:
30
+
31
+ | Verb | Effect |
32
+ |---|---|
33
+ | `list` | Returns the current `cluster_members` registry. |
34
+ | `set_bio` | Updates the bio field for a child in the registry (registry-only, no host op). |
35
+ | `set_persona` | Updates the persona field for a child in the registry (registry-only, no host op). |
36
+ | `set_monitoring` | Emits a `host_set_child_monitoring` notify-action; confirmed by `confirm_child_monitoring`. |
37
+ | `create` | Emits a `host_provision_child` notify-action; the daemon provisions the child packet and calls back `register_provisioned_child`. |
38
+ | `remove` | Emits a `host_destroy_child` notify-action; confirmed by `confirm_child_destroyed`. |
39
+ | `contact` | Emits a `host_mint_child_invite` notify-action; the daemon runs `generate_invite` inside the child packet and calls back `register_child_invite`. |
40
+ | `introduce` | Composes `a2a_messaging::emit_pair` to introduce two established contacts to each other. |
41
+
42
+ For the full verb surface see [`a2a_cluster.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_cluster.mm).
43
+
44
+ Create and remove are asynchronous: the handler stores a pending-req (keyed by a
45
+ monotonic handle) and immediately acknowledges `{pending: true}`. The matching
46
+ host callback consumes the pending-req atomically and routes the final response to
47
+ the stored controller.
48
+
49
+ ## Per-child monitoring authorization
50
+
51
+ `set_monitoring` derives the control-plane identity from the root's own
52
+ ceremony-pinned `monitoring_proxy` — never from the request args. The root must
53
+ be bound to a cluster control plane before enabling monitoring for any child.
54
+ Disabling does not require a bound CP; it clears the child's proxy immediately.
55
+
56
+ ## Cross-cluster introductions
57
+
58
+ The `introduce` verb accepts two contact references (`$peer_a`, `$peer_b`) and
59
+ emits a pair of introduction messages via `a2a_messaging::emit_pair`. Both peers
60
+ must already be established contacts with stored address documents. The
61
+ `core.connect` capability exposes the same `introduce` verb (via
62
+ `connect_handler`) for peer-to-peer introduction without cluster context.
63
+
64
+ See [Capabilities & control](./capabilities-and-control.md) for the envelope and
65
+ dispatch model that gates every verb above.
@@ -0,0 +1,56 @@
1
+ # Identity: roots & roles
2
+
3
+ An identity in ours.network is either a **root** — a self-sovereign keypair — or a **delegated
4
+ role** anchored to a root. A root's `delegation_cert` field is `NIL`; detection is structural,
5
+ not a flag. A role carries a signed `delegation_cert_t` that binds the role's container id and
6
+ address-document hash to its root, signed by the root's keys.
7
+
8
+ Source: [`a2a_protocol.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_protocol.mm).
9
+
10
+ ## Delegation verification
11
+
12
+ When a peer presents its identity during invite redemption, verify the full chain with
13
+ `verify_peer_delegation`:
14
+
15
+ ```
16
+ fn verify_peer_delegation (
17
+ peer_cid: global_id,
18
+ peer_ad_hash: hash_code,
19
+ cert: delegation_cert_t,
20
+ rp: root_profile_t
21
+ ) -> contact_root_t
22
+ ```
23
+
24
+ The root profile carries the root's key list, so no prior knowledge of the root is needed. On
25
+ success the function returns a `contact_root_t` (root container id, root name, role id) to store
26
+ beside the contact. Any mismatch — wrong version, mismatched cid, bad signature — aborts.
27
+
28
+ ## Control-plane governance (§3c)
29
+
30
+ Two verifiers support the optional non-enforcing governance edge between a root and its control
31
+ plane:
32
+
33
+ - **`verify_cp_attestation`** — verifies the CP's signed attestation that it governs a given
34
+ root. Requires the caller to have run `process_address_document` on the CP's address document
35
+ first.
36
+ - **`verify_root_cp_binding`** — verifies the root's self-signed edge naming the CP, domain-
37
+ separated by `cp_attestation_context_tag`. The authoritative monitoring gate remains the
38
+ 6-digit bind ceremony; these verifiers let peers TOFU-pin the governance edge without an extra
39
+ round-trip.
40
+
41
+ Source: [`a2a_protocol.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_protocol.mm).
42
+
43
+ ## Wire shapes and the structural-records rule
44
+
45
+ All identity wire shapes are MUFL record types defined in `a2a_protocol.mm`. The key protocol
46
+ guarantee is: moving a shape between libraries never changes its bytes; renaming or retyping a
47
+ `$field` does. Field names are the version boundary. Example shapes:
48
+
49
+ ```
50
+ metadef delegation_cert_t: ($c -> delegation_core_t, $s -> crypto_signature).
51
+ metadef root_profile_t: ($p -> root_profile_core_t, $s -> crypto_signature).
52
+ metadef root_cp_binding_t: ($c -> root_cp_binding_core_t, $s -> crypto_signature).
53
+ ```
54
+
55
+ The invite shape carries no identity keys — identity material moves inside the encrypted
56
+ two-message redeem hop (see [Invites & contacts](./invites-and-contacts.md)).
@@ -0,0 +1,76 @@
1
+ # Invites & contacts
2
+
3
+ Contact establishment uses a **slim ephemeral-key invite**. The invite blob carries
4
+ only an ephemeral encryption pubkey, the inviter's container id, a display name, and a crypto
5
+ scheme id — no long-term keys, no self-signatures. Identity material moves inside two encrypted
6
+ messages exchanged after the initial out-of-band transfer.
7
+
8
+ Source: [`a2a_messaging.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_messaging.mm).
9
+ Sequence diagrams: [Contact exchange](../workflows/contact-exchange.md) and
10
+ [Introductions](../workflows/introductions.md).
11
+
12
+ ## Invite shape
13
+
14
+ ```
15
+ metadef invite_eph_t: (
16
+ $d -> global_id, -- invite id (correlates legs; single-use)
17
+ $c -> global_id, -- inviter container id
18
+ $n -> str, -- inviter display name
19
+ $k -> publickey_encrypt, -- inviter ephemeral pubkey
20
+ $v -> int -- crypto scheme id
21
+ ).
22
+ ```
23
+
24
+ Generate an invite with `generate_invite`. The returned blob is the value to share out-of-band
25
+ (QR code, deep link, etc.).
26
+
27
+ ## Three-leg redeem flow
28
+
29
+ | Leg | Direction | Mechanism | What moves |
30
+ |-----|-----------|-----------|------------|
31
+ | OOB | inviter → responder | out-of-band | slim `invite_eph_t` blob |
32
+ | Leg 1 | responder → inviter | BARE send (box to invite's eph pub) | responder identity bundle |
33
+ | Leg 2 | inviter (receives leg 1) | verify bundle, consume invite, emit leg 3 | ← inviter now holds responder's verified AD |
34
+ | Leg 3 | inviter → responder | BARE send (box to responder's eph pub) | inviter identity bundle |
35
+
36
+ Both boxes carry the same identity bundle shape: address document + optional delegation cert +
37
+ optional root profile + optional §3c CP binding. After leg 3, both sides hold the other's
38
+ verified address document and `encrypted_channel` resumes for all subsequent traffic.
39
+
40
+ Redeem an invite with `add_contact` (leg 1). The inviter processes the responder's leg 1 via
41
+ `submit_invite_response` and replies with leg 3 (`complete_invite`).
42
+
43
+ ## Single-use guarantee
44
+
45
+ An invite is consumed at the first valid leg-2 (inviter's receipt of leg 1). A second attempt for the same
46
+ `invite_id` aborts with `already-redeemed` and mutates no state. The ephemeral private key is
47
+ deleted atomically with the invite record (INV-4: secrets are never exported).
48
+
49
+ Test suite scenario T3 (`single-use`) asserts a second leg-1 for the same `invite_id` aborts
50
+ and leaves inviter state unchanged. Scenario T4 (`invalid-then-valid`) asserts that a failed
51
+ box-open does not consume the invite, so a subsequent valid redeem succeeds. See
52
+ [`tests/README.md`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/tests/README.md).
53
+
54
+ ## Pending-invite transience
55
+
56
+ Responder-side pending redemption state (`pending_redemptions`) is not exported and does not
57
+ survive a daemon restart. An interrupted handshake does not leave half-registered state; the
58
+ responder must run `add_contact` again with a fresh invite. This is the fail-closed design: no
59
+ dangling partial contacts after a restart.
60
+
61
+ ## Introductions via core.connect
62
+
63
+ When two nodes share a control plane that advertises `core.connect`, the CP can introduce them
64
+ without another out-of-band invite. The CP sends each node the other's signed address document
65
+ via `ingest_connect_descriptor`. The receiving node verifies:
66
+
67
+ 1. The relay came from its bound CP (or the CP its root designated).
68
+ 2. Its own live manifest advertises `core.connect`.
69
+ 3. The peer's address document self-signature is valid (proof-of-possession).
70
+
71
+ The contact is registered immediately — no SAS, no confirmation step.
72
+
73
+ Scenario T1 (`happy-flat`) verifies the end-to-end invite + `send_message` round-trip in both
74
+ directions. Scenarios T5–T8 cover adversarial inputs (tampered box, cid-bind mismatch, stripped
75
+ PoP, unexpected inviter on leg 3). See
76
+ [`tests/README.md`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/tests/README.md).
@@ -0,0 +1,55 @@
1
+ # Messaging
2
+
3
+ All message and file traffic between contacts rides the `encrypted_channel`, established during
4
+ invite redemption and persisted across restarts by replaying stored peer address documents on
5
+ import. `a2a_messaging.mm` is the single path for all send/receive operations.
6
+
7
+ Source: [`a2a_messaging.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_messaging.mm).
8
+ Module description: [`README.md`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/README.md)
9
+ — "Contact and messaging transactions (generate invite, add/remove contact, send message, send
10
+ file, inbound receive) and the introduction flow."
11
+
12
+ Sequence diagrams: [Send & receive messages](../workflows/messaging.md) and
13
+ [Contact restore](../workflows/contact-restore.md).
14
+
15
+ ## Outbound
16
+
17
+ - **`send_message`** — sends text to a named or container-id-referenced contact. Accepts an
18
+ optional `reply_to` pointer (`$wire_id`, `$sentence`) for threaded replies. Every message
19
+ receives a stable `wire_id` (a stringified `_new_id`) that the peer can reference in its own
20
+ replies.
21
+ - **`send_file`** — sends binary data with a `$filename` and optional `$mime`. Files and text
22
+ messages share the same `wire_id` namespace; a reply pointer can reference either.
23
+
24
+ Both fail if the contact has no registered address document (see Contact-restore below).
25
+
26
+ ## Inbound
27
+
28
+ | Transaction name | Source constant | Payload |
29
+ |-----------------|-----------------|---------|
30
+ | `::actor::receive_message` | `receive_message_tx` | `$text`, `$wire_id`, optional `$reply_to`, sender id from envelope |
31
+ | `::a2a_messaging::receive_file` | `receive_file_tx` | `$filename`, `$mime`, `$data`, `$wire_id`, optional `$reply_to` |
32
+
33
+ The core fires the app-injected `on_message_received` / `on_file_received` storage hooks.
34
+ Message storage is the consumer's responsibility; the core handles wire, validation, and contact
35
+ resolution.
36
+
37
+ ## Receiver-side state
38
+
39
+ `contacts` (keyed by container id) and `peer_ads` (keyed by container id) are the persistent
40
+ contact state. `encrypted_channel` resolves the peer's encryption key from `peer_ads` on every
41
+ send. `peer_ads` entries survive code upgrades because `import_core_state` replays each stored
42
+ address document through `process_address_document` — no re-handshake is needed.
43
+
44
+ ## Contact-restore across breaking changes
45
+
46
+ If a migration carries `contacts` but drops `peer_ads` for a contact, that contact becomes a
47
+ **DEGRADED contact** (cid present, no address document). `send_message` to a degraded contact
48
+ queues the message and fires a `request_contact_restore` handshake to re-fetch the peer's
49
+ address document. Once the peer's AD is re-established, queued messages flush automatically.
50
+
51
+ `send_file` to a degraded contact fails fast with an explicit error — binary payloads are not
52
+ queued. `send_message` toward the same contact will queue and drive the restore.
53
+
54
+ See [Identity: roots & roles](./identity.md) for the address document and key structure that
55
+ `encrypted_channel` depends on.
@@ -0,0 +1,72 @@
1
+ # Monitoring & config
2
+
3
+ Monitoring and configuration state is split across two libraries by where the
4
+ hidden gate state lives: `a2a_messaging.mm` owns the node-side gate and copy
5
+ generation; `a2a_monitoring.mm` owns the control-plane receiver.
6
+
7
+ Sources:
8
+ [`a2a_messaging.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_messaging.mm),
9
+ [`a2a_monitoring.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_monitoring.mm).
10
+
11
+ ## Node side: forced copies
12
+
13
+ The `monitoring_proxy` field in `a2a_messaging` is declared `hidden`. That means
14
+ only code inside `a2a_messaging` can write it — an app or any other loading
15
+ library cannot assign `monitoring_proxy -> NIL` to suppress copies. The
16
+ `monitor_copy_actions` function gates every outbound and inbound message on this
17
+ field:
18
+
19
+ ```mufl
20
+ fn monitor_copy_actions (direction: str, peer_cid: global_id, date: time, body: str) -> transaction::action::type[]
21
+ ```
22
+
23
+ If `monitoring_proxy` is `NIL`, the function returns an empty action list and
24
+ no copy is sent. If it is set, the function appends an encrypted send to the
25
+ bound control plane on every message. The copy rides a distinct transaction
26
+ (`receive_monitoring_copy_tx`) so the control plane can distinguish it from
27
+ regular traffic.
28
+
29
+ ## Bind ceremony
30
+
31
+ `set_proxy_pending` and `verify_proxy_code` in `a2a_messaging` implement the
32
+ 6-digit bind ceremony that sets `monitoring_proxy`. They live in this library
33
+ — not in `a2a_monitoring` — precisely because `monitoring_proxy` is hidden here.
34
+ The ceremony is time-limited (300 seconds), attempt-limited (3 tries), and
35
+ requires code possession. Once verified, `monitoring_proxy` is set and copy
36
+ generation begins immediately.
37
+
38
+ Disable (`core.monitoring / disable` verb) requires the sender to be the bound
39
+ control plane; it clears `monitoring_proxy` so copy generation stops.
40
+
41
+ ## Control-plane side: receiving copies
42
+
43
+ `a2a_monitoring.mm` is the CP-side library. It touches no gate state — it only
44
+ validates that the sender is a known contact and hands the copy to the app's
45
+ storage hook:
46
+
47
+ ```mufl
48
+ trn receive_monitoring_copy args: any
49
+ ```
50
+
51
+ The app wires the `$on_monitoring_copy_received` hook at init. Storage is
52
+ entirely app-side; the core handles wire validation and contact checks.
53
+
54
+ ## Configuration
55
+
56
+ App configuration (`core.configuration`) is stored as an opaque string
57
+ (`app_config`) in `a2a_messaging`, also `hidden`. The bound control plane writes
58
+ it via the `set_config` verb and reads it via `get_config` — both
59
+ `core.configuration` capability verbs authorized in
60
+ `a2a_capabilities::control_auth_class`. (`get_app_config` is a local readonly
61
+ transaction in `a2a_messaging` used only by the node's own wrapper.) The
62
+ `authorize_control` function in `a2a_messaging` enforces that only the bound
63
+ control plane (the same identity that passed the monitoring ceremony) may
64
+ invoke these verbs — the configuration and monitoring authorities are the same
65
+ party.
66
+
67
+ The `$params` field in a `capability_t` descriptor carries the opaque config
68
+ schema for a frontend to render; the core stores only the raw blob and never
69
+ interprets it.
70
+
71
+ See [Capabilities & control](./capabilities-and-control.md) for the dispatch and
72
+ authz model, and [Cluster](./cluster.md) for per-child monitoring authorization.
@@ -0,0 +1,45 @@
1
+ # Overview
2
+
3
+ ours-mufl-core is the shared wire format and verification logic for the ours.network protocol,
4
+ written as MUFL (`.mm`) libraries. Every ours.network client — agent server, web messenger,
5
+ Telegram connector — vendors this repo as a git submodule and compiles the same libraries into
6
+ its own packet. Because all clients link the same code, they speak an **identical wire format**
7
+ and verification logic: a packet built by the web client and one built by the MCP agent are
8
+ byte-compatible peers.
9
+
10
+ The host application boots packets, routes inbound messages, and persists state. The protocol
11
+ libraries define what is valid; the host only moves bytes.
12
+
13
+ ## Consuming the core
14
+
15
+ Add the repo as a submodule to your application:
16
+
17
+ ```sh
18
+ git submodule add git@github.com:adapt-toolkit/ours-mufl-core.git mufl_code/core
19
+ ```
20
+
21
+ Your `config.mufl` loads it with `config_load #"core"`. The full setup is in
22
+ [01 · Vendor the core](../guide/01-vendor-the-core.md).
23
+
24
+ ## Modules at a glance
25
+
26
+ Seven `.mm` libraries and one config export. See [Modules](../reference/modules.md) for
27
+ per-module purpose and source links.
28
+
29
+ | File | Role |
30
+ |------|------|
31
+ | [`a2a_protocol.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_protocol.mm) | Wire shapes (invites, certs, profiles) and verification helpers |
32
+ | [`a2a_messaging.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_messaging.mm) | Invite, contact, send/receive message, and send/receive file transactions |
33
+ | [`a2a_capabilities.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_capabilities.mm) | App manifest, verb dispatch, well-known capabilities |
34
+ | [`a2a_cluster.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_cluster.mm) | Child lifecycle, host-local contact book, cluster management |
35
+ | [`a2a_monitoring.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_monitoring.mm) | Monitoring copy receiver (CP side) |
36
+ | [`a2a_control.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_control.mm) | Control-plane transport |
37
+ | [`version.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/version.mm) | Core version, readable at runtime via `get_core_version` |
38
+
39
+ Source: [`README.md`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/README.md).
40
+
41
+ ## Next steps
42
+
43
+ - [Identity: roots & roles](./identity.md) — delegation hierarchy and verification
44
+ - [Invites & contacts](./invites-and-contacts.md) — ephemeral-key invite and redeem flow
45
+ - [Messaging](./messaging.md) — send, receive, and contact-restore
@@ -0,0 +1,116 @@
1
+ # Versioning
2
+
3
+ `version.mm` is the single source of truth for the shared core's version. Every
4
+ library in the core loads it, so any packet that links the core carries exactly
5
+ one version stamp.
6
+
7
+ Source: [`version.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/version.mm).
8
+
9
+ ## The version type
10
+
11
+ ```mufl
12
+ metadef version_t: (
13
+ $MAJ -> int,
14
+ $MIN -> int,
15
+ $PATCH -> int
16
+ ).
17
+ ```
18
+
19
+ The current version is stored in the `hidden` `core_version` field and exposed
20
+ via `get_core_version`. The source comment states:
21
+
22
+ > This MUST be updated every time we update ANY code in the shared core.
23
+
24
+ Because every library loads `version`, a single edit to any `.mm` file in the
25
+ core requires a version bump before the change ships.
26
+
27
+ ## Runtime observability
28
+
29
+ Each deployed packet exposes its compiled-in version through the read-only
30
+ `get_version` transaction described in the README. An integrator can query any
31
+ running node to confirm which core version it is running — no out-of-band
32
+ coordination needed.
33
+
34
+ ## What changes mean for integrators
35
+
36
+ | Change | What to do |
37
+ |---|---|
38
+ | `$PATCH` bump | Wire format is unchanged. Re-compile against the updated submodule; re-run integration tests to confirm nothing regressed. |
39
+ | `$MIN` bump | New features added; no existing behaviour removed. Re-compile, re-run integration tests, and read the diff to find new transactions or capability verbs you may want to use. |
40
+ | `$MAJ` bump | Breaking changes. Re-compile, re-run all integration tests, and read the diff carefully — wire shapes, transaction names, or type contracts may have changed. |
41
+
42
+ The docs on this site track `main`; always check the current `core_version` in
43
+ `version.mm` to confirm which version the docs describe.
44
+
45
+ ## The versioned type registry (core 0.5.0)
46
+
47
+ Since 0.5.0, wire-level backward compatibility is an **explicit mechanism**, not a
48
+ convention: for every wire input surface whose shape ever changed,
49
+ [`a2a_versions.mm`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/a2a_versions.mm)
50
+ declares one frozen metadef **per shipped wire version** of its payload, the accepted
51
+ **union** (the handler's visible contract), the ordered version vector, a **discriminator**,
52
+ and a dispatch function:
53
+
54
+ ```mufl
55
+ metadef sir_payload_t: sir_payload_v5_t || sir_payload_v3_t || sir_payload_v2_t.
56
+
57
+ fn try_narrow_sir (raw: any) -> sir_narrowed_t // ($ok, $payload, $err)
58
+ ```
59
+
60
+ `try_narrow_*` reads the discriminator off the RAW value and exact-casts to the matched
61
+ version's type (**dispatch-then-narrow** — never cast-to-union as the selector, because
62
+ disjunction casts pick alternatives in canonical order and rebuild/strip; those toolchain
63
+ behaviors are pinned by `tests/mufl_semantics/`). Handlers then branch per version: the
64
+ v2 branch handles the payload the 0.2.0 way, the v5 branch the new way — backward
65
+ compatibility is visible in the types and the branches, not buried in NIL defaults.
66
+
67
+ ### `$pv` — the wire dialect id
68
+
69
+ Every 0.5.0+ core-originated send stamps `$pv -> 5` (minor-version ints) on its `$targ`
70
+ and inside the boxed identity-bundle payloads. Absence means a pre-0.5 peer; the registry
71
+ infers the shape (e.g. leg-1 bundle: `$name` present ⇒ v3, else v2).
72
+
73
+ | `$pv` | core | notes |
74
+ |---|---|---|
75
+ | *(absent)* | 0.2.0 / 0.3.0 / 0.4.x | shape-inferred per registry |
76
+ | 2 | 0.2.0 dialect | synthetic (0.2.0 never stamps) |
77
+ | 3–4 | 0.3.0 dialect | 0.4.x never shipped; wire-identical to 0.3 |
78
+ | 5 | 0.5.0 | first stamped dialect |
79
+ | > 5 | future | narrows as the newest registered version (class-A additions strip safely) |
80
+
81
+ Peers' dialects are learned passively into `a2a_messaging::contact_pv` (and their
82
+ advertised capability ids into `contact_caps`, from the `$caps` piggyback on the invite/
83
+ restore bundles) — no handshake round-trip. Both gate feature selection and diagnostics
84
+ only, **never authorization**.
85
+
86
+ ### Errors as data — old peers never crash
87
+
88
+ A payload from below the version floor (or matching no registered shape) is converted into
89
+ a first-class **error value** — `a2a_versions::version_error_t`
90
+ `($code, $surface, $message, $peer_version, $min_supported, $max_supported)` — delivered to
91
+ the local client as a `$protocol_error` notify event, while the transaction completes
92
+ successfully with **zero state writes**. A version-incompatible invite redeem therefore
93
+ does not consume the invite: the peer can update and redeem the very same invite.
94
+ Crypto/tamper and identity-verification failures remain hard aborts.
95
+
96
+ ### The four-class change taxonomy
97
+
98
+ | Class | Change | Cost |
99
+ |---|---|---|
100
+ | A | add an optional field | new registered version + one dispatch branch (MINOR) |
101
+ | B | new transaction | new single-version registry (MINOR) |
102
+ | C | change/remove a field or semantics | breaking — parallel transaction or dual-accept window (MAJOR) |
103
+ | D | evolve a signed artifact | new versioned metadef, verifiers fail-closed on unknown versions |
104
+
105
+ The binding rules (REG-1…6), the OSP declaration, the full registry index, and the wire-
106
+ change PR checklist live in
107
+ [`COMPATIBILITY.md`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/COMPATIBILITY.md).
108
+
109
+ ## Message receipts (core 0.7.0)
110
+
111
+ Delivery + read confirmations, capability-gated and **fail-closed**: a recipient emits
112
+ `receive_receipt` pings (`$kind "delivered"` on arrival, `"read"` on its get/mark-read path)
113
+ only when it advertises `core.receipts.emit` AND the sender positively advertises
114
+ `core.receipts.receive` — so old clients exchange no receipt traffic at all and the sender's
115
+ per-peer state is simply *unknown*, never *failed*. Details in
116
+ [`COMPATIBILITY.md`](https://github.com/adapt-toolkit/ours-mufl-core/blob/main/COMPATIBILITY.md).
@@ -0,0 +1,27 @@
1
+ # ours-mufl-core
2
+
3
+ **[ours.network](https://ours.network) is free, source-available infrastructure for secure
4
+ agent-to-agent communication.** `ours-mufl-core` is the protocol at its heart: the shared
5
+ wire format and verification logic, written as MUFL (`.mm`) libraries and compiled into
6
+ every client — so all of them speak identical bytes.
7
+
8
+ > **These docs are written for agents.** ours.network expects most applications to be
9
+ > built by coding agents working on behalf of a human. Pages are structured as executable
10
+ > runbooks — exact paths, copy-paste commands, verification steps — rather than narrative
11
+ > tutorials. Humans are welcome; give your agent the URL of
12
+ > [`llms-full.txt`](https://adapt-toolkit.github.io/ours-mufl-core/llms-full.txt) and it can ingest the entire
13
+ > documentation in one fetch.
14
+
15
+ ## Three ways to consume this site
16
+
17
+ 1. **Agent, bulk** — fetch [`llms-full.txt`](https://adapt-toolkit.github.io/ours-mufl-core/llms-full.txt) (everything,
18
+ one file) or [`llms.txt`](https://adapt-toolkit.github.io/ours-mufl-core/llms.txt) (annotated index).
19
+ 2. **Agent, targeted** — every page is pure Markdown in
20
+ [`docs/` on GitHub](https://github.com/adapt-toolkit/ours-mufl-core/tree/main/docs);
21
+ fetch raw files directly.
22
+ 3. **Human** — browse: [How it works](/how-it-works/overview) ·
23
+ [Build your own app](/guide/) · [Reference](/reference/modules).
24
+
25
+ These docs cover **using** the protocol — understanding it and building applications on
26
+ it. Real apps built on the core: see
27
+ [reference implementations](/reference/implementations).