@adapt-toolkit/a2adapt 0.11.3 → 0.11.5
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/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +400 -38
- package/dist/mufl_code/D317A840D97E3B17E4EC644D50BDBB19DE3B78F9941C25853E1F86164A234593.muflo +0 -0
- package/dist/mufl_code/actor.mu +517 -451
- package/package.json +1 -1
- package/dist/mufl_code/7127B9740290EA8B2F30AFAE251198357D780A81DBF5EF30FE7DD9B66A510C21.muflo +0 -0
package/dist/mufl_code/actor.mu
CHANGED
|
@@ -5,20 +5,39 @@
|
|
|
5
5
|
// encrypted; the key exchange is handled for us by the stdlib `encrypted_channel`
|
|
6
6
|
// library — we only ever address peers by their container id.
|
|
7
7
|
//
|
|
8
|
-
// The
|
|
9
|
-
//
|
|
10
|
-
//
|
|
8
|
+
// The shared transaction logic lives in the a2adapt-mufl-core repo (checked
|
|
9
|
+
// out as the core/ subfolder of this directory): `a2a_protocol` (wire shapes +
|
|
10
|
+
// verification), `a2a_messaging` (contact + message wire path, addressed
|
|
11
|
+
// ::a2a_messaging::<name> by the host), and `version`. They are shared with
|
|
11
12
|
// the web messenger — change them there, bump core_version, and recompile
|
|
12
13
|
// every consumer.
|
|
13
14
|
//
|
|
15
|
+
// This packet keeps only what is agent-specific:
|
|
16
|
+
// - the inbox message store with its lifecycle (unread -> processed ->
|
|
17
|
+
// ready_to_delete -> deleted; see get_messages / gc / defer_messages)
|
|
18
|
+
// - the local contact book + identity hierarchy transactions (host-fired)
|
|
19
|
+
// - export_state / import_state wrappers composing the core helpers with
|
|
20
|
+
// the app-side fields (including the legacy-blob migrations)
|
|
21
|
+
// - ::actor:: compat shims for the network-visible inbound transactions
|
|
22
|
+
// (accept_contact, receive_message) — pre-migration peers send to these
|
|
23
|
+
// names, and the core keeps SENDING to them too (Option A: zero network
|
|
24
|
+
// break; drop both only when no old clients remain)
|
|
25
|
+
//
|
|
26
|
+
// Storage is wired into the core via a2a_messaging::init hooks (see the
|
|
27
|
+
// hidden block): on_message_received deposits into the inbox (or the
|
|
28
|
+
// pending-introduction queue for unknown senders); send/remove hooks are
|
|
29
|
+
// agent no-ops.
|
|
30
|
+
//
|
|
14
31
|
// User transactions (each backs one MCP tool, except gc which the host fires):
|
|
15
|
-
// set_my_name
|
|
16
|
-
// set_my_bio
|
|
17
|
-
// generate_invite
|
|
18
|
-
// add_contact
|
|
19
|
-
// send_message
|
|
20
|
-
// remove_contact
|
|
21
|
-
// list_contacts
|
|
32
|
+
// ::a2a_messaging::set_my_name — set the display name peers see for me
|
|
33
|
+
// ::a2a_messaging::set_my_bio — set my profile bio (free-text, self-asserted)
|
|
34
|
+
// ::a2a_messaging::generate_invite — make an invite blob for a named peer
|
|
35
|
+
// ::a2a_messaging::add_contact — join via an invite blob, reply to the inviter
|
|
36
|
+
// ::a2a_messaging::send_message — send an e2e-encrypted message to a contact
|
|
37
|
+
// ::a2a_messaging::remove_contact — forget a contact
|
|
38
|
+
// ::a2a_messaging::list_contacts — (readonly) my contacts
|
|
39
|
+
// ::a2a_messaging::list_contact_roots — (readonly) verified root linkage per contact
|
|
40
|
+
// ::a2a_messaging::get_version — (readonly) shared-core version
|
|
22
41
|
// list_incoming_messages — (readonly) my inbox, with per-message id + status
|
|
23
42
|
// get_messages — return unread messages + mark them processed (sole body egress)
|
|
24
43
|
// defer_messages — flip processed/ready_to_delete messages back to unread
|
|
@@ -29,7 +48,6 @@
|
|
|
29
48
|
// export_root_profile — root-only in practice: my self-signed root profile blob
|
|
30
49
|
// set_delegation — role: store my verified delegation cert + root material
|
|
31
50
|
// describe_identity — (readonly) name/bio/hierarchy position
|
|
32
|
-
// list_contact_roots — (readonly) verified root linkage per contact
|
|
33
51
|
// connect_sibling — register an intra-root peer + send sibling_introduce
|
|
34
52
|
//
|
|
35
53
|
// Local contact book (host-fired transactions; see the design notes above
|
|
@@ -44,18 +62,25 @@
|
|
|
44
62
|
// reject_introduction — drop a pending local introduction
|
|
45
63
|
// list_pending_introductions — (readonly) pending introductions (names + queue sizes)
|
|
46
64
|
//
|
|
65
|
+
// Monitoring + control plane (host-fired unless noted; see
|
|
66
|
+
// MONITORING-AND-SHARED-LIBRARY-DESIGN.md):
|
|
67
|
+
// get_monitoring_status — (readonly) enabled flag / proxy binding / queue sizes
|
|
68
|
+
// sign_monitoring_auth — root-only: sign an enable/disable auth for a role
|
|
69
|
+
// set_monitoring — role: verify the root-signed auth, set the flag
|
|
70
|
+
// get_monitoring_copies — root: drain queued copies for proxy forwarding
|
|
71
|
+
// get_control_requests — root: drain queued proxy control requests
|
|
72
|
+
// set_proxy_pending — root: store the host-generated 6-digit code
|
|
73
|
+
// verify_proxy_code — root: check a bind attempt (atomic attempts/expiry)
|
|
74
|
+
// clear_monitoring_proxy — root: drop the proxy binding
|
|
75
|
+
// ::a2a_control::send_control — send an opaque control payload to a contact
|
|
76
|
+
//
|
|
47
77
|
// External transactions (inbound, not exposed as tools):
|
|
48
|
-
// accept_contact — inviter learns the joiner's identity + name
|
|
49
|
-
// receive_message — store a decrypted inbound message
|
|
78
|
+
// accept_contact — inviter learns the joiner's identity + name (core shim)
|
|
79
|
+
// receive_message — store a decrypted inbound message (core shim)
|
|
80
|
+
// receive_monitoring_copy — a monitored role of mine reports a message copy
|
|
81
|
+
// ::a2a_control::control_message — control payload from a contact (queued for the daemon)
|
|
50
82
|
// local_introduce — same-host peer connects via the local contact book
|
|
51
83
|
// sibling_introduce — intra-root peer connects, authorized by its delegation cert
|
|
52
|
-
//
|
|
53
|
-
// Naming model (personal invites): generate_invite('Bob') tags a pending invite
|
|
54
|
-
// with the peer-name "Bob"; whoever redeems it is registered under "Bob" (the
|
|
55
|
-
// inviter's assigned name wins). Generating WITHOUT a name tags the invite with
|
|
56
|
-
// "" — then the joiner's self-announced name wins (falling back to its container
|
|
57
|
-
// id if the joiner never set one). The joiner, in turn, sees the inviter under
|
|
58
|
-
// the inviter's own self-name (set via set_my_name), unless the joiner overrides it.
|
|
59
84
|
|
|
60
85
|
application actor loads libraries
|
|
61
86
|
identity_proof_document,
|
|
@@ -70,6 +95,8 @@ application actor loads libraries
|
|
|
70
95
|
encrypted_channel,
|
|
71
96
|
current_transaction_info,
|
|
72
97
|
a2a_protocol,
|
|
98
|
+
a2a_messaging,
|
|
99
|
+
a2a_control,
|
|
73
100
|
version
|
|
74
101
|
uses transactions
|
|
75
102
|
{
|
|
@@ -101,33 +128,54 @@ application actor loads libraries
|
|
|
101
128
|
seen_nonce_cap is int = 1024.
|
|
102
129
|
pending_queue_cap is int = 50.
|
|
103
130
|
|
|
131
|
+
// ---- monitoring shapes + limits (see MONITORING-AND-SHARED-LIBRARY-DESIGN.md) ----
|
|
132
|
+
// Root-signed authorization for flipping a role's monitoring flag. Like
|
|
133
|
+
// a delegation cert, verified against the role's pinned root keys, so
|
|
134
|
+
// only this hierarchy's root can change what its roles report.
|
|
135
|
+
metadef monitoring_auth_core_t: ($version -> int, $role_cid -> global_id, $enabled -> bool, $issued_at -> time).
|
|
136
|
+
metadef monitoring_auth_t: ($c -> monitoring_auth_core_t, $s -> crypto_signature).
|
|
137
|
+
// One monitored message, as the role reports it to its root.
|
|
138
|
+
metadef monitoring_copy_t: (
|
|
139
|
+
$version -> int,
|
|
140
|
+
$source_cid -> global_id,
|
|
141
|
+
$source_name -> str,
|
|
142
|
+
$direction -> str,
|
|
143
|
+
$peer_cid -> global_id,
|
|
144
|
+
$peer_name -> str,
|
|
145
|
+
$date -> time,
|
|
146
|
+
$body -> str
|
|
147
|
+
).
|
|
148
|
+
// Proxy binding (root only): a pending 6-digit-code verification and
|
|
149
|
+
// the verified binding that replaces it on success.
|
|
150
|
+
metadef proxy_pending_t: ($code -> str, $proxy_cid -> global_id, $created_at -> time, $attempts -> int).
|
|
151
|
+
metadef proxy_binding_t: ($proxy_cid -> global_id, $bound_at -> time).
|
|
152
|
+
// One queued control request from the bound browser proxy.
|
|
153
|
+
metadef control_req_t: ($sender_cid -> global_id, $sender_name -> str, $payload -> str, $date -> time).
|
|
154
|
+
|
|
155
|
+
proxy_code_max_age_seconds is int = 300.
|
|
156
|
+
proxy_max_attempts is int = 3.
|
|
157
|
+
monitoring_inbox_cap is int = 500.
|
|
158
|
+
control_inbox_cap is int = 200.
|
|
159
|
+
|
|
104
160
|
// Wire the deserialization primitive into the libraries that need it.
|
|
105
161
|
_read_or_abort = grab( _read_or_abort ).
|
|
106
162
|
key_storage::init ($_read_or_abort -> _read_or_abort).
|
|
107
163
|
encrypted_channel::init ($_read_or_abort -> _read_or_abort).
|
|
108
164
|
|
|
109
165
|
// ---- packet state ---------------------------------------------------
|
|
110
|
-
// The
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
pending_invites is (global_id ->> str) = (,).
|
|
166
|
+
// (The shared contact/profile/hierarchy state — my_name, contacts,
|
|
167
|
+
// pending_invites, peer_ads, my_bio, delegation_cert, root_ad,
|
|
168
|
+
// root_profile, contact_roots — lives in a2a_messaging and is read /
|
|
169
|
+
// assigned as a2a_messaging::<field> below.)
|
|
170
|
+
//
|
|
116
171
|
// Received messages. Each carries its own lifecycle status (see
|
|
117
|
-
// message_t / get_messages /
|
|
172
|
+
// message_t / get_messages / gc / defer_messages), so the
|
|
118
173
|
// packet is the single authority on what has been read or processed —
|
|
119
174
|
// no host-side cursor, safe across concurrent sessions.
|
|
120
175
|
inbox is message_t[] = [].
|
|
121
176
|
// Monotonic source of per-message ids (the stable handle the agent uses
|
|
122
177
|
// to mark a message processed or defer it).
|
|
123
178
|
next_msg_seq is int = 1.
|
|
124
|
-
// Peer address documents, captured when a contact is established. These are
|
|
125
|
-
// self-signed, code-independent, and seed-stable, so on a code upgrade the
|
|
126
|
-
// host re-creates this packet from the same seed and import_state replays
|
|
127
|
-
// them through address_document::process_address_document — re-registering
|
|
128
|
-
// every peer's keys in key_storage so encrypted channels survive the upgrade
|
|
129
|
-
// with no re-handshake. Only peer PUBLIC keys travel here, never secrets.
|
|
130
|
-
peer_ads is (global_id ->> address_document_types::t_address_document) = (,).
|
|
131
179
|
// The host registrar's address document (pinned once at identity
|
|
132
180
|
// creation / injected on upgrade) — its $identity $key_list is what
|
|
133
181
|
// introduction credentials are verified against. NIL means this identity
|
|
@@ -142,18 +190,23 @@ application actor loads libraries
|
|
|
142
190
|
// Verified-but-unapproved local introductions (when auto-accept is off),
|
|
143
191
|
// each with a bounded queue of messages awaiting approval.
|
|
144
192
|
pending_introductions is (global_id ->> pending_intro_t) = (,).
|
|
145
|
-
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
|
|
193
|
+
|
|
194
|
+
// ---- monitoring state -------------------------------------------------
|
|
195
|
+
// Whether THIS packet (a role) reports its message traffic to its root.
|
|
196
|
+
// Only flipped by a root-signed authorization (set_monitoring).
|
|
197
|
+
monitoring_enabled is bool = FALSE.
|
|
198
|
+
// Root only: copies received from monitored roles, awaiting the host's
|
|
199
|
+
// get_monitoring_copies pull (forwarded to the bound proxy). Capped;
|
|
200
|
+
// oldest copies are dropped first when no proxy drains the queue.
|
|
201
|
+
monitoring_inbox is monitoring_copy_t[] = [].
|
|
202
|
+
// Root only: the in-flight proxy binding (6-digit code verification)
|
|
203
|
+
// and the verified proxy that monitoring traffic is forwarded to.
|
|
204
|
+
proxy_pending is proxy_pending_t+ = NIL.
|
|
205
|
+
monitoring_proxy is proxy_binding_t+ = NIL.
|
|
206
|
+
// Root only: control requests from the bound proxy, awaiting the
|
|
207
|
+
// host's get_control_requests pull. Kept out of the message inbox so
|
|
208
|
+
// agent sessions never see them.
|
|
209
|
+
control_inbox is control_req_t[] = [].
|
|
157
210
|
|
|
158
211
|
// Signal the host to persist the packet. Only emitted at the end of a
|
|
159
212
|
// complete procedure — intermediate states (e.g. channel handshake) are
|
|
@@ -162,19 +215,6 @@ application actor loads libraries
|
|
|
162
215
|
fn _return_data (payload: any) = (transaction::action::return_data ($kind -> $data, $payload -> payload)).
|
|
163
216
|
fn _notify_agent (payload: any) = (transaction::action::return_data ($kind -> $notify_agent, $payload -> payload)).
|
|
164
217
|
|
|
165
|
-
// Resolve a contact reference (a display name or stringified container id)
|
|
166
|
-
// to a container id; aborts if no contact matches.
|
|
167
|
-
fn resolve_contact (ref: str) -> global_id
|
|
168
|
-
{
|
|
169
|
-
found is global_id+ = NIL.
|
|
170
|
-
sc contacts -- (cid -> c) ?? found == NIL && ((c $name) == ref || (_str cid) == ref)
|
|
171
|
-
{
|
|
172
|
-
found -> cid.
|
|
173
|
-
}
|
|
174
|
-
abort "Unknown contact: " + ref when found == NIL.
|
|
175
|
-
return found?.
|
|
176
|
-
}
|
|
177
|
-
|
|
178
218
|
// Append a message to the inbox under a fresh id; returns the id.
|
|
179
219
|
fn deposit_message (sender_id: global_id, sender_name: str, text: str, msg_date: time) -> int
|
|
180
220
|
{
|
|
@@ -191,6 +231,39 @@ application actor loads libraries
|
|
|
191
231
|
return mid.
|
|
192
232
|
}
|
|
193
233
|
|
|
234
|
+
// Build the monitoring-copy action for one message IF this packet is a
|
|
235
|
+
// monitored role with a live encrypted channel to its root; [] otherwise.
|
|
236
|
+
// The is_container_registered guard makes a missing/lost root channel
|
|
237
|
+
// degrade to "no copy" instead of failing the user's message — the
|
|
238
|
+
// enable flow (host-side) establishes the channel via connect_sibling.
|
|
239
|
+
fn monitor_copy_actions (direction: str, peer_cid: global_id, text: str, msg_date: time) -> transaction::action::type[]
|
|
240
|
+
{
|
|
241
|
+
if monitoring_enabled == FALSE || a2a_messaging::delegation_cert == NIL { return []. }
|
|
242
|
+
root_cid = a2a_messaging::delegation_cert? $c $root_cid.
|
|
243
|
+
if key_storage::is_container_registered(root_cid) != TRUE { return []. }
|
|
244
|
+
|
|
245
|
+
peer_name is str = "".
|
|
246
|
+
p = a2a_messaging::contacts peer_cid.
|
|
247
|
+
if p != NIL { peer_name -> p? $name. }
|
|
248
|
+
|
|
249
|
+
copy is monitoring_copy_t = (
|
|
250
|
+
$version -> 1,
|
|
251
|
+
$source_cid -> _get_container_id(),
|
|
252
|
+
$source_name -> a2a_messaging::my_name,
|
|
253
|
+
$direction -> direction,
|
|
254
|
+
$peer_cid -> peer_cid,
|
|
255
|
+
$peer_name -> peer_name,
|
|
256
|
+
$date -> msg_date,
|
|
257
|
+
$body -> text
|
|
258
|
+
).
|
|
259
|
+
return [
|
|
260
|
+
encrypted_channel::send_encrypted_tx root_cid (
|
|
261
|
+
$name -> "::actor::receive_monitoring_copy",
|
|
262
|
+
$targ -> ($copy -> copy)
|
|
263
|
+
)
|
|
264
|
+
].
|
|
265
|
+
}
|
|
266
|
+
|
|
194
267
|
// Resolve a pending introduction by joiner name or stringified container
|
|
195
268
|
// id; aborts when nothing matches.
|
|
196
269
|
fn resolve_pending (ref: str) -> global_id
|
|
@@ -203,229 +276,83 @@ application actor loads libraries
|
|
|
203
276
|
abort "No pending introduction matches: " + ref when found == NIL.
|
|
204
277
|
return found?.
|
|
205
278
|
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ---- user transactions --------------------------------------------------
|
|
209
|
-
|
|
210
|
-
trn set_my_name _:($name -> name: str)
|
|
211
|
-
{
|
|
212
|
-
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
213
|
-
my_name -> name.
|
|
214
|
-
return transaction::success [
|
|
215
|
-
_return_data ($name -> name),
|
|
216
|
-
_save_state NIL
|
|
217
|
-
].
|
|
218
|
-
}
|
|
219
279
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
$
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
$invite -> (_write role_invite),
|
|
269
|
-
$invite_id -> invite_id,
|
|
270
|
-
$peer_name -> assigned
|
|
271
|
-
),
|
|
272
|
-
_save_state NIL
|
|
273
|
-
].
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
invite is a2a_protocol::invite_t = (
|
|
277
|
-
$d -> invite_id,
|
|
278
|
-
$n -> my_name,
|
|
279
|
-
$c -> my_identity $container_id,
|
|
280
|
-
$k -> my_identity $key_list,
|
|
281
|
-
$a -> my_ad $authorizations
|
|
280
|
+
// Wire the agent's storage into the shared messaging core. The receive
|
|
281
|
+
// hook owns everything app-specific about an inbound message: known
|
|
282
|
+
// senders deposit into the inbox; an unknown sender may be a verified-
|
|
283
|
+
// but-unapproved local introduction messaging before approval — queue
|
|
284
|
+
// (bounded) inside its pending entry, approval flushes the queue into
|
|
285
|
+
// the inbox in order; anything else from an unknown sender is rejected.
|
|
286
|
+
// The notification deliberately carries NO message body — only that a
|
|
287
|
+
// message arrived, from whom, and its id. The body stays in the packet
|
|
288
|
+
// and only leaves through get_messages.
|
|
289
|
+
a2a_messaging::init (
|
|
290
|
+
$_read_or_abort -> _read_or_abort,
|
|
291
|
+
$on_message_received -> fn (arg: any) -> transaction::action::type[]
|
|
292
|
+
{
|
|
293
|
+
sender_id = (arg $sender_id) safe global_id.
|
|
294
|
+
text = (arg $text) safe str.
|
|
295
|
+
msg_date = (arg $date) safe time.
|
|
296
|
+
|
|
297
|
+
if (arg $sender_name) == NIL
|
|
298
|
+
{
|
|
299
|
+
p = pending_introductions sender_id.
|
|
300
|
+
abort "Message from an unknown sender was rejected." when p == NIL.
|
|
301
|
+
entry = p?.
|
|
302
|
+
queued = entry $messages.
|
|
303
|
+
abort "Pending-introduction message queue is full; awaiting approval." when (_count queued|) >= pending_queue_cap.
|
|
304
|
+
queued (_count queued|) -> ($text -> text, $date -> msg_date).
|
|
305
|
+
pending_introductions sender_id -> ($name -> entry $name, $ad -> entry $ad, $messages -> queued).
|
|
306
|
+
return [
|
|
307
|
+
_notify_agent ($event -> $pending_message, $sender_name -> entry $name, $queued -> _count queued|),
|
|
308
|
+
_save_state NIL
|
|
309
|
+
].
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
sender_name = (arg $sender_name) safe str.
|
|
313
|
+
mid = deposit_message sender_id sender_name text msg_date.
|
|
314
|
+
actions is transaction::action::type[] = [].
|
|
315
|
+
sc monitor_copy_actions "in" sender_id text msg_date -- ( -> a)
|
|
316
|
+
{
|
|
317
|
+
actions (_count actions|) -> a.
|
|
318
|
+
}
|
|
319
|
+
actions (_count actions|) -> _notify_agent ($event -> $message_received, $sender_name -> sender_name, $msg_id -> mid, $date -> msg_date).
|
|
320
|
+
actions (_count actions|) -> _save_state NIL.
|
|
321
|
+
return actions.
|
|
322
|
+
},
|
|
323
|
+
$on_message_sent -> fn (arg: any) -> transaction::action::type[]
|
|
324
|
+
{
|
|
325
|
+
return monitor_copy_actions "out" ((arg $target_id) safe global_id) ((arg $text) safe str) ((arg $date) safe time).
|
|
326
|
+
},
|
|
327
|
+
$on_contact_removed -> fn (_: any) -> transaction::action::type[] { return []. }
|
|
282
328
|
).
|
|
283
329
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
inviter_keys = (raw $k) safe (key_utils::t_publickey(,)).
|
|
308
|
-
inviter_auths = (raw $a) safe (crypto_signature(,)).
|
|
309
|
-
|
|
310
|
-
// Rebuild the inviter's full address document from the carried material
|
|
311
|
-
// (see a2a_protocol::rebuild_peer_address_document — the reconstructed
|
|
312
|
-
// identity is byte-for-byte the signed one). import_state later replays
|
|
313
|
-
// this document through process_address_document to re-register the
|
|
314
|
-
// inviter's keys after a code upgrade — so it must validate, and it does.
|
|
315
|
-
inviter_ad = a2a_protocol::rebuild_peer_address_document inviter_id inviter_keys inviter_auths.
|
|
316
|
-
|
|
317
|
-
// A role invite carries a delegation chain — verify it BEFORE anything
|
|
318
|
-
// is registered (an invalid chain rejects the whole invite), and record
|
|
319
|
-
// the root linkage. A legacy/root invite has no chain; nothing to check.
|
|
320
|
-
inviter_root is a2a_protocol::contact_root_t+ = NIL.
|
|
321
|
-
inviter_bio is str = "".
|
|
322
|
-
if (raw $dc) != NIL
|
|
323
|
-
{
|
|
324
|
-
cert = (raw $dc) safe a2a_protocol::delegation_cert_t.
|
|
325
|
-
rp = (raw $rp) safe a2a_protocol::root_profile_t.
|
|
326
|
-
inviter_root -> a2a_protocol::verify_peer_delegation inviter_id (_value_id inviter_ad) cert rp.
|
|
327
|
-
inviter_bio -> (raw $b) safe str.
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Register the inviter under my chosen name, or the name they embedded.
|
|
331
|
-
contact_name = (custom_name == NIL ?? inviter_name ; custom_name?).
|
|
332
|
-
contacts inviter_id -> ($name -> contact_name, $container_id -> inviter_id).
|
|
333
|
-
// Remember the inviter's address document so I can re-register them after
|
|
334
|
-
// an upgrade (their keys are seed-stable, so this stays valid).
|
|
335
|
-
peer_ads inviter_id -> inviter_ad.
|
|
336
|
-
if inviter_root != NIL
|
|
337
|
-
{
|
|
338
|
-
contact_roots inviter_id -> inviter_root?.
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
my_self_name = my_name.
|
|
342
|
-
my_ad = address_document::get_my_address_document().
|
|
343
|
-
// If I am a delegated role myself, carry my own chain in the reply so
|
|
344
|
-
// the inviter learns my root linkage symmetrically.
|
|
345
|
-
my_cert_blob is bin+ = NIL.
|
|
346
|
-
my_rp_blob is bin+ = NIL.
|
|
347
|
-
if delegation_cert != NIL && root_profile != NIL
|
|
348
|
-
{
|
|
349
|
-
my_cert_blob -> (_write delegation_cert?).
|
|
350
|
-
my_rp_blob -> (_write root_profile?).
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
root_name_out is str = "".
|
|
354
|
-
role_id_out is str = "".
|
|
355
|
-
if inviter_root != NIL
|
|
356
|
-
{
|
|
357
|
-
root_name_out -> inviter_root? $root_name.
|
|
358
|
-
role_id_out -> inviter_root? $role_id.
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Establish the encrypted channel with the inviter (handshake happens
|
|
362
|
-
// transparently if we haven't talked before), then tell them I redeemed
|
|
363
|
-
// their invite, what my own name is, and my address document, so they can
|
|
364
|
-
// finish the handshake and remember me for future upgrades.
|
|
365
|
-
return encrypted_channel::execute_transaction inviter_id (fn (_) -> transaction::results::type {
|
|
366
|
-
return transaction::success [
|
|
367
|
-
encrypted_channel::send_encrypted_tx inviter_id (
|
|
368
|
-
$name -> "::actor::accept_contact",
|
|
369
|
-
$targ -> (
|
|
370
|
-
$invite_id -> invite_id,
|
|
371
|
-
$joiner_name -> my_self_name,
|
|
372
|
-
$joiner_ad -> my_ad,
|
|
373
|
-
$joiner_cert -> my_cert_blob,
|
|
374
|
-
$joiner_root_profile -> my_rp_blob
|
|
375
|
-
)
|
|
376
|
-
),
|
|
377
|
-
_return_data (
|
|
378
|
-
$added -> contact_name,
|
|
379
|
-
$container_id -> inviter_id,
|
|
380
|
-
$root_name -> root_name_out,
|
|
381
|
-
$role_id -> role_id_out,
|
|
382
|
-
$bio -> inviter_bio
|
|
383
|
-
),
|
|
384
|
-
_save_state NIL
|
|
385
|
-
].
|
|
386
|
-
}).
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
trn send_message _:($contact -> contact_ref: str, $text -> text: str)
|
|
390
|
-
{
|
|
391
|
-
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
392
|
-
|
|
393
|
-
target_id = resolve_contact contact_ref.
|
|
394
|
-
|
|
395
|
-
return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
|
|
396
|
-
return transaction::success [
|
|
397
|
-
encrypted_channel::send_encrypted_tx target_id (
|
|
398
|
-
$name -> "::actor::receive_message",
|
|
399
|
-
$targ -> ($text -> text)
|
|
400
|
-
),
|
|
401
|
-
_return_data ($sent_to -> target_id, $text -> text),
|
|
402
|
-
_save_state NIL
|
|
403
|
-
].
|
|
404
|
-
}).
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
trn remove_contact _:($contact -> contact_ref: str)
|
|
408
|
-
{
|
|
409
|
-
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
410
|
-
|
|
411
|
-
target_id = resolve_contact contact_ref.
|
|
412
|
-
removed = contacts target_id.
|
|
413
|
-
removed_name = removed? $name.
|
|
414
|
-
|
|
415
|
-
delete contacts target_id.
|
|
416
|
-
if peer_ads target_id != NIL { delete peer_ads target_id. }
|
|
417
|
-
if contact_roots target_id != NIL { delete contact_roots target_id. }
|
|
418
|
-
|
|
419
|
-
return transaction::success [
|
|
420
|
-
_return_data ($removed -> removed_name, $container_id -> target_id),
|
|
421
|
-
_save_state NIL
|
|
422
|
-
].
|
|
330
|
+
// Wire the control plane (see MONITORING-AND-SHARED-LIBRARY-DESIGN.md
|
|
331
|
+
// Part 4): control requests from the bound browser proxy queue in
|
|
332
|
+
// control_inbox — NEVER the message inbox, so agent sessions don't see
|
|
333
|
+
// them — and the notify event wakes the daemon's dispatcher. The
|
|
334
|
+
// payload stays opaque here; sender authorization happens in the
|
|
335
|
+
// daemon against the packet's monitoring_proxy / proxy_pending state.
|
|
336
|
+
a2a_control::init (
|
|
337
|
+
$on_control_received -> fn (arg: any) -> transaction::action::type[]
|
|
338
|
+
{
|
|
339
|
+
abort "Control queue is full." when (_count control_inbox|) >= control_inbox_cap.
|
|
340
|
+
sender_name = (arg $sender_name) safe str.
|
|
341
|
+
control_inbox (_count control_inbox|) -> (
|
|
342
|
+
$sender_cid -> (arg $sender_id) safe global_id,
|
|
343
|
+
$sender_name -> sender_name,
|
|
344
|
+
$payload -> (arg $payload) safe str,
|
|
345
|
+
$date -> (arg $date) safe time
|
|
346
|
+
).
|
|
347
|
+
return [
|
|
348
|
+
_notify_agent ($event -> $control_request, $sender_name -> sender_name, $queued -> _count control_inbox|),
|
|
349
|
+
_save_state NIL
|
|
350
|
+
].
|
|
351
|
+
}
|
|
352
|
+
).
|
|
423
353
|
}
|
|
424
354
|
|
|
425
|
-
|
|
426
|
-
{
|
|
427
|
-
return contacts.
|
|
428
|
-
}
|
|
355
|
+
// ---- message store --------------------------------------------------------
|
|
429
356
|
|
|
430
357
|
trn readonly list_incoming_messages _
|
|
431
358
|
{
|
|
@@ -668,10 +595,10 @@ application actor loads libraries
|
|
|
668
595
|
entry_sig = (_read_or_abort entry_sig_blob) safe crypto_signature.
|
|
669
596
|
abort "Contact-book entry failed registrar verification." when key_storage::check_signature_new_container (_value_id entry) entry_sig (registrar_ad? $identity $key_list) != TRUE.
|
|
670
597
|
|
|
671
|
-
contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
672
|
-
peer_ads target_id -> target_ad.
|
|
598
|
+
a2a_messaging::contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
599
|
+
a2a_messaging::peer_ads target_id -> target_ad.
|
|
673
600
|
|
|
674
|
-
my_self_name = my_name.
|
|
601
|
+
my_self_name = a2a_messaging::my_name.
|
|
675
602
|
my_ad = address_document::get_my_address_document().
|
|
676
603
|
return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
|
|
677
604
|
return transaction::success [
|
|
@@ -691,8 +618,8 @@ application actor loads libraries
|
|
|
691
618
|
|
|
692
619
|
pid = resolve_pending ref.
|
|
693
620
|
entry = (pending_introductions pid)?.
|
|
694
|
-
contacts pid -> ($name -> entry $name, $container_id -> pid).
|
|
695
|
-
peer_ads pid -> entry $ad.
|
|
621
|
+
a2a_messaging::contacts pid -> ($name -> entry $name, $container_id -> pid).
|
|
622
|
+
a2a_messaging::peer_ads pid -> entry $ad.
|
|
696
623
|
|
|
697
624
|
queued = entry $messages.
|
|
698
625
|
flushed is int = 0.
|
|
@@ -751,7 +678,7 @@ application actor loads libraries
|
|
|
751
678
|
trn sign_delegation _:($role_ad -> role_ad_blob: bin, $role_id -> role_id: str)
|
|
752
679
|
{
|
|
753
680
|
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
754
|
-
abort "Only a root identity can sign delegation certificates." when delegation_cert != NIL.
|
|
681
|
+
abort "Only a root identity can sign delegation certificates." when a2a_messaging::delegation_cert != NIL.
|
|
755
682
|
|
|
756
683
|
role_ad = (_read_or_abort role_ad_blob) safe address_document_types::t_address_document.
|
|
757
684
|
role_cid = role_ad $identity $container_id.
|
|
@@ -777,14 +704,14 @@ application actor loads libraries
|
|
|
777
704
|
trn export_root_profile _
|
|
778
705
|
{
|
|
779
706
|
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
780
|
-
abort "Only a root identity can export a root profile." when delegation_cert != NIL.
|
|
707
|
+
abort "Only a root identity can export a root profile." when a2a_messaging::delegation_cert != NIL.
|
|
781
708
|
|
|
782
709
|
my_ad = address_document::get_my_address_document().
|
|
783
710
|
core is a2a_protocol::root_profile_core_t = (
|
|
784
711
|
$version -> 1,
|
|
785
712
|
$root_cid -> _get_container_id(),
|
|
786
|
-
$name -> my_name,
|
|
787
|
-
$bio -> my_bio,
|
|
713
|
+
$name -> a2a_messaging::my_name,
|
|
714
|
+
$bio -> a2a_messaging::my_bio,
|
|
788
715
|
$keys -> my_ad $identity $key_list
|
|
789
716
|
).
|
|
790
717
|
profile is a2a_protocol::root_profile_t = ($p -> core, $s -> key_storage::default_sign (_value_id core)).
|
|
@@ -817,9 +744,9 @@ application actor loads libraries
|
|
|
817
744
|
abort "The root profile's key list does not match the root's address document." when (_value_id (rp $p $keys)) != (_value_id (new_root_ad $identity $key_list)).
|
|
818
745
|
abort "The root profile signature is invalid." when key_storage::check_signature_new_container (_value_id (rp $p)) (rp $s) (new_root_ad $identity $key_list) != TRUE.
|
|
819
746
|
|
|
820
|
-
delegation_cert -> cert.
|
|
821
|
-
root_ad -> new_root_ad.
|
|
822
|
-
root_profile -> rp.
|
|
747
|
+
a2a_messaging::delegation_cert -> cert.
|
|
748
|
+
a2a_messaging::root_ad -> new_root_ad.
|
|
749
|
+
a2a_messaging::root_profile -> rp.
|
|
823
750
|
|
|
824
751
|
return transaction::success [
|
|
825
752
|
_return_data ($delegated -> TRUE, $root_cid -> (_str (cert $c $root_cid)), $role_id -> cert $c $role_id),
|
|
@@ -829,34 +756,24 @@ application actor loads libraries
|
|
|
829
756
|
|
|
830
757
|
trn readonly describe_identity _
|
|
831
758
|
{
|
|
832
|
-
if delegation_cert == NIL
|
|
759
|
+
if a2a_messaging::delegation_cert == NIL
|
|
833
760
|
{
|
|
834
|
-
return ($name -> my_name, $bio -> my_bio, $has_cert -> FALSE, $role_id -> "", $root_cid -> "", $root_name -> "").
|
|
761
|
+
return ($name -> a2a_messaging::my_name, $bio -> a2a_messaging::my_bio, $has_cert -> FALSE, $role_id -> "", $root_cid -> "", $root_name -> "", $monitoring_enabled -> monitoring_enabled).
|
|
835
762
|
}
|
|
836
|
-
cert = delegation_cert?.
|
|
763
|
+
cert = a2a_messaging::delegation_cert?.
|
|
837
764
|
rname is str = "".
|
|
838
|
-
if root_profile != NIL { rname -> root_profile? $p $name. }
|
|
765
|
+
if a2a_messaging::root_profile != NIL { rname -> a2a_messaging::root_profile? $p $name. }
|
|
839
766
|
return (
|
|
840
|
-
$name -> my_name,
|
|
841
|
-
$bio -> my_bio,
|
|
767
|
+
$name -> a2a_messaging::my_name,
|
|
768
|
+
$bio -> a2a_messaging::my_bio,
|
|
842
769
|
$has_cert -> TRUE,
|
|
843
770
|
$role_id -> cert $c $role_id,
|
|
844
771
|
$root_cid -> (_str (cert $c $root_cid)),
|
|
845
|
-
$root_name -> rname
|
|
772
|
+
$root_name -> rname,
|
|
773
|
+
$monitoring_enabled -> monitoring_enabled
|
|
846
774
|
).
|
|
847
775
|
}
|
|
848
776
|
|
|
849
|
-
trn readonly list_contact_roots _
|
|
850
|
-
{
|
|
851
|
-
return contact_roots.
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
// The shared-core version this packet was compiled with (see core/version.mm).
|
|
855
|
-
trn readonly get_version _
|
|
856
|
-
{
|
|
857
|
-
return ($core -> version::get_core_version()).
|
|
858
|
-
}
|
|
859
|
-
|
|
860
777
|
// Connect to an intra-root sibling (Ring 1): register it as a contact and
|
|
861
778
|
// introduce myself over the encrypted channel, presenting my delegation
|
|
862
779
|
// cert (NIL when I am the root itself — the channel proves I control the
|
|
@@ -872,22 +789,22 @@ application actor loads libraries
|
|
|
872
789
|
abort "This sibling is your own identity." when target_id == _get_container_id().
|
|
873
790
|
|
|
874
791
|
cert_blob is bin+ = NIL.
|
|
875
|
-
if delegation_cert != NIL { cert_blob -> (_write delegation_cert?). }
|
|
792
|
+
if a2a_messaging::delegation_cert != NIL { cert_blob -> (_write a2a_messaging::delegation_cert?). }
|
|
876
793
|
|
|
877
|
-
contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
878
|
-
peer_ads target_id -> target_ad.
|
|
794
|
+
a2a_messaging::contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
795
|
+
a2a_messaging::peer_ads target_id -> target_ad.
|
|
879
796
|
// Record the target's root linkage: a sibling shares my root by
|
|
880
797
|
// definition (the receiving side verifies the converse independently).
|
|
881
|
-
if delegation_cert != NIL && root_profile != NIL
|
|
798
|
+
if a2a_messaging::delegation_cert != NIL && a2a_messaging::root_profile != NIL
|
|
882
799
|
{
|
|
883
|
-
contact_roots target_id -> ($root_cid -> delegation_cert? $c $root_cid, $root_name -> root_profile? $p $name, $role_id -> name).
|
|
800
|
+
a2a_messaging::contact_roots target_id -> ($root_cid -> a2a_messaging::delegation_cert? $c $root_cid, $root_name -> a2a_messaging::root_profile? $p $name, $role_id -> name).
|
|
884
801
|
}
|
|
885
802
|
else
|
|
886
803
|
{
|
|
887
|
-
contact_roots target_id -> ($root_cid -> _get_container_id(), $root_name -> my_name, $role_id -> name).
|
|
804
|
+
a2a_messaging::contact_roots target_id -> ($root_cid -> _get_container_id(), $root_name -> a2a_messaging::my_name, $role_id -> name).
|
|
888
805
|
}
|
|
889
806
|
|
|
890
|
-
my_self_name = my_name.
|
|
807
|
+
my_self_name = a2a_messaging::my_name.
|
|
891
808
|
my_ad = address_document::get_my_address_document().
|
|
892
809
|
return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
|
|
893
810
|
return transaction::success [
|
|
@@ -901,6 +818,232 @@ application actor loads libraries
|
|
|
901
818
|
}).
|
|
902
819
|
}
|
|
903
820
|
|
|
821
|
+
// ---- monitoring + control plane -------------------------------------------
|
|
822
|
+
// (see MONITORING-AND-SHARED-LIBRARY-DESIGN.md). A monitored ROLE reports
|
|
823
|
+
// every message it sends/receives to its ROOT (the monitor_copy_actions
|
|
824
|
+
// branches in the storage hooks above); the root queues the copies and the
|
|
825
|
+
// host forwards them to a human proxy bound via 6-digit-code verification.
|
|
826
|
+
// The proxy's control requests (create agent, update role, …) queue in
|
|
827
|
+
// control_inbox and are executed by the host daemon.
|
|
828
|
+
|
|
829
|
+
trn readonly get_monitoring_status _
|
|
830
|
+
{
|
|
831
|
+
pending is bool = FALSE.
|
|
832
|
+
if proxy_pending != NIL { pending -> TRUE. }
|
|
833
|
+
proxy_out is str = "".
|
|
834
|
+
if monitoring_proxy != NIL { proxy_out -> _str (monitoring_proxy? $proxy_cid). }
|
|
835
|
+
return (
|
|
836
|
+
$monitoring_enabled -> monitoring_enabled,
|
|
837
|
+
$proxy_cid -> proxy_out,
|
|
838
|
+
$proxy_pending -> pending,
|
|
839
|
+
$copies_queued -> _count monitoring_inbox|,
|
|
840
|
+
$control_queued -> _count control_inbox|
|
|
841
|
+
).
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Sign a monitoring authorization for a role (ROOT packet only — the role
|
|
845
|
+
// verifies the signature against its pinned root keys, so an auth minted
|
|
846
|
+
// by any other packet fails). Stateless, mirrors sign_delegation.
|
|
847
|
+
trn sign_monitoring_auth _:($role_ad -> role_ad_blob: bin, $enabled -> enabled: bool)
|
|
848
|
+
{
|
|
849
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
850
|
+
abort "Only a root identity can sign monitoring authorizations." when a2a_messaging::delegation_cert != NIL.
|
|
851
|
+
|
|
852
|
+
role_ad = (_read_or_abort role_ad_blob) safe address_document_types::t_address_document.
|
|
853
|
+
core is monitoring_auth_core_t = (
|
|
854
|
+
$version -> 1,
|
|
855
|
+
$role_cid -> role_ad $identity $container_id,
|
|
856
|
+
$enabled -> enabled,
|
|
857
|
+
$issued_at -> (current_transaction_info::get_transaction_time())?
|
|
858
|
+
).
|
|
859
|
+
auth is monitoring_auth_t = ($c -> core, $s -> key_storage::default_sign (_value_id core)).
|
|
860
|
+
return transaction::success [
|
|
861
|
+
_return_data ($auth -> (_write auth))
|
|
862
|
+
].
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Store a verified monitoring flag (ROLE packet, host-fired after the root
|
|
866
|
+
// signed the auth). An auth that does not name me or was not signed by my
|
|
867
|
+
// root is rejected — so even a compromised host process cannot silently
|
|
868
|
+
// flip monitoring without the root packet's keys.
|
|
869
|
+
trn set_monitoring _:($auth -> auth_blob: bin)
|
|
870
|
+
{
|
|
871
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
872
|
+
abort "Only a delegated role can be monitored." when a2a_messaging::delegation_cert == NIL || a2a_messaging::root_ad == NIL.
|
|
873
|
+
|
|
874
|
+
auth = (_read_or_abort auth_blob) safe monitoring_auth_t.
|
|
875
|
+
abort "Unsupported monitoring authorization version." when (auth $c $version) != 1.
|
|
876
|
+
abort "This monitoring authorization was issued to a different identity." when (auth $c $role_cid) != _get_container_id().
|
|
877
|
+
abort "The monitoring authorization was not signed by my root." when key_storage::check_signature_new_container (_value_id (auth $c)) (auth $s) (a2a_messaging::root_ad? $identity $key_list) != TRUE.
|
|
878
|
+
|
|
879
|
+
monitoring_enabled -> auth $c $enabled.
|
|
880
|
+
return transaction::success [
|
|
881
|
+
_return_data ($monitoring_enabled -> monitoring_enabled),
|
|
882
|
+
_save_state NIL
|
|
883
|
+
].
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// A monitored role reports one message copy (ROOT packet, inbound). Only
|
|
887
|
+
// accepted from a verified role of THIS root (the contact_roots linkage
|
|
888
|
+
// recorded by sibling_introduce), and the copy must name its actual sender
|
|
889
|
+
// — a role cannot forge copies on another role's behalf.
|
|
890
|
+
trn receive_monitoring_copy _:($copy -> copy: monitoring_copy_t)
|
|
891
|
+
{
|
|
892
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::external,).
|
|
893
|
+
encrypted_channel::check_encrypted_or_abort().
|
|
894
|
+
|
|
895
|
+
sender_id = current_transaction_info::get_external_envelope_or_abort() $from.
|
|
896
|
+
link = a2a_messaging::contact_roots sender_id.
|
|
897
|
+
abort "Monitoring copies are only accepted from my own roles." when link == NIL || (link? $root_cid) != _get_container_id().
|
|
898
|
+
abort "Monitoring copy does not name its sender as the source." when (copy $source_cid) != sender_id.
|
|
899
|
+
abort "Unsupported monitoring copy version." when (copy $version) != 1.
|
|
900
|
+
|
|
901
|
+
// Capped queue, oldest first out: if no proxy drains the root, recent
|
|
902
|
+
// traffic wins over old.
|
|
903
|
+
if (_count monitoring_inbox|) >= monitoring_inbox_cap
|
|
904
|
+
{
|
|
905
|
+
trimmed is monitoring_copy_t[] = [].
|
|
906
|
+
i is int = 0.
|
|
907
|
+
sc monitoring_inbox -- ( -> m)
|
|
908
|
+
{
|
|
909
|
+
if i > 0 { trimmed (_count trimmed|) -> m. }
|
|
910
|
+
i -> i + 1.
|
|
911
|
+
}
|
|
912
|
+
monitoring_inbox -> trimmed.
|
|
913
|
+
}
|
|
914
|
+
monitoring_inbox (_count monitoring_inbox|) -> copy.
|
|
915
|
+
|
|
916
|
+
return transaction::success [
|
|
917
|
+
_notify_agent ($event -> $monitoring_copy, $source_name -> copy $source_name, $queued -> _count monitoring_inbox|),
|
|
918
|
+
_save_state NIL
|
|
919
|
+
].
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Drain the queued monitoring copies (ROOT packet, host-fired before
|
|
923
|
+
// forwarding to the bound proxy). Cleared on read, like get_messages.
|
|
924
|
+
trn get_monitoring_copies _
|
|
925
|
+
{
|
|
926
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
927
|
+
|
|
928
|
+
copies = monitoring_inbox.
|
|
929
|
+
monitoring_inbox -> [].
|
|
930
|
+
return transaction::success [
|
|
931
|
+
_return_data ($copies -> copies),
|
|
932
|
+
_save_state NIL
|
|
933
|
+
].
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Drain the queued control requests (ROOT packet, host-fired by the
|
|
937
|
+
// control dispatcher). Cleared on read.
|
|
938
|
+
trn get_control_requests _
|
|
939
|
+
{
|
|
940
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
941
|
+
|
|
942
|
+
reqs = control_inbox.
|
|
943
|
+
control_inbox -> [].
|
|
944
|
+
return transaction::success [
|
|
945
|
+
_return_data ($requests -> reqs),
|
|
946
|
+
_save_state NIL
|
|
947
|
+
].
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Start a proxy binding (ROOT packet, host-fired): remember the code the
|
|
951
|
+
// host generated (MUFL has no random source) for one specific contact.
|
|
952
|
+
// Restarting overwrites any previous pending binding.
|
|
953
|
+
trn set_proxy_pending _:($code -> code: str, $proxy -> proxy_ref: str)
|
|
954
|
+
{
|
|
955
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
956
|
+
abort "Only a root identity can bind a monitoring proxy." when a2a_messaging::delegation_cert != NIL.
|
|
957
|
+
|
|
958
|
+
pid = a2a_messaging::resolve_contact proxy_ref.
|
|
959
|
+
proxy_pending -> (
|
|
960
|
+
$code -> code,
|
|
961
|
+
$proxy_cid -> pid,
|
|
962
|
+
$created_at -> (current_transaction_info::get_transaction_time())?,
|
|
963
|
+
$attempts -> 0
|
|
964
|
+
).
|
|
965
|
+
return transaction::success [
|
|
966
|
+
_return_data ($pending -> TRUE, $proxy_cid -> (_str pid)),
|
|
967
|
+
_save_state NIL
|
|
968
|
+
].
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Verify a proxy's code attempt (ROOT packet, host-fired when a `bind`
|
|
972
|
+
// control request arrives). Failures are returned as DATA — not aborts —
|
|
973
|
+
// so the attempt counter and expiry clearing persist atomically; an abort
|
|
974
|
+
// would roll them back and reopen the brute-force window.
|
|
975
|
+
trn verify_proxy_code _:($code -> code: str, $sender -> sender_ref: str)
|
|
976
|
+
{
|
|
977
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
978
|
+
|
|
979
|
+
if proxy_pending == NIL
|
|
980
|
+
{
|
|
981
|
+
return transaction::success [ _return_data ($verified -> FALSE, $reason -> "no_pending") ].
|
|
982
|
+
}
|
|
983
|
+
p = proxy_pending?.
|
|
984
|
+
now = (current_transaction_info::get_transaction_time())?.
|
|
985
|
+
|
|
986
|
+
if (_substract_seconds now (p $created_at)) > proxy_code_max_age_seconds
|
|
987
|
+
{
|
|
988
|
+
proxy_pending -> NIL.
|
|
989
|
+
return transaction::success [
|
|
990
|
+
_return_data ($verified -> FALSE, $reason -> "expired"),
|
|
991
|
+
_save_state NIL
|
|
992
|
+
].
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
sid = a2a_messaging::resolve_contact sender_ref.
|
|
996
|
+
if sid != (p $proxy_cid)
|
|
997
|
+
{
|
|
998
|
+
// Not the contact this binding was started for: reject without
|
|
999
|
+
// burning an attempt (the code was never compared).
|
|
1000
|
+
return transaction::success [ _return_data ($verified -> FALSE, $reason -> "wrong_sender") ].
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if code != (p $code)
|
|
1004
|
+
{
|
|
1005
|
+
attempts = (p $attempts) + 1.
|
|
1006
|
+
if attempts >= proxy_max_attempts
|
|
1007
|
+
{
|
|
1008
|
+
proxy_pending -> NIL.
|
|
1009
|
+
return transaction::success [
|
|
1010
|
+
_return_data ($verified -> FALSE, $reason -> "too_many_attempts"),
|
|
1011
|
+
_save_state NIL
|
|
1012
|
+
].
|
|
1013
|
+
}
|
|
1014
|
+
proxy_pending -> (
|
|
1015
|
+
$code -> p $code,
|
|
1016
|
+
$proxy_cid -> p $proxy_cid,
|
|
1017
|
+
$created_at -> p $created_at,
|
|
1018
|
+
$attempts -> attempts
|
|
1019
|
+
).
|
|
1020
|
+
return transaction::success [
|
|
1021
|
+
_return_data ($verified -> FALSE, $reason -> "wrong_code", $attempts_left -> proxy_max_attempts - attempts),
|
|
1022
|
+
_save_state NIL
|
|
1023
|
+
].
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
monitoring_proxy -> ($proxy_cid -> sid, $bound_at -> now).
|
|
1027
|
+
proxy_pending -> NIL.
|
|
1028
|
+
return transaction::success [
|
|
1029
|
+
_return_data ($verified -> TRUE, $proxy_cid -> (_str sid)),
|
|
1030
|
+
_save_state NIL
|
|
1031
|
+
].
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// Drop the proxy binding (and any in-flight code verification).
|
|
1035
|
+
trn clear_monitoring_proxy _
|
|
1036
|
+
{
|
|
1037
|
+
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
1038
|
+
|
|
1039
|
+
monitoring_proxy -> NIL.
|
|
1040
|
+
proxy_pending -> NIL.
|
|
1041
|
+
return transaction::success [
|
|
1042
|
+
_return_data ($cleared -> TRUE),
|
|
1043
|
+
_save_state NIL
|
|
1044
|
+
].
|
|
1045
|
+
}
|
|
1046
|
+
|
|
904
1047
|
// ---- upgrade: state export / import -------------------------------------
|
|
905
1048
|
// The host persists state by calling export_state (readonly) and serializing
|
|
906
1049
|
// the returned value to a code-independent blob. On a code upgrade it recreates
|
|
@@ -909,27 +1052,37 @@ application actor loads libraries
|
|
|
909
1052
|
//
|
|
910
1053
|
// The packet-level snapshot is NOT used for upgrades: it is bound to the unit
|
|
911
1054
|
// code hash, so a new .muflo cannot load an old snapshot. This data blob is.
|
|
1055
|
+
//
|
|
1056
|
+
// The blob stays FLAT with the historical field names — the core fields come
|
|
1057
|
+
// from a2a_messaging::export_core_state / import_core_state, the app fields
|
|
1058
|
+
// are composed in here — so a PRE-migration export imports unchanged.
|
|
912
1059
|
|
|
913
1060
|
trn readonly export_state _
|
|
914
1061
|
{
|
|
1062
|
+
core_state = a2a_messaging::export_core_state NIL.
|
|
915
1063
|
return (
|
|
916
|
-
$my_name -> my_name,
|
|
917
|
-
$contacts -> contacts,
|
|
918
|
-
$pending_invites -> pending_invites,
|
|
1064
|
+
$my_name -> core_state $my_name,
|
|
1065
|
+
$contacts -> core_state $contacts,
|
|
1066
|
+
$pending_invites -> core_state $pending_invites,
|
|
919
1067
|
$inbox -> inbox,
|
|
920
1068
|
$next_msg_seq -> next_msg_seq,
|
|
921
|
-
$peer_ads -> peer_ads,
|
|
1069
|
+
$peer_ads -> core_state $peer_ads,
|
|
922
1070
|
$registrar_ad -> registrar_ad,
|
|
923
1071
|
$local_auto_accept -> local_auto_accept,
|
|
924
1072
|
// Nonces are exported so a restart does not reopen the replay window
|
|
925
1073
|
// for still-fresh credentials; stale ones are purged lazily anyway.
|
|
926
1074
|
$seen_nonces -> seen_nonces,
|
|
927
1075
|
$pending_introductions -> pending_introductions,
|
|
928
|
-
$my_bio -> my_bio,
|
|
929
|
-
$delegation_cert -> delegation_cert,
|
|
930
|
-
$root_ad -> root_ad,
|
|
931
|
-
$root_profile -> root_profile,
|
|
932
|
-
$contact_roots -> contact_roots
|
|
1076
|
+
$my_bio -> core_state $my_bio,
|
|
1077
|
+
$delegation_cert -> core_state $delegation_cert,
|
|
1078
|
+
$root_ad -> core_state $root_ad,
|
|
1079
|
+
$root_profile -> core_state $root_profile,
|
|
1080
|
+
$contact_roots -> core_state $contact_roots,
|
|
1081
|
+
$monitoring_enabled -> monitoring_enabled,
|
|
1082
|
+
$monitoring_inbox -> monitoring_inbox,
|
|
1083
|
+
$proxy_pending -> proxy_pending,
|
|
1084
|
+
$monitoring_proxy -> monitoring_proxy,
|
|
1085
|
+
$control_inbox -> control_inbox
|
|
933
1086
|
).
|
|
934
1087
|
}
|
|
935
1088
|
|
|
@@ -937,12 +1090,11 @@ application actor loads libraries
|
|
|
937
1090
|
{
|
|
938
1091
|
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
939
1092
|
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
peer_ads -> (data $peer_ads) safe (global_id ->> address_document_types::t_address_document).
|
|
1093
|
+
// Core fields (contacts/profile/hierarchy) — validated and restored by
|
|
1094
|
+
// the shared library, which also replays every peer's address document
|
|
1095
|
+
// through process_address_document so encrypted channels keep working
|
|
1096
|
+
// after the upgrade with no re-handshake.
|
|
1097
|
+
a2a_messaging::import_core_state data.
|
|
946
1098
|
|
|
947
1099
|
// The inbox + next_msg_seq are the only parts the message-lifecycle changes
|
|
948
1100
|
// touched. A pre-lifecycle blob has no $next_msg_seq and inbox entries with
|
|
@@ -1013,36 +1165,29 @@ application actor loads libraries
|
|
|
1013
1165
|
pending_introductions -> (data $pending_introductions) safe (global_id ->> pending_intro_t).
|
|
1014
1166
|
}
|
|
1015
1167
|
|
|
1016
|
-
//
|
|
1017
|
-
//
|
|
1018
|
-
if (data $
|
|
1168
|
+
// Monitoring + control state arrived after the local-book schema —
|
|
1169
|
+
// optional in old blobs the same way.
|
|
1170
|
+
if (data $monitoring_enabled) != NIL
|
|
1019
1171
|
{
|
|
1020
|
-
|
|
1172
|
+
monitoring_enabled -> (data $monitoring_enabled) safe bool.
|
|
1021
1173
|
}
|
|
1022
|
-
if (data $
|
|
1174
|
+
if (data $monitoring_inbox) != NIL
|
|
1023
1175
|
{
|
|
1024
|
-
|
|
1176
|
+
monitoring_inbox -> (data $monitoring_inbox) safe (monitoring_copy_t[]).
|
|
1025
1177
|
}
|
|
1026
|
-
if (data $
|
|
1178
|
+
if (data $proxy_pending) != NIL
|
|
1027
1179
|
{
|
|
1028
|
-
|
|
1180
|
+
proxy_pending -> (data $proxy_pending) safe proxy_pending_t.
|
|
1029
1181
|
}
|
|
1030
|
-
if (data $
|
|
1182
|
+
if (data $monitoring_proxy) != NIL
|
|
1031
1183
|
{
|
|
1032
|
-
|
|
1184
|
+
monitoring_proxy -> (data $monitoring_proxy) safe proxy_binding_t.
|
|
1033
1185
|
}
|
|
1034
|
-
if (data $
|
|
1186
|
+
if (data $control_inbox) != NIL
|
|
1035
1187
|
{
|
|
1036
|
-
|
|
1188
|
+
control_inbox -> (data $control_inbox) safe (control_req_t[]).
|
|
1037
1189
|
}
|
|
1038
1190
|
|
|
1039
|
-
// Re-register every peer's keys so encrypted channels keep working after
|
|
1040
|
-
// the upgrade — no handshake needed (my own keys are unchanged, and the
|
|
1041
|
-
// peers' self-signed address documents re-authorize on this fresh packet).
|
|
1042
|
-
sc peer_ads -- ( -> ad)
|
|
1043
|
-
{
|
|
1044
|
-
address_document::process_address_document ad TRUE.
|
|
1045
|
-
}
|
|
1046
1191
|
// Pending introducers' keys too: their channel to me predates approval,
|
|
1047
1192
|
// so it must survive an upgrade exactly like an approved contact's.
|
|
1048
1193
|
sc pending_introductions -- ( -> p)
|
|
@@ -1051,103 +1196,24 @@ application actor loads libraries
|
|
|
1051
1196
|
}
|
|
1052
1197
|
|
|
1053
1198
|
return transaction::success [
|
|
1054
|
-
_return_data ($imported -> TRUE, $contacts -> _count contacts|, $peers -> _count peer_ads|),
|
|
1199
|
+
_return_data ($imported -> TRUE, $contacts -> _count a2a_messaging::contacts|, $peers -> _count a2a_messaging::peer_ads|),
|
|
1055
1200
|
_save_state NIL
|
|
1056
1201
|
].
|
|
1057
1202
|
}
|
|
1058
1203
|
|
|
1059
1204
|
// ---- external (inbound) transactions ------------------------------------
|
|
1060
1205
|
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1206
|
+
// Compat shims (Option A): pre-migration peers address these as ::actor::*,
|
|
1207
|
+
// and the core keeps SENDING to the ::actor:: names too. Each delegates to
|
|
1208
|
+
// the shared handler — remove only when no old clients remain.
|
|
1063
1209
|
trn accept_contact args: any
|
|
1064
1210
|
{
|
|
1065
|
-
|
|
1066
|
-
encrypted_channel::check_encrypted_or_abort().
|
|
1067
|
-
|
|
1068
|
-
sender_id = current_transaction_info::get_external_envelope_or_abort() $from.
|
|
1069
|
-
invite_id = (args $invite_id) safe global_id.
|
|
1070
|
-
joiner_ad = (args $joiner_ad) safe address_document_types::t_address_document.
|
|
1071
|
-
|
|
1072
|
-
// Only an invite I generated (and have not yet consumed) authorizes a
|
|
1073
|
-
// contact registration. Without this gate any invite blob would be a
|
|
1074
|
-
// multi-use bearer credential: anyone who ever saw one could register
|
|
1075
|
-
// themselves as my contact with a self-chosen name.
|
|
1076
|
-
assigned_name = pending_invites invite_id.
|
|
1077
|
-
abort "Unknown or already-redeemed invite." when assigned_name == NIL.
|
|
1078
|
-
// An empty assigned name means the invite was generated without one:
|
|
1079
|
-
// the joiner's self-announced name wins (container id as a last resort
|
|
1080
|
-
// when the joiner never set a name either).
|
|
1081
|
-
contact_name is str = assigned_name?.
|
|
1082
|
-
if contact_name == ""
|
|
1083
|
-
{
|
|
1084
|
-
joiner_self = (args $joiner_name) safe str.
|
|
1085
|
-
contact_name -> (joiner_self == "" ?? (_str sender_id) ; joiner_self).
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
// A delegated-role joiner carries its chain so I learn its root linkage
|
|
1089
|
-
// symmetrically; an invalid chain rejects the redemption outright.
|
|
1090
|
-
joiner_root is a2a_protocol::contact_root_t+ = NIL.
|
|
1091
|
-
if (args $joiner_cert) != NIL
|
|
1092
|
-
{
|
|
1093
|
-
cert = (_read_or_abort ((args $joiner_cert) safe bin)) safe a2a_protocol::delegation_cert_t.
|
|
1094
|
-
rp = (_read_or_abort ((args $joiner_root_profile) safe bin)) safe a2a_protocol::root_profile_t.
|
|
1095
|
-
joiner_root -> a2a_protocol::verify_peer_delegation sender_id (_value_id joiner_ad) cert rp.
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
contacts sender_id -> ($name -> contact_name, $container_id -> sender_id).
|
|
1099
|
-
// Remember the joiner's address document for upgrade-time re-registration.
|
|
1100
|
-
peer_ads sender_id -> joiner_ad.
|
|
1101
|
-
if joiner_root != NIL
|
|
1102
|
-
{
|
|
1103
|
-
contact_roots sender_id -> joiner_root?.
|
|
1104
|
-
}
|
|
1105
|
-
delete pending_invites invite_id.
|
|
1106
|
-
|
|
1107
|
-
return transaction::success [
|
|
1108
|
-
_notify_agent ($event -> $contact_accepted, $name -> contact_name, $container_id -> sender_id),
|
|
1109
|
-
_save_state NIL
|
|
1110
|
-
].
|
|
1211
|
+
return a2a_messaging::handle_accept_contact args.
|
|
1111
1212
|
}
|
|
1112
1213
|
|
|
1113
1214
|
trn receive_message _:($text -> text: str)
|
|
1114
1215
|
{
|
|
1115
|
-
|
|
1116
|
-
encrypted_channel::check_encrypted_or_abort().
|
|
1117
|
-
|
|
1118
|
-
sender_id = current_transaction_info::get_external_envelope_or_abort() $from.
|
|
1119
|
-
msg_date = (current_transaction_info::get_transaction_time())?.
|
|
1120
|
-
sender = contacts sender_id.
|
|
1121
|
-
|
|
1122
|
-
if sender == NIL
|
|
1123
|
-
{
|
|
1124
|
-
// A verified-but-unapproved local introduction may message me before
|
|
1125
|
-
// approval: queue (bounded) inside its pending entry. Approval
|
|
1126
|
-
// flushes the queue into the inbox in order; anything else from an
|
|
1127
|
-
// unknown sender is rejected as before.
|
|
1128
|
-
p = pending_introductions sender_id.
|
|
1129
|
-
abort "Message from an unknown sender was rejected." when p == NIL.
|
|
1130
|
-
entry = p?.
|
|
1131
|
-
queued = entry $messages.
|
|
1132
|
-
abort "Pending-introduction message queue is full; awaiting approval." when (_count queued|) >= pending_queue_cap.
|
|
1133
|
-
queued (_count queued|) -> ($text -> text, $date -> msg_date).
|
|
1134
|
-
pending_introductions sender_id -> ($name -> entry $name, $ad -> entry $ad, $messages -> queued).
|
|
1135
|
-
return transaction::success [
|
|
1136
|
-
_notify_agent ($event -> $pending_message, $sender_name -> entry $name, $queued -> _count queued|),
|
|
1137
|
-
_save_state NIL
|
|
1138
|
-
].
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
sender_name = sender? $name.
|
|
1142
|
-
mid = deposit_message sender_id sender_name text msg_date.
|
|
1143
|
-
|
|
1144
|
-
// The notification deliberately carries NO message body — only that a
|
|
1145
|
-
// message arrived, from whom, and its id. The body stays in the packet
|
|
1146
|
-
// and only leaves through get_messages.
|
|
1147
|
-
return transaction::success [
|
|
1148
|
-
_notify_agent ($event -> $message_received, $sender_name -> sender_name, $msg_id -> mid, $date -> msg_date),
|
|
1149
|
-
_save_state NIL
|
|
1150
|
-
].
|
|
1216
|
+
return a2a_messaging::handle_receive_message text.
|
|
1151
1217
|
}
|
|
1152
1218
|
|
|
1153
1219
|
// A same-host peer connects via the local contact book. The credential must
|
|
@@ -1196,12 +1262,12 @@ application actor loads libraries
|
|
|
1196
1262
|
abort "Too many concurrent introductions; try again shortly." when (_count seen_nonces|) >= seen_nonce_cap.
|
|
1197
1263
|
seen_nonces (intro $nonce) -> now.
|
|
1198
1264
|
|
|
1199
|
-
existing = contacts sender_id.
|
|
1265
|
+
existing = a2a_messaging::contacts sender_id.
|
|
1200
1266
|
if existing != NIL
|
|
1201
1267
|
{
|
|
1202
1268
|
// Already a contact (idempotent re-introduction): keep my assigned
|
|
1203
1269
|
// name, refresh the stored address document, deliver any payload.
|
|
1204
|
-
peer_ads sender_id -> joiner_ad.
|
|
1270
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1205
1271
|
if text != NIL
|
|
1206
1272
|
{
|
|
1207
1273
|
mid = deposit_message sender_id (existing? $name) text? now.
|
|
@@ -1215,8 +1281,8 @@ application actor loads libraries
|
|
|
1215
1281
|
|
|
1216
1282
|
if local_auto_accept
|
|
1217
1283
|
{
|
|
1218
|
-
contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1219
|
-
peer_ads sender_id -> joiner_ad.
|
|
1284
|
+
a2a_messaging::contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1285
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1220
1286
|
if text != NIL
|
|
1221
1287
|
{
|
|
1222
1288
|
mid = deposit_message sender_id joiner_name text? now.
|
|
@@ -1267,8 +1333,8 @@ application actor loads libraries
|
|
|
1267
1333
|
if cert_blob == NIL
|
|
1268
1334
|
{
|
|
1269
1335
|
// Sender claims to be my root.
|
|
1270
|
-
abort "A certificate-less sibling introduction is only accepted from my root." when delegation_cert == NIL.
|
|
1271
|
-
abort "Sender is not my root." when sender_id != (delegation_cert? $c $root_cid).
|
|
1336
|
+
abort "A certificate-less sibling introduction is only accepted from my root." when a2a_messaging::delegation_cert == NIL.
|
|
1337
|
+
abort "Sender is not my root." when sender_id != (a2a_messaging::delegation_cert? $c $root_cid).
|
|
1272
1338
|
link -> ($root_cid -> sender_id, $root_name -> joiner_name, $role_id -> "").
|
|
1273
1339
|
}
|
|
1274
1340
|
else
|
|
@@ -1279,14 +1345,14 @@ application actor loads libraries
|
|
|
1279
1345
|
abort "Sibling certificate does not match the sender's address document." when (cert $c $role_ad_hash) != (_value_id joiner_ad).
|
|
1280
1346
|
|
|
1281
1347
|
root_name_known is str = "".
|
|
1282
|
-
if delegation_cert != NIL
|
|
1348
|
+
if a2a_messaging::delegation_cert != NIL
|
|
1283
1349
|
{
|
|
1284
1350
|
// I am a role: the cert must name MY root and verify against the
|
|
1285
1351
|
// root key material pinned at delegation time.
|
|
1286
|
-
abort "My root material is missing — cannot verify sibling certificates." when root_ad == NIL.
|
|
1287
|
-
abort "Sibling certificate names a different root." when (cert $c $root_cid) != (delegation_cert? $c $root_cid).
|
|
1288
|
-
abort "Sibling certificate was not signed by my root." when key_storage::check_signature_new_container (_value_id (cert $c)) (cert $s) (root_ad? $identity $key_list) != TRUE.
|
|
1289
|
-
if root_profile != NIL { root_name_known -> root_profile? $p $name. }
|
|
1352
|
+
abort "My root material is missing — cannot verify sibling certificates." when a2a_messaging::root_ad == NIL.
|
|
1353
|
+
abort "Sibling certificate names a different root." when (cert $c $root_cid) != (a2a_messaging::delegation_cert? $c $root_cid).
|
|
1354
|
+
abort "Sibling certificate was not signed by my root." when key_storage::check_signature_new_container (_value_id (cert $c)) (cert $s) (a2a_messaging::root_ad? $identity $key_list) != TRUE.
|
|
1355
|
+
if a2a_messaging::root_profile != NIL { root_name_known -> a2a_messaging::root_profile? $p $name. }
|
|
1290
1356
|
}
|
|
1291
1357
|
else
|
|
1292
1358
|
{
|
|
@@ -1295,18 +1361,18 @@ application actor loads libraries
|
|
|
1295
1361
|
abort "Sibling certificate names a different root." when (cert $c $root_cid) != _get_container_id().
|
|
1296
1362
|
my_ad = address_document::get_my_address_document().
|
|
1297
1363
|
abort "Sibling certificate was not signed by me." when key_storage::check_signature_new_container (_value_id (cert $c)) (cert $s) (my_ad $identity $key_list) != TRUE.
|
|
1298
|
-
root_name_known -> my_name.
|
|
1364
|
+
root_name_known -> a2a_messaging::my_name.
|
|
1299
1365
|
}
|
|
1300
1366
|
link -> ($root_cid -> cert $c $root_cid, $root_name -> root_name_known, $role_id -> cert $c $role_id).
|
|
1301
1367
|
}
|
|
1302
1368
|
|
|
1303
|
-
existing = contacts sender_id.
|
|
1369
|
+
existing = a2a_messaging::contacts sender_id.
|
|
1304
1370
|
if existing != NIL
|
|
1305
1371
|
{
|
|
1306
1372
|
// Already a contact (idempotent re-introduction): keep my assigned
|
|
1307
1373
|
// name, refresh the stored material, deliver any payload.
|
|
1308
|
-
peer_ads sender_id -> joiner_ad.
|
|
1309
|
-
contact_roots sender_id -> link?.
|
|
1374
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1375
|
+
a2a_messaging::contact_roots sender_id -> link?.
|
|
1310
1376
|
if text != NIL
|
|
1311
1377
|
{
|
|
1312
1378
|
mid = deposit_message sender_id (existing? $name) text? now.
|
|
@@ -1318,9 +1384,9 @@ application actor loads libraries
|
|
|
1318
1384
|
return transaction::success [ _save_state NIL ].
|
|
1319
1385
|
}
|
|
1320
1386
|
|
|
1321
|
-
contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1322
|
-
peer_ads sender_id -> joiner_ad.
|
|
1323
|
-
contact_roots sender_id -> link?.
|
|
1387
|
+
a2a_messaging::contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1388
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1389
|
+
a2a_messaging::contact_roots sender_id -> link?.
|
|
1324
1390
|
if text != NIL
|
|
1325
1391
|
{
|
|
1326
1392
|
mid = deposit_message sender_id joiner_name text? now.
|