~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-iroh

iroh transport for atlibs hosts: com.fedfork.exec.iroh.connect in Rust (atlibs-host) and JavaScript

0
0
main
116 lines · 6.0 KB Raw History

atlibs-iroh

The iroh transport for atlibs (knotless://johnandersen777.bsky.social/atlibs) hosts: it serves com.fedfork.exec.iroh.connect, so a module can reach a VM over an iroh ticket. It dials with dumbpipe: an ssh command on the guest, or a raw ALPN exchange; with subscribe: true it is a session (output arrives as messages, send writes stdin). It also provides the iroh: scheme fetcher behind com.fedfork.exec.http.fetch: HTTP/1.1 to a unix socket on the guest (the guest agent's shell MCP service) over ssh root@guest socialweb-guest-agent relay <socket>. iroh://<ticket>/<path> (a market endpoint, e.g. a bidder's offering endpoints) is HTTP/1.1 over dumbpipe connect <ticket> itself: no ssh, no key; atlibs hosts send module calls with endpoints there with service auth. The host's ssh key never enters wasm (ATLIBS_IROH_KEY, else <state>/iroh_ed25519).

  • src/lib.rs: the Rust transport (atlibs_iroh::transport), on atlibs-host's seam
  • js/index.js: the same transport for Node hosts; js/fake-bin/: stand-in dumbpipe, ssh, guest relay and HTTP service for tests
  • modules/iroh-user-data: an atlibs module, cloud-init for a guest that serves ssh over iroh (dumbpipe in front of sshd); computer.socialweb.vm.irohUserData
  • examples/iroh.mjs: reach socialweb.computer guests and atirc from a shell
  • tests/: both atlibs CLIs through this transport
  • lexicons/com/fedfork/exec/iroh/connect.json: the connect call's schema

Set up

Clone atlibs next to this repo; the JS side and the tests import it from ../atlibs:

git clone https://knotless.dev/johnandersen777.bsky.social/atlibs-iroh
git clone https://knotless.dev/johnandersen777.bsky.social/atlibs && git -C atlibs checkout -q 88b2ba8
brew install dumbpipe   # for real dials and tests/loopback.test.mjs

Build

The crate depends on atlibs-host by git (Cargo.lock pins the commit):

cargo build --release -p atlibs-iroh   # the transport
cargo wasm                             # modules/* -> target/wasm32-unknown-unknown/release/*.wasm

Link it into a Rust host

atlibs-host at the rev this crate takes it at (the same full rev, so cargo builds one copy):

[dependencies]
atlibs-host = { git = "https://knotless.dev/johnandersen777.bsky.social/atlibs", rev = "88b2ba88a22946a104c28c283bd0e9ecf26dec1f" }
atlibs-iroh = { git = "https://knotless.dev/johnandersen777.bsky.social/atlibs-iroh", rev = "0aae189c258e0e72ef1b170cf73b79ef4c6228c9" }

Register it with the host's other transports, as the atlibs CLI does behind its iroh feature:

transports.push(atlibs_iroh::transport(state_dir.join("iroh_ed25519")));

A host that takes atlibs-host from a path (atlibs itself) must point this crate's copy at it too, or cargo builds two atlibs-hosts:

[patch."https://knotless.dev/johnandersen777.bsky.social/atlibs"]
atlibs-host = { path = "host" }

Use it from Node

npm install @publicdomainrelay/atlibs-iroh @publicdomainrelay/atlibs (Node >= 25, and dumbpipe on PATH), or use a checkout. The atlibs Node CLI loads transports named in ATLIBS_TRANSPORTS:

cd ../atlibs && cargo wasm
ATLIBS_TRANSPORTS=../atlibs-iroh/js/index.js node javascript/src/server/cli.mjs run \
  target/wasm32-unknown-unknown/release/echo.wasm examples/rust/echo/module.json session '{"ticket":"<an iroh ticket>"}'

Fetch from a guest

iroh:///<ref>/<socket>/<path...>, where ref is base64url of the JSON {"ticket", "hostKeys": [...]} and socket is shell (/run/socialweb-guest-agent/shell.sock, the guest agent's MCP service: POST .../shell/mcp) or a percent-encoded absolute unix path. Nothing in the URL is a secret. The request goes as HTTP/1.1 over ssh root@guest socialweb-guest-agent relay <socket> through dumbpipe, with the host keys pinned as for connect. Each relay carries one request at a time and stays open (HTTP keep-alive) for the next request to the same (ref, socket); concurrent requests open more relays (a long-poll read holds one). A request whose relay fails before any response byte arrives is retried once on a fresh relay. Responses come back whole, chunked bodies decoded, at most 16 MiB.

  • Node: fetchers({ env, stateDir }) gives { "iroh:": async ({ method, url, headers, body, signal }) => ({ status, headers, body }) }, ready to spread into runModule's fetchers. Idle relays never keep the process up; fetchers()["iroh:"].close() ends them.
  • Rust: the transport's call answers Call::schemes() (["iroh"]) and Call::fetch(), so atlibs_host::transport::fetchers puts it in run::Http::schemes (the atlibs CLI does, for iroh://* in ATLIBS_HTTP). Its relays end with the transport.

A module reaches it through com.fedfork.exec.http.fetch once its http list and the host allowlist both accept iroh://*.

Run the example

atlibs run bobvmbuilder.bsky.social/iroh-user-data iroh_user_data "{\"ssh_key\":\"$(cat ~/.ssh/id_ed25519.pub)\"}" | jq -r .user_data   # cloud-init
node examples/iroh.mjs ssh guest.json ~/.ssh/id_ed25519 uname -a

Test

(cd ../atlibs && cargo wasm && cargo host)   # echo.wasm and the atlibs CLI (or set ATLIBS_BIN)
cargo test --release -p atlibs-iroh && cargo wasm
node --test js/*.test.mjs examples/*.test.mjs tests/*.test.mjs modules/*/test.mjs

tests/conformance.test.mjs runs both CLIs with js/fake-bin on PATH, and http.fetch to an iroh:/// URL on both engines; tests/loopback.test.mjs dials a throwaway sshd through real dumbpipe (it skips, saying why, without dumbpipe, sshd or ssh-keygen). The Rust CLI in these tests is atlibs' cargo host build, which links this crate.

Publish

js/ is @publicdomainrelay/atlibs-iroh on npm (npm publish --access public; the tests and js/fake-bin/ stay out of the tarball). It shells out to dumbpipe (and ssh), which must be on the host's PATH. The crate is not on crates.io: hosts link it by git. modules/*: atlibs publish --dir modules.