johnandersen777.bsky.social/atlibs-iroh
iroh transport for atlibs hosts: com.fedfork.exec.iroh.connect in Rust (atlibs-host) and JavaScript
0
0
// node --test tests/loopback.test.mjs (needs ../atlibs: `cargo wasm` and `cargo host`, or ATLIBS_BIN; no VM, no market, no account)
// The pre-commit iroh check: the real transport on this machine. A throwaway sshd
// (random 127.0.0.1 port, temp host key + authorized key, no system config) behind `dumbpipe listen-tcp`, and a
// TCP echo service behind a raw-ALPN listener; the echo module's `connect` (one-shot) and `session` (subscribe)
// entries through `atlibs run`, the Node CLI (atlibs javascript/src/server/cli.mjs with ATLIBS_TRANSPORTS=
// js/index.js) and `atlibs serve` POST /run, host keys pinned; and the iroh: fetcher through a relay to a local
// unix socket (js/fake-bin/relay.mjs standing in for `socialweb-guest-agent relay`). Skips (with the reason) when
// dumbpipe, sshd or ssh-keygen is missing. Full market rounds are only for requester / market flow changes.
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { createServer as createHttpServer } from "node:http";
import { createServer } from "node:net";
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir, userInfo } from "node:os";
import { join } from "node:path";
import { after, before, describe, test } from "node:test";
import { hostBin } from "../../atlibs/rust/host/conformance.mjs";
import { irohFetch } from "../js/index.js";
import { ROOT, cleanup, exec, missing, servers, spawnUntil, sshd, temps, which } from "../../atlibs/rust/host/loopback.mjs";
after(cleanup);
describe("iroh: dumbpipe + a throwaway sshd", async () => {
const absent = await missing("dumbpipe", "sshd", "ssh", "ssh-keygen");
const skip = absent.length ? `missing ${absent.join(", ")} (brew install dumbpipe; OpenSSH)` : false;
let env, ssh, alpn, bidder, hostKeys, served, bin, socket;
const stubs = [];
after(() => stubs.forEach((s) => s.kill()));
before(async () => {
if (skip) return;
const dir = await mkdtemp(join(tmpdir(), "atlibs-loopback-iroh-"));
temps.push(dir);
await exec("ssh-keygen", ["-q", "-t", "ed25519", "-N", "", "-C", "loopback", "-f", join(dir, "iroh_ed25519")]);
await writeFile(join(dir, "authorized_keys"), await readFile(join(dir, "iroh_ed25519.pub")));
const { port, hostKey } = await sshd(dir, "ed25519");
// The test's stand-ins: ssh with root@guest -> <user>@guest, and the guest agent's relay -> js/fake-bin/relay.mjs
// (stdin/stdout <-> a unix socket, run by the real sshd's shell, which parses the command's quoting) in front of
// js/fake-bin/http-stub.mjs; every other argument (-F /dev/null, ProxyCommand dumbpipe connect, the pinned
// known_hosts, StrictHostKeyChecking=yes) is what the hosts built.
bin = join(dir, "bin");
await exec("mkdir", [bin]);
const relay = `${process.execPath} ${new URL("../js/fake-bin/relay.mjs", import.meta.url).pathname}`;
await writeFile(join(bin, "ssh"), `#!/bin/sh\nfor a; do shift; [ "$a" = root@guest ] && a=${userInfo().username}@guest; case "$a" in "socialweb-guest-agent relay "*) a="${relay} \${a#socialweb-guest-agent relay }";; esac; set -- "$@" "$a"; done\nexec ${await which("ssh")} "$@"\n`, { mode: 0o755 });
socket = join(dir, "shell.sock");
const stub = spawn(process.execPath, [new URL("../js/fake-bin/http-stub.mjs", import.meta.url).pathname, socket], { stdio: ["ignore", "pipe", "inherit"] });
stubs.push(stub);
await new Promise((r) => stub.stdout.once("data", r));
const echo = createServer((socket) => {
socket.write("ready\n");
socket.once("data", (line) => socket.end(`got ${line}`));
}).listen(0, "127.0.0.1");
servers.push(echo);
await new Promise((r) => echo.once("listening", r));
const ticket = /endpoint[a-z0-9]+(?=\s)/; // the whole ticket: stderr may arrive split mid-ticket
const market = createHttpServer((req, res) => {
let body = "";
req.on("data", (d) => (body += d)).on("end", () => res.end(JSON.stringify({ method: req.method, url: req.url, auth: req.headers.authorization ?? null, body })));
}).listen(0, "127.0.0.1");
servers.push(market);
await new Promise((r) => market.once("listening", r));
[ssh, alpn, bidder] = await Promise.all([
spawnUntil("dumbpipe", ["listen-tcp", "--host", `127.0.0.1:${port}`], ticket),
spawnUntil("dumbpipe", ["listen-tcp", "--custom-alpn", "utf8:loopback/1", "--host", `127.0.0.1:${echo.address().port}`], ticket),
spawnUntil("dumbpipe", ["listen-tcp", "--host", `127.0.0.1:${market.address().port}`], ticket),
]).then((r) => r.map(({ match }) => match[0]));
const [type, publicKey] = hostKey.split(" ");
hostKeys = [{ type, publicKey: `${type} ${publicKey}` }]; // the vm.hostKeys form seen live
env = { PATH: `${bin}:${process.env.PATH}`, HOME: process.env.HOME, ATLIBS_SERVER_STATE: join(dir, "state"), ATLIBS_IROH_KEY: join(dir, "iroh_ed25519"), ATLIBS_TRANSPORTS: new URL("../js/index.js", import.meta.url).pathname };
const { match } = await spawnUntil(hostBin(), ["serve", "examples/rust/echo", "--port", "0"], /http:\/\/(127\.0\.0\.1:\d+)\//, { cwd: ROOT, env });
served = `http://${match[1]}`;
});
// dumbpipe's own stderr lines (its ephemeral client key, iroh's log lines) reach a session's stderr
// through ssh's ProxyCommand: different every run, so they are dropped before comparing.
const quiet = (result) => (typeof result?.stderr === "string" ? { ...result, stderr: result.stderr.replace(/^(using secret key [0-9a-f]+|\x1b\[2m\d{4}-.*)\n/gm, "") } : result);
// One input through the three hosts, one after another (parallel dumbpipe clients to one listener
// hung or lost the connection, dumbpipe 0.40.1); resolves the run's result, the same from each.
async function run(entry, input) {
const json = JSON.stringify(input);
const options = { cwd: ROOT, env, timeout: 30000 };
const rust = JSON.parse((await exec(hostBin(), ["run", "examples/rust/echo", entry, json], options)).stdout);
const js = JSON.parse((await exec(process.execPath, [join(ROOT, "javascript/src/server/cli.mjs"), "run", "target/wasm32-unknown-unknown/release/echo.wasm", "examples/rust/echo/module.json", entry, json], options)).stdout);
const serve = await fetch(`${served}/run/${entry}`, { method: "POST", headers: { "content-type": "application/json" }, body: json, signal: AbortSignal.timeout(30000) }).then((r) => r.json());
for (const [name, ran] of [["atlibs run", rust], ["cli.mjs", js], ["atlibs serve", serve]]) assert.equal(ran.error, undefined, `${name}: ${ran.error}`);
assert.deepEqual(quiet(js.result), quiet(rust.result), "cli.mjs vs atlibs run");
assert.deepEqual(quiet(serve.result), quiet(rust.result), "atlibs serve vs atlibs run");
return quiet(rust.result);
}
test("one-shot ssh: stdin to the command, its output and exit code", { skip }, async () => {
assert.deepEqual(await run("connect", { ticket: `iroh://${ssh}`, hostKeys, command: "cat; echo done", stdin: "secret\n" }), { output: "secret\ndone\n", exitCode: 0 });
assert.deepEqual(await run("connect", { ticket: ssh, hostKeys, command: "exit 7" }), { output: "", exitCode: 7 });
});
test("one-shot raw alpn: send, then everything until the peer closes", { skip }, async () => {
assert.deepEqual(await run("connect", { ticket: alpn, alpn: "loopback/1", send: "hello\n" }), { output: "ready\ngot hello\n" });
});
// No raw-alpn session here: real `dumbpipe connect` (0.40.1) exits on stdin EOF only, not when the
// peer closes, so a session's module must cancel it (fake-bin's dumbpipe exits; conformance.test.mjs).
test("ssh sessions: output as it arrives, send to stdin, the exit code", { skip }, async () => {
assert.deepEqual(await run("session", { ticket: ssh, hostKeys, command: "echo one; echo two >&2; exit 3" }), { stdout: "one\n", stderr: "two\n", exitCode: 3, error: null });
assert.deepEqual(await run("session", { ticket: ssh, hostKeys, command: "echo ready; read line; echo got $line", send: "hello\n" }), { stdout: "ready\ngot hello\n", stderr: "", exitCode: 0, error: null });
});
test("a host key that is not the pinned one fails as ssh's own failure", { skip }, async () => {
const other = [{ type: "ssh-ed25519", publicKey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMTlQfhHkM2ZD3wPKn0WSxVbbhgWMvVnLsVqH4NM8x4G" }];
const args = ["run", "examples/rust/echo", "connect", JSON.stringify({ ticket: ssh, hostKeys: other, command: "true" })];
const failed = await exec(hostBin(), args, { cwd: ROOT, env }).then(() => assert.fail("a run error exits 1"), (e) => e);
const ran = JSON.parse(failed.stdout); // the run is printed either way
assert.match(ran.error, /ssh failed: .*Host key verification failed/s);
});
test("iroh: fetch: HTTP over real dumbpipe, ssh and a relay to a unix socket, the relay kept alive", { skip }, async () => {
const fetch = irohFetch({ key: env.ATLIBS_IROH_KEY, spawn: (cmd, args, options) => spawn(join(bin, cmd), args, options) });
const url = (path) => `iroh:///${Buffer.from(JSON.stringify({ ticket: `iroh://${ssh}`, hostKeys })).toString("base64url")}/${encodeURIComponent(socket)}${path}`;
try {
const started = performance.now();
const first = await fetch({ method: "POST", url: url("/mcp"), headers: { "content-type": "application/json" }, body: '{"jsonrpc":"2.0","id":1,"method":"initialize"}' });
console.log(`iroh fetch (dial + ssh + relay + first request): ${(performance.now() - started).toFixed(0)} ms`);
assert.equal(first.status, 200);
assert.deepEqual([JSON.parse(first.body).body, JSON.parse(first.body).n], ['{"jsonrpc":"2.0","id":1,"method":"initialize"}', 1]);
const chunked = JSON.parse((await fetch({ url: url("/again?chunked=1") })).body);
assert.deepEqual([chunked.url, chunked.n, chunked.conns], ["/again?chunked=1", 2, 1], "the same relay, kept alive");
} finally {
fetch.close();
}
});
test("iroh: fetch to a market endpoint: HTTP/1.1 over raw dumbpipe connect <ticket> (no ssh, no key), kept alive", { skip }, async () => {
const fetch = irohFetch({ key: "/nonexistent" });
try {
const sent = { method: "POST", url: `iroh://${bidder}/xrpc/com.publicdomainrelay.temp.market.submitRfp`, headers: { authorization: "Bearer jwt1", "content-type": "application/json" }, body: '{"a":1}' };
const first = await fetch(sent);
assert.deepEqual([first.status, JSON.parse(first.body)], [200, { method: "POST", url: "/xrpc/com.publicdomainrelay.temp.market.submitRfp", auth: "Bearer jwt1", body: '{"a":1}' }]);
assert.equal(JSON.parse((await fetch({ url: `iroh://${bidder}/xrpc/a.b.c?x=1` })).body).url, "/xrpc/a.b.c?x=1");
} finally {
fetch.close();
}
});
});