johnandersen777.bsky.social/atlibs-iroh
iroh transport for atlibs hosts: com.fedfork.exec.iroh.connect in Rust (atlibs-host) and JavaScript
0
0
#!/usr/bin/env node
// Reach socialweb.computer guests and atirc over iroh. No deps; shells out to dumbpipe and ssh.
// The host pseudo-call (irohHost) is js/index.js.
// node examples/iroh.mjs ssh <guest.json> <ssh-private-key> [command...]
// ATIRC_TOKEN=... node examples/iroh.mjs irc <text>
import { spawn, spawnSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { knownHosts, sshArgs, ticket } from "../js/index.js";
export const ATIRC_ALPN = "atirc/irc/1";
// The atirc token's `endpoint` claim is the server's iroh ticket.
export function atircTicket(token) {
const claims = JSON.parse(Buffer.from(token.split(".")[1], "base64url"));
return ticket(claims.endpoint);
}
function ssh(guestFile, key, command) {
const guest = JSON.parse(readFileSync(guestFile, "utf8"));
const dir = mkdtempSync(join(tmpdir(), "iroh-ssh-"));
try {
writeFileSync(join(dir, "known_hosts"), knownHosts("guest", guest.hostKeys));
return spawnSync("ssh", sshArgs(key, guest.iroh ?? guest.address, join(dir, "known_hosts"), command), { stdio: "inherit" }).status ?? 1;
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
// IRC over the atirc server's iroh endpoint: register, post one line, PING/PONG round trip.
function irc(text) {
const token = process.env.ATIRC_TOKEN;
if (!token) throw new Error("ATIRC_TOKEN is required");
const claims = JSON.parse(Buffer.from(token.split(".")[1], "base64url"));
let nick = claims.nick;
const channel = "#" + claims.handle.replace(/\./g, "_");
const pipe = spawn("dumbpipe", ["connect", "--custom-alpn", `utf8:${ATIRC_ALPN}`, atircTicket(token)], { stdio: ["pipe", "pipe", "inherit"] });
const send = (line) => pipe.stdin.write(line + "\r\n");
const t0 = Date.now();
const timer = setTimeout(() => { console.error("timeout"); pipe.kill(); process.exitCode = 1; }, 30000);
let buf = "";
send(`PASS ${token}`);
send(`NICK ${nick}`);
send(`USER ${nick} 0 * :${nick}`);
pipe.stdout.on("data", (d) => {
buf += d;
let i;
while ((i = buf.indexOf("\n")) >= 0) {
const line = buf.slice(0, i).trim();
buf = buf.slice(i + 1);
const [, cmd] = line.startsWith(":") ? line.split(" ") : [null, line.split(" ")[0]];
if (cmd === "PING") send("PONG " + line.split(" ").slice(line.startsWith(":") ? 2 : 1).join(" "));
if (cmd === "433") send(`NICK ${(nick += "_")}`); // nick held by another session (e.g. atirc sub)
if (cmd === "001") {
console.log(`registered ${nick} in ${Date.now() - t0}ms`);
send(`JOIN ${channel}`); // atirc drops PRIVMSG to a channel the session has not joined
}
if (cmd === "JOIN" && line.startsWith(`:${nick}!`)) {
send(`PRIVMSG ${channel} :${text}`);
send("PING :iroh-rt");
}
if (cmd === "PONG" && line.endsWith(":iroh-rt")) {
console.log(`posted to ${channel}; PONG round trip ${Date.now() - t0}ms`);
clearTimeout(timer);
send("QUIT :done");
pipe.stdin.end();
}
if (/^(ERROR|464|465)$/.test(cmd)) console.error(line);
}
});
pipe.on("exit", () => clearTimeout(timer));
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const [cmd, ...args] = process.argv.slice(2);
if (cmd === "ssh") process.exitCode = ssh(args[0], args[1], args.slice(2));
else if (cmd === "irc") irc(args.join(" "));
else {
console.error("usage: iroh.mjs ssh <guest.json> <key> [cmd...] | irc <text>");
process.exitCode = 2;
}
}