johnandersen777.bsky.social/atlibs-iroh
iroh transport for atlibs hosts: com.fedfork.exec.iroh.connect in Rust (atlibs-host) and JavaScript
0
0
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { existsSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "node:test";
import { OUTPUT_MAX, exchange, irohCall, irohHost, irohSession, sshArgs } from "./index.js";
// fake-bin/dumbpipe and fake-bin/ssh stand in for the real binaries (no network).
const FAKE = new URL("./fake-bin/", import.meta.url).pathname;
const T = "endpointabex5k47heqqkdykv2b27ydg75jjaxeucm2ej74nt34a7up6wqlkyaiaenuhi5dqom5c6l3von3tcljrfzzgk3dbpexg4mbonfzg62bonruw42zof4";
const hostKeys = [{ type: "ssh-ed25519", publicKey: "AAAAC3Bare" }];
const spawned = [];
const session = irohSession({ key: "/nonexistent/key", spawn: (cmd, args, options) => (spawned.push(args), spawn(join(FAKE, cmd), args, options)) });
const all = async (stream) => {
const got = [];
for await (const m of stream) got.push(m);
return got;
};
test("a raw alpn session streams stdout, takes sends on stdin, and ends with the exit code", async () => {
const s = await session({ ticket: `iroh://${T}`, alpn: "echo/1" });
const it = s[Symbol.asyncIterator]();
assert.deepEqual((await it.next()).value, { stdout: "ready\n" });
await s.send("hello\n");
const rest = [];
for (let n = await it.next(); !n.done; n = await it.next()) rest.push(n.value);
assert.deepEqual(rest, [{ stdout: "got hello\n" }, { exitCode: 0 }]);
await assert.rejects(s.send("late"), /session is closed/);
});
test("ssh sessions: output as it arrives, then the exit code; ssh's own failure fails the stream", async () => {
assert.deepEqual(await all(await session({ ticket: T, hostKeys, command: "uname -a" })), [{ stdout: "line 1\n" }, { stdout: "line 2\n" }, { stderr: "oops\n" }, { exitCode: 3 }]);
await assert.rejects(all(await session({ ticket: T, hostKeys, command: "fail" })), /ssh failed: Host key verification failed\./);
await assert.rejects(all(await session({ ticket: T, alpn: "fail/1" })), /dumbpipe exited 1: no route to endpoint/);
const files = spawned.flat().filter((a) => a.startsWith("UserKnownHostsFile=")).map((a) => a.slice("UserKnownHostsFile=".length));
assert.ok(files.length >= 2 && files.every((f) => !existsSync(f)), "each session's known_hosts dir is removed when it ends");
});
test("a shell session (no command) runs until closed; bad input is refused before anything runs", async () => {
const s = await session({ ticket: T, hostKeys });
const it = s[Symbol.asyncIterator]();
assert.deepEqual((await it.next()).value, { stdout: "$ " });
await s.send("hello\n");
assert.deepEqual((await it.next()).value, { stdout: "hello\n$ " });
s.close();
assert.equal((await it.next()).done, true, "killed: no exitCode, a clean end");
const stop = new AbortController();
const aborted = await session({ ticket: T, hostKeys }, { signal: stop.signal });
stop.abort();
assert.deepEqual(await all(aborted), []);
await assert.rejects(session({ ticket: T }), /no host keys/);
await assert.rejects(session({ ticket: T, hostKeys, command: "" }), /command must be a non-empty string/);
await assert.rejects(session({ ticket: "not a ticket!" }), /not an iroh ticket/);
await assert.rejects(session({ ticket: T, alpn: "bad alpn" }), /alpn must be/);
});
test("dumbpipe's 'using secret key' line never reaches the module; stderr is capped; a spawn failure has no exitCode", async () => {
const messages = await all(await session({ ticket: T, alpn: "secret/1" }));
assert.deepEqual(messages, [{ stdout: "ok\n" }, { exitCode: 0 }]);
process.env.PATH = `${FAKE}:${process.env.PATH}`; // the one-shot call spawns dumbpipe from PATH
const oneShot = irohHost({ key: "/nonexistent/key" });
assert.deepEqual(await oneShot({ ticket: T, alpn: "secret/1" }), { output: "ok\n" });
for (const run of [async () => all(await session({ ticket: T, alpn: "noisy/1" })), () => oneShot({ ticket: T, alpn: "noisy/1" })]) {
const error = await run().then(() => assert.fail("noisy/1 should fail"), (e) => e);
assert.match(error.message, /^dumbpipe exited 1: x+\nlast line$/);
assert.ok(error.message.length < 16500, `stderr kept: ${error.message.length} bytes`);
}
const missing = irohSession({ key: "/nonexistent/key", spawn: (cmd, args, options) => spawn(join(FAKE, "no-such-binary"), args, options) });
const got = [];
const failed = await (async () => {
for await (const m of await missing({ ticket: T, alpn: "echo/1" })) got.push(m);
})().then(() => null, (e) => e);
assert.match(failed?.message ?? "", /spawn dumbpipe .*ENOENT/);
assert.deepEqual(got, [], "no spurious exitCode -2 after the spawn error");
});
test("irohCall: com.fedfork.exec.iroh.connect with the host key; ssh needs the key, a raw alpn does not", async () => {
const stateDir = () => mkdtempSync(join(tmpdir(), "atlibs-iroh-"));
const key = join(stateDir(), "iroh_ed25519");
const ran = [];
const run = async (cmd, args, input) => (ran.push({ cmd, args, input }), { stdout: "fc-1\n", stderr: "", code: 0 });
const iroh = irohCall({ key, run });
const ssh = { input: { ticket: `iroh://${T}`, hostKeys, command: "hostname" } };
await assert.rejects(iroh(ssh), /set ATLIBS_IROH_KEY or put it at .*iroh_ed25519/);
assert.equal(ran.length, 0);
writeFileSync(key, "not a real key\n", { mode: 0o600 });
assert.deepEqual(await iroh(ssh), { output: "fc-1\n", exitCode: 0 });
assert.equal(ran[0].cmd, "ssh");
assert.equal(ran[0].args[ran[0].args.indexOf("-i") + 1], key);
// A raw ALPN exchange needs no ssh key.
const raw = irohCall({ key: join(stateDir(), "missing"), run });
assert.deepEqual(await raw({ input: { ticket: T, alpn: "atirc/irc/1", send: "PING\r\n" } }), { output: "fc-1\n" });
// review 3b #7: a peer that never closes is killed at the timeout (code null); what it sent comes back, flagged.
const slow = irohCall({ key: join(stateDir(), "missing"), run: async () => ({ stdout: "partial", stderr: "", code: null }) });
assert.deepEqual(await slow({ input: { ticket: T, alpn: "atirc/irc/1" } }), { output: "partial", timedOut: true });
// review 4a M3: one killed at OUTPUT_MAX says truncated (ssh and alpn), not timed out or failed
const flood = irohCall({ key, run: async () => ({ stdout: "x", stderr: "", code: null, truncated: true }) });
assert.deepEqual(await flood(ssh), { output: "x", truncated: true });
assert.deepEqual(await flood({ input: { ticket: T, alpn: "atirc/irc/1" } }), { output: "x", truncated: true });
});
// review 4a M2/M3: no user ssh config, no host env (ATPROTO_*, SSH_AUTH_SOCK) in dumbpipe or ssh, stdout capped.
test("children: ssh -F /dev/null, a cleared env (PATH, HOME, LANG, IROH_SECRET), at most OUTPUT_MAX of stdout", async () => {
assert.deepEqual(sshArgs("/k", T, "/kh", ["true"]).slice(0, 2), ["-F", "/dev/null"]);
process.env.ATPROTO_PASSWORD_TEST = "never-in-a-child";
try {
const keys = (await exchange("/usr/bin/env", [])).stdout.split("\n").filter(Boolean).map((l) => l.split("=")[0]);
assert.ok(keys.includes("IROH_SECRET") && keys.every((k) => ["PATH", "HOME", "LANG", "IROH_SECRET"].includes(k)), keys.join(" "));
const seen = [];
const spy = irohSession({ key: "/nonexistent/key", spawn: (cmd, args, options) => (seen.push(options.env), spawn(join(FAKE, cmd), args, options)) });
await all(await spy({ ticket: T, alpn: "secret/1" }));
assert.ok(!Object.keys(seen[0]).some((k) => k.startsWith("ATPROTO_")), "a session's child env");
} finally {
delete process.env.ATPROTO_PASSWORD_TEST;
}
const flood = await exchange("sh", ["-c", "head -c 10000000 /dev/zero; sleep 5"]);
assert.deepEqual([flood.stdout.length, flood.truncated, flood.code], [OUTPUT_MAX, true, null]);
const exact = await exchange("sh", ["-c", `head -c ${OUTPUT_MAX} /dev/zero`]);
assert.deepEqual([exact.stdout.length, exact.truncated, exact.code], [OUTPUT_MAX, undefined, 0], "exactly the cap is all of it");
});