@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.
- package/LICENSE +98 -0
- package/README.md +231 -0
- package/dist/cli.js +446 -0
- package/dist/config.js +104 -0
- package/dist/daemon.js +353 -0
- package/dist/http.js +31 -0
- package/dist/log-store.js +48 -0
- package/dist/presence.js +157 -0
- package/dist/provision.js +29 -0
- package/dist/push.js +6 -0
- package/dist/web-push-sender.js +41 -0
- package/mufl_code/20E39399689579D5AFA5132C59DEDDD328C3BCA1A74D546F2193B36585DDD283.muflo +0 -0
- package/mufl_code/config.mufl +21 -0
- package/mufl_code/core/.github/FUNDING.yml +3 -0
- package/mufl_code/core/.github/workflows/docs.yml +39 -0
- package/mufl_code/core/.github/workflows/tests.yml +66 -0
- package/mufl_code/core/COMMERCIAL-LICENCE.md +25 -0
- package/mufl_code/core/COMPATIBILITY.md +221 -0
- package/mufl_code/core/CONTRIBUTING.md +37 -0
- package/mufl_code/core/LICENSE +98 -0
- package/mufl_code/core/NOTIFICATIONS_CONTRACT.md +290 -0
- package/mufl_code/core/README.md +91 -0
- package/mufl_code/core/SECURITY.md +40 -0
- package/mufl_code/core/TRADEMARKS.md +24 -0
- package/mufl_code/core/a2a_capabilities.mm +449 -0
- package/mufl_code/core/a2a_cluster.mm +601 -0
- package/mufl_code/core/a2a_control.mm +90 -0
- package/mufl_code/core/a2a_messaging.mm +4817 -0
- package/mufl_code/core/a2a_monitoring.mm +94 -0
- package/mufl_code/core/a2a_notification_integration.mm +144 -0
- package/mufl_code/core/a2a_notifications.mm +1127 -0
- package/mufl_code/core/a2a_protocol.mm +246 -0
- package/mufl_code/core/a2a_versions.mm +801 -0
- package/mufl_code/core/config.mufl +29 -0
- package/mufl_code/core/docs/.vitepress/config.mts +58 -0
- package/mufl_code/core/docs/citations.json +688 -0
- package/mufl_code/core/docs/guide/01-vendor-the-core.md +50 -0
- package/mufl_code/core/docs/guide/02-configure-and-compile.md +100 -0
- package/mufl_code/core/docs/guide/03-wire-the-host.md +154 -0
- package/mufl_code/core/docs/guide/04-connect-and-message.md +156 -0
- package/mufl_code/core/docs/guide/05-test-your-app.md +75 -0
- package/mufl_code/core/docs/guide/index.md +63 -0
- package/mufl_code/core/docs/how-it-works/capabilities-and-control.md +77 -0
- package/mufl_code/core/docs/how-it-works/cluster.md +65 -0
- package/mufl_code/core/docs/how-it-works/identity.md +56 -0
- package/mufl_code/core/docs/how-it-works/invites-and-contacts.md +76 -0
- package/mufl_code/core/docs/how-it-works/messaging.md +55 -0
- package/mufl_code/core/docs/how-it-works/monitoring-and-config.md +72 -0
- package/mufl_code/core/docs/how-it-works/overview.md +45 -0
- package/mufl_code/core/docs/how-it-works/versioning.md +116 -0
- package/mufl_code/core/docs/index.md +27 -0
- package/mufl_code/core/docs/package-lock.json +3467 -0
- package/mufl_code/core/docs/package.json +15 -0
- package/mufl_code/core/docs/reference/contributing.md +26 -0
- package/mufl_code/core/docs/reference/glossary.md +108 -0
- package/mufl_code/core/docs/reference/implementations.md +23 -0
- package/mufl_code/core/docs/reference/modules.md +19 -0
- package/mufl_code/core/docs/scripts/check-citations.mjs +36 -0
- package/mufl_code/core/docs/scripts/check-denylist.mjs +30 -0
- package/mufl_code/core/docs/scripts/gen-llms.mjs +32 -0
- package/mufl_code/core/docs/workflows/cluster.md +89 -0
- package/mufl_code/core/docs/workflows/contact-exchange.md +60 -0
- package/mufl_code/core/docs/workflows/contact-restore.md +60 -0
- package/mufl_code/core/docs/workflows/control-verbs.md +70 -0
- package/mufl_code/core/docs/workflows/index.md +57 -0
- package/mufl_code/core/docs/workflows/introductions.md +60 -0
- package/mufl_code/core/docs/workflows/messaging.md +75 -0
- package/mufl_code/core/docs/workflows/monitoring.md +84 -0
- package/mufl_code/core/docs/workflows/notifications.md +109 -0
- package/mufl_code/core/release-notes/0.1.md +60 -0
- package/mufl_code/core/release-notes/0.3.md +22 -0
- package/mufl_code/core/release-notes/0.4.md +97 -0
- package/mufl_code/core/tests/README.md +83 -0
- package/mufl_code/core/tests/compat/README.md +82 -0
- package/mufl_code/core/tests/compat/compat.mjs +245 -0
- package/mufl_code/core/tests/compat/compat_peer.mjs +146 -0
- package/mufl_code/core/tests/compat/run_compat.sh +96 -0
- package/mufl_code/core/tests/corpus.mjs +139 -0
- package/mufl_code/core/tests/corpus_actor.mu +242 -0
- package/mufl_code/core/tests/fatcompile_actor.mu +52 -0
- package/mufl_code/core/tests/mig.mjs +805 -0
- package/mufl_code/core/tests/mig_actor.mu +518 -0
- package/mufl_code/core/tests/migapp.mjs +431 -0
- package/mufl_code/core/tests/migapp_actor.mu +294 -0
- package/mufl_code/core/tests/mufl_semantics/run.sh +69 -0
- package/mufl_code/core/tests/mufl_semantics/t11_three_version.mufl +47 -0
- package/mufl_code/core/tests/mufl_semantics/t2_extra_fields.mufl +11 -0
- package/mufl_code/core/tests/mufl_semantics/t7_runtime.mufl +30 -0
- package/mufl_code/core/tests/mufl_semantics/t9_passthrough.mufl +19 -0
- package/mufl_code/core/tests/mufl_semantics/t_str_order.mufl +19 -0
- package/mufl_code/core/tests/notif.mjs +755 -0
- package/mufl_code/core/tests/notif_actor.mu +450 -0
- package/mufl_code/core/tests/protocol_container.mm +18 -0
- package/mufl_code/core/tests/run.sh +65 -0
- package/mufl_code/core/tests/run_corpus.sh +51 -0
- package/mufl_code/core/tests/run_fatcompile.sh +40 -0
- package/mufl_code/core/tests/run_mig.sh +51 -0
- package/mufl_code/core/tests/run_migapp.sh +51 -0
- package/mufl_code/core/tests/run_notif.sh +57 -0
- package/mufl_code/core/tests/test.mjs +750 -0
- package/mufl_code/core/tests/test_actor.mu +564 -0
- package/mufl_code/core/tests/test_common.mjs +93 -0
- package/mufl_code/core/version.mm +24 -0
- package/mufl_code/packet.mu +170 -0
- package/mufl_code/protocol_container.mm +18 -0
- package/package.json +43 -0
- package/scripts/gen-vapid.mjs +16 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
// Shared ours versioned-type-registry library (core 0.5.0).
|
|
2
|
+
//
|
|
3
|
+
// THE backward-compatibility mechanism (SPEC v2 §1, COMPATIBILITY.md): for every
|
|
4
|
+
// wire input surface whose shape ever changed, this library declares — in one
|
|
5
|
+
// place, in MUFL types — one metadef PER SHIPPED WIRE VERSION of its payload,
|
|
6
|
+
// the accepted UNION (the handler's visible contract), the ordered version
|
|
7
|
+
// vector, the DISCRIMINATOR (the $pv wire-version tag, with a per-registry
|
|
8
|
+
// shape-inference rule for pre-$pv peers), and the dispatch fns:
|
|
9
|
+
//
|
|
10
|
+
// try_narrow_<r> (raw) — dispatch on the discriminator, exact-cast to the
|
|
11
|
+
// matched version's type; a below-floor or unrecognized payload returns
|
|
12
|
+
// an ERROR VALUE AS DATA (version_error_t), never an abort. Handlers on
|
|
13
|
+
// async inbound surfaces use THIS and surface the error to the local
|
|
14
|
+
// client via _notify_agent — the "old peers never crash" invariant plus
|
|
15
|
+
// the owner's "too-old versions return error-as-data" rule (Addition A).
|
|
16
|
+
// narrow_<r> (raw) — the strict form: same dispatch, aborts with the error
|
|
17
|
+
// message on failure. For corpus tests and callers where abort is right.
|
|
18
|
+
//
|
|
19
|
+
// Rules (REG-1…6, COMPATIBILITY.md): shipped v*_t metadefs are FROZEN — a wire
|
|
20
|
+
// shape change registers a NEW versioned type beside them and adds one branch;
|
|
21
|
+
// dispatch reads the discriminator off the RAW value and exact-casts (never
|
|
22
|
+
// cast-to-union as the selector — disjunction casts pick alternatives in
|
|
23
|
+
// canonical order and strip, see tests/mufl_semantics); a $pv NEWER than the
|
|
24
|
+
// newest registered version narrows as the newest (class-A changes are
|
|
25
|
+
// additive by taxonomy, extra fields strip safely); $pv is peer-asserted
|
|
26
|
+
// metadata — it gates parsing branches and diagnostics, NEVER authz.
|
|
27
|
+
//
|
|
28
|
+
// Self-contained: no library deps (base types only), loadable from anywhere.
|
|
29
|
+
library a2a_versions
|
|
30
|
+
{
|
|
31
|
+
// ---- this build's wire dialect id -------------------------------------
|
|
32
|
+
// Stamped as $pv on every 0.5.0+ core-originated send (cleartext $targ
|
|
33
|
+
// envelopes AND inside the boxed identity-bundle payloads). Minor-version
|
|
34
|
+
// ints: core 0.5.0 -> 5, 0.7.0 -> 7. Monotone; bump ONLY when a wire
|
|
35
|
+
// surface registers a new versioned type (SPEC Q2), not on every release.
|
|
36
|
+
// 7: the rcp surface (receive_receipt) registered in 0.7 — a peer whose
|
|
37
|
+
// learned dialect is >= 7 is KNOWN to parse receipts-era transactions,
|
|
38
|
+
// which the receipts gate uses to self-heal stale-caps contacts (see
|
|
39
|
+
// a2a_messaging::receipt_gate). Correcting an under-bump: 0.7.0 initially
|
|
40
|
+
// shipped still stamping 5, which left pre-receipts contacts permanently
|
|
41
|
+
// gated (caps only re-learn on invite/restore legs — the owner-reported
|
|
42
|
+
// single-tick bug).
|
|
43
|
+
// 8: the e2e surface (e2e_signed_message) registered in 0.8 — a peer whose
|
|
44
|
+
// learned dialect is >= 8 is KNOWN to parse the E2E signed-message envelope,
|
|
45
|
+
// the belt to the core.e2e-cap suspenders for send-side routing (mirrors the
|
|
46
|
+
// receipts -> 7 precedent). Additive; no existing >= 7 gate changes (8 >= 7).
|
|
47
|
+
wire_version = 8.
|
|
48
|
+
// The version floor: OSP (oldest supported peer) = core 0.2.0 -> 2.
|
|
49
|
+
// Raising this = an owner decision recorded in COMPATIBILITY.md (drop the
|
|
50
|
+
// v2 types from the unions + prune the corpus — a visible, reviewed act).
|
|
51
|
+
min_wire_version = 2.
|
|
52
|
+
|
|
53
|
+
// ---- safety-net reads (COMPAT-1, the layer UNDER the registry, REG-5) --
|
|
54
|
+
// For display/UX-class optional fields ONLY: defense in depth so a bare
|
|
55
|
+
// NIL is never again the only thing between an old peer and an EVAL_ERROR.
|
|
56
|
+
// The registry dispatch above is the documented mechanism; any new `safe`
|
|
57
|
+
// on wire data belongs inside a registry narrow/branch or one of these.
|
|
58
|
+
fn opt_str (v: any, dflt: str) -> str { return (v == NIL ?? dflt ; v safe str). }
|
|
59
|
+
fn opt_int (v: any, dflt: int) -> int { return (v == NIL ?? dflt ; v safe int). }
|
|
60
|
+
fn opt_bool (v: any, dflt: bool) -> bool { return (v == NIL ?? dflt ; v safe bool). }
|
|
61
|
+
|
|
62
|
+
// ---- runtime type-domain guards (M1) -----------------------------------
|
|
63
|
+
// `x safe T` ABORTS on a present-but-wrong-DOMAIN field (mufl has no
|
|
64
|
+
// try/catch), so presence checks alone cannot make try_narrow abort-free.
|
|
65
|
+
// Every NON-nullable field a registry cast reads is pre-checked against its
|
|
66
|
+
// _typeof runtime domain (probed on the vendored toolchain, mufl 0.8.0):
|
|
67
|
+
// str -> "STRING", int -> "INTEGER", bin -> "BINARY",
|
|
68
|
+
// global_id -> "STRING" (ids are hex strings at runtime).
|
|
69
|
+
// Residual (documented, accepted): `safe global_id` additionally
|
|
70
|
+
// hex-validates, so a STRING that is not valid hex still aborts inside the
|
|
71
|
+
// exact cast. No shipped version's sender can produce that (senders stamp
|
|
72
|
+
// real ids); reaching it requires a hostile hand-crafted box, which is the
|
|
73
|
+
// malformed/tamper class where a hard abort is the correct outcome.
|
|
74
|
+
td_str = "STRING".
|
|
75
|
+
td_int = "INTEGER".
|
|
76
|
+
|
|
77
|
+
fn is_str (v: any) -> bool { return v != NIL && (_typeof v) == td_str. }
|
|
78
|
+
fn is_int (v: any) -> bool { return v != NIL && (_typeof v) == td_int. }
|
|
79
|
+
|
|
80
|
+
// $pv as sent by the peer; 0 = pre-0.5 peer (never stamped it). Tolerant
|
|
81
|
+
// of a mistyped $pv (non-int => treated as unstamped, shape inference
|
|
82
|
+
// applies) so reading the discriminator itself can never abort (M1).
|
|
83
|
+
fn peer_pv (raw: any) -> int
|
|
84
|
+
{
|
|
85
|
+
p = raw $pv.
|
|
86
|
+
if is_int p != TRUE { return 0. }
|
|
87
|
+
return p safe int.
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---- error-as-data (owner Addition A) ----------------------------------
|
|
91
|
+
// The first-class protocol return type for a version-incompatible input:
|
|
92
|
+
// when a peer's wire version is below the floor, or its payload matches no
|
|
93
|
+
// registered version, the handler RETURNS this value as data (via a
|
|
94
|
+
// _notify_agent $protocol_error event on async inbound surfaces) instead
|
|
95
|
+
// of aborting — the MCP server and every client resolve this type and
|
|
96
|
+
// render $message cleanly. Codes are STABLE wire contract:
|
|
97
|
+
// "peer_version_unsupported" — peer dialect below min_wire_version
|
|
98
|
+
// "payload_shape_unrecognized" — matches no registered version >= floor
|
|
99
|
+
metadef version_error_t: (
|
|
100
|
+
$code -> str,
|
|
101
|
+
$surface -> str, // registry id: "sir" | "cin" | "rst" | "acc" | ...
|
|
102
|
+
$message -> str, // human-readable, render-ready
|
|
103
|
+
$peer_version -> int, // as read/inferred by the discriminator
|
|
104
|
+
$min_supported -> int,
|
|
105
|
+
$max_supported -> int
|
|
106
|
+
).
|
|
107
|
+
|
|
108
|
+
fn mk_version_error (surface: str, code: str, message: str, peer_version: int, max_supported: int) -> version_error_t
|
|
109
|
+
{
|
|
110
|
+
return (
|
|
111
|
+
$code -> code, $surface -> surface, $message -> message,
|
|
112
|
+
$peer_version -> peer_version,
|
|
113
|
+
$min_supported -> min_wire_version, $max_supported -> max_supported
|
|
114
|
+
).
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fn too_old_error (surface: str, peer_version: int, max_supported: int) -> version_error_t
|
|
118
|
+
{
|
|
119
|
+
return mk_version_error surface "peer_version_unsupported"
|
|
120
|
+
("Peer speaks unsupported (too old) protocol version v" + (_str peer_version)
|
|
121
|
+
+ "; minimum supported is v" + (_str min_wire_version) + ". Ask the peer to update.")
|
|
122
|
+
peer_version max_supported.
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fn shape_error (surface: str, peer_version: int, max_supported: int) -> version_error_t
|
|
126
|
+
{
|
|
127
|
+
return mk_version_error surface "payload_shape_unrecognized"
|
|
128
|
+
("Payload matches no supported wire shape of surface '" + surface
|
|
129
|
+
+ "' (peer dialect v" + (_str peer_version) + ", supported v"
|
|
130
|
+
+ (_str min_wire_version) + "..v" + (_str max_supported) + ").")
|
|
131
|
+
peer_version max_supported.
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---- reusable registry scaffold (type-level, via type reductions) ------
|
|
135
|
+
// Generic union/vector pair; a registry may write these out directly (as
|
|
136
|
+
// the concrete registries below do — 2 lines each, SPEC Q10) or
|
|
137
|
+
// instantiate: `module sir instantiates versioned_input_3 with v5, v3, v2.`
|
|
138
|
+
generic module versioned_input_2 takes Vnewest, Voldest
|
|
139
|
+
{
|
|
140
|
+
metadef union_t: Vnewest || Voldest.
|
|
141
|
+
metadef versions_t: [Voldest, Vnewest].
|
|
142
|
+
}
|
|
143
|
+
generic module versioned_input_3 takes Vnewest, Vmid, Voldest
|
|
144
|
+
{
|
|
145
|
+
metadef union_t: Vnewest || Vmid || Voldest.
|
|
146
|
+
metadef versions_t: [Voldest, Vmid, Vnewest].
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ========================================================================
|
|
150
|
+
// REGISTRY "sir" — submit_invite_response, leg-1 boxed identity bundle.
|
|
151
|
+
// The incident surface: 0.3.0 added $name; 0.2.0 peers omit it.
|
|
152
|
+
// ========================================================================
|
|
153
|
+
|
|
154
|
+
// v0.2.0 — the deployed OSP shape (ours-mcp). NO $name. $ad stays `any`:
|
|
155
|
+
// the AD is verified downstream by verify_identity_bundle on the RAW
|
|
156
|
+
// payload, never by this cast. bin+ = nullable: absent reads NIL, passes.
|
|
157
|
+
metadef sir_payload_v2_t: (
|
|
158
|
+
$ad -> any,
|
|
159
|
+
$cert -> bin+,
|
|
160
|
+
$root_profile -> bin+,
|
|
161
|
+
$cp_binding -> bin+,
|
|
162
|
+
$invite_id -> global_id
|
|
163
|
+
).
|
|
164
|
+
// v0.3.0 — added $name (the field whose strict read caused the incident).
|
|
165
|
+
metadef sir_payload_v3_t: (
|
|
166
|
+
$ad -> any,
|
|
167
|
+
$cert -> bin+,
|
|
168
|
+
$root_profile -> bin+,
|
|
169
|
+
$cp_binding -> bin+,
|
|
170
|
+
$invite_id -> global_id,
|
|
171
|
+
$name -> str
|
|
172
|
+
).
|
|
173
|
+
// v0.5.0 — adds the wire-version tag + capability piggyback (SPEC §4).
|
|
174
|
+
metadef sir_payload_v5_t: (
|
|
175
|
+
$ad -> any,
|
|
176
|
+
$cert -> bin+,
|
|
177
|
+
$root_profile -> bin+,
|
|
178
|
+
$cp_binding -> bin+,
|
|
179
|
+
$invite_id -> global_id,
|
|
180
|
+
$name -> str,
|
|
181
|
+
$pv -> int,
|
|
182
|
+
$caps -> str[]+
|
|
183
|
+
).
|
|
184
|
+
|
|
185
|
+
// The handler-visible contract: union of every version >= OSP (REG-3).
|
|
186
|
+
metadef sir_payload_t: sir_payload_v5_t || sir_payload_v3_t || sir_payload_v2_t.
|
|
187
|
+
// Ordered registry listing — the reviewable "what do we accept" artifact.
|
|
188
|
+
metadef sir_versions_t: [sir_payload_v2_t, sir_payload_v3_t, sir_payload_v5_t].
|
|
189
|
+
sir_max_version = 5.
|
|
190
|
+
|
|
191
|
+
// Discriminator: $pv when stamped (0.5.0+); pre-$pv shapes are inferred:
|
|
192
|
+
// $name present => v3, else v2. Registry-local rule, documented here only.
|
|
193
|
+
fn sir_version_of (raw: any) -> int
|
|
194
|
+
{
|
|
195
|
+
pv = peer_pv raw.
|
|
196
|
+
if pv != 0 { return pv. }
|
|
197
|
+
return ((raw $name) != NIL ?? 3 ; 2).
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Field presence + runtime-domain check for the REGISTERED version `reg`
|
|
201
|
+
// (2|3|5): the abort-free "does this payload actually carry its version's
|
|
202
|
+
// required fields, with the right domains" probe (M1) try_narrow uses to
|
|
203
|
+
// make no-match an ERROR VALUE instead of a cast abort. Nullable fields
|
|
204
|
+
// ($cert/$root_profile/$cp_binding bin+, $caps str[]+) are excluded: absent
|
|
205
|
+
// reads NIL and passes; present-but-mistyped is the malformed/hostile class
|
|
206
|
+
// (aborts in the cast, correctly). $ad is `any` — verified downstream.
|
|
207
|
+
fn sir_shape_ok (raw: any, reg: int) -> bool
|
|
208
|
+
{
|
|
209
|
+
if (raw $ad) == NIL || is_str (raw $invite_id) != TRUE { return FALSE. }
|
|
210
|
+
if reg >= 3 && is_str (raw $name) != TRUE { return FALSE. }
|
|
211
|
+
if reg >= 5 && is_int (raw $pv) != TRUE { return FALSE. }
|
|
212
|
+
return TRUE.
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// try-narrow result: error-as-data union ($ok TRUE => $payload, else $err).
|
|
216
|
+
metadef sir_narrowed_t: ($ok -> bool, $payload -> sir_payload_t+, $err -> version_error_t+).
|
|
217
|
+
|
|
218
|
+
// REG-4 dispatch-then-narrow, error-as-data form (Addition A). $pv NEWER
|
|
219
|
+
// than we know narrows as our newest (class-A forward compat).
|
|
220
|
+
fn try_narrow_sir (raw: any) -> sir_narrowed_t
|
|
221
|
+
{
|
|
222
|
+
v = sir_version_of raw.
|
|
223
|
+
if v < min_wire_version
|
|
224
|
+
{
|
|
225
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "sir" v sir_max_version).
|
|
226
|
+
}
|
|
227
|
+
// Registered-version mapping: 5+ -> v5, 3..4 -> v3 (0.4.x never shipped
|
|
228
|
+
// and its wire on this surface is byte-identical to 0.3's; a synthetic
|
|
229
|
+
// $pv=4 therefore narrows as v3 — requires $name, else shape error),
|
|
230
|
+
// else v2.
|
|
231
|
+
reg = (v >= 5 ?? 5 ; (v >= 3 ?? 3 ; 2)).
|
|
232
|
+
if sir_shape_ok raw reg != TRUE
|
|
233
|
+
{
|
|
234
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "sir" v sir_max_version).
|
|
235
|
+
}
|
|
236
|
+
if reg == 5 { return ($ok -> TRUE, $payload -> raw safe sir_payload_v5_t, $err -> NIL). }
|
|
237
|
+
if reg == 3 { return ($ok -> TRUE, $payload -> raw safe sir_payload_v3_t, $err -> NIL). }
|
|
238
|
+
return ($ok -> TRUE, $payload -> raw safe sir_payload_v2_t, $err -> NIL).
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Strict form: same dispatch, aborts with the stable message naming the
|
|
242
|
+
// surface — never an EVAL_ERROR from a NIL cast deeper in.
|
|
243
|
+
fn narrow_sir (raw: any) -> sir_payload_t
|
|
244
|
+
{
|
|
245
|
+
r = try_narrow_sir raw.
|
|
246
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
247
|
+
return (r $payload)?.
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Versioned accessor — the per-branch "old way / new way" for the $name
|
|
251
|
+
// gap: v0.3/v0.5 carry $name in their registered type; v0.2 has no such
|
|
252
|
+
// field, so the v2 branch RETURNS the empty sentinel (caller falls back
|
|
253
|
+
// to the sender cid — explicit, typed degradation).
|
|
254
|
+
fn sir_joiner_name (input: sir_payload_t) -> str
|
|
255
|
+
{
|
|
256
|
+
raw = input as any.
|
|
257
|
+
v = sir_version_of raw.
|
|
258
|
+
if v >= 3 { return ((raw safe sir_payload_v3_t) $name). }
|
|
259
|
+
return "".
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Versioned $caps accessor (M2 pattern: re-cast from raw per version —
|
|
263
|
+
// never read version fields off the union binding): v5 carries $caps
|
|
264
|
+
// (nullable); every earlier version has no such field — empty list.
|
|
265
|
+
fn sir_caps (input: sir_payload_t) -> str[]
|
|
266
|
+
{
|
|
267
|
+
raw = input as any.
|
|
268
|
+
if (sir_version_of raw) >= 5
|
|
269
|
+
{
|
|
270
|
+
c = (raw safe sir_payload_v5_t) $caps.
|
|
271
|
+
if c != NIL { return c?. }
|
|
272
|
+
}
|
|
273
|
+
return [].
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ========================================================================
|
|
277
|
+
// REGISTRY "cin" — complete_invite, leg-3 boxed identity bundle.
|
|
278
|
+
// Never gained $name; v5 adds $pv/$caps only.
|
|
279
|
+
// ========================================================================
|
|
280
|
+
metadef cin_payload_v2_t: (
|
|
281
|
+
$ad -> any,
|
|
282
|
+
$cert -> bin+,
|
|
283
|
+
$root_profile -> bin+,
|
|
284
|
+
$cp_binding -> bin+,
|
|
285
|
+
$invite_id -> global_id
|
|
286
|
+
).
|
|
287
|
+
metadef cin_payload_v5_t: (
|
|
288
|
+
$ad -> any,
|
|
289
|
+
$cert -> bin+,
|
|
290
|
+
$root_profile -> bin+,
|
|
291
|
+
$cp_binding -> bin+,
|
|
292
|
+
$invite_id -> global_id,
|
|
293
|
+
$pv -> int,
|
|
294
|
+
$caps -> str[]+
|
|
295
|
+
).
|
|
296
|
+
metadef cin_payload_t: cin_payload_v5_t || cin_payload_v2_t.
|
|
297
|
+
metadef cin_versions_t: [cin_payload_v2_t, cin_payload_v5_t].
|
|
298
|
+
cin_max_version = 5.
|
|
299
|
+
|
|
300
|
+
// Pre-$pv cin shapes are all v2 (no inferable v3 delta on this surface).
|
|
301
|
+
fn cin_version_of (raw: any) -> int
|
|
302
|
+
{
|
|
303
|
+
pv = peer_pv raw.
|
|
304
|
+
return (pv != 0 ?? pv ; 2).
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
fn cin_shape_ok (raw: any, reg: int) -> bool
|
|
308
|
+
{
|
|
309
|
+
if (raw $ad) == NIL || is_str (raw $invite_id) != TRUE { return FALSE. }
|
|
310
|
+
if reg >= 5 && is_int (raw $pv) != TRUE { return FALSE. }
|
|
311
|
+
return TRUE.
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
metadef cin_narrowed_t: ($ok -> bool, $payload -> cin_payload_t+, $err -> version_error_t+).
|
|
315
|
+
|
|
316
|
+
fn try_narrow_cin (raw: any) -> cin_narrowed_t
|
|
317
|
+
{
|
|
318
|
+
v = cin_version_of raw.
|
|
319
|
+
if v < min_wire_version
|
|
320
|
+
{
|
|
321
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "cin" v cin_max_version).
|
|
322
|
+
}
|
|
323
|
+
reg = (v >= 5 ?? 5 ; 2).
|
|
324
|
+
if cin_shape_ok raw reg != TRUE
|
|
325
|
+
{
|
|
326
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "cin" v cin_max_version).
|
|
327
|
+
}
|
|
328
|
+
if reg >= 5 { return ($ok -> TRUE, $payload -> raw safe cin_payload_v5_t, $err -> NIL). }
|
|
329
|
+
return ($ok -> TRUE, $payload -> raw safe cin_payload_v2_t, $err -> NIL).
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
fn narrow_cin (raw: any) -> cin_payload_t
|
|
333
|
+
{
|
|
334
|
+
r = try_narrow_cin raw.
|
|
335
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
336
|
+
return (r $payload)?.
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
fn cin_caps (input: cin_payload_t) -> str[]
|
|
340
|
+
{
|
|
341
|
+
raw = input as any.
|
|
342
|
+
if (cin_version_of raw) >= 5
|
|
343
|
+
{
|
|
344
|
+
c = (raw safe cin_payload_v5_t) $caps.
|
|
345
|
+
if c != NIL { return c?. }
|
|
346
|
+
}
|
|
347
|
+
return [].
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ========================================================================
|
|
351
|
+
// REGISTRY "rst" — contact-restore boxed identity bundles (legs 1 and 2,
|
|
352
|
+
// submit_restore_response / complete_restore). Same bundle as cin with
|
|
353
|
+
// $rid instead of $invite_id; v5 adds $pv/$caps.
|
|
354
|
+
// ========================================================================
|
|
355
|
+
metadef rst_payload_v2_t: (
|
|
356
|
+
$ad -> any,
|
|
357
|
+
$cert -> bin+,
|
|
358
|
+
$root_profile -> bin+,
|
|
359
|
+
$cp_binding -> bin+,
|
|
360
|
+
$rid -> global_id
|
|
361
|
+
).
|
|
362
|
+
metadef rst_payload_v5_t: (
|
|
363
|
+
$ad -> any,
|
|
364
|
+
$cert -> bin+,
|
|
365
|
+
$root_profile -> bin+,
|
|
366
|
+
$cp_binding -> bin+,
|
|
367
|
+
$rid -> global_id,
|
|
368
|
+
$pv -> int,
|
|
369
|
+
$caps -> str[]+
|
|
370
|
+
).
|
|
371
|
+
metadef rst_payload_t: rst_payload_v5_t || rst_payload_v2_t.
|
|
372
|
+
metadef rst_versions_t: [rst_payload_v2_t, rst_payload_v5_t].
|
|
373
|
+
rst_max_version = 5.
|
|
374
|
+
|
|
375
|
+
fn rst_version_of (raw: any) -> int
|
|
376
|
+
{
|
|
377
|
+
pv = peer_pv raw.
|
|
378
|
+
return (pv != 0 ?? pv ; 2).
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
fn rst_shape_ok (raw: any, reg: int) -> bool
|
|
382
|
+
{
|
|
383
|
+
if (raw $ad) == NIL || is_str (raw $rid) != TRUE { return FALSE. }
|
|
384
|
+
if reg >= 5 && is_int (raw $pv) != TRUE { return FALSE. }
|
|
385
|
+
return TRUE.
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
metadef rst_narrowed_t: ($ok -> bool, $payload -> rst_payload_t+, $err -> version_error_t+).
|
|
389
|
+
|
|
390
|
+
fn try_narrow_rst (raw: any) -> rst_narrowed_t
|
|
391
|
+
{
|
|
392
|
+
v = rst_version_of raw.
|
|
393
|
+
if v < min_wire_version
|
|
394
|
+
{
|
|
395
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "rst" v rst_max_version).
|
|
396
|
+
}
|
|
397
|
+
reg = (v >= 5 ?? 5 ; 2).
|
|
398
|
+
if rst_shape_ok raw reg != TRUE
|
|
399
|
+
{
|
|
400
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "rst" v rst_max_version).
|
|
401
|
+
}
|
|
402
|
+
if reg >= 5 { return ($ok -> TRUE, $payload -> raw safe rst_payload_v5_t, $err -> NIL). }
|
|
403
|
+
return ($ok -> TRUE, $payload -> raw safe rst_payload_v2_t, $err -> NIL).
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fn narrow_rst (raw: any) -> rst_payload_t
|
|
407
|
+
{
|
|
408
|
+
r = try_narrow_rst raw.
|
|
409
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
410
|
+
return (r $payload)?.
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
fn rst_caps (input: rst_payload_t) -> str[]
|
|
414
|
+
{
|
|
415
|
+
raw = input as any.
|
|
416
|
+
if (rst_version_of raw) >= 5
|
|
417
|
+
{
|
|
418
|
+
c = (raw safe rst_payload_v5_t) $caps.
|
|
419
|
+
if c != NIL { return c?. }
|
|
420
|
+
}
|
|
421
|
+
return [].
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ========================================================================
|
|
425
|
+
// REGISTRY "acc" — legacy accept_contact $targ args (encrypted channel).
|
|
426
|
+
// Hygiene sibling of sir: 0.3.0 added $joiner_name; 0.2.0 omits it. The
|
|
427
|
+
// path itself is slated for class-C removal at the next OSP raise. Never
|
|
428
|
+
// gained $pv (0.5.0 does not send it), so v3 is the newest registered.
|
|
429
|
+
// ========================================================================
|
|
430
|
+
metadef acc_args_v2_t: (
|
|
431
|
+
$invite_id -> global_id,
|
|
432
|
+
$joiner_ad -> any,
|
|
433
|
+
$joiner_cert -> bin+,
|
|
434
|
+
$joiner_root_profile -> bin+,
|
|
435
|
+
$joiner_cp_binding -> bin+
|
|
436
|
+
).
|
|
437
|
+
metadef acc_args_v3_t: (
|
|
438
|
+
$invite_id -> global_id,
|
|
439
|
+
$joiner_ad -> any,
|
|
440
|
+
$joiner_cert -> bin+,
|
|
441
|
+
$joiner_root_profile -> bin+,
|
|
442
|
+
$joiner_cp_binding -> bin+,
|
|
443
|
+
$joiner_name -> str
|
|
444
|
+
).
|
|
445
|
+
metadef acc_args_t: acc_args_v3_t || acc_args_v2_t.
|
|
446
|
+
metadef acc_versions_t: [acc_args_v2_t, acc_args_v3_t].
|
|
447
|
+
acc_max_version = 3.
|
|
448
|
+
|
|
449
|
+
fn acc_version_of (raw: any) -> int
|
|
450
|
+
{
|
|
451
|
+
pv = peer_pv raw.
|
|
452
|
+
if pv != 0 { return pv. }
|
|
453
|
+
return ((raw $joiner_name) != NIL ?? 3 ; 2).
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
fn acc_shape_ok (raw: any, reg: int) -> bool
|
|
457
|
+
{
|
|
458
|
+
if is_str (raw $invite_id) != TRUE || (raw $joiner_ad) == NIL { return FALSE. }
|
|
459
|
+
if reg >= 3 && is_str (raw $joiner_name) != TRUE { return FALSE. }
|
|
460
|
+
return TRUE.
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
metadef acc_narrowed_t: ($ok -> bool, $payload -> acc_args_t+, $err -> version_error_t+).
|
|
464
|
+
|
|
465
|
+
fn try_narrow_acc (raw: any) -> acc_narrowed_t
|
|
466
|
+
{
|
|
467
|
+
v = acc_version_of raw.
|
|
468
|
+
if v < min_wire_version
|
|
469
|
+
{
|
|
470
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "acc" v acc_max_version).
|
|
471
|
+
}
|
|
472
|
+
reg = (v >= 3 ?? 3 ; 2).
|
|
473
|
+
if acc_shape_ok raw reg != TRUE
|
|
474
|
+
{
|
|
475
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "acc" v acc_max_version).
|
|
476
|
+
}
|
|
477
|
+
if reg >= 3 { return ($ok -> TRUE, $payload -> raw safe acc_args_v3_t, $err -> NIL). }
|
|
478
|
+
return ($ok -> TRUE, $payload -> raw safe acc_args_v2_t, $err -> NIL).
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
fn narrow_acc (raw: any) -> acc_args_t
|
|
482
|
+
{
|
|
483
|
+
r = try_narrow_acc raw.
|
|
484
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
485
|
+
return (r $payload)?.
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// acc analogue of sir_joiner_name: v3 carries $joiner_name; v2 has no such
|
|
489
|
+
// field — return the empty sentinel (caller falls back to the sender cid).
|
|
490
|
+
fn acc_joiner_name (input: acc_args_t) -> str
|
|
491
|
+
{
|
|
492
|
+
raw = input as any.
|
|
493
|
+
v = acc_version_of raw.
|
|
494
|
+
if v >= 3 { return ((raw safe acc_args_v3_t) $joiner_name). }
|
|
495
|
+
return "".
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ========================================================================
|
|
499
|
+
// REGISTRY "e2e" — e2e_signed_message, the end-to-end-encrypted message
|
|
500
|
+
// VARIANT (core 0.8.0, class-B NEW surface). The decode branch (adapt) keys
|
|
501
|
+
// on presence of the $e2e_envelope MARKER field, NOT a tx name; ours
|
|
502
|
+
// registers the wire variant so an old/hostile unit narrows it as
|
|
503
|
+
// error-as-data instead of a SAFE-cast crash (the fix-3 class). Single
|
|
504
|
+
// registered version, LOAD-BEARING (returns $payload via an exact `safe`
|
|
505
|
+
// cast, `sir`-style — not the best-effort `rcp` ignore shape). $ciphertext is
|
|
506
|
+
// an OPAQUE Olm blob the core never parses; all crypto + session state live
|
|
507
|
+
// in the adapt `e2e` library, which also owns the receive-side decrypt+verify.
|
|
508
|
+
// ========================================================================
|
|
509
|
+
// The INNER opaque body (contract `t_e2e_envelope`). $pv rides HERE — it is
|
|
510
|
+
// the version discriminator for the whole signed message.
|
|
511
|
+
metadef e2e_env_v1_t: (
|
|
512
|
+
$session_id -> any, // adapt-derived session id: BINARY on the SHIPPED wire
|
|
513
|
+
// (session_id() bytes, e2e.mm); a hex string is also accepted
|
|
514
|
+
// (§5.9-1 dual-accept). `any` here + the flat cast target below
|
|
515
|
+
// (e2e_signed_message keeps $e2e_envelope `any`) mean NO frozen
|
|
516
|
+
// type mutates; e2e_env_shape_ok validates the domain.
|
|
517
|
+
$olm_type -> int, // 0 = PRE_KEY (establishment inside $ciphertext), 1 = normal ratchet
|
|
518
|
+
$ciphertext -> bin, // opaque Olm blob
|
|
519
|
+
$pv -> int+ // wire dialect stamp (= wire_version at send); nullable in the cast so an
|
|
520
|
+
// unstamped body narrows (shape_ok guards present-non-int -> shape_error)
|
|
521
|
+
).
|
|
522
|
+
// The registered WIRE MESSAGE VARIANT (contract `e2e_signed_message`). Both
|
|
523
|
+
// fields are `any`: the inner envelope is fully validated by e2e_env_shape_ok
|
|
524
|
+
// BEFORE the cast (exactly as `sir` validates then leaves $ad `any`), and
|
|
525
|
+
// $emsignature — the outer sender-auth (default_sign over _value_id(env),
|
|
526
|
+
// a crypto_signature on the wire) — is verified by adapt's decode hook, not
|
|
527
|
+
// ours. Keeping both `any` makes the cast a flat abort-free pass-through and
|
|
528
|
+
// avoids a nested-metadef type-level reduction. e2e_env_v1_t above documents
|
|
529
|
+
// the inner shape (used by the send-side builder + e2e_env_shape_ok's reads).
|
|
530
|
+
metadef e2e_signed_message_v1_t: (
|
|
531
|
+
$e2e_envelope -> any,
|
|
532
|
+
$emsignature -> any
|
|
533
|
+
).
|
|
534
|
+
metadef e2e_msg_t: e2e_signed_message_v1_t.
|
|
535
|
+
metadef e2e_versions_t: [e2e_signed_message_v1_t].
|
|
536
|
+
e2e_max_version = 8.
|
|
537
|
+
|
|
538
|
+
// Discriminator lives INSIDE $e2e_envelope. Unstamped defaults to e2e's
|
|
539
|
+
// introduction dialect (8) — an e2e envelope only exists 0.8.0+ (cf. rcp -> 7).
|
|
540
|
+
fn e2e_version_of (raw: any) -> int
|
|
541
|
+
{
|
|
542
|
+
env = raw $e2e_envelope.
|
|
543
|
+
if env == NIL { return 8. } // no $e2e_envelope marker -> default; shape_ok rejects it
|
|
544
|
+
pv = peer_pv env.
|
|
545
|
+
return (pv != 0 ?? pv ; 8).
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Abort-free INNER-envelope probe (M1): every NON-nullable field checked
|
|
549
|
+
// against its runtime domain before the cast. The cast target keeps
|
|
550
|
+
// $e2e_envelope `any` (flat pass-through), so this probe is the ONLY domain
|
|
551
|
+
// gate — no residual `safe global_id` hex-validation happens on $session_id.
|
|
552
|
+
fn e2e_env_shape_ok (env: any) -> bool
|
|
553
|
+
{
|
|
554
|
+
ct = env $ciphertext.
|
|
555
|
+
pv = env $pv.
|
|
556
|
+
sid = env $session_id.
|
|
557
|
+
// $session_id DUAL-ACCEPT (§5.9-1): the SHIPPED adapt wire is BINARY
|
|
558
|
+
// (session_id() bytes); the historically-documented form was a hex STRING.
|
|
559
|
+
// Accept BOTH — an additive relaxation that rejects strictly less (a present-
|
|
560
|
+
// but-mistyped session_id, e.g. an int, still fails -> shape_error). All 0.9
|
|
561
|
+
// stores/payloads carry canonical BINARY and compare bytewise.
|
|
562
|
+
sid_ok = is_str sid || (sid != NIL && (_typeof sid) == "BINARY").
|
|
563
|
+
// $pv: absent is tolerated (defaults to 8 in e2e_version_of); present MUST be an int.
|
|
564
|
+
// Every 0.8.0+ e2e sender stamps a real int $pv, so a present-non-int is malformed ->
|
|
565
|
+
// shape_error (error-as-data), which also keeps the `$pv -> int+` cast abort-free.
|
|
566
|
+
return sid_ok && is_int (env $olm_type)
|
|
567
|
+
&& ct != NIL && (_typeof ct) == "BINARY"
|
|
568
|
+
&& (pv == NIL || is_int pv).
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Whole-variant shape: the $e2e_envelope MARKER present + a valid inner
|
|
572
|
+
// envelope + an $emsignature present (its crypto domain is adapt's to verify).
|
|
573
|
+
fn e2e_msg_shape_ok (raw: any) -> bool
|
|
574
|
+
{
|
|
575
|
+
env = raw $e2e_envelope.
|
|
576
|
+
if env == NIL { return FALSE. }
|
|
577
|
+
return e2e_env_shape_ok env && (raw $emsignature) != NIL.
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
metadef e2e_narrowed_t: ($ok -> bool, $payload -> e2e_msg_t+, $err -> version_error_t+).
|
|
581
|
+
|
|
582
|
+
// REG-4 dispatch-then-narrow, error-as-data (Addition A). Single registered
|
|
583
|
+
// version: any $pv >= floor with a valid shape narrows as v1 (class-A forward
|
|
584
|
+
// compat); a future dialect is accepted, not rejected.
|
|
585
|
+
fn try_narrow_e2e (raw: any) -> e2e_narrowed_t
|
|
586
|
+
{
|
|
587
|
+
v = e2e_version_of raw.
|
|
588
|
+
if v < min_wire_version
|
|
589
|
+
{
|
|
590
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "e2e" v e2e_max_version).
|
|
591
|
+
}
|
|
592
|
+
if e2e_msg_shape_ok raw != TRUE
|
|
593
|
+
{
|
|
594
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "e2e" v e2e_max_version).
|
|
595
|
+
}
|
|
596
|
+
return ($ok -> TRUE, $payload -> raw safe e2e_signed_message_v1_t, $err -> NIL).
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Strict form — same dispatch, aborts with the stable surface-named message.
|
|
600
|
+
fn narrow_e2e (raw: any) -> e2e_msg_t
|
|
601
|
+
{
|
|
602
|
+
r = try_narrow_e2e raw.
|
|
603
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
604
|
+
return (r $payload)?.
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ========================================================================
|
|
608
|
+
// Single-version registrations (REG-1) — surfaces whose shape never
|
|
609
|
+
// changed since OSP. Registering them pre-wires the change procedure: a
|
|
610
|
+
// future shape change adds a v<next>_t + one union branch here instead of
|
|
611
|
+
// an ad-hoc read. Their handlers keep field-by-field tolerant reads (the
|
|
612
|
+
// shapes are all-optional beyond the required core); the entries below are
|
|
613
|
+
// the registry index + the $pv learning hook. Remaining envelope surfaces
|
|
614
|
+
// (invite/restore cleartext envelopes, enroll/ingest/control/monitoring,
|
|
615
|
+
// notify_*) are catalogued in COMPATIBILITY.md.
|
|
616
|
+
// ========================================================================
|
|
617
|
+
|
|
618
|
+
// receive_message $targ (v5 adds only the $pv stamp — same shape).
|
|
619
|
+
metadef rmsg_targ_v2_t: (
|
|
620
|
+
$text -> str,
|
|
621
|
+
$wire_id -> str+,
|
|
622
|
+
$reply_to -> any,
|
|
623
|
+
$pv -> int+
|
|
624
|
+
).
|
|
625
|
+
metadef rmsg_targ_t: rmsg_targ_v2_t.
|
|
626
|
+
fn rmsg_version_of (raw: any) -> int
|
|
627
|
+
{
|
|
628
|
+
pv = peer_pv raw.
|
|
629
|
+
return (pv != 0 ?? pv ; 2).
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// receive_receipt $targ (core 0.7.0, class-B new surface — single version;
|
|
633
|
+
// reachable only behind positive core.receipts.* caps, so no $pv bump).
|
|
634
|
+
metadef rcp_targ_v1_t: (
|
|
635
|
+
$kind -> str, // "delivered" | "read" (frozen id strings)
|
|
636
|
+
$wire_ids -> any, // str[] — 1..N stable wire ids (shared msg+file namespace)
|
|
637
|
+
$date -> time+,
|
|
638
|
+
$pv -> int+
|
|
639
|
+
).
|
|
640
|
+
metadef rcp_targ_t: rcp_targ_v1_t.
|
|
641
|
+
fn rcp_version_of (raw: any) -> int
|
|
642
|
+
{
|
|
643
|
+
pv = peer_pv raw.
|
|
644
|
+
return (pv != 0 ?? pv ; 7).
|
|
645
|
+
}
|
|
646
|
+
// Abort-free classification (M1): the handler IGNORES (success, no-op) any
|
|
647
|
+
// payload failing this — receipts are best-effort UX, never load-bearing.
|
|
648
|
+
// $wire_ids must be a real list (lists ride IMMUTABLE_DICTIONARY at
|
|
649
|
+
// runtime): iterating a scalar would char-walk strings / abort on ints.
|
|
650
|
+
fn rcp_shape_ok (raw: any) -> bool
|
|
651
|
+
{
|
|
652
|
+
ids = raw $wire_ids.
|
|
653
|
+
return is_str (raw $kind) && ids != NIL && (_typeof ids) == "IMMUTABLE_DICTIONARY".
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// receive_file $targ (v5 adds only the $pv stamp — same shape).
|
|
657
|
+
metadef rfil_targ_v2_t: (
|
|
658
|
+
$filename -> str,
|
|
659
|
+
$data -> bin,
|
|
660
|
+
$mime -> str+,
|
|
661
|
+
$wire_id -> str+,
|
|
662
|
+
$reply_to -> any,
|
|
663
|
+
$pv -> int+
|
|
664
|
+
).
|
|
665
|
+
metadef rfil_targ_t: rfil_targ_v2_t.
|
|
666
|
+
fn rfil_version_of (raw: any) -> int
|
|
667
|
+
{
|
|
668
|
+
pv = peer_pv raw.
|
|
669
|
+
return (pv != 0 ?? pv ; 2).
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// ========================================================================
|
|
673
|
+
// REGISTRY "mgb" — e2e-migration bundle (core 0.9.0, class-B NEW surface,
|
|
674
|
+
// wire 9). The offer ($name e2e_migrate_offer) and ack (e2e_migrate_ack)
|
|
675
|
+
// legs SHARE this one registry, exactly as "rst" serves both restore legs.
|
|
676
|
+
// The payload IS an identity bundle (sir-shape: $ad `any`, verified
|
|
677
|
+
// downstream by verify_identity_bundle on the RAW value, never by this cast)
|
|
678
|
+
// PLUS the agreement fields ($nonce, ack-only $peer_nonce). Introduction
|
|
679
|
+
// dialect 9 — this surface cannot predate 0.9.0, so an unstamped body is
|
|
680
|
+
// malformed (a real sender always stamps $pv, cf. the offer/ack builders).
|
|
681
|
+
// ========================================================================
|
|
682
|
+
metadef mgb_targ_v1_t: (
|
|
683
|
+
$ad -> any, // FRESH signed ADv2 (must carry $e2e_bundle — checked downstream)
|
|
684
|
+
$cert -> bin+,
|
|
685
|
+
$root_profile -> bin+,
|
|
686
|
+
$cp_binding -> bin+,
|
|
687
|
+
$nonce -> bin, // sender's proposal nonce (agreement uniquifier, not key material)
|
|
688
|
+
$peer_nonce -> bin+, // ack only: echo of the offer nonce (pairs the exchange); absent on offer
|
|
689
|
+
$pv -> int, // wire dialect stamp (= wire_version at send); REQUIRED on this surface
|
|
690
|
+
$caps -> str[]+ // capability piggyback (the missing caps/AD refresh, spec §5.4 step 2)
|
|
691
|
+
).
|
|
692
|
+
metadef mgb_targ_t: mgb_targ_v1_t.
|
|
693
|
+
metadef mgb_versions_t: [mgb_targ_v1_t].
|
|
694
|
+
mgb_max_version = 9.
|
|
695
|
+
|
|
696
|
+
// Discriminator: $pv when stamped; unstamped defaults to the introduction
|
|
697
|
+
// dialect 9 (cf. e2e -> 8). shape_ok then rejects the unstamped body since
|
|
698
|
+
// every real mgb sender stamps an int $pv.
|
|
699
|
+
fn mgb_version_of (raw: any) -> int
|
|
700
|
+
{
|
|
701
|
+
pv = peer_pv raw.
|
|
702
|
+
return (pv != 0 ?? pv ; 9).
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Abort-free classification (M1): the load-bearing NON-nullable fields
|
|
706
|
+
// ($ad present, $nonce a real BINARY, $pv a real int) checked before the
|
|
707
|
+
// cast so no-match is error-as-data, not a SAFE-cast abort. Nullable fields
|
|
708
|
+
// ($cert/$root_profile/$cp_binding bin+, $peer_nonce bin+, $caps str[]+) are
|
|
709
|
+
// excluded: absent reads NIL and passes; present-but-mistyped is the
|
|
710
|
+
// hostile/malformed class (aborts in the cast, correctly). $ad is `any`.
|
|
711
|
+
fn mgb_shape_ok (raw: any) -> bool
|
|
712
|
+
{
|
|
713
|
+
n = raw $nonce.
|
|
714
|
+
return (raw $ad) != NIL && n != NIL && (_typeof n) == "BINARY" && is_int (raw $pv).
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
metadef mgb_narrowed_t: ($ok -> bool, $payload -> mgb_targ_t+, $err -> version_error_t+).
|
|
718
|
+
|
|
719
|
+
// REG-4 dispatch-then-narrow, error-as-data. Single registered version: any
|
|
720
|
+
// $pv >= floor with a valid shape narrows as v1 (class-A forward compat).
|
|
721
|
+
fn try_narrow_mgb (raw: any) -> mgb_narrowed_t
|
|
722
|
+
{
|
|
723
|
+
v = mgb_version_of raw.
|
|
724
|
+
if v < min_wire_version
|
|
725
|
+
{
|
|
726
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "mgb" v mgb_max_version).
|
|
727
|
+
}
|
|
728
|
+
if mgb_shape_ok raw != TRUE
|
|
729
|
+
{
|
|
730
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "mgb" v mgb_max_version).
|
|
731
|
+
}
|
|
732
|
+
return ($ok -> TRUE, $payload -> raw safe mgb_targ_v1_t, $err -> NIL).
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Strict form — same dispatch, aborts with the stable surface-named message.
|
|
736
|
+
fn narrow_mgb (raw: any) -> mgb_targ_t
|
|
737
|
+
{
|
|
738
|
+
r = try_narrow_mgb raw.
|
|
739
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
740
|
+
return (r $payload)?.
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// ========================================================================
|
|
744
|
+
// REGISTRY "mgc" — e2e-migration commit/confirm inner $targ (core 0.9.0,
|
|
745
|
+
// class-B NEW surface, wire 9). Both the commit (e2e_migrate_commit) and
|
|
746
|
+
// confirm (e2e_migrate_confirm) inner transactions SHARE this registry.
|
|
747
|
+
// These ride INSIDE the fresh Olm session (the commit is a PRE_KEY on the
|
|
748
|
+
// staged session; the confirm rides the new session), so $pv is nullable
|
|
749
|
+
// here — the E2E transcript already authenticates the sender. $epoch binds
|
|
750
|
+
// the agreement; $session_id (commit only) must byte-equal the CARRIER
|
|
751
|
+
// envelope's session id, binding the epoch to the concrete fresh session.
|
|
752
|
+
// Both are CANONICAL BYTES (adapt session_id() output) — every comparison in
|
|
753
|
+
// the FSM path is byte-equality on this form, no hex re-encoding anywhere.
|
|
754
|
+
// ========================================================================
|
|
755
|
+
metadef mgc_targ_v1_t: (
|
|
756
|
+
$epoch -> bin, // the agreement epoch id (32 bytes, mig_epoch)
|
|
757
|
+
$session_id -> bin+, // present on COMMIT, absent on CONFIRM
|
|
758
|
+
$pv -> int+ // nullable: the E2E session authenticates; unstamped defaults to 9
|
|
759
|
+
).
|
|
760
|
+
metadef mgc_targ_t: mgc_targ_v1_t.
|
|
761
|
+
metadef mgc_versions_t: [mgc_targ_v1_t].
|
|
762
|
+
mgc_max_version = 9.
|
|
763
|
+
|
|
764
|
+
fn mgc_version_of (raw: any) -> int
|
|
765
|
+
{
|
|
766
|
+
pv = peer_pv raw.
|
|
767
|
+
return (pv != 0 ?? pv ; 9).
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Abort-free classification (M1): $epoch is the only required field (a real
|
|
771
|
+
// BINARY); $session_id, when present, MUST be BINARY (commit form) — absent
|
|
772
|
+
// is the confirm form and passes. $pv is nullable (int+), tolerated absent.
|
|
773
|
+
fn mgc_shape_ok (raw: any) -> bool
|
|
774
|
+
{
|
|
775
|
+
e = raw $epoch. s = raw $session_id.
|
|
776
|
+
return e != NIL && (_typeof e) == "BINARY" && (s == NIL || (_typeof s) == "BINARY").
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
metadef mgc_narrowed_t: ($ok -> bool, $payload -> mgc_targ_t+, $err -> version_error_t+).
|
|
780
|
+
|
|
781
|
+
fn try_narrow_mgc (raw: any) -> mgc_narrowed_t
|
|
782
|
+
{
|
|
783
|
+
v = mgc_version_of raw.
|
|
784
|
+
if v < min_wire_version
|
|
785
|
+
{
|
|
786
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> too_old_error "mgc" v mgc_max_version).
|
|
787
|
+
}
|
|
788
|
+
if mgc_shape_ok raw != TRUE
|
|
789
|
+
{
|
|
790
|
+
return ($ok -> FALSE, $payload -> NIL, $err -> shape_error "mgc" v mgc_max_version).
|
|
791
|
+
}
|
|
792
|
+
return ($ok -> TRUE, $payload -> raw safe mgc_targ_v1_t, $err -> NIL).
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
fn narrow_mgc (raw: any) -> mgc_targ_t
|
|
796
|
+
{
|
|
797
|
+
r = try_narrow_mgc raw.
|
|
798
|
+
if (r $ok) != TRUE { abort ((r $err)? $message) when TRUE. }
|
|
799
|
+
return (r $payload)?.
|
|
800
|
+
}
|
|
801
|
+
}
|