@adapt-toolkit/a2adapt 0.11.3 → 0.11.4
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 +11 -11
- package/dist/mufl_code/264E6E0FF6F2C1587AD5725F9B2E788CE792B617B6141D097F38CBB41EF3AB6A.muflo +0 -0
- package/dist/mufl_code/actor.mu +148 -462
- package/package.json +1 -1
- package/dist/mufl_code/7127B9740290EA8B2F30AFAE251198357D780A81DBF5EF30FE7DD9B66A510C21.muflo +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "a2adapt",
|
|
4
4
|
"displayName": "a2adapt",
|
|
5
5
|
"description": "Secure agent-to-agent communication channel over ADAPT: self-sovereign pubkey identity, end-to-end encryption, plan-first execution.",
|
|
6
|
-
"version": "0.11.
|
|
6
|
+
"version": "0.11.4",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Adapt Toolkit"
|
|
9
9
|
},
|
package/dist/index.js
CHANGED
|
@@ -22512,7 +22512,7 @@ function writeIdentityFile(target, opts, overwrite = false) {
|
|
|
22512
22512
|
}
|
|
22513
22513
|
|
|
22514
22514
|
// src/index.ts
|
|
22515
|
-
var VERSION = true ? "0.11.
|
|
22515
|
+
var VERSION = true ? "0.11.4" : "0.0.0-dev";
|
|
22516
22516
|
var CONFIG = loadConfig();
|
|
22517
22517
|
var STATE_DIR = CONFIG.stateDir;
|
|
22518
22518
|
var BROKER_URL = CONFIG.brokerUrl;
|
|
@@ -22962,7 +22962,7 @@ async function provisionIdentity(name, opts = { exposeLocal: true, localAutoAcce
|
|
|
22962
22962
|
const seed = randomBytes(24).toString("hex");
|
|
22963
22963
|
fs2.writeFileSync(seedPath(dir), seed, { mode: 384 });
|
|
22964
22964
|
const id = await createPacket(name, seed, dir);
|
|
22965
|
-
await mutatingTx(id, "::
|
|
22965
|
+
await mutatingTx(id, "::a2a_messaging::set_my_name", { name });
|
|
22966
22966
|
await pinRegistrar(id);
|
|
22967
22967
|
if (!opts.localAutoAccept) {
|
|
22968
22968
|
await mutatingTx(id, "::actor::set_local_policy", { auto_accept: false });
|
|
@@ -23174,7 +23174,7 @@ function createMcpServer(getSessionId) {
|
|
|
23174
23174
|
if (identities.has(name)) return textResult(`create_identity failed: an identity named "${name}" already exists.`, true);
|
|
23175
23175
|
try {
|
|
23176
23176
|
const id = await provisionIdentity(name, { exposeLocal: expose_local, localAutoAccept: local_auto_accept });
|
|
23177
|
-
if (bio) await mutatingTx(id, "::
|
|
23177
|
+
if (bio) await mutatingTx(id, "::a2a_messaging::set_my_bio", { bio });
|
|
23178
23178
|
let hierarchy = "";
|
|
23179
23179
|
const root = rootName ? identities.get(rootName) : void 0;
|
|
23180
23180
|
if (root) {
|
|
@@ -23216,7 +23216,7 @@ Ask the user: do you want to arm a message monitor for "${name}"? If yes, use: M
|
|
|
23216
23216
|
}
|
|
23217
23217
|
try {
|
|
23218
23218
|
const id = await provisionIdentity(name, { exposeLocal: expose_local, localAutoAccept: local_auto_accept });
|
|
23219
|
-
if (bio) await mutatingTx(id, "::
|
|
23219
|
+
if (bio) await mutatingTx(id, "::a2a_messaging::set_my_bio", { bio });
|
|
23220
23220
|
rootName = name;
|
|
23221
23221
|
writeRootMarker(name);
|
|
23222
23222
|
const adopted = [];
|
|
@@ -23444,7 +23444,7 @@ ${inbox.map((m) => fmtMsg(m)).join("\n")}`;
|
|
|
23444
23444
|
try {
|
|
23445
23445
|
const targ = {};
|
|
23446
23446
|
if (name) targ.name = name;
|
|
23447
|
-
const data = await mutatingTx(id, "::
|
|
23447
|
+
const data = await mutatingTx(id, "::a2a_messaging::generate_invite", targ);
|
|
23448
23448
|
const blob = packInvite(Buffer.from(data.Reduce("invite").GetBinary()));
|
|
23449
23449
|
const heading = name ? `Invite for "${name}" created.` : "Invite created \u2014 the contact will be registered under the name the recipient announces when accepting.";
|
|
23450
23450
|
return textResult(
|
|
@@ -23478,7 +23478,7 @@ ${blob}`
|
|
|
23478
23478
|
const blobValue = id.pw.packet.NewBinaryFromBuffer(buf);
|
|
23479
23479
|
const targ = { invite: blobValue };
|
|
23480
23480
|
if (name) targ.name = name;
|
|
23481
|
-
const data = await mutatingTx(id, "::
|
|
23481
|
+
const data = await mutatingTx(id, "::a2a_messaging::add_contact", targ);
|
|
23482
23482
|
const added = data.Reduce("added").Visualize();
|
|
23483
23483
|
const cid = data.Reduce("container_id").Visualize();
|
|
23484
23484
|
const roleId = data.Reduce("role_id").Visualize();
|
|
@@ -23499,9 +23499,9 @@ ${blob}`
|
|
|
23499
23499
|
const { id, err } = boundOr();
|
|
23500
23500
|
if (err) return err;
|
|
23501
23501
|
try {
|
|
23502
|
-
const contacts = renderContacts(readonlyTx(id, "::
|
|
23502
|
+
const contacts = renderContacts(readonlyTx(id, "::a2a_messaging::list_contacts"));
|
|
23503
23503
|
const pending = renderPending(readonlyTx(id, "::actor::list_pending_introductions"));
|
|
23504
|
-
const roots = renderContactRoots(readonlyTx(id, "::
|
|
23504
|
+
const roots = renderContactRoots(readonlyTx(id, "::a2a_messaging::list_contact_roots"));
|
|
23505
23505
|
const lines = [];
|
|
23506
23506
|
lines.push(
|
|
23507
23507
|
contacts.length === 0 ? "No contacts yet." : `Contacts (${contacts.length}):
|
|
@@ -23576,7 +23576,7 @@ ${lines.join("\n")}`);
|
|
|
23576
23576
|
const { id, err } = boundOr();
|
|
23577
23577
|
if (err) return err;
|
|
23578
23578
|
try {
|
|
23579
|
-
await mutatingTx(id, "::
|
|
23579
|
+
await mutatingTx(id, "::a2a_messaging::set_my_bio", { bio });
|
|
23580
23580
|
let refreshed = 0;
|
|
23581
23581
|
if (id.name === rootName) {
|
|
23582
23582
|
for (const other of identities.values()) {
|
|
@@ -23638,7 +23638,7 @@ ${lines.join("\n")}`);
|
|
|
23638
23638
|
const { id, err } = boundOr();
|
|
23639
23639
|
if (err) return err;
|
|
23640
23640
|
try {
|
|
23641
|
-
await mutatingTx(id, "::
|
|
23641
|
+
await mutatingTx(id, "::a2a_messaging::send_message", { contact, text });
|
|
23642
23642
|
return textResult(`Message sent to "${contact}".`);
|
|
23643
23643
|
} catch (e) {
|
|
23644
23644
|
if (!/Unknown contact/.test(String(e))) {
|
|
@@ -23662,7 +23662,7 @@ ${lines.join("\n")}`);
|
|
|
23662
23662
|
const { id, err } = boundOr();
|
|
23663
23663
|
if (err) return err;
|
|
23664
23664
|
try {
|
|
23665
|
-
const data = await mutatingTx(id, "::
|
|
23665
|
+
const data = await mutatingTx(id, "::a2a_messaging::remove_contact", { contact });
|
|
23666
23666
|
const name = data.Reduce("removed").Visualize();
|
|
23667
23667
|
const cid = data.Reduce("container_id").Visualize();
|
|
23668
23668
|
return textResult(`Removed contact "${name}" (${cid}).`);
|
|
Binary file
|
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
|
|
@@ -45,17 +63,10 @@
|
|
|
45
63
|
// list_pending_introductions — (readonly) pending introductions (names + queue sizes)
|
|
46
64
|
//
|
|
47
65
|
// 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
|
|
66
|
+
// accept_contact — inviter learns the joiner's identity + name (core shim)
|
|
67
|
+
// receive_message — store a decrypted inbound message (core shim)
|
|
50
68
|
// local_introduce — same-host peer connects via the local contact book
|
|
51
69
|
// 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
70
|
|
|
60
71
|
application actor loads libraries
|
|
61
72
|
identity_proof_document,
|
|
@@ -70,6 +81,7 @@ application actor loads libraries
|
|
|
70
81
|
encrypted_channel,
|
|
71
82
|
current_transaction_info,
|
|
72
83
|
a2a_protocol,
|
|
84
|
+
a2a_messaging,
|
|
73
85
|
version
|
|
74
86
|
uses transactions
|
|
75
87
|
{
|
|
@@ -107,27 +119,19 @@ application actor loads libraries
|
|
|
107
119
|
encrypted_channel::init ($_read_or_abort -> _read_or_abort).
|
|
108
120
|
|
|
109
121
|
// ---- packet state ---------------------------------------------------
|
|
110
|
-
// The
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
pending_invites is (global_id ->> str) = (,).
|
|
122
|
+
// (The shared contact/profile/hierarchy state — my_name, contacts,
|
|
123
|
+
// pending_invites, peer_ads, my_bio, delegation_cert, root_ad,
|
|
124
|
+
// root_profile, contact_roots — lives in a2a_messaging and is read /
|
|
125
|
+
// assigned as a2a_messaging::<field> below.)
|
|
126
|
+
//
|
|
116
127
|
// Received messages. Each carries its own lifecycle status (see
|
|
117
|
-
// message_t / get_messages /
|
|
128
|
+
// message_t / get_messages / gc / defer_messages), so the
|
|
118
129
|
// packet is the single authority on what has been read or processed —
|
|
119
130
|
// no host-side cursor, safe across concurrent sessions.
|
|
120
131
|
inbox is message_t[] = [].
|
|
121
132
|
// Monotonic source of per-message ids (the stable handle the agent uses
|
|
122
133
|
// to mark a message processed or defer it).
|
|
123
134
|
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
135
|
// The host registrar's address document (pinned once at identity
|
|
132
136
|
// creation / injected on upgrade) — its $identity $key_list is what
|
|
133
137
|
// introduction credentials are verified against. NIL means this identity
|
|
@@ -142,18 +146,6 @@ application actor loads libraries
|
|
|
142
146
|
// Verified-but-unapproved local introductions (when auto-accept is off),
|
|
143
147
|
// each with a bounded queue of messages awaiting approval.
|
|
144
148
|
pending_introductions is (global_id ->> pending_intro_t) = (,).
|
|
145
|
-
// ---- identity hierarchy state ----
|
|
146
|
-
// My profile bio (free-text, self-asserted; carried in role invites).
|
|
147
|
-
my_bio is str = "".
|
|
148
|
-
// My delegation cert. NIL == I am a root or a legacy flat identity.
|
|
149
|
-
delegation_cert is a2a_protocol::delegation_cert_t+ = NIL.
|
|
150
|
-
// My root's address document (set with the cert; its key list is what
|
|
151
|
-
// sibling introductions and my own cert are verified against).
|
|
152
|
-
root_ad is address_document_types::t_address_document+ = NIL.
|
|
153
|
-
// My root's self-signed profile, embedded in the invites I generate.
|
|
154
|
-
root_profile is a2a_protocol::root_profile_t+ = NIL.
|
|
155
|
-
// Verified root linkage per contact, keyed by the contact's container id.
|
|
156
|
-
contact_roots is (global_id ->> a2a_protocol::contact_root_t) = (,).
|
|
157
149
|
|
|
158
150
|
// Signal the host to persist the packet. Only emitted at the end of a
|
|
159
151
|
// complete procedure — intermediate states (e.g. channel handshake) are
|
|
@@ -162,19 +154,6 @@ application actor loads libraries
|
|
|
162
154
|
fn _return_data (payload: any) = (transaction::action::return_data ($kind -> $data, $payload -> payload)).
|
|
163
155
|
fn _notify_agent (payload: any) = (transaction::action::return_data ($kind -> $notify_agent, $payload -> payload)).
|
|
164
156
|
|
|
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
157
|
// Append a message to the inbox under a fresh id; returns the id.
|
|
179
158
|
fn deposit_message (sender_id: global_id, sender_name: str, text: str, msg_date: time) -> int
|
|
180
159
|
{
|
|
@@ -203,229 +182,52 @@ application actor loads libraries
|
|
|
203
182
|
abort "No pending introduction matches: " + ref when found == NIL.
|
|
204
183
|
return found?.
|
|
205
184
|
}
|
|
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
185
|
|
|
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
|
-
$a -> my_ad $authorizations,
|
|
262
|
-
$b -> my_bio,
|
|
263
|
-
$dc -> delegation_cert?,
|
|
264
|
-
$rp -> root_profile?
|
|
265
|
-
).
|
|
266
|
-
return transaction::success [
|
|
267
|
-
_return_data (
|
|
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
|
|
186
|
+
// Wire the agent's storage into the shared messaging core. The receive
|
|
187
|
+
// hook owns everything app-specific about an inbound message: known
|
|
188
|
+
// senders deposit into the inbox; an unknown sender may be a verified-
|
|
189
|
+
// but-unapproved local introduction messaging before approval — queue
|
|
190
|
+
// (bounded) inside its pending entry, approval flushes the queue into
|
|
191
|
+
// the inbox in order; anything else from an unknown sender is rejected.
|
|
192
|
+
// The notification deliberately carries NO message body — only that a
|
|
193
|
+
// message arrived, from whom, and its id. The body stays in the packet
|
|
194
|
+
// and only leaves through get_messages.
|
|
195
|
+
a2a_messaging::init (
|
|
196
|
+
$_read_or_abort -> _read_or_abort,
|
|
197
|
+
$on_message_received -> fn (arg: any) -> transaction::action::type[]
|
|
198
|
+
{
|
|
199
|
+
sender_id = (arg $sender_id) safe global_id.
|
|
200
|
+
text = (arg $text) safe str.
|
|
201
|
+
msg_date = (arg $date) safe time.
|
|
202
|
+
|
|
203
|
+
if (arg $sender_name) == NIL
|
|
204
|
+
{
|
|
205
|
+
p = pending_introductions sender_id.
|
|
206
|
+
abort "Message from an unknown sender was rejected." when p == NIL.
|
|
207
|
+
entry = p?.
|
|
208
|
+
queued = entry $messages.
|
|
209
|
+
abort "Pending-introduction message queue is full; awaiting approval." when (_count queued|) >= pending_queue_cap.
|
|
210
|
+
queued (_count queued|) -> ($text -> text, $date -> msg_date).
|
|
211
|
+
pending_introductions sender_id -> ($name -> entry $name, $ad -> entry $ad, $messages -> queued).
|
|
212
|
+
return [
|
|
213
|
+
_notify_agent ($event -> $pending_message, $sender_name -> entry $name, $queued -> _count queued|),
|
|
214
|
+
_save_state NIL
|
|
215
|
+
].
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
sender_name = (arg $sender_name) safe str.
|
|
219
|
+
mid = deposit_message sender_id sender_name text msg_date.
|
|
220
|
+
return [
|
|
221
|
+
_notify_agent ($event -> $message_received, $sender_name -> sender_name, $msg_id -> mid, $date -> msg_date),
|
|
222
|
+
_save_state NIL
|
|
223
|
+
].
|
|
224
|
+
},
|
|
225
|
+
$on_message_sent -> fn (_: any) -> transaction::action::type[] { return []. },
|
|
226
|
+
$on_contact_removed -> fn (_: any) -> transaction::action::type[] { return []. }
|
|
282
227
|
).
|
|
283
|
-
|
|
284
|
-
return transaction::success [
|
|
285
|
-
_return_data (
|
|
286
|
-
$invite -> (_write invite),
|
|
287
|
-
$invite_id -> invite_id,
|
|
288
|
-
$peer_name -> assigned
|
|
289
|
-
),
|
|
290
|
-
_save_state NIL
|
|
291
|
-
].
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
trn add_contact _:($invite -> invite_blob: bin, $name -> custom_name: str+)
|
|
295
|
-
{
|
|
296
|
-
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
297
|
-
|
|
298
|
-
// Parse field-by-field rather than via one `safe invite_t` cast: the
|
|
299
|
-
// shared fields are identical between invite_t and invite_role_t, so
|
|
300
|
-
// this one path redeems both the legacy shape and the role shape (the
|
|
301
|
-
// hierarchy fields are simply NIL on a legacy invite).
|
|
302
|
-
raw = _read_or_abort invite_blob.
|
|
303
|
-
inviter_id = (raw $c) safe global_id.
|
|
304
|
-
abort "This invite is your own — you cannot add yourself." when inviter_id == _get_container_id().
|
|
305
|
-
invite_id = (raw $d) safe global_id.
|
|
306
|
-
inviter_name = (raw $n) safe str.
|
|
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
228
|
}
|
|
388
229
|
|
|
389
|
-
|
|
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
|
-
].
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
trn readonly list_contacts _
|
|
426
|
-
{
|
|
427
|
-
return contacts.
|
|
428
|
-
}
|
|
230
|
+
// ---- message store --------------------------------------------------------
|
|
429
231
|
|
|
430
232
|
trn readonly list_incoming_messages _
|
|
431
233
|
{
|
|
@@ -668,10 +470,10 @@ application actor loads libraries
|
|
|
668
470
|
entry_sig = (_read_or_abort entry_sig_blob) safe crypto_signature.
|
|
669
471
|
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
472
|
|
|
671
|
-
contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
672
|
-
peer_ads target_id -> target_ad.
|
|
473
|
+
a2a_messaging::contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
474
|
+
a2a_messaging::peer_ads target_id -> target_ad.
|
|
673
475
|
|
|
674
|
-
my_self_name = my_name.
|
|
476
|
+
my_self_name = a2a_messaging::my_name.
|
|
675
477
|
my_ad = address_document::get_my_address_document().
|
|
676
478
|
return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
|
|
677
479
|
return transaction::success [
|
|
@@ -691,8 +493,8 @@ application actor loads libraries
|
|
|
691
493
|
|
|
692
494
|
pid = resolve_pending ref.
|
|
693
495
|
entry = (pending_introductions pid)?.
|
|
694
|
-
contacts pid -> ($name -> entry $name, $container_id -> pid).
|
|
695
|
-
peer_ads pid -> entry $ad.
|
|
496
|
+
a2a_messaging::contacts pid -> ($name -> entry $name, $container_id -> pid).
|
|
497
|
+
a2a_messaging::peer_ads pid -> entry $ad.
|
|
696
498
|
|
|
697
499
|
queued = entry $messages.
|
|
698
500
|
flushed is int = 0.
|
|
@@ -751,7 +553,7 @@ application actor loads libraries
|
|
|
751
553
|
trn sign_delegation _:($role_ad -> role_ad_blob: bin, $role_id -> role_id: str)
|
|
752
554
|
{
|
|
753
555
|
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.
|
|
556
|
+
abort "Only a root identity can sign delegation certificates." when a2a_messaging::delegation_cert != NIL.
|
|
755
557
|
|
|
756
558
|
role_ad = (_read_or_abort role_ad_blob) safe address_document_types::t_address_document.
|
|
757
559
|
role_cid = role_ad $identity $container_id.
|
|
@@ -777,14 +579,14 @@ application actor loads libraries
|
|
|
777
579
|
trn export_root_profile _
|
|
778
580
|
{
|
|
779
581
|
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.
|
|
582
|
+
abort "Only a root identity can export a root profile." when a2a_messaging::delegation_cert != NIL.
|
|
781
583
|
|
|
782
584
|
my_ad = address_document::get_my_address_document().
|
|
783
585
|
core is a2a_protocol::root_profile_core_t = (
|
|
784
586
|
$version -> 1,
|
|
785
587
|
$root_cid -> _get_container_id(),
|
|
786
|
-
$name -> my_name,
|
|
787
|
-
$bio -> my_bio,
|
|
588
|
+
$name -> a2a_messaging::my_name,
|
|
589
|
+
$bio -> a2a_messaging::my_bio,
|
|
788
590
|
$keys -> my_ad $identity $key_list
|
|
789
591
|
).
|
|
790
592
|
profile is a2a_protocol::root_profile_t = ($p -> core, $s -> key_storage::default_sign (_value_id core)).
|
|
@@ -817,9 +619,9 @@ application actor loads libraries
|
|
|
817
619
|
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
620
|
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
621
|
|
|
820
|
-
delegation_cert -> cert.
|
|
821
|
-
root_ad -> new_root_ad.
|
|
822
|
-
root_profile -> rp.
|
|
622
|
+
a2a_messaging::delegation_cert -> cert.
|
|
623
|
+
a2a_messaging::root_ad -> new_root_ad.
|
|
624
|
+
a2a_messaging::root_profile -> rp.
|
|
823
625
|
|
|
824
626
|
return transaction::success [
|
|
825
627
|
_return_data ($delegated -> TRUE, $root_cid -> (_str (cert $c $root_cid)), $role_id -> cert $c $role_id),
|
|
@@ -829,16 +631,16 @@ application actor loads libraries
|
|
|
829
631
|
|
|
830
632
|
trn readonly describe_identity _
|
|
831
633
|
{
|
|
832
|
-
if delegation_cert == NIL
|
|
634
|
+
if a2a_messaging::delegation_cert == NIL
|
|
833
635
|
{
|
|
834
|
-
return ($name -> my_name, $bio -> my_bio, $has_cert -> FALSE, $role_id -> "", $root_cid -> "", $root_name -> "").
|
|
636
|
+
return ($name -> a2a_messaging::my_name, $bio -> a2a_messaging::my_bio, $has_cert -> FALSE, $role_id -> "", $root_cid -> "", $root_name -> "").
|
|
835
637
|
}
|
|
836
|
-
cert = delegation_cert?.
|
|
638
|
+
cert = a2a_messaging::delegation_cert?.
|
|
837
639
|
rname is str = "".
|
|
838
|
-
if root_profile != NIL { rname -> root_profile? $p $name. }
|
|
640
|
+
if a2a_messaging::root_profile != NIL { rname -> a2a_messaging::root_profile? $p $name. }
|
|
839
641
|
return (
|
|
840
|
-
$name -> my_name,
|
|
841
|
-
$bio -> my_bio,
|
|
642
|
+
$name -> a2a_messaging::my_name,
|
|
643
|
+
$bio -> a2a_messaging::my_bio,
|
|
842
644
|
$has_cert -> TRUE,
|
|
843
645
|
$role_id -> cert $c $role_id,
|
|
844
646
|
$root_cid -> (_str (cert $c $root_cid)),
|
|
@@ -846,17 +648,6 @@ application actor loads libraries
|
|
|
846
648
|
).
|
|
847
649
|
}
|
|
848
650
|
|
|
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
651
|
// Connect to an intra-root sibling (Ring 1): register it as a contact and
|
|
861
652
|
// introduce myself over the encrypted channel, presenting my delegation
|
|
862
653
|
// cert (NIL when I am the root itself — the channel proves I control the
|
|
@@ -872,22 +663,22 @@ application actor loads libraries
|
|
|
872
663
|
abort "This sibling is your own identity." when target_id == _get_container_id().
|
|
873
664
|
|
|
874
665
|
cert_blob is bin+ = NIL.
|
|
875
|
-
if delegation_cert != NIL { cert_blob -> (_write delegation_cert?). }
|
|
666
|
+
if a2a_messaging::delegation_cert != NIL { cert_blob -> (_write a2a_messaging::delegation_cert?). }
|
|
876
667
|
|
|
877
|
-
contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
878
|
-
peer_ads target_id -> target_ad.
|
|
668
|
+
a2a_messaging::contacts target_id -> ($name -> name, $container_id -> target_id).
|
|
669
|
+
a2a_messaging::peer_ads target_id -> target_ad.
|
|
879
670
|
// Record the target's root linkage: a sibling shares my root by
|
|
880
671
|
// definition (the receiving side verifies the converse independently).
|
|
881
|
-
if delegation_cert != NIL && root_profile != NIL
|
|
672
|
+
if a2a_messaging::delegation_cert != NIL && a2a_messaging::root_profile != NIL
|
|
882
673
|
{
|
|
883
|
-
contact_roots target_id -> ($root_cid -> delegation_cert? $c $root_cid, $root_name -> root_profile? $p $name, $role_id -> name).
|
|
674
|
+
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
675
|
}
|
|
885
676
|
else
|
|
886
677
|
{
|
|
887
|
-
contact_roots target_id -> ($root_cid -> _get_container_id(), $root_name -> my_name, $role_id -> name).
|
|
678
|
+
a2a_messaging::contact_roots target_id -> ($root_cid -> _get_container_id(), $root_name -> a2a_messaging::my_name, $role_id -> name).
|
|
888
679
|
}
|
|
889
680
|
|
|
890
|
-
my_self_name = my_name.
|
|
681
|
+
my_self_name = a2a_messaging::my_name.
|
|
891
682
|
my_ad = address_document::get_my_address_document().
|
|
892
683
|
return encrypted_channel::execute_transaction target_id (fn (_) -> transaction::results::type {
|
|
893
684
|
return transaction::success [
|
|
@@ -909,27 +700,32 @@ application actor loads libraries
|
|
|
909
700
|
//
|
|
910
701
|
// The packet-level snapshot is NOT used for upgrades: it is bound to the unit
|
|
911
702
|
// code hash, so a new .muflo cannot load an old snapshot. This data blob is.
|
|
703
|
+
//
|
|
704
|
+
// The blob stays FLAT with the historical field names — the core fields come
|
|
705
|
+
// from a2a_messaging::export_core_state / import_core_state, the app fields
|
|
706
|
+
// are composed in here — so a PRE-migration export imports unchanged.
|
|
912
707
|
|
|
913
708
|
trn readonly export_state _
|
|
914
709
|
{
|
|
710
|
+
core_state = a2a_messaging::export_core_state NIL.
|
|
915
711
|
return (
|
|
916
|
-
$my_name -> my_name,
|
|
917
|
-
$contacts -> contacts,
|
|
918
|
-
$pending_invites -> pending_invites,
|
|
712
|
+
$my_name -> core_state $my_name,
|
|
713
|
+
$contacts -> core_state $contacts,
|
|
714
|
+
$pending_invites -> core_state $pending_invites,
|
|
919
715
|
$inbox -> inbox,
|
|
920
716
|
$next_msg_seq -> next_msg_seq,
|
|
921
|
-
$peer_ads -> peer_ads,
|
|
717
|
+
$peer_ads -> core_state $peer_ads,
|
|
922
718
|
$registrar_ad -> registrar_ad,
|
|
923
719
|
$local_auto_accept -> local_auto_accept,
|
|
924
720
|
// Nonces are exported so a restart does not reopen the replay window
|
|
925
721
|
// for still-fresh credentials; stale ones are purged lazily anyway.
|
|
926
722
|
$seen_nonces -> seen_nonces,
|
|
927
723
|
$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
|
|
724
|
+
$my_bio -> core_state $my_bio,
|
|
725
|
+
$delegation_cert -> core_state $delegation_cert,
|
|
726
|
+
$root_ad -> core_state $root_ad,
|
|
727
|
+
$root_profile -> core_state $root_profile,
|
|
728
|
+
$contact_roots -> core_state $contact_roots
|
|
933
729
|
).
|
|
934
730
|
}
|
|
935
731
|
|
|
@@ -937,12 +733,11 @@ application actor loads libraries
|
|
|
937
733
|
{
|
|
938
734
|
current_transaction_info::validate_origin_or_abort (transaction::envelope::origin::user,).
|
|
939
735
|
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
peer_ads -> (data $peer_ads) safe (global_id ->> address_document_types::t_address_document).
|
|
736
|
+
// Core fields (contacts/profile/hierarchy) — validated and restored by
|
|
737
|
+
// the shared library, which also replays every peer's address document
|
|
738
|
+
// through process_address_document so encrypted channels keep working
|
|
739
|
+
// after the upgrade with no re-handshake.
|
|
740
|
+
a2a_messaging::import_core_state data.
|
|
946
741
|
|
|
947
742
|
// The inbox + next_msg_seq are the only parts the message-lifecycle changes
|
|
948
743
|
// touched. A pre-lifecycle blob has no $next_msg_seq and inbox entries with
|
|
@@ -1013,36 +808,6 @@ application actor loads libraries
|
|
|
1013
808
|
pending_introductions -> (data $pending_introductions) safe (global_id ->> pending_intro_t).
|
|
1014
809
|
}
|
|
1015
810
|
|
|
1016
|
-
// Identity-hierarchy state arrived after the local-contact-book schema —
|
|
1017
|
-
// same pattern: every field is optional, defaults stay when absent.
|
|
1018
|
-
if (data $my_bio) != NIL
|
|
1019
|
-
{
|
|
1020
|
-
my_bio -> (data $my_bio) safe str.
|
|
1021
|
-
}
|
|
1022
|
-
if (data $delegation_cert) != NIL
|
|
1023
|
-
{
|
|
1024
|
-
delegation_cert -> (data $delegation_cert) safe a2a_protocol::delegation_cert_t.
|
|
1025
|
-
}
|
|
1026
|
-
if (data $root_ad) != NIL
|
|
1027
|
-
{
|
|
1028
|
-
root_ad -> (data $root_ad) safe address_document_types::t_address_document.
|
|
1029
|
-
}
|
|
1030
|
-
if (data $root_profile) != NIL
|
|
1031
|
-
{
|
|
1032
|
-
root_profile -> (data $root_profile) safe a2a_protocol::root_profile_t.
|
|
1033
|
-
}
|
|
1034
|
-
if (data $contact_roots) != NIL
|
|
1035
|
-
{
|
|
1036
|
-
contact_roots -> (data $contact_roots) safe (global_id ->> a2a_protocol::contact_root_t).
|
|
1037
|
-
}
|
|
1038
|
-
|
|
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
811
|
// Pending introducers' keys too: their channel to me predates approval,
|
|
1047
812
|
// so it must survive an upgrade exactly like an approved contact's.
|
|
1048
813
|
sc pending_introductions -- ( -> p)
|
|
@@ -1051,103 +816,24 @@ application actor loads libraries
|
|
|
1051
816
|
}
|
|
1052
817
|
|
|
1053
818
|
return transaction::success [
|
|
1054
|
-
_return_data ($imported -> TRUE, $contacts -> _count contacts|, $peers -> _count peer_ads|),
|
|
819
|
+
_return_data ($imported -> TRUE, $contacts -> _count a2a_messaging::contacts|, $peers -> _count a2a_messaging::peer_ads|),
|
|
1055
820
|
_save_state NIL
|
|
1056
821
|
].
|
|
1057
822
|
}
|
|
1058
823
|
|
|
1059
824
|
// ---- external (inbound) transactions ------------------------------------
|
|
1060
825
|
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
826
|
+
// Compat shims (Option A): pre-migration peers address these as ::actor::*,
|
|
827
|
+
// and the core keeps SENDING to the ::actor:: names too. Each delegates to
|
|
828
|
+
// the shared handler — remove only when no old clients remain.
|
|
1063
829
|
trn accept_contact args: any
|
|
1064
830
|
{
|
|
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
|
-
].
|
|
831
|
+
return a2a_messaging::handle_accept_contact args.
|
|
1111
832
|
}
|
|
1112
833
|
|
|
1113
834
|
trn receive_message _:($text -> text: str)
|
|
1114
835
|
{
|
|
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
|
-
].
|
|
836
|
+
return a2a_messaging::handle_receive_message text.
|
|
1151
837
|
}
|
|
1152
838
|
|
|
1153
839
|
// A same-host peer connects via the local contact book. The credential must
|
|
@@ -1196,12 +882,12 @@ application actor loads libraries
|
|
|
1196
882
|
abort "Too many concurrent introductions; try again shortly." when (_count seen_nonces|) >= seen_nonce_cap.
|
|
1197
883
|
seen_nonces (intro $nonce) -> now.
|
|
1198
884
|
|
|
1199
|
-
existing = contacts sender_id.
|
|
885
|
+
existing = a2a_messaging::contacts sender_id.
|
|
1200
886
|
if existing != NIL
|
|
1201
887
|
{
|
|
1202
888
|
// Already a contact (idempotent re-introduction): keep my assigned
|
|
1203
889
|
// name, refresh the stored address document, deliver any payload.
|
|
1204
|
-
peer_ads sender_id -> joiner_ad.
|
|
890
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1205
891
|
if text != NIL
|
|
1206
892
|
{
|
|
1207
893
|
mid = deposit_message sender_id (existing? $name) text? now.
|
|
@@ -1215,8 +901,8 @@ application actor loads libraries
|
|
|
1215
901
|
|
|
1216
902
|
if local_auto_accept
|
|
1217
903
|
{
|
|
1218
|
-
contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1219
|
-
peer_ads sender_id -> joiner_ad.
|
|
904
|
+
a2a_messaging::contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
905
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1220
906
|
if text != NIL
|
|
1221
907
|
{
|
|
1222
908
|
mid = deposit_message sender_id joiner_name text? now.
|
|
@@ -1267,8 +953,8 @@ application actor loads libraries
|
|
|
1267
953
|
if cert_blob == NIL
|
|
1268
954
|
{
|
|
1269
955
|
// 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).
|
|
956
|
+
abort "A certificate-less sibling introduction is only accepted from my root." when a2a_messaging::delegation_cert == NIL.
|
|
957
|
+
abort "Sender is not my root." when sender_id != (a2a_messaging::delegation_cert? $c $root_cid).
|
|
1272
958
|
link -> ($root_cid -> sender_id, $root_name -> joiner_name, $role_id -> "").
|
|
1273
959
|
}
|
|
1274
960
|
else
|
|
@@ -1279,14 +965,14 @@ application actor loads libraries
|
|
|
1279
965
|
abort "Sibling certificate does not match the sender's address document." when (cert $c $role_ad_hash) != (_value_id joiner_ad).
|
|
1280
966
|
|
|
1281
967
|
root_name_known is str = "".
|
|
1282
|
-
if delegation_cert != NIL
|
|
968
|
+
if a2a_messaging::delegation_cert != NIL
|
|
1283
969
|
{
|
|
1284
970
|
// I am a role: the cert must name MY root and verify against the
|
|
1285
971
|
// 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. }
|
|
972
|
+
abort "My root material is missing — cannot verify sibling certificates." when a2a_messaging::root_ad == NIL.
|
|
973
|
+
abort "Sibling certificate names a different root." when (cert $c $root_cid) != (a2a_messaging::delegation_cert? $c $root_cid).
|
|
974
|
+
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.
|
|
975
|
+
if a2a_messaging::root_profile != NIL { root_name_known -> a2a_messaging::root_profile? $p $name. }
|
|
1290
976
|
}
|
|
1291
977
|
else
|
|
1292
978
|
{
|
|
@@ -1295,18 +981,18 @@ application actor loads libraries
|
|
|
1295
981
|
abort "Sibling certificate names a different root." when (cert $c $root_cid) != _get_container_id().
|
|
1296
982
|
my_ad = address_document::get_my_address_document().
|
|
1297
983
|
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.
|
|
984
|
+
root_name_known -> a2a_messaging::my_name.
|
|
1299
985
|
}
|
|
1300
986
|
link -> ($root_cid -> cert $c $root_cid, $root_name -> root_name_known, $role_id -> cert $c $role_id).
|
|
1301
987
|
}
|
|
1302
988
|
|
|
1303
|
-
existing = contacts sender_id.
|
|
989
|
+
existing = a2a_messaging::contacts sender_id.
|
|
1304
990
|
if existing != NIL
|
|
1305
991
|
{
|
|
1306
992
|
// Already a contact (idempotent re-introduction): keep my assigned
|
|
1307
993
|
// name, refresh the stored material, deliver any payload.
|
|
1308
|
-
peer_ads sender_id -> joiner_ad.
|
|
1309
|
-
contact_roots sender_id -> link?.
|
|
994
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
995
|
+
a2a_messaging::contact_roots sender_id -> link?.
|
|
1310
996
|
if text != NIL
|
|
1311
997
|
{
|
|
1312
998
|
mid = deposit_message sender_id (existing? $name) text? now.
|
|
@@ -1318,9 +1004,9 @@ application actor loads libraries
|
|
|
1318
1004
|
return transaction::success [ _save_state NIL ].
|
|
1319
1005
|
}
|
|
1320
1006
|
|
|
1321
|
-
contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1322
|
-
peer_ads sender_id -> joiner_ad.
|
|
1323
|
-
contact_roots sender_id -> link?.
|
|
1007
|
+
a2a_messaging::contacts sender_id -> ($name -> joiner_name, $container_id -> sender_id).
|
|
1008
|
+
a2a_messaging::peer_ads sender_id -> joiner_ad.
|
|
1009
|
+
a2a_messaging::contact_roots sender_id -> link?.
|
|
1324
1010
|
if text != NIL
|
|
1325
1011
|
{
|
|
1326
1012
|
mid = deposit_message sender_id joiner_name text? now.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adapt-toolkit/a2adapt",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
4
4
|
"description": "MCP server daemon for a2adapt — one native ADAPT wrapper hosting N self-sovereign identities, exposing secure agent-to-agent messaging tools over HTTP (Streamable HTTP). Run `a2adapt-mcp start`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|