OverviewFilesCommitsIssuesDocsReleasesPeers

atomspace-gpu commits / 0f32ca3

M4: two Autobase daemons converge to one log (multi-agent, device=agent)
Turns the single event-sourced agent into a swarm. Two separate Bare daemons, each its own Autobase writer (keet pattern: optimistic + ackInterval:1000 + ackThreshold:4), replicate cross-process over a UDS pipe and converge to ONE linearized log -> ONE AtomSpace. [m4] phase: agent A stores {cat, animal, Inheritance}, agent B stores {whale, water, lives-in, List, Eval}; both project to the identical full 8-atom AtomSpace (A==src, B==src, A==B). Reliable across repeated runs. - daemon-ab.js: Autobase-backed daemon; plain-core view; Pattern-B addwriter (joiner announces base.local.key, creator addWriters it); cross-process peer replication (--peer-listen/--peer-connect, base .replicate(true/false) piped to a UDS socket). Kept SEPARATE from daemon.js so M0-M3 stay on the simpler Hypercore path (no regression). - abctl.js: one-shot client for the writer-add ceremony + writable poll. - m0_roundtrip.cc: `m4` mode — two channels, convergence poll on count, gold-check both agents reached the same full graph. + count()/writable(). - run.sh: Phase 5 orchestrates two daemons + ceremony. M0-M4 ALL PASS. Production form runs this over Hyperswarm (join base.discoveryKey), already proven in pear-git-v3/test/keet-pattern-e2e.js; the UDS pipe here is for a deterministic gold check. Grounded in the autobase skill + hyper-rooms room.js Pattern-B apply/addWriter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0f32ca3659dc265b74d5612f11ad2f8aee02300c
by plankatron <fleeky@gmail.com> · 2026-07-05T15:01:23.000Z

Changed files (7) +504 -18

experiments/m0-uds-bridge/README.md +28 -12
……@@ -1,4 +1,4 @@
1# hyper-atomspace bridge — M0 · M1 · M3
1# hyper-atomspace bridge — M0 · M1 · M3 · M4
22
33Milestones of the HyperStorageNode (spec: `../../spec/hyperstoragenode.md`;
44`atomspace` org issue #4). A Bare daemon holds the op-log; a C++ client speaks the
……@@ -39,13 +39,24 @@ All three ActiveGraph properties (arXiv:2605.21997) now hold over the log:
3939 and the causally-unrelated `cat@0 animal@1 Inheritance@2` correctly **excluded**.
4040 `[lineage]` phase → `GOLD #3`. The daemon answers `seqof` (log position); the C++
4141 side composes the causal walk — structure is the projection's job, not the log's.
42- **M4 — multi-agent convergence.** TWO daemons (agents), each its own **Autobase**
43 writer, replicate cross-process and converge to ONE linearized log. Agent A stores
44 {cat, animal, Inheritance}, agent B stores {whale, water, lives-in, List, Eval};
45 both project to the **identical full 8-atom AtomSpace** (A==src, B==src, A==B).
46 `[m4]` phase → `M4 MULTI-AGENT PASS`. The device-as-agent step: the log is the
47 agent's memory, the projection its world model, replication its communication.
48 (Production form runs this over Hyperswarm — already proven in
49 `pear-git-v3/test/keet-pattern-e2e.js`; here the peer transport is a direct UDS
50 pipe for a deterministic gold check.)
4251
4352## Files
4453| File | Role |
4554|---|---|
46| `daemon.js` | Bare daemon. Op-log = a **Hypercore** (`append` on store, `get(0..len)` on load, `get(0..K)` on `loadat`, `truncate(0)` on clear). Ordered promise-chain so load/barrier observe all prior stores. `allowHalfOpen:false` + destroy-on-`end` (hyper-svc leak fix). `usage: bare daemon.js <sock> <storage-dir>`. |
47| `m0_roundtrip.cc` | C++ harness. `HyperChannel` = reusable `AF_UNIX` transport (from `CogSimpleStorage` do_send/do_recv) that lifts into `HyperStorageNode : public StorageNode`. Modes `full` / `verify` / `fork`. |
48| `run.sh` | build + 3-phase drive (full → restart-verify → fork) + cleanup. |
55| `daemon.js` | Bare daemon (M0-M3). Op-log = a **Hypercore** (`append`/`get`/`truncate`). Ordered promise-chain so load/barrier observe all prior stores. `allowHalfOpen:false` + destroy-on-`end` (hyper-svc leak fix). `usage: bare daemon.js <sock> <storage-dir>`. |
56| `daemon-ab.js` | Bare daemon (M4). Op-log = an **Autobase** (keet pattern: `optimistic + ackInterval:1000 + ackThreshold:4`); plain-core view; Pattern-B `addwriter`; cross-process peer replication over a UDS pipe (`--peer-listen`/`--peer-connect`). Kept separate so M0-M3 stay on the simpler Hypercore path. |
57| `m0_roundtrip.cc` | C++ harness. `HyperChannel` = reusable `AF_UNIX` transport (from `CogSimpleStorage` do_send/do_recv) that lifts into `HyperStorageNode : public StorageNode`. Modes `full` / `verify` / `fork` / `lineage` / `m4`. |
58| `abctl.js` | One-shot Bare client for the M4 writer-add ceremony + polling (`addwriter`, `writable`). |
59| `run.sh` | build + 5-phase drive (full → verify → fork → lineage → m4) + cleanup. |
4960
5061## Run
5162```bash
……@@ -54,12 +65,14 @@ All three ActiveGraph properties (arXiv:2605.21997) now hold over the log:
5465# [verify] durable replay src=8 dst=8 GOLD #1 PASS
5566# [fork] full=8 checkout@3=3 subset=1 strict=1 GOLD #2 PASS
5667# [lineage] X=(EvaluationLink …) @seq 7 … found=1 deps<X=1 excl-unrelated=1 chain=1 GOLD #3 PASS
57# M0-M3 ALL PASS
68# [m4] converge A=8 B=8 | A==src=1 B==src=1 A==B=1 M4 MULTI-AGENT PASS
69# M0-M4 ALL PASS
5870```
5971Requires: `bare` (v1.26+), `g++` (**C++20** — opencog headers use `std::atomic::wait`),
6072opencog under `/usr/local` (`libatomspace`/`libsexpr`/`libatombase`/`libatom_types`).
61`npm install` pulls `bare-net`/`bare-fs`/`bare-process`/`hypercore` into a local
62`node_modules` (gitignored).
73`npm install` pulls `bare-net`/`bare-fs`/`bare-process`/`hypercore` (M0-M3) and
74`autobase`/`corestore`/`b4a`/`autobase-test-helpers` (M4) into a local `node_modules`
75(gitignored).
6376
6477## Wire protocol (newline-framed)
6578| Request | Reply | Meaning |
……@@ -74,12 +87,15 @@ opencog under `/usr/local` (`libatomspace`/`libsexpr`/`libatombase`/`libatom_typ
7487
7588## Deferred (next)
7689- **`HyperStorageNode : public StorageNode` + guile module** — packaging, not new
77 capability: all three properties are proven and `HyperChannel` is the transplant
90 capability: all four milestones are proven and `HyperChannel` is the transplant
7891 unit. Needs atom_types codegen + PersistSCM + CMake (a loadable `.so`) so opencog
7992 can `(cog-open (HyperStorageNode "hyper://…"))` directly.
8093- **Values** — structure only; per *event-source the graph, checkpoint the Values*
8194 (`../../spec/activegraph-synthesis.md`). The GPU slice-table (Linas Step 4).
82- **Autobase multi-writer** (M4) — two daemons, one linearized log = two agents;
83 the device-as-agent step. `seqof`'s linear scan also wants a real seq index here.
84- **Branch-fork with divergent writes** — `loadat` is a read-only checkout; true
85 forking-with-writes rides Autobase.
95- **Hyperswarm peer transport** — M4 replicates over a direct UDS pipe for a
96 deterministic gold check; the real device-as-agent transport is Hyperswarm (join
97 `base.discoveryKey`), already proven in `pear-git-v3`.
98- **Seq index** — `seqof`/convergence use linear scans; fine at this scale, an index
99 is the scale upgrade.
100- **Branch-fork with divergent writes** — `loadat` is a read-only checkout; genuine
101 divergent-history forking now has its substrate (Autobase) from M4.
experiments/m0-uds-bridge/abctl.js +23 -0
……@@ -0,0 +1,23 @@
1// abctl.js — one-shot client for the M4 autobase daemon (ceremony + polling).
2// usage: bare abctl.js <client-sock> "<protocol line>"
3// Prints the daemon's single reply line (nothing for fire-and-forget `store`).
4import net from 'bare-net'
5import process from 'bare-process'
6
7const sock = process.argv[2]
8const line = process.argv[3]
9if (!sock || line === undefined) { console.error('usage: abctl.js <sock> "<line>"'); process.exit(1) }
10
11const noReply = line.startsWith('store ')
12const c = net.createConnection(sock)
13let buf = ''
14c.on('connect', () => {
15 c.write(line + '\n')
16 if (noReply) setTimeout(() => { c.destroy(); process.exit(0) }, 60)
17})
18c.on('data', (d) => {
19 buf += d.toString()
20 const nl = buf.indexOf('\n')
21 if (nl !== -1) { console.log(buf.slice(0, nl)); c.destroy(); process.exit(0) }
22})
23c.on('error', (e) => { console.error('ERR ' + e.message); process.exit(2) })
experiments/m0-uds-bridge/daemon-ab.js +174 -0
……@@ -0,0 +1,174 @@
1// hyper-atomspace daemon (Bare) — M4: Autobase multi-writer op-log.
2//
3// Two agents (daemons), each its own Autobase writer, replicate over a peer UDS
4// and converge to ONE linearized log -> ONE AtomSpace projection. This is the
5// device-as-agent step: the production form runs this over Hyperswarm (already
6// proven in pear-git-v3/test/keet-pattern-e2e.js); here the peer transport is a
7// direct UDS pipe for a deterministic gold check.
8//
9// usage:
10// bare daemon-ab.js <client-sock> <storage-dir> [--bootstrap <hex>]
11// [--peer-listen <path> | --peer-connect <path>]
12//
13// Startup prints (for orchestration):
14// AB key=<autobase-key> local=<local-writer-key> writable=<bool>
15//
16// Client protocol — newline-framed over the client UDS (superset of M0-M3, minus
17// clear which an append-only multi-writer log does not support):
18// store <sexpr>\n append an atom op to THIS writer. no reply (fire-and-forget).
19// load\n reply: (<sexpr>…)\n — the linearized view.
20// loadat <K>\n reply: (<sexpr0>…<sexpr_{K-1}>)\n
21// seqof <sexpr>\n reply: <seq>\n
22// count\n reply: <view.length>\n
23// barrier\n await base.update(); reply: ok\n
24// addwriter <hex>\n append an add-writer op for that local key; reply: ok\n
25// writable\n reply: true|false\n
26
27import net from 'bare-net'
28import fs from 'bare-fs'
29import process from 'bare-process'
30import Corestore from 'corestore'
31import Autobase from 'autobase'
32import b4a from 'b4a'
33
34const argv = process.argv.slice(2)
35const clientSock = argv[0]
36const storageDir = argv[1]
37function flag (name) { const i = argv.indexOf(name); return i === -1 ? null : argv[i + 1] }
38const bootstrapHex = flag('--bootstrap')
39const peerListen = flag('--peer-listen')
40const peerConnect = flag('--peer-connect')
41
42if (!clientSock || !storageDir) {
43 console.error('usage: bare daemon-ab.js <client-sock> <storage-dir> [--bootstrap hex] [--peer-listen p | --peer-connect p]')
44 process.exit(1)
45}
46try { fs.unlinkSync(clientSock) } catch (_) {}
47
48const store = new Corestore(storageDir)
49const base = new Autobase(store, bootstrapHex ? b4a.from(bootstrapHex, 'hex') : null, {
50 open: (s) => s.get('atomlog', { valueEncoding: 'utf-8' }),
51 apply: async (nodes, view, host) => {
52 for (const node of nodes) {
53 const op = node.value
54 if (op.t === 'add-writer') {
55 await host.addWriter(b4a.from(op.key, 'hex'), { indexer: !!op.indexer })
56 continue
57 }
58 await view.append(op.s)
59 }
60 },
61 valueEncoding: 'json',
62 optimistic: true,
63 ackInterval: 1000,
64 ackThreshold: 4
65})
66await base.ready()
67
68// ── peer replication (cross-process, over a UDS pipe) ─────────────────────────
69function wireReplication (sock, initiator) {
70 const s = base.replicate(initiator)
71 s.on('error', () => {})
72 sock.on('error', () => {})
73 s.pipe(sock).pipe(s)
74}
75if (peerListen) {
76 try { fs.unlinkSync(peerListen) } catch (_) {}
77 const rserver = net.createServer({ allowHalfOpen: false }, (sock) => wireReplication(sock, false))
78 rserver.listen(peerListen)
79}
80if (peerConnect) {
81 const connect = () => {
82 const sock = net.createConnection(peerConnect)
83 sock.on('error', () => setTimeout(connect, 100)) // peer may not be listening yet
84 sock.on('connect', () => wireReplication(sock, true))
85 }
86 connect()
87}
88
89// ── replay helper ─────────────────────────────────────────────────────────────
90async function replay (upto) {
91 await base.update()
92 const v = base.view
93 const n = Math.max(0, Math.min(upto, v.length))
94 let body = ''
95 for (let i = 0; i < n; i++) body += await v.get(i)
96 return '(' + body + ')\n'
97}
98
99async function handleLine (line, sock) {
100 if (line.length === 0) return
101 const sp = line.indexOf(' ')
102 const verb = sp === -1 ? line : line.slice(0, sp)
103 const rest = sp === -1 ? '' : line.slice(sp + 1)
104
105 switch (verb) {
106 case 'store':
107 if (!base.writable) return // silently drop; harness checks `writable` first
108 await base.append({ t: 'atom', s: rest })
109 return
110 case 'load':
111 sock.write(await replay(Infinity))
112 return
113 case 'loadat':
114 sock.write(await replay(parseInt(rest, 10) || 0))
115 return
116 case 'seqof': {
117 await base.update()
118 const v = base.view
119 let found = -1
120 for (let i = 0; i < v.length; i++) { if (await v.get(i) === rest) { found = i; break } }
121 sock.write(String(found) + '\n')
122 return
123 }
124 case 'count':
125 await base.update()
126 sock.write(String(base.view.length) + '\n')
127 return
128 case 'barrier':
129 await base.update()
130 sock.write('ok\n')
131 return
132 case 'addwriter':
133 await base.append({ t: 'add-writer', key: rest, indexer: false })
134 sock.write('ok\n')
135 return
136 case 'writable':
137 sock.write((base.writable ? 'true' : 'false') + '\n')
138 return
139 default:
140 sock.write('err unknown-verb ' + verb + '\n')
141 }
142}
143
144const server = net.createServer({ allowHalfOpen: false }, (sock) => {
145 let buf = ''
146 let tail = Promise.resolve()
147 sock.on('data', (chunk) => {
148 buf += chunk.toString()
149 let nl
150 while ((nl = buf.indexOf('\n')) !== -1) {
151 const line = buf.slice(0, nl)
152 buf = buf.slice(nl + 1)
153 tail = tail.then(() => handleLine(line, sock)).catch((e) => console.error(e))
154 }
155 })
156 sock.on('end', () => sock.destroy())
157 sock.on('error', () => sock.destroy())
158})
159
160server.listen(clientSock, () => {
161 console.log('AB key=' + b4a.toString(base.key, 'hex') +
162 ' local=' + b4a.toString(base.local.key, 'hex') +
163 ' writable=' + base.writable + ' sock=' + clientSock)
164})
165
166async function shutdown () {
167 try { server.close() } catch (_) {}
168 try { await base.close() } catch (_) {}
169 try { fs.unlinkSync(clientSock) } catch (_) {}
170 try { if (peerListen) fs.unlinkSync(peerListen) } catch (_) {}
171 process.exit(0)
172}
173process.on('SIGINT', shutdown)
174process.on('SIGTERM', shutdown)
experiments/m0-uds-bridge/m0_roundtrip.cc +55 -0
……@@ -19,6 +19,9 @@
1919 * causal history through the log via seqof and check it is the
2020 * exact set of ops that produced X, ordered, excluding
2121 * causally-unrelated atoms (complete lineage, M3 #3)
22 * m4 <sockB> TWO daemons (agents) on one Autobase log: agent A stores
23 * half the graph, agent B (arg 3) the other half; both must
24 * converge to the identical full AtomSpace (multi-agent, M4)
2225 *
2326 * SPDX-License-Identifier: AGPL-3.0-or-later
2427 */
……@@ -114,6 +117,8 @@ public:
114117 send_line("seqof " + sexpr + "\n");
115118 return std::stoi(recv_line());
116119 }
120 int count() { send_line("count\n"); return std::stoi(recv_line()); }
121 bool writable(){ send_line("writable\n"); return recv_line() == "true"; }
117122};
118123
119124// ---------------------------------------------------------------------------
……@@ -280,6 +285,56 @@ int main(int argc, char** argv)
280285 return ok ? 0 : 1;
281286 }
282287
288 if (mode == "m4")
289 {
290 if (argc < 4) { std::cerr << "m4 needs <sockB>\n"; return 2; }
291 HyperChannel chB;
292 chB.open(argv[3]);
293
294 // Ceremony (addWriter) is done by run.sh before we run; both must be
295 // writable now. A = this channel (ch), B = chB.
296 if (!ch.writable() || !chB.writable())
297 {
298 std::cout << "[m4] A.writable=" << ch.writable()
299 << " B.writable=" << chB.writable()
300 << " FAIL (ceremony not complete)\n";
301 return 1;
302 }
303
304 // Each agent stores a self-contained half of the graph (no cross-agent
305 // dependency): A = {cat, animal, Inheritance}; B = {whale, water,
306 // lives-in, List, Eval}.
307 for (int i = 0; i < 3; i++) ch.store(Sexpr::encode_atom(order[i]));
308 for (int i = 3; i < 8; i++) chB.store(Sexpr::encode_atom(order[i]));
309 ch.barrier();
310 chB.barrier();
311
312 // Wait for the two writers' streams to linearize + replicate to 8 ops.
313 int ca = 0, cb = 0;
314 for (int t = 0; t < 100; t++) // ~10s budget
315 {
316 ca = ch.count();
317 cb = chB.count();
318 if (ca == 8 && cb == 8) break;
319 usleep(100000);
320 }
321
322 AtomSpacePtr dstA = decode_reply(ch.load());
323 AtomSpacePtr dstB = decode_reply(chB.load());
324 std::vector<std::string> ea = encoded_sorted(dstA);
325 std::vector<std::string> eb = encoded_sorted(dstB);
326
327 bool a_ok = (ea == es); // agent A converged to the full graph
328 bool b_ok = (eb == es); // agent B converged to the full graph
329 bool agree = (ea == eb); // and to the SAME graph
330 bool ok = a_ok && b_ok && agree && dstA->get_size() == 8;
331 std::cout << "[m4] converge A=" << ca << " B=" << cb
332 << " | A==src=" << a_ok << " B==src=" << b_ok
333 << " A==B=" << agree
334 << (ok ? " M4 MULTI-AGENT PASS\n" : " M4 MULTI-AGENT FAIL\n");
335 return ok ? 0 : 1;
336 }
337
283338 // mode == full : live append + replay round-trip.
284339 ch.clear();
285340 for (const Handle& h : order) ch.store(Sexpr::encode_atom(h));
experiments/m0-uds-bridge/package-lock.json +186 -0
……@@ -8,9 +8,13 @@
88 "name": "hyper-atomspace-m0",
99 "version": "0.0.0",
1010 "dependencies": {
11 "autobase": "^7.28.1",
12 "autobase-test-helpers": "^3.1.0",
13 "b4a": "^1.8.1",
1114 "bare-fs": "^4.7.3",
1215 "bare-net": "^2.3.2",
1316 "bare-process": "^4.5.0",
17 "corestore": "^7.11.0",
1418 "hypercore": "^11.33.5"
1519 }
1620 },
……@@ -31,6 +35,45 @@
3135 "unslab": "^1.3.0"
3236 }
3337 },
38 "node_modules/autobase": {
39 "version": "7.28.1",
40 "resolved": "https://registry.npmjs.org/autobase/-/autobase-7.28.1.tgz",
41 "integrity": "sha512-I8SHcJE/ru5Nun6NpqwRjenB8aK01b9z1qNeyx4XuG+BxeFUpXk0ZT2u3/nG+GZnObUTFMCKOo2fbAv7ipcaLw==",
42 "license": "Apache-2.0",
43 "dependencies": {
44 "b4a": "^1.6.1",
45 "bare-events": "^2.2.0",
46 "compact-encoding": "^3.0.0",
47 "core-coupler": "^2.0.0",
48 "debounceify": "^1.0.0",
49 "encryption-encoding": "^1.0.3",
50 "hyperbee": "^2.22.0",
51 "hypercore": "^11.27.12",
52 "hypercore-crypto": "^3.4.0",
53 "hypercore-id-encoding": "^1.2.0",
54 "hyperschema": "^1.12.1",
55 "index-encoder": "^3.3.2",
56 "nanoassert": "^2.0.0",
57 "protomux-wakeup": "^2.0.0",
58 "ready-resource": "^1.0.0",
59 "resolve-reject-promise": "^1.1.0",
60 "safety-catch": "^1.0.2",
61 "scope-lock": "^1.2.4",
62 "signal-promise": "^1.0.3",
63 "sodium-universal": "^5.0.1",
64 "sub-encoder": "^2.1.1",
65 "tiny-buffer-map": "^1.1.1"
66 }
67 },
68 "node_modules/autobase-test-helpers": {
69 "version": "3.1.0",
70 "resolved": "https://registry.npmjs.org/autobase-test-helpers/-/autobase-test-helpers-3.1.0.tgz",
71 "integrity": "sha512-3Jk1nWEcMxYOtU6rt4ujpchLz0W/Uv7S6v0tea53wW9rZqwFzgffEYbufNak53/93DBbp3gwX+AgHdfHtpx4RQ==",
72 "license": "Apache-2.0",
73 "dependencies": {
74 "b4a": "^1.6.4"
75 }
76 },
3477 "node_modules/b4a": {
3578 "version": "1.8.1",
3679 "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
……@@ -370,6 +413,15 @@
370413 "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==",
371414 "license": "MIT"
372415 },
416 "node_modules/codecs": {
417 "version": "3.1.0",
418 "resolved": "https://registry.npmjs.org/codecs/-/codecs-3.1.0.tgz",
419 "integrity": "sha512-Dqx8NwvBvnMeuPQdVKy/XEF71igjR5apxBvCGeV0pP1tXadOiaLvDTXt7xh+/5wI1ASB195mXQGJbw3Ml4YDWQ==",
420 "license": "MIT",
421 "dependencies": {
422 "b4a": "^1.6.3"
423 }
424 },
373425 "node_modules/compact-encoding": {
374426 "version": "3.3.0",
375427 "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-3.3.0.tgz",
……@@ -379,6 +431,39 @@
379431 "b4a": "^1.3.0"
380432 }
381433 },
434 "node_modules/core-coupler": {
435 "version": "2.0.0",
436 "resolved": "https://registry.npmjs.org/core-coupler/-/core-coupler-2.0.0.tgz",
437 "integrity": "sha512-FJuEvsdCMwx0Wu+gFQ49rGCi8LCXh8kizHsCQwkdgPZFEFiF0z2HDvyIs+fPt5wMIfU2UVFDuN+dtpfbIxJE6g==",
438 "license": "Apache-2.0",
439 "dependencies": {
440 "safety-catch": "^1.0.2"
441 }
442 },
443 "node_modules/corestore": {
444 "version": "7.11.0",
445 "resolved": "https://registry.npmjs.org/corestore/-/corestore-7.11.0.tgz",
446 "integrity": "sha512-zGlF6dBZ9bvyRliz6g+NW0tGRwGYmmc84o1TARbXOJ+fPRXeKTkVzVV95eWIJEU3+6WBUbcvIBQKmKiNmiTAdg==",
447 "license": "MIT",
448 "dependencies": {
449 "b4a": "^1.6.7",
450 "bare-events": "^2.8.3",
451 "hypercore": "^11.32.0",
452 "hypercore-crypto": "^3.4.2",
453 "hypercore-errors": "^1.4.0",
454 "hypercore-id-encoding": "^1.3.0",
455 "ready-resource": "^1.1.1",
456 "sodium-universal": "^5.0.1",
457 "streamx": "^2.26.0",
458 "which-runtime": "^1.2.1"
459 }
460 },
461 "node_modules/debounceify": {
462 "version": "1.1.0",
463 "resolved": "https://registry.npmjs.org/debounceify/-/debounceify-1.1.0.tgz",
464 "integrity": "sha512-eKuHDVfJVg+u/0nPy8P+fhnLgbyuTgVxuCRrS/R7EpDSMMkBDgSes41MJtSAY1F1hcqfHz3Zy/qpqHHIp/EhdA==",
465 "license": "MIT"
466 },
382467 "node_modules/device-file": {
383468 "version": "2.3.1",
384469 "resolved": "https://registry.npmjs.org/device-file/-/device-file-2.3.1.tgz",
……@@ -393,6 +478,15 @@
393478 "ready-resource": "^1.2.0"
394479 }
395480 },
481 "node_modules/encryption-encoding": {
482 "version": "1.0.3",
483 "resolved": "https://registry.npmjs.org/encryption-encoding/-/encryption-encoding-1.0.3.tgz",
484 "integrity": "sha512-+SlKCeULnNnwBF2rGrJlSLYjwITwfjO8PLSHzue4yt+2DsVJUU8GakRkH4+mTaa3FzpxVjpTy1kKR2gYGctNWg==",
485 "license": "Apache-2.0",
486 "dependencies": {
487 "hyperschema": "^1.19.0"
488 }
489 },
396490 "node_modules/events-universal": {
397491 "version": "1.0.1",
398492 "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
……@@ -451,6 +545,26 @@
451545 "integrity": "sha512-IfTY0dKZM43ACyGvXkbG7De7WY7MxTS5VO6Juhe8oJKpCmrYYXoqp/cJMskkpi0k9H8wuXq0H+eI898/BCqvXg==",
452546 "license": "MIT"
453547 },
548 "node_modules/hyperbee": {
549 "version": "2.27.3",
550 "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-2.27.3.tgz",
551 "integrity": "sha512-PXURH2U4juUZyJRKHTrY5z1zX851pmI1Q0jfv5F/hCIErDt/ND8jOZuxc3hfOLM9f0W3qJEDTMlV5AJBkVPy8w==",
552 "license": "MIT",
553 "dependencies": {
554 "b4a": "^1.6.0",
555 "codecs": "^3.0.0",
556 "debounceify": "^1.0.0",
557 "hypercore-errors": "^1.0.0",
558 "mutexify": "^1.4.0",
559 "protocol-buffers-encodings": "^1.2.0",
560 "rache": "^1.0.0",
561 "ready-resource": "^1.0.0",
562 "resolve-reject-promise": "^1.1.0",
563 "safety-catch": "^1.0.2",
564 "streamx": "^2.12.4",
565 "unslab": "^1.2.0"
566 }
567 },
454568 "node_modules/hypercore": {
455569 "version": "11.33.5",
456570 "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-11.33.5.tgz",
……@@ -566,6 +680,15 @@
566680 "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
567681 "license": "MIT"
568682 },
683 "node_modules/mutexify": {
684 "version": "1.4.0",
685 "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.4.0.tgz",
686 "integrity": "sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==",
687 "license": "MIT",
688 "dependencies": {
689 "queue-tick": "^1.0.0"
690 }
691 },
569692 "node_modules/nanoassert": {
570693 "version": "2.0.0",
571694 "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz",
……@@ -594,6 +717,17 @@
594717 "sodium-universal": "^5.0.0"
595718 }
596719 },
720 "node_modules/protocol-buffers-encodings": {
721 "version": "1.2.0",
722 "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz",
723 "integrity": "sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==",
724 "license": "MIT",
725 "dependencies": {
726 "b4a": "^1.6.0",
727 "signed-varint": "^2.0.1",
728 "varint": "5.0.0"
729 }
730 },
597731 "node_modules/protomux": {
598732 "version": "3.11.0",
599733 "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.11.0.tgz",
……@@ -607,6 +741,18 @@
607741 "unslab": "^1.3.0"
608742 }
609743 },
744 "node_modules/protomux-wakeup": {
745 "version": "2.9.0",
746 "resolved": "https://registry.npmjs.org/protomux-wakeup/-/protomux-wakeup-2.9.0.tgz",
747 "integrity": "sha512-K93WhS9qIRL8WAQPU2V3PxdpQ1oRlQCFyCyIYInoq/iRkcuO1BegRERWo7SS1Tcme14VKGWOr/IivjjYnHan3Q==",
748 "license": "Apache-2.0",
749 "dependencies": {
750 "b4a": "^1.6.7",
751 "hypercore-crypto": "^3.5.0",
752 "hyperschema": "^1.10.4",
753 "protomux": "^3.10.1"
754 }
755 },
610756 "node_modules/queue-tick": {
611757 "version": "1.0.1",
612758 "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
……@@ -636,6 +782,12 @@
636782 "quickbit-native": "^2.2.0"
637783 }
638784 },
785 "node_modules/rache": {
786 "version": "1.0.0",
787 "resolved": "https://registry.npmjs.org/rache/-/rache-1.0.0.tgz",
788 "integrity": "sha512-e0k0g0w/8jOCB+7YqCIlOa+OJ38k0wrYS4x18pMSmqOvLKoyhmMhmQyCcvfY6VaP8D75cqkEnlakXs+RYYLqNg==",
789 "license": "Apache-2.0"
790 },
639791 "node_modules/random-array-iterator": {
640792 "version": "1.0.0",
641793 "resolved": "https://registry.npmjs.org/random-array-iterator/-/random-array-iterator-1.0.0.tgz",
……@@ -717,6 +869,15 @@
717869 "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==",
718870 "license": "MIT"
719871 },
872 "node_modules/signed-varint": {
873 "version": "2.0.1",
874 "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz",
875 "integrity": "sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==",
876 "license": "MIT",
877 "dependencies": {
878 "varint": "~5.0.0"
879 }
880 },
720881 "node_modules/simdle-native": {
721882 "version": "1.3.9",
722883 "resolved": "https://registry.npmjs.org/simdle-native/-/simdle-native-1.3.9.tgz",
……@@ -792,6 +953,16 @@
792953 "text-decoder": "^1.1.0"
793954 }
794955 },
956 "node_modules/sub-encoder": {
957 "version": "2.1.3",
958 "resolved": "https://registry.npmjs.org/sub-encoder/-/sub-encoder-2.1.3.tgz",
959 "integrity": "sha512-Xxx04ygZo/1J3yHvaSA6VhDmiSaBQkw/PmO3YnnYFXle+tfOGToC6FcDpIfMztWZXJzuKG14b/57HMkiL58C6A==",
960 "license": "Apache-2.0",
961 "dependencies": {
962 "b4a": "^1.6.0",
963 "codecs": "^3.1.0"
964 }
965 },
795966 "node_modules/teex": {
796967 "version": "1.0.1",
797968 "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
……@@ -816,6 +987,15 @@
816987 "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==",
817988 "license": "MIT"
818989 },
990 "node_modules/tiny-buffer-map": {
991 "version": "1.1.1",
992 "resolved": "https://registry.npmjs.org/tiny-buffer-map/-/tiny-buffer-map-1.1.1.tgz",
993 "integrity": "sha512-C1eDw6ks9CmkDbWVCPHobuixPTkxGa7IDERlaVk98dv4tOUdz42o3haHBr0uhNxbj0gczBTVIyS2uQsu+1vc2Q==",
994 "license": "MIT",
995 "dependencies": {
996 "b4a": "^1.6.0"
997 }
998 },
819999 "node_modules/unslab": {
8201000 "version": "1.3.0",
8211001 "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz",
……@@ -825,6 +1005,12 @@
8251005 "b4a": "^1.6.6"
8261006 }
8271007 },
1008 "node_modules/varint": {
1009 "version": "5.0.0",
1010 "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
1011 "integrity": "sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==",
1012 "license": "MIT"
1013 },
8281014 "node_modules/which-runtime": {
8291015 "version": "1.4.0",
8301016 "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.4.0.tgz",
experiments/m0-uds-bridge/package.json +4 -0
……@@ -5,9 +5,13 @@
55 "description": "M0 UDS bridge experiment: Bare daemon (in-memory s-expr log) for HyperStorageNode",
66 "type": "module",
77 "dependencies": {
8 "autobase": "^7.28.1",
9 "autobase-test-helpers": "^3.1.0",
10 "b4a": "^1.8.1",
811 "bare-fs": "^4.7.3",
912 "bare-net": "^2.3.2",
1013 "bare-process": "^4.5.0",
14 "corestore": "^7.11.0",
1115 "hypercore": "^11.33.5"
1216 }
1317}
experiments/m0-uds-bridge/run.sh +34 -6
……@@ -13,13 +13,16 @@ WORK="$(mktemp -d /tmp/m1-XXXXXX)"
1313SOCK="$WORK/svc.sock"
1414STORE1="$WORK/store1"
1515STORE2="$WORK/store2"
16DPID=""
16SA="$WORK/a.sock"; SB="$WORK/b.sock"; PEER="$WORK/peer.sock"
17DPID=""; PA=""; PB=""
1718
1819cleanup() {
19 # `bare` is an nvm shim that execs a child runtime; kill $DPID AND match the
20 # daemon by its unique socket path.
21 [ -n "$DPID" ] && kill -9 "$DPID" 2>/dev/null || true
20 # `bare` is an nvm shim that execs a child runtime; kill the shims AND match
21 # the daemons by their unique socket paths.
22 for p in "$DPID" "$PA" "$PB"; do [ -n "$p" ] && kill -9 "$p" 2>/dev/null || true; done
2223 pkill -9 -f "daemon.js $SOCK" 2>/dev/null || true
24 pkill -9 -f "daemon-ab.js $SA" 2>/dev/null || true
25 pkill -9 -f "daemon-ab.js $SB" 2>/dev/null || true
2326 rm -rf "$WORK"
2427}
2528trap cleanup EXIT
……@@ -41,7 +44,7 @@ stop_daemon() {
4144}
4245
4346# deps + build
44[ -d node_modules/hypercore ] || npm install --silent
47[ -d node_modules/autobase ] || npm install --silent
4548g++ -std=gnu++20 -O2 -I/usr/local/include m0_roundtrip.cc \
4649 -L/usr/local/lib/opencog -Wl,-rpath,/usr/local/lib/opencog \
4750 -latomspace -lsexpr -latombase -latom_types \
……@@ -67,4 +70,29 @@ start_daemon "$WORK/store3"
6770./m0_roundtrip "$SOCK" lineage
6871stop_daemon
6972
70echo "M0-M3 ALL PASS"
73# Phase 5 — M4: two Autobase daemons (agents) converge to one linearized log.
74bare daemon-ab.js "$SA" "$WORK/abA" --peer-listen "$PEER" > "$WORK/a.log" 2>&1 &
75PA=$!
76for _ in $(seq 1 100); do grep -q "AB key=" "$WORK/a.log" && break; sleep 0.05; done
77AKEY=$(sed -n 's/.*AB key=\([0-9a-f]*\).*/\1/p' "$WORK/a.log")
78[ -n "$AKEY" ] || { echo "daemon A failed:"; cat "$WORK/a.log"; exit 1; }
79
80bare daemon-ab.js "$SB" "$WORK/abB" --bootstrap "$AKEY" --peer-connect "$PEER" > "$WORK/b.log" 2>&1 &
81PB=$!
82for _ in $(seq 1 100); do grep -q "AB key=" "$WORK/b.log" && break; sleep 0.05; done
83BLOCAL=$(sed -n 's/.*local=\([0-9a-f]*\).*/\1/p' "$WORK/b.log")
84[ -n "$BLOCAL" ] || { echo "daemon B failed:"; cat "$WORK/b.log"; exit 1; }
85
86# Pattern-B ceremony: A (the writer) addWriters B's announced local key.
87bare abctl.js "$SA" "addwriter $BLOCAL" >/dev/null
88for _ in $(seq 1 60); do
89 [ "$(bare abctl.js "$SB" writable)" = "true" ] && break
90 sleep 0.1
91done
92./m0_roundtrip "$SA" m4 "$SB"
93for p in "$PA" "$PB"; do kill -9 "$p" 2>/dev/null || true; done
94pkill -9 -f "daemon-ab.js $SA" 2>/dev/null || true
95pkill -9 -f "daemon-ab.js $SB" 2>/dev/null || true
96PA=""; PB=""
97
98echo "M0-M4 ALL PASS"