OverviewFilesCommitsIssuesDocsReleasesPeers

atomspace-gpu commits / 7cde431

M0: HyperStorageNode UDS bridge + codec round-trip (GOLD PASS)
First milestone of the HyperStorageNode (spec/hyperstoragenode.md, org issue #4). Proves the data path with no Hypercore and no full StorageNode/guile-module boilerplate: C++ AtomSpace --Sexpr::encode_atom--> UDS --> Bare daemon in-memory s-expr log --replay--> Sexpr::decode_atom --> fresh AtomSpace, then gold-check the two AtomSpaces are byte-identical (8/8 atoms). - daemon.js: Bare (bare-net) UDS server, in-memory log[], verbs store/load/clear/barrier/count. allowHalfOpen:false + destroy-on-end (hyper-svc leak fix). At M1 the array becomes a Hypercore. - m0_roundtrip.cc: HyperChannel = reusable AF_UNIX transport (adapted from CogSimpleStorage do_send/do_recv, TCP->UDS) that lifts into HyperStorageNode at M1; main() is the gold-check harness. Uses the real libsexpr codec (encode is a genuine inverse of decode). - run.sh: build + start daemon + run + gold check + cleanup. Verified reproducible from clean: "M0 GOLD PASS", exit 0, no leftover daemons. Deferred by design: Hypercore (M1), Values, guile module, Autobase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7cde431fefe7681f115e395e27335078f1b29a97
by plankatron <fleeky@gmail.com> · 2026-07-05T14:28:45.000Z

Changed files (7) +704 -0

experiments/m0-uds-bridge/.gitignore +4 -0
……@@ -0,0 +1,4 @@
1node_modules/
2m0_roundtrip
3*.o
4*.sock
experiments/m0-uds-bridge/README.md +57 -0
……@@ -0,0 +1,57 @@
1# M0 — UDS bridge + codec round-trip
2
3First milestone of the HyperStorageNode (spec: `../../spec/hyperstoragenode.md`;
4`atomspace` org issue #4). Proves the data path with **no Hypercore** and **no full
5StorageNode/guile-module boilerplate** yet:
6
7```
8 C++ (real Sexpr codec) Bare daemon (in-memory s-expr log)
9 ──────────────────────── ─────────────────────────────────
10 build small AtomSpace
11 Sexpr::encode_atom ──store──▶ log.push(sexpr)
12 (barrier) ──barrier─▶ ok
13 (replay) ──load────▶ (sexpr1 sexpr2 …)
14 Sexpr::decode_atom ◀───────────
15 gold check: src AtomSpace ≡ dst AtomSpace → M0 GOLD PASS
16```
17
18## What this establishes
19- A C++ `AF_UNIX` client (`HyperChannel`, adapted from `CogSimpleStorage`'s
20 `do_send`/`do_recv`, TCP→UDS) round-trips atoms through a **Bare** daemon.
21- The wire payload is the **real opencog s-expr codec** (`Sexpr::encode_atom` /
22 `Sexpr::decode_atom` from `libsexpr`), not a bespoke serialization — so encode is
23 a genuine inverse of decode.
24- bare-net UDS framing + half-open handling (`allowHalfOpen:false` + destroy on
25 `end`, the hyper-svc leak fix) work against a foreign (C++) client.
26
27## Files
28| File | Role |
29|---|---|
30| `daemon.js` | Bare UDS server; in-memory `log[]` of s-expr strings; verbs `store`/`load`/`clear`/`barrier`/`count`. At M1 the array becomes a Hypercore. |
31| `m0_roundtrip.cc` | C++ harness. `HyperChannel` = the reusable transport the future `HyperStorageNode` embeds; `main()` builds an 8-atom AtomSpace, stores, replays, gold-checks. |
32| `run.sh` | build + start daemon + run harness + gold check + cleanup. |
33
34## Run
35```bash
36./run.sh # prints "M0 GOLD PASS", exits 0
37```
38Requires: `bare` (v1.26+), `g++` (C++20), opencog installed under `/usr/local`
39(`libatomspace`, `libsexpr`, `libatombase`, `libatom_types`). `npm install` pulls
40`bare-net`/`bare-fs`/`bare-process` into a local `node_modules` (gitignored).
41
42## Wire protocol (newline-framed)
43| Request | Reply | Meaning |
44|---|---|---|
45| `store <sexpr>` | — | append one atom's s-expr to the log (fire-and-forget) |
46| `load` | `(<sexpr>…)\n` | whole log wrapped in one outer paren (the shape the C++ decode loop expects) |
47| `clear` | `ok\n` | empty the log |
48| `barrier` | `ok\n` | sync point (ordered stream ⇒ all prior stores are applied) |
49| `count` | `<n>\n` | log length (debug) |
50
51## Deliberately NOT here (deferred)
52- **Hypercore** — the log is a plain array (that is M1: append + replay + fork).
53- **Values** — structure only; per the synthesis rule *event-source the graph,
54 checkpoint the Values* (`../../spec/activegraph-synthesis.md`).
55- **Full `HyperStorageNode : public StorageNode` + guile module** — `HyperChannel`
56 is written to lift straight into it at M1; no atom_types/PersistSCM/CMake yet.
57- **Autobase multi-writer** — single client, single daemon (M4).
experiments/m0-uds-bridge/daemon.js +95 -0
……@@ -0,0 +1,95 @@
1// M0 hyper-atomspace daemon (Bare).
2//
3// Purpose: prove the HyperStorageNode bridge + s-expr codec round-trip with an
4// in-memory op-log. NO Hypercore yet (that is M1). The daemon holds an array of
5// s-expr strings (one per stored atom) and can replay them on demand.
6//
7// Wire protocol — newline-framed request lines over a UNIX-domain socket:
8// store <sexpr>\n append <sexpr> to the log. NO reply (fire-and-forget,
9// mirrors CogSimpleStorage::storeAtom).
10// load\n reply one line: (<sexpr1><sexpr2>...)\n — the whole log
11// wrapped in a single outer paren, the exact shape the C++
12// side's decode loop (cf. CogSimpleStorage::decode_atom_list)
13// expects.
14// clear\n empty the log. reply: ok\n
15// barrier\n sync point. reply: ok\n
16// count\n reply: <n>\n (debug)
17//
18// Framing note: bare-net defaults allowHalfOpen:true and only emits 'close' on an
19// explicit destroy (see hyper-svc UDS leak post-mortem). We create the server
20// allowHalfOpen:false AND destroy on 'end' so client fds are released.
21
22import net from 'bare-net'
23import fs from 'bare-fs'
24import process from 'bare-process'
25
26const sockPath = process.argv[2]
27if (!sockPath) {
28 console.error('usage: bare daemon.js <socket-path>')
29 process.exit(1)
30}
31
32// Fresh bind: a stale socket file makes listen() fail with EADDRINUSE.
33try { fs.unlinkSync(sockPath) } catch (_) { /* not there — fine */ }
34
35/** @type {string[]} the in-memory op-log: one s-expr per stored atom. */
36const log = []
37
38function handleLine (line, sock) {
39 if (line.length === 0) return
40 const sp = line.indexOf(' ')
41 const verb = sp === -1 ? line : line.slice(0, sp)
42 const rest = sp === -1 ? '' : line.slice(sp + 1)
43
44 switch (verb) {
45 case 'store':
46 log.push(rest)
47 return // no reply
48 case 'load':
49 sock.write('(' + log.join('') + ')\n')
50 return
51 case 'clear':
52 log.length = 0
53 sock.write('ok\n')
54 return
55 case 'barrier':
56 sock.write('ok\n')
57 return
58 case 'count':
59 sock.write(String(log.length) + '\n')
60 return
61 default:
62 sock.write('err unknown-verb ' + verb + '\n')
63 }
64}
65
66const server = net.createServer({ allowHalfOpen: false }, (sock) => {
67 let buf = ''
68 sock.on('data', (chunk) => {
69 buf += chunk.toString()
70 let nl
71 while ((nl = buf.indexOf('\n')) !== -1) {
72 const line = buf.slice(0, nl)
73 buf = buf.slice(nl + 1)
74 handleLine(line, sock)
75 }
76 })
77 // Peer finished + disconnected. bare-net will not emit 'close' on its own;
78 // destroy to release the fd (the hyper-svc leak fix).
79 sock.on('end', () => sock.destroy())
80 sock.on('error', () => sock.destroy())
81})
82
83server.listen(sockPath, () => {
84 // Single line on stdout = readiness signal for run.sh / test harness.
85 console.log('hyper-atomspace-m0 listening ' + sockPath)
86})
87
88// Clean shutdown so the socket file does not linger.
89function shutdown () {
90 try { server.close() } catch (_) {}
91 try { fs.unlinkSync(sockPath) } catch (_) {}
92 process.exit(0)
93}
94process.on('SIGINT', shutdown)
95process.on('SIGTERM', shutdown)
experiments/m0-uds-bridge/m0_roundtrip.cc +191 -0
……@@ -0,0 +1,191 @@
1/*
2 * m0_roundtrip.cc — M0 bridge + codec round-trip harness.
3 *
4 * Proves the HyperStorageNode data path with NO Hypercore and NO full
5 * StorageNode/guile-module boilerplate yet: build a small AtomSpace, encode each
6 * atom with the REAL opencog s-expr codec (Sexpr::encode_atom), ship it over a
7 * UNIX-domain socket to the Bare `hyper-atomspace` daemon (daemon.js), replay the
8 * log back, decode with the REAL codec (Sexpr::decode_atom) into a fresh
9 * AtomSpace, and gold-check that the two AtomSpaces are identical.
10 *
11 * `HyperChannel` is the reusable AF_UNIX transport (adapted from
12 * CogSimpleStorage's do_send/do_recv, TCP -> UDS). At M1 it lifts straight into
13 * `HyperStorageNode : public StorageNode` (cf. CogChannel in cog-storage); here
14 * main() is just the test harness around it.
15 *
16 * SPDX-License-Identifier: AGPL-3.0-or-later
17 */
18
19#include <algorithm>
20#include <iostream>
21#include <string>
22#include <vector>
23
24#include <string.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <unistd.h>
28
29#include <opencog/atomspace/AtomSpace.h>
30#include <opencog/atoms/atom_types/atom_types.h>
31#include <opencog/atoms/base/Handle.h>
32#include <opencog/persist/sexpr/Sexpr.h>
33
34using namespace opencog;
35
36// ---------------------------------------------------------------------------
37// HyperChannel — newline-framed s-expr protocol over a UNIX-domain socket.
38// (The transport the future HyperStorageNode will embed.)
39class HyperChannel
40{
41 int _fd = -1;
42 std::string _rbuf; // leftover bytes past a newline
43
44public:
45 void open(const std::string& path)
46 {
47 _fd = socket(AF_UNIX, SOCK_STREAM, 0);
48 if (_fd < 0) throw std::runtime_error("socket() failed");
49
50 struct sockaddr_un addr;
51 memset(&addr, 0, sizeof(addr));
52 addr.sun_family = AF_UNIX;
53 if (path.size() >= sizeof(addr.sun_path))
54 throw std::runtime_error("socket path too long");
55 strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
56
57 if (connect(_fd, (struct sockaddr*) &addr, sizeof(addr)) != 0)
58 throw std::runtime_error(std::string("connect() failed: ")
59 + strerror(errno));
60 }
61
62 void close_fd()
63 {
64 if (_fd >= 0) { ::close(_fd); _fd = -1; }
65 }
66 ~HyperChannel() { close_fd(); }
67
68 // Fire-and-forget send of one line (must include its own '\n').
69 void send_line(const std::string& line)
70 {
71 size_t off = 0;
72 while (off < line.size())
73 {
74 ssize_t n = ::send(_fd, line.data() + off, line.size() - off, 0);
75 if (n < 0) throw std::runtime_error("send() failed");
76 off += n;
77 }
78 }
79
80 // Read exactly one '\n'-terminated line; returns it without the newline.
81 std::string recv_line()
82 {
83 for (;;)
84 {
85 size_t nl = _rbuf.find('\n');
86 if (nl != std::string::npos)
87 {
88 std::string line = _rbuf.substr(0, nl);
89 _rbuf.erase(0, nl + 1);
90 return line;
91 }
92 char buf[8192];
93 ssize_t n = ::recv(_fd, buf, sizeof(buf), 0);
94 if (n < 0) throw std::runtime_error("recv() failed");
95 if (n == 0) throw std::runtime_error("daemon closed connection");
96 _rbuf.append(buf, n);
97 }
98 }
99
100 // Protocol verbs
101 void store(const std::string& sexpr) { send_line("store " + sexpr + "\n"); }
102 void clear() { send_line("clear\n"); (void) recv_line(); }
103 void barrier() { send_line("barrier\n"); (void) recv_line(); }
104 std::string load() { send_line("load\n"); return recv_line(); }
105};
106
107// ---------------------------------------------------------------------------
108// Enumerate every atom in `as`, encode each to its s-expr, return sorted vector.
109static std::vector<std::string> encoded_sorted(const AtomSpacePtr& as)
110{
111 HandleSeq all;
112 as->get_handles_by_type(all, ATOM, true /* subclasses */);
113 std::vector<std::string> out;
114 for (const Handle& h : all)
115 out.push_back(Sexpr::encode_atom(h));
116 std::sort(out.begin(), out.end());
117 return out;
118}
119
120int main(int argc, char** argv)
121{
122 if (argc < 2)
123 {
124 std::cerr << "usage: m0_roundtrip <socket-path>\n";
125 return 2;
126 }
127 const std::string sockpath = argv[1];
128
129 // --- 1. Build a source AtomSpace: nodes, a link, a nested Eval/List. ---
130 AtomSpacePtr src = createAtomSpace();
131 Handle cat = src->add_node(CONCEPT_NODE, "cat");
132 Handle animal = src->add_node(CONCEPT_NODE, "animal");
133 src->add_link(INHERITANCE_LINK, cat, animal);
134
135 Handle whale = src->add_node(CONCEPT_NODE, "whale");
136 Handle water = src->add_node(CONCEPT_NODE, "water");
137 Handle livesIn = src->add_node(PREDICATE_NODE, "lives-in");
138 Handle lst = src->add_link(LIST_LINK, whale, water);
139 src->add_link(EVALUATION_LINK, livesIn, lst);
140
141 // --- 2. Store every atom over the UDS bridge. ---
142 HyperChannel ch;
143 ch.open(sockpath);
144 ch.clear();
145 for (const std::string& s : encoded_sorted(src))
146 ch.store(s);
147 ch.barrier();
148
149 // --- 3. Replay the log; decode into a fresh AtomSpace. ---
150 std::string reply = ch.load();
151 AtomSpacePtr dst = createAtomSpace();
152 {
153 std::unordered_map<std::string, Handle> cache;
154 size_t l = reply.find('(');
155 size_t end = reply.rfind(')');
156 if (l != std::string::npos && end != std::string::npos && l < end)
157 {
158 l += 1; // step past the outer '('
159 size_t r = end;
160 while (true)
161 {
162 int pcnt = Sexpr::get_next_expr(reply, l, r, 0);
163 if (l == r) break;
164 if (0 < pcnt) break; // unbalanced — malformed reply
165 Handle h = Sexpr::decode_atom(reply, l, r, 0, cache);
166 dst->add_atom(h);
167 l = r + 1;
168 r = end;
169 }
170 }
171 }
172 ch.close_fd();
173
174 // --- 4. Gold check: src and dst must be byte-identical atom sets. ---
175 std::vector<std::string> es = encoded_sorted(src);
176 std::vector<std::string> ed = encoded_sorted(dst);
177
178 std::cout << "src atoms: " << src->get_size()
179 << " dst atoms: " << dst->get_size() << "\n";
180
181 bool ok = (es == ed) && (src->get_size() == dst->get_size());
182 if (not ok)
183 {
184 std::cout << "MISMATCH\n src:\n";
185 for (const auto& s : es) std::cout << " " << s << "\n";
186 std::cout << " dst:\n";
187 for (const auto& s : ed) std::cout << " " << s << "\n";
188 }
189 std::cout << (ok ? "M0 GOLD PASS" : "M0 GOLD FAIL") << "\n";
190 return ok ? 0 : 1;
191}
experiments/m0-uds-bridge/package-lock.json +305 -0
……@@ -0,0 +1,305 @@
1{
2 "name": "hyper-atomspace-m0",
3 "version": "0.0.0",
4 "lockfileVersion": 3,
5 "requires": true,
6 "packages": {
7 "": {
8 "name": "hyper-atomspace-m0",
9 "version": "0.0.0",
10 "dependencies": {
11 "bare-fs": "^4.7.3",
12 "bare-net": "^2.3.2",
13 "bare-process": "^4.5.0"
14 }
15 },
16 "node_modules/b4a": {
17 "version": "1.8.1",
18 "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
19 "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
20 "license": "Apache-2.0",
21 "peerDependencies": {
22 "react-native-b4a": "*"
23 },
24 "peerDependenciesMeta": {
25 "react-native-b4a": {
26 "optional": true
27 }
28 }
29 },
30 "node_modules/bare-abort": {
31 "version": "2.0.13",
32 "resolved": "https://registry.npmjs.org/bare-abort/-/bare-abort-2.0.13.tgz",
33 "integrity": "sha512-zdc8l88eB11Jsz5rDd6sCAgv2kUFXgdrZWoMlgU6JMkfAi1/uuGFC3IEHswKbIRQTk5H3T5CMuechsXYxiaHlQ==",
34 "license": "Apache-2.0"
35 },
36 "node_modules/bare-dns": {
37 "version": "2.1.4",
38 "resolved": "https://registry.npmjs.org/bare-dns/-/bare-dns-2.1.4.tgz",
39 "integrity": "sha512-abwjHmpWqSRNB7V5615QxPH92L71AVzFm/kKTs8VYiNTAi2xVdonpv0BjJ0hwXLwomoW+xsSOPjW6PZPO14asg==",
40 "license": "Apache-2.0",
41 "engines": {
42 "bare": ">=1.7.0"
43 }
44 },
45 "node_modules/bare-env": {
46 "version": "3.0.0",
47 "resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.0.tgz",
48 "integrity": "sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw==",
49 "license": "Apache-2.0",
50 "dependencies": {
51 "bare-os": "^3.0.1"
52 }
53 },
54 "node_modules/bare-events": {
55 "version": "2.9.1",
56 "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
57 "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
58 "license": "Apache-2.0",
59 "peerDependencies": {
60 "bare-abort-controller": "*"
61 },
62 "peerDependenciesMeta": {
63 "bare-abort-controller": {
64 "optional": true
65 }
66 }
67 },
68 "node_modules/bare-fs": {
69 "version": "4.7.3",
70 "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.3.tgz",
71 "integrity": "sha512-xRgplks8SvcKkdlv2M6Z2LZmRsmqd+x0nXXGXeMEjwdibj1HSDrlnqBRLeYdMvsgCox7Bq0e+DHwfczOfsn6IA==",
72 "license": "Apache-2.0",
73 "dependencies": {
74 "bare-events": "^2.5.4",
75 "bare-path": "^3.0.0",
76 "bare-stream": "^2.6.4",
77 "bare-url": "^2.2.2",
78 "fast-fifo": "^1.3.2"
79 },
80 "engines": {
81 "bare": ">=1.16.0"
82 },
83 "peerDependencies": {
84 "bare-buffer": "*"
85 },
86 "peerDependenciesMeta": {
87 "bare-buffer": {
88 "optional": true
89 }
90 }
91 },
92 "node_modules/bare-hrtime": {
93 "version": "2.1.1",
94 "resolved": "https://registry.npmjs.org/bare-hrtime/-/bare-hrtime-2.1.1.tgz",
95 "integrity": "sha512-VMb3tHo05gsnbu3OXTmkDiwTjMlOsbQmKoysKqKEyR09m77TuDrYFbj3Q5GGk10dAKsUHrnXmwCaeJqzVpB5ZA==",
96 "license": "Apache-2.0"
97 },
98 "node_modules/bare-net": {
99 "version": "2.3.2",
100 "resolved": "https://registry.npmjs.org/bare-net/-/bare-net-2.3.2.tgz",
101 "integrity": "sha512-I+yz+pqbYsBkxDsnu5vkKvy7RSNY9CcAvu2jZT6PsmdXJQG1i3dmD5V7xc3334OVp2absgtUEYLmmuNFlphBzg==",
102 "license": "Apache-2.0",
103 "dependencies": {
104 "bare-events": "^2.2.2",
105 "bare-pipe": "^4.0.0",
106 "bare-stream": "^2.0.0",
107 "bare-tcp": "^2.0.0"
108 }
109 },
110 "node_modules/bare-os": {
111 "version": "3.9.3",
112 "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.3.tgz",
113 "integrity": "sha512-fF4Q7QsyKVF5Rj0qvI8BgUNjqzC2JvQlpTaPLjVJVxYVUX5Zr9un+y3w1HmA4nNKdFmRBT8z/WmrjvXzXVerKQ==",
114 "license": "Apache-2.0",
115 "engines": {
116 "bare": ">=1.14.0"
117 }
118 },
119 "node_modules/bare-path": {
120 "version": "3.0.1",
121 "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz",
122 "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==",
123 "license": "Apache-2.0",
124 "dependencies": {
125 "bare-os": "^3.0.1"
126 }
127 },
128 "node_modules/bare-pipe": {
129 "version": "4.2.2",
130 "resolved": "https://registry.npmjs.org/bare-pipe/-/bare-pipe-4.2.2.tgz",
131 "integrity": "sha512-Wkf6//CmKS8inHrkgi3X/xNMeaGfs2I0ayLM0B8cbPB4DxYHcoes6Ng+muCBfXNIJ9qmEjwDDJjAUz7nRxxjgQ==",
132 "license": "Apache-2.0",
133 "peer": true,
134 "dependencies": {
135 "bare-events": "^2.0.0",
136 "bare-stream": "^2.0.0"
137 },
138 "engines": {
139 "bare": ">=1.16.0"
140 }
141 },
142 "node_modules/bare-posix": {
143 "version": "1.0.1",
144 "resolved": "https://registry.npmjs.org/bare-posix/-/bare-posix-1.0.1.tgz",
145 "integrity": "sha512-b4quA8dyRVvX0aFIAIiev5zFATHwIUGr42Gmt+CqiBNg/bdsNkrj4bbkeotMOMiljJS4DCME5kywJO/TFwKo1A==",
146 "license": "Apache-2.0"
147 },
148 "node_modules/bare-process": {
149 "version": "4.5.0",
150 "resolved": "https://registry.npmjs.org/bare-process/-/bare-process-4.5.0.tgz",
151 "integrity": "sha512-NTtYDiwleJjWWNg4+yzS6B/RovL/LLPHVvoOM+18W+dYAXCmky0zFfN66hqqBbrlGAlb52/QgKrdzPR3x1QyHA==",
152 "license": "Apache-2.0",
153 "dependencies": {
154 "bare-abort": "^2.0.13",
155 "bare-env": "^3.0.0",
156 "bare-events": "^2.3.1",
157 "bare-hrtime": "^2.0.0",
158 "bare-os": "^3.7.1",
159 "bare-posix": "^1.0.1",
160 "bare-signals": "^4.0.0",
161 "bare-stdio": "^1.0.1"
162 }
163 },
164 "node_modules/bare-signals": {
165 "version": "4.2.0",
166 "resolved": "https://registry.npmjs.org/bare-signals/-/bare-signals-4.2.0.tgz",
167 "integrity": "sha512-fNHMOdQIlYuTvMB3Oh9Apk99hLKn351+Ir8vz+khiPTcOqIyGG4uWWjdLTzxWdYGsA0eT+We3y0K74hjj2nq7A==",
168 "license": "Apache-2.0",
169 "dependencies": {
170 "bare-events": "^2.5.3",
171 "bare-os": "^3.3.1"
172 },
173 "engines": {
174 "bare": ">=1.7.0"
175 }
176 },
177 "node_modules/bare-stdio": {
178 "version": "1.0.2",
179 "resolved": "https://registry.npmjs.org/bare-stdio/-/bare-stdio-1.0.2.tgz",
180 "integrity": "sha512-3WJDqtvVGP4f+j68kyEC05umOYNwKJ1xG+YAXL8yZ605WgNqiRhVaFq+mVIhBt2eKNp7pa5vCQdhOt1pNh79SA==",
181 "license": "Apache-2.0",
182 "dependencies": {
183 "bare-fs": "^4.5.2",
184 "bare-pipe": "^4.1.5",
185 "bare-tty": "^5.0.3"
186 }
187 },
188 "node_modules/bare-stream": {
189 "version": "2.13.3",
190 "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz",
191 "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==",
192 "license": "Apache-2.0",
193 "dependencies": {
194 "b4a": "^1.8.1",
195 "streamx": "^2.25.0",
196 "teex": "^1.0.1"
197 },
198 "peerDependencies": {
199 "bare-abort-controller": "*",
200 "bare-buffer": "*",
201 "bare-events": "*"
202 },
203 "peerDependenciesMeta": {
204 "bare-abort-controller": {
205 "optional": true
206 },
207 "bare-buffer": {
208 "optional": true
209 },
210 "bare-events": {
211 "optional": true
212 }
213 }
214 },
215 "node_modules/bare-tcp": {
216 "version": "2.5.1",
217 "resolved": "https://registry.npmjs.org/bare-tcp/-/bare-tcp-2.5.1.tgz",
218 "integrity": "sha512-N32OosAegmGzksr/huxIYLU/uaUARte6utO+vAu/EPQztMM7iAC55VeQSfzr2/XBJoz2OIETMvmU7/sdfiOGjw==",
219 "license": "Apache-2.0",
220 "dependencies": {
221 "bare-dns": "^2.0.4",
222 "bare-events": "^2.5.4",
223 "bare-stream": "^2.6.4"
224 },
225 "engines": {
226 "bare": ">=1.16.0"
227 },
228 "peerDependencies": {
229 "bare-pipe": "*"
230 },
231 "peerDependenciesMeta": {
232 "bare-pipe": {
233 "optional": true
234 }
235 }
236 },
237 "node_modules/bare-tty": {
238 "version": "5.1.1",
239 "resolved": "https://registry.npmjs.org/bare-tty/-/bare-tty-5.1.1.tgz",
240 "integrity": "sha512-cXCMAlcLCFTCWwCtXBIhDuXa4hutTEbrHHIK5pvSSb4BLaSGJ9tvxVN3mvGOvXXqONS7l04mTiP+eIUlZKI92g==",
241 "license": "Apache-2.0",
242 "dependencies": {
243 "bare-events": "^2.2.0",
244 "bare-signals": "^4.0.0",
245 "bare-stream": "^2.0.0"
246 },
247 "engines": {
248 "bare": ">=1.16.0"
249 }
250 },
251 "node_modules/bare-url": {
252 "version": "2.4.5",
253 "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz",
254 "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==",
255 "license": "Apache-2.0",
256 "dependencies": {
257 "bare-path": "^3.0.0"
258 }
259 },
260 "node_modules/events-universal": {
261 "version": "1.0.1",
262 "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
263 "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
264 "license": "Apache-2.0",
265 "dependencies": {
266 "bare-events": "^2.7.0"
267 }
268 },
269 "node_modules/fast-fifo": {
270 "version": "1.3.2",
271 "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
272 "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
273 "license": "MIT"
274 },
275 "node_modules/streamx": {
276 "version": "2.28.0",
277 "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
278 "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==",
279 "license": "MIT",
280 "dependencies": {
281 "events-universal": "^1.0.0",
282 "fast-fifo": "^1.3.2",
283 "text-decoder": "^1.1.0"
284 }
285 },
286 "node_modules/teex": {
287 "version": "1.0.1",
288 "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
289 "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
290 "license": "MIT",
291 "dependencies": {
292 "streamx": "^2.12.5"
293 }
294 },
295 "node_modules/text-decoder": {
296 "version": "1.2.7",
297 "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
298 "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
299 "license": "Apache-2.0",
300 "dependencies": {
301 "b4a": "^1.6.4"
302 }
303 }
304 }
305}
experiments/m0-uds-bridge/package.json +12 -0
……@@ -0,0 +1,12 @@
1{
2 "name": "hyper-atomspace-m0",
3 "version": "0.0.0",
4 "private": true,
5 "description": "M0 UDS bridge experiment: Bare daemon (in-memory s-expr log) for HyperStorageNode",
6 "type": "module",
7 "dependencies": {
8 "bare-fs": "^4.7.3",
9 "bare-net": "^2.3.2",
10 "bare-process": "^4.5.0"
11 }
12}
experiments/m0-uds-bridge/run.sh +40 -0
……@@ -0,0 +1,40 @@
1#!/usr/bin/env bash
2# M0 end-to-end: build the C++ harness, start the Bare daemon, round-trip a small
3# AtomSpace through the UDS bridge, gold-check identity. Exits 0 on PASS.
4set -euo pipefail
5cd "$(dirname "$0")"
6
7# Short socket path: AF_UNIX sun_path caps at ~108 bytes, so NOT the scratchpad.
8SOCK="$(mktemp -u /tmp/m0-XXXXXX.sock)"
9DLOG="$(mktemp /tmp/m0-daemon-XXXXXX.log)"
10DPID=""
11
12cleanup() {
13 # `bare` is an nvm shim that execs a child runtime, so killing $DPID alone can
14 # leave the child. Also match the daemon by its unique (mktemp) socket path.
15 [ -n "$DPID" ] && kill -9 "$DPID" 2>/dev/null || true
16 pkill -9 -f "daemon.js ${SOCK}\$" 2>/dev/null || true
17 rm -f "$SOCK" "$DLOG"
18}
19trap cleanup EXIT
20
21# 1. deps + build
22[ -d node_modules/bare-net ] || npm install --silent
23g++ -std=gnu++20 -O2 -I/usr/local/include m0_roundtrip.cc \
24 -L/usr/local/lib/opencog -Wl,-rpath,/usr/local/lib/opencog \
25 -latomspace -lsexpr -latombase -latom_types \
26 -o m0_roundtrip
27
28# 2. start the Bare daemon, wait for its readiness line
29bare daemon.js "$SOCK" > "$DLOG" 2>&1 &
30DPID=$!
31for _ in $(seq 1 100); do
32 grep -q listening "$DLOG" && break
33 sleep 0.05
34done
35if ! grep -q listening "$DLOG"; then
36 echo "daemon failed to start:"; cat "$DLOG"; exit 1
37fi
38
39# 3. run the harness (build src AtomSpace -> UDS -> replay -> gold check)
40./m0_roundtrip "$SOCK"