Skip to content

Latest commit

 

History

History
188 lines (180 loc) · 62.8 KB

decisions.md

File metadata and controls

188 lines (180 loc) · 62.8 KB

Decisions

This page lists the decisions of the operator (the owner of the project). The newest decision is first in each section.

  • Do not change a decision without the operator.
  • When a decision changes, edit its row and move the old text to "Superseded". Do not keep two versions as current.

Product

Date Decision
2026-11-05 Whatever is necessary, statically compiled (the operator: "we use whatever is necessary, including c, zig, modern rust, no backwards compat. our users & clients do not care about building the tool, they care about downloading & using it. the only rule is: everything must be statically compiled"; given to the podcrates session, and applied to podssh: "change it and avoid self sabotage"). C, zig and the newest stable Rust (edition 2024) may be used in each crate, the library crates included; no crate keeps a minimum Rust version for others' sake. The one rule of the build: each released binary is statically compiled. The checks that enforced the old rules (the gate's CC=/nonexistent and its steps at a declared minimum Rust) are retired by T-685, so that none blocks the crates of podcrates.
2026-11-05 No compatibility with the released binaries, for now (the operator: "we do not care about legacy clients or published binaries, we only did beta and only my agents tested them, so we are free to break and migrate / we can think about legacy and compatibility later"). The move to PodUtils (above) and the crates of podcrates may change what a released binary needs: the signer that podssh update accepts, the feed it reads, the pairing's AppID and each other name of the wire. No bridge release, no test against a released binary. Compatibility is a later decision.
2026-11-05 The repository moves to PodUtils/podssh, and the shared crates go to PodUtils/podcrates (the operator: "repo will migrate to: https://github.com/PodUtils/podssh / and many of the crates will be shared with other projects here, will go into https://github.com/PodUtils/podcrates"). podnet (formerly cfrs) moves to PodUtils/podnet. The names of the repository in the code and the documents follow the move (T-683); the crates of podcrates and their waves are Q101 (T-684). crates.io holds podssh, podnet and podvm for PodUtils (0.0.0, 2026-11-05).
2026-11-05 podbox is renamed podvm (the operator), and podvm is reserved on crates.io. The text of podssh names it podvm; dated records and the paths of the old project keep podbox.
2026-11-04 Q100: the testers' issues #126 to #148, the defect first (the operator). Each of GitHub #126 to #148 (Nemo-010, 2026-11-04) becomes an entry now. #126, a refusal of the relay role that names the address a target resolved to, is a P1 defect and goes first; the 22 features go by their priority, beside M10 and the entries of Q99 (Q85). A fork of policy that one meets is decided and listed for the operator to confirm (Q86).
2026-11-04 Q99: osewa's network and connectivity, at parity and beyond (the operator: "i want us to have everything they have network/connectivity wise + beat/exceed them", after an honest study first). Two studies, of the code of rjyo/osewa and of its paper (https://osewa.dev/paper), compare it with podssh; each gap where osewa has a network or connectivity feature that podssh lacks becomes an entry, with a way to exceed it. Those entries go right after the testers' defects, ahead of the rest of M10's order. Their live tests may reach osewa's public servers with short runs, where the servers' terms allow, as Q65 and Q91 allow.
2026-11-04 Q93 to Q97, confirmed (the operator, as each was recommended). Q93: the TURN road (T-621) opens the data connection that RFC 6062 needs, to the same server, only when the user names one with --turn, an exception of rule 2 of AGENTS.md. Q94: short live tests against smee.io, hostc, Tunnelmole and Sandhole's public server (T-623, T-624), with throwaway channels and tunnels given up at the end, once each research entry has read the service's terms. Q95: TURN is a road of the user's own server with a file of credentials, and the one-POST HTTP form is a form of the relay role's HTTP road, not forms of --forwarder (T-621, T-622). Q96: with PODSSH_RELAY_LAYER unset, proxy and ssh read the offer of the resumable layer of a relay host that the user named, once a day, 1 s at most, before the session's connection (T-173). Q97: before the race of a session on the default list, podssh asks the /relays.json of each relay member that the user's net names, once a day for each member, 5 s each and 8 s in all, and takes a list only by the member's own key (T-617). AGENTS.md, rule 2, names the exceptions of Q93, Q96 and Q97.
2026-11-03 Q90: the calls of Q86, each ruled (the operator, asked one by one). Confirmed as written in each entry's ## Decision: T-503 (each hop of -J checked by the direct road's rule), T-476 (a special file skipped with exit 0), T-505 (the algorithms that known_hosts records go first; russh's lists stay the base, ssh-rsa among them), T-470 (cp --delta's one remote command where there is no copy-data), T-471 (sftp's ls -n), T-563 (a strike only for a session that the host's side cut), T-560 (a reset inside WARP's tunnel is an answer), T-587 (proxy node:// is operator's pipe), T-590 (doctor dials the local sshd once), T-592 (a renewal keeps the tokens, with no cap; each failure 403), T-593 (a cache only where the uid owns it), T-594 (STATUS's release marks), T-595 (--carrier) and T-531 (OpenTunnel at the node when --opentunnel names it, and unasked for a node with no other road, as Q76 rules, which the operator kept when asked again; a late fallback at the client). Changed: T-574's binary ships a short list of public proxy nodes as a fallback when no list can be fetched, each node checked live before each release and dropped when it fails, the filters of T-574 kept (a node that fronts another party's name, or under a school's, a church's or a government's suffix, is never one); T-586's resumption gets a way off, --no-resume; T-589's commands take --timeout and PODSSH_TIMEOUT as other names of --ConnectTimeout, a bound on the open alone; T-596's exit over a carrier is taken by ssh, proxy and pipe too, each with --exit beside --carrier.
2026-11-03 Q89: podssh ranks relay hosts unasked only with RelayRank auto (the operator). By default podssh probes no relay host that the user did not ask it to: podssh relay rank measures on demand, and the default list uses a rank kept from it and the timings of each session's own race. With RelayRank auto, a session may rank in its background, at most once a day for each egress, 8 hosts, one probe each 10 s at most, each probe with ?dial=lazy so that no target is dialed: a bounded exception to rule 2 (one outbound connection), which the user asks for by the setting.
2026-11-02 Q87: v1.0.1 and the next beta go out unattended (the operator). Once the testers' defects are merged, v1.0.1 is tagged and published as soon as its checks pass (the gate green on its commit, the release workflow run by hand on it, the local checks of its binary, as for v1.0.0), and its published files are checked; then v1.1.0-beta.1 for the work of M10, at most one beta a day.
2026-11-02 Q86: overnight, a session decides a fork of policy and records it (the operator). Where a draft or an entry meets a question that changes a policy of this file, the session makes the defensible call, writes it in the entry's ## Decision and as a row "for the operator to confirm" in TODO/PROGRESS.md, and goes on; a call that the operator overturns becomes an entry of repair.
2026-11-02 Q85: the testers' features go too (the operator: "you may begin working on the rest of the items you were waiting for the go ahead"). The features of the testers' reports (GitHub #83 to #111, the bundle and the sweep) and the entries that Q74 to Q82 filed are done in the work order's turn, beside M10, after the defects.
2026-11-02 Q84: the testers' defects go now (the operator). The defects among the entries of the testers (GitHub #83 to #106, the bundle and the sweep of 2026-11-02) are done now, P1 first, then P2, and go into v1.0.1 with the checks of a release; their features followed by Q85. M10's entries go on beside them.
2026-11-02 Q83: the experience of an agent and of a human comes first (the operator: "podssh will be used by agents and humans, and better ux we can offer, better they like and prefer podssh over writing their own wrappers or using another tool"). The calls of the triage of #83 to #105 stay: a patched copy of russh (T-460), cp's files several at once by default (T-474), job --no-tmux (T-481), and health --preflight's brief listen at the far server (T-482); and each call of the triage of the bundle (T-501, T-503, T-504, T-506, T-507, T-510, T-512).
2026-11-02 Q82: roads over UDP where UDP passes (the operator). Hysteria 2, TUIC and MASQUE over HTTP/3 become client roads, tried only where a probe found that UDP passes, after the roads over TCP.
2026-11-02 Q81: a node with no iroh road keeps its pkarr record at n0's server too (the operator; T-445). A node of the quick tunnel or of the DERP road alone puts its record, which names only public names, at n0's pkarr server unasked; this widens Q57.
2026-11-02 Q80: the operator passes the sweep's defect of cfrs on (the operator). A session acts on this repository alone, as Q26 left podbox's pin to the operator.
2026-11-02 Q79: the repository names only the hosts that podssh uses (the operator). Of a sweep of public services, the repository and the binary name each host that podssh uses or tries, with its source, and keep the rest as classes and counts. A domain that looks taken over (a school's, a church's, a shop's, or a name made to look like one) is never used and never published.
2026-11-02 Q78: each service is a last fallback, whatever its terms (the operator: "use all as last resort"). A public service that carries bytes is tried after podssh's own roads and after the services that allow the use, even where its own terms forbid it (file hosts such as 0x0.st, hosts such as serv00), and the lists of public proxy nodes (VLESS, VMess, Trojan) are a last fallback; podssh tells the user that the target goes to a third party that nobody vouches for, and SSH and the end-to-end channel keep the bytes private. The session's own note at the ruling: a service run by one volunteer that asks not to be used so can close for each user.
2026-11-02 Q77: use the fallbacks that exist (the operator: "doing nothing or not using available fallbacks/relays/features is just making podssh useless in the exact scenarios it is being built for"). Overlay networks of other projects (NetBird, Firezone, ZeroTier, Tox, Yggdrasil, croc, HyperDHT, Cabal, Hyphanet) each get a research entry, for a host with TCP to 443 alone or where UDP passes; each that passes becomes a road, tried unasked after podssh's own. Nostr's ephemeral events and the trackers become meeting points of the pairing, in the same order.
2026-11-02 Q76: OpenTunnel, unasked, for a node with no other road (the operator; T-530 to T-532). One tunnel for each node, off with PODSSH_OPENTUNNEL=no; short live tests from GitHub Actions and the session's own host.
2026-11-02 Q75: the public Wisp servers that are made for it, as fallbacks (the operator). Beside Mercury Workshop's demo server, podssh tries the public Wisp servers that their operators run for that use, after the other forwarders; never a domain that looks taken over (Q79). Short live tests.
2026-11-02 Q74: Tailscale's main DERP fleet, after tailcat's (the operator). A client that is not Tailscale's may try the main fleet's servers after tailcat's, once a short test shows that they let podssh in; the test may reach them, with short runs.
2026-11-02 Q73: doctor runs its sections at once (the operator: "all at once, but make it configurable"). Each section starts at once by default; a flag and a setting limit how many run together, down to one at a time (T-511).
2026-11-02 Q72: scp and sftp take --timeout (the operator; T-508). An optional bound there, PODSSH_TIMEOUT as its default, never required; cp and mv keep theirs.
2026-11-02 Q71: the keywords of a restriction apply (the operator; T-505). Ciphers, MACs, KexAlgorithms, HostKeyAlgorithms, PubkeyAcceptedAlgorithms (and its old name), CASignatureAlgorithms and RequiredRSASize filter what podssh offers, in OpenSSH's +, - and ^ forms; a list that leaves nothing is refused before anything connects. The other keywords of a restriction that the user set get one note at the default level.
2026-11-02 Q70: a file's ProxyCommand for each host stays refused (the operator; T-502). Exit 78, as T-043 decided, and the refusal names -o ProxyCommand=none.
2026-11-02 Q69: the fallback to other products' forwarders follows only a failure where no relay host answered about the target (the operator; T-509, GitHub #106). It stays on by default (Q56, Q65), after a dial, TLS, a timeout, an upgrade, the relay's own 5xx or a refused token; never after the relay's 502 about the target or its refusal of a target by policy. Its line says what each forwarder gets; doctor marks it as not measured.
2026-11-02 Q68: the login name from the user database, last, and PODSSH_USER (the operator; T-500). PODSSH_USER names the login, then USER, LOGNAME and USERNAME, then the effective uid's entry of /etc/passwd (read as a file; getpwuid_r on macOS); a host with no entry goes on with a refusal that names -l, PODSSH_USER and USER.
2026-11-02 Q67: XHTTP over HTTP/1.1 may open its second connection (the operator; T-456). Another exception of rule 2 of AGENTS.md: tried only after HTTP/2 was refused, to the same server, and named by doctor --vless.
2026-11-02 Q63: podssh grows into a mesh of its user's own nodes, at parity with Tailscale, and the relay becomes a role of podssh itself (the operator; mesh.md, milestone M10). The operator's words: "having our own relay seems just passing the problem to someone else ... perhaps it is time, we went full on tailscale-like parity? introduce, exit mode, serve, funnel, etc; then it would make sense to have the relay in podssh itself". A node may lend its egress to its owner's other ends (exit mode, and routes to the networks that it reaches), share a local service with them (Tailscale's serve), and publish one at a public https:// name (funnel). The relay's role is podssh's own: a node may serve the relay's contract to its owner's ends, on a listener that the user asks for (Q1) or behind its quick tunnel with no listener, and may relay between two of its owner's ends. The entries that waited for the relay's operator are podssh's work, done in podssh's own relay role and on its client. tcp.ssh.relay.ajam.dev stays the default relay; a change of that deployment stays an item of relay-operator.md, and no entry waits for one.
2026-11-02 Q64: Cloudflare's terms are accepted (the operator: "for cloudflare, i have accepted the terms & risks, do not relitigate again"). The operator accepted Cloudflare's terms and their risks, for the relay, the quick tunnel, WARP and each other use of Cloudflare. A session does not raise them again, and writes no question and no risk of them. WARP's registration still asks its user (PODSSH_WARP_ACCEPT_TERMS=yes, Q59), as that acceptance is the user's own.
2026-11-02 Q65: public services with no account, tried unasked (the operator; carriers.md). The magic-wormhole transit relay of Least Authority (a carrier of a pair on the reverse road), the public magic-wormhole mailboxes (pairing by a short code, T-086), Piping Server (ppng.io, for a proxy that refuses the WebSocket upgrade) and the Wisp demo server (a forwarder's word after the three of Q56): each may be tried when the user did not ask, as Q50 allows n0's relays. So may each other public service that fits these needs, once its terms and limits are read and written in carriers.md. Their live tests are short runs, from GitHub Actions and from a session's own host.
2026-11-02 Q66: standard protocols, so that a server that a user runs is a road (the operator). podssh speaks, as a client, HTTP CONNECT to a second proxy over TLS (HTTP/1.1 and HTTP/2), VLESS over a WebSocket and over XHTTP, and wstunnel's protocol; and the others that the expansion of Q63 needs. Each is tested against the server of its own project.
2026-11-02 v1.0.0 now, and the expansion after it (the operator: "yes release it, i will have clients test it for bugs while you work on the expansion"). v1.0.0 is published from 35a0d19, where the whole release check passed (T-251); the testers test it while sessions build the expansion of Q63 (milestone M10). A defect found in it goes into v1.0.1.
2026-11-01 Q60: the Tor road's live test may reach the public Tor network (the operator; T-353). Short runs, from GitHub Actions and from a session's own host: one bootstrap of Arti, a few MiB of directory, then one stream to github.com:22, as Q52 allowed for WARP. The private network on the loopback (chutney) stays the test against software that podssh did not write.
2026-11-01 Q61: this binary started again at the user's request is no helper, and a detached run may ask systemd for a scope (the operator; T-380, T-381). --detach, ssh -f and session start start podssh again; rule 3 of AGENTS.md says that this is no helper. On Linux a detached podssh may ask the user's systemd, natively over D-Bus on its socket of this host, for a scope of its own once it is ready, so that logind's KillUserProcesses at a logout does not end it, as tmux does; a failure is a line, and PODSSH_SYSTEMD=no turns it off.
2026-11-01 Q62: the quick tunnel's node keeps a second connection to the edge (the operator; T-414). A fourth exception of rule 2 of AGENTS.md, for podssh node --quick-tunnel alone: a second standing connection to Cloudflare's edge, of the index 1, to the edge's other region first, so that a region in trouble or a GOAWAY leaves one connection standing. No new host and no listener.
2026-11-01 v1.0.0 may be published unattended (the operator). Once each workable entry is done (not those of the relay's operator, those that Q32 holds, nor T-328 and T-376, which wait for the operator's exports and throwaway cloud accounts and stay known gaps that the notes name), and the whole release check (T-251) passes on its commit, a session tags and publishes v1.0.0 (T-250) and checks the published files; v1.0.0-beta.4 goes out first, after 2026-11-02 01:44 UTC. Q32's holds stay, and stay out of v1.0.0.
2026-10-31 Q49: relays outside ssh.relay.ajam.dev, when they are compatible (the operator; T-346). A binary may use a relay of another deployment and another domain that speaks the relay's contract. Relays that their users bring (the relay template of #60) and a podssh that updates itself (T-375) make a signed list of deployments less pressing. A token goes only to the deployment that minted it.
2026-10-31 Q50: the iroh road in the release binaries, with n0's public relays and Tailscale's tailcat fleet as an automatic fallback (the operator; T-348, T-349, T-350). A node and a client with no pair may try them when the user did not ask, as a fallback of podssh node and ssh node://; the operator does not ask n0 or Tailscale first. The iroh road brings its C into the binary, as russh does.
2026-10-31 Q51: roads through a cloud's own tunnel, GCP IAP and AWS EC2 Instance Connect Endpoint (the operator; T-351). They need the user's cloud account and a token file. EICE's presigned URL may carry its credential, an exception to "credentials never in URLs" for that URL alone, which podssh never prints or logs. They are built against stand-ins; their live run waits for the operator's throwaway accounts (T-376).
2026-10-31 Q52: WARP's MASQUE road and Tor through Arti (the operator; T-352, T-353). Tor is a feature crate, podssh-tor, with the C of arti-client, as podssh-iroh is one. WARP's live test may run in GitHub Actions and on a session's own host.
2026-10-31 Q53: doctor listens once on each socket that it binds (the operator; T-358). It calls listen() with a backlog of 1 on 127.0.0.1 port 0 and on a Unix socket of mode 0600 in the temporary directory, accepts nothing, and closes each at once, so that a host that allows bind() and refuses listen() reads as one that cannot listen; never with PODSSH_LISTEN=no. Rule 2 of AGENTS.md, SECURITY.md and docs/architecture.md say so with T-358.
2026-10-31 Q56: the public WebSocket forwarders of #59, built in and as a fallback (the operator; T-369, T-370). The binary may carry templates of the three that #59 measured (Mudlet's Worker web-proxy.mudlet.org, its older copy, and ssheasy's ssheasy.com/p), each selected by a word, and may try them when each relay host fails, as Q50 allowed for n0's and tailcat's relays. Live tests against them run in GitHub Actions only, short. No relay token goes to a forwarder.
2026-10-31 Q57: a node's record of pkarr at n0's server, by default (the operator; T-350). A node whose home relays are n0's publishes a signed record of its home relay to n0's public pkarr server, at its start, when its home changes, and each 5 minutes, unasked, as Q50 lets it use n0's relays; a client whose ticket's relay does not reach the node asks for that record. PODSSH_IROH_PKARR=off turns it off, or names another server.
2026-10-31 Q58: tailcat's DERP fleet, in tests and as the unasked fallback (the operator; T-349, T-379). A session may run short live tests (exchanges of 64 KiB or so) against Tailscale's public tailcat DERP fleet, from its own host and from GitHub Actions. A node with no pair serves the DERP road unasked, as Q50's fallback; PODSSH_DERP=no turns it off.
2026-10-31 Q59: WARP in the release binaries once its live run passes (the operator; T-352). The live test may register a throwaway WARP device with Cloudflare, from GitHub Actions and from a session's own host (Q52); a registration needs PODSSH_WARP_ACCEPT_TERMS=yes, the user's acceptance of Cloudflare's terms. Once warp-live.yml passes, the feature warp joins RELEASE_FEATURES, as the iroh road did.
2026-10-31 podssh update (the operator; T-375). An explicit command that fetches the release for its own target through the proxy, checks the signed SHA256SUMS and the binary's sum (T-210, T-211), and replaces itself at once; it never runs on its own.
2026-10-31 Q48: podssh is a client first, and no legacy holds it back (the operator; T-333). Its clients run in the most restricted hosts there are: they cannot build podssh, and some cannot even download it, so it is uploaded or copied to them. podssh therefore never gives up a capability, performance or the health of the project for a legacy constraint that no one outside this project and its CI cares about, such as an older minimum Rust for an embedder: the library crates' minimum rises with what their dependencies need (Rust 1.89 for aes-gcm 0.11).
2026-10-30 Q47: podssh makes a quick tunnel itself (T-300). HTTP/2 only, in a module of podssh-relay, with the three roots that cloudflared adds beside podssh's own; the relay's road to the edge where TCP 7844 is not reached; its live tests in GitHub Actions only, as T-299's (Q43). The operator reads Cloudflare's terms as allowing a client other than cloudflared.
2026-10-30 Q44: a second connection for the split HTTP road (T-319). Where a probe finds that a proxy refuses the WebSocket upgrade, a session may run as one streamed GET down and a POST for each chunk up, two connections at once, on this road only; the serial form (one POST after another, one connection) is its fallback. It is the third exception of rule 2 of AGENTS.md (section 5). The relay's endpoints for it wait for the relay's operator.
2026-10-30 Q45: a cleartext road on port 443, last (T-326). Tried only after the TLS road failed, with a relay token minted for that one session and never cached, as each hop can read the header; doctor names the road that answered. podssh's own relay role serves the endpoint (Q63, T-326).
2026-10-30 Betas before v1.0.0 (the operator). Betas v1.0.0-beta.N are prereleases from main, so that the testers in real sandboxes, whose reports are the issues, test the current code. A beta is tagged when the gate (build.yml, its job all) passed on its commit, the release workflow passed by hand on it, and the local checks of the binary passed (T-309). The next beta follows a batch of repairs that the testers should test again, at most one a day. A beta does not wait for T-210, T-211, T-218 or T-251; its notes say what it does not have. A defect found in a beta goes into the next beta, and a tag never moves. v1.0.0 stays the final release (T-250), after the checks of T-251.
2026-10-09 A node that lost its socket connects again on 409 (Q31; T-261). It tries again with the backoff until the resume deadline of 10 minutes, so the sessions that the resumable layer keeps on it go on; a first registration still exits on 409. Rule 6 of reverse.md gives both cases.
2026-10-09 Cloudflare: a client first (Q33; T-264). podssh reaches a service that cloudflared or Cloudflare Access publishes, over a WebSocket through HTTPS_PROXY. A quick tunnel that podssh makes itself follows (Q47, 2026-10-30; T-300).
2026-10-09 A node has a plain mode (Q34; T-263). podssh node --plain carries each session's bytes as they are, with no resumable layer, for a client that does not speak it; the layer stays the default.
2026-10-08 Sessions run unattended, toward v1.0.0 (Q13 to Q28). A session works until the operator interrupts it, or until each entry of TODO/ is done except the entries that the operator holds, each test passes, and the release of the work (v1.0.0, then the release of milestone M10, Q63) is published and verified from end to end. It asks nothing and waits for nothing: it follows this page and each entry's Decision; else it makes a defensible call and records it in the entry. It corrects a row of this page whose facts its own work changed, with the old text under Superseded; a change of policy waits for the operator, and the entry is blocked with a question. It may use the live relay, railway.new, GitHub's SSH endpoint, the two tailnet hosts, the Podman box and GitHub Actions, with throwaway keys. AGENTS.md (section 2) tells how.
2026-10-08 v1.0.0, the final release, at the end (Q13 to Q18, Q29, Q30). Betas come before it (the row of 2026-10-30). v1.0.0 comes after every other entry except the relay's (T-250), and is tagged once: the check from end to end with no human (T-251) runs before the tag on the gate's artifact and a local build, and after the tag on the published files; a defect found then goes into v1.0.1. The check: checksums and Sigstore signatures; the commands of the README; doctor; proxy and ssh through the live relay; the reverse road; serve with vi, less and Ctrl-C in the box; cp of 200 MiB each way; a session that survives a killed relay connection; pipe. It ships macOS x86_64 and aarch64, Linux x86_64, aarch64, armv7 and riscv64 (static: x86_64 and aarch64 in the gate's image, armv7 with Alpine's own rust, riscv64 cross-compiled with cargo-zigbuild), and Windows x86_64 and aarch64, each built and run in CI (T-218). T-004 and T-005 are measured in the box; a run in the operator's real sandboxes follows the release. Confirmed 2026-10-09 (Q37): an unattended session tags and publishes it after the checks of T-251.
2026-10-08 A GitHub issue closes after its work (Q20). When each entry of an issue is done, the session comments with the commits and a short summary, and closes it. An issue that also has entries that wait for the relay's operator closes when its other entries are done; the comment names the entries that wait. Nothing is posted before the work is done.
2026-10-08 Chat on the roads first, and IRC as a second transport (Q21). podssh chat runs on the roads between two podssh ends, end to end encrypted (T-088), after M6 (T-099). The repaired IRC client (T-091 to T-098) is a second transport for public networks (T-252).
2026-10-08 podssh pipe exec:CMD is allowed (Q22; T-174). The user names the program, as with SSH_ASKPASS; the rule against helper processes is about podssh's own shims.
2026-10-08 Several relay sessions for one copy, when the user asks (Q23; T-141). A flag sets the number; the default is 1. Several channels in one connection come first.
2026-10-08 AF_UNIX and named pipes on Windows (Q24; T-176). podssh pipe probes each at run time, and refuses with the reason where it fails.
2026-10-08 podbox: the facade here, the pin later (Q26). The blocking facade of podssh-relay for podbox is built and tested in this repository (T-081), with Rust 1.88 if it needs that; podbox follows. Pinning it in podbox is an operator action after the release: no session changes another repository (T-085).
2026-10-08 A tty that podssh makes where /dev/ptmx is missing (Q11; T-248). Two routes, each after a probe at run time: a new devpts instance in a user namespace, which is a real pty (the operator: the boxes have no user namespaces), and a tty in user space: podssh serve answers its own child's tty system calls (seccomp user notification, or ptrace). The second is not a shim: it puts no code into the child, and it works with static binaries. LD_PRELOAD stays forbidden. The exit criterion of M5 keeps vi, less and Ctrl-C.
2026-10-08 A local listener when the user asks for it and a probe allows it (Q1). -L and -D (T-038), connection sharing (T-039), serve --listen (T-124), a local session daemon (T-186) and podssh agent (T-034) may listen when the user asks for them and a probe at run time shows that the bind works. The default address is loopback, or an AF_UNIX socket. The user can set the address, and can turn listening off. Where the probe fails, podssh refuses with a clear message, as now. Nothing listens until those entries are done.
2026-10-08 Three ways to try the relay hosts (Q10; T-220, GitHub #30), selected by a flag: a staggered start (the default: the next host starts when the first has not opened within a short delay), all hosts at once, or one host at a time with a shorter connect limit for a host that has a successor. The first success in the order of preference wins, and the other attempts close at once. So podssh can have more than one outbound connection for a short time; the session uses one.
2026-10-08 Agent forwarding (-A) is in the scope of podssh (Q2; T-036), off by default as in OpenSSH.
2026-10-08 The SSH agent and the credential helper are parts of podssh (Q5), not separate binaries: podssh agent (T-034), a credential helper (T-228), and hardware keys (T-229).
2026-10-08 No road depends on a program of another project (Q3: cloudflared, mosh-server). podssh makes their useful functions itself where that makes sense: a Cloudflare quick tunnel, which trycloudflare.com makes scriptable (T-170), and the functions of Mosh that M6 and T-160 do not give (T-171).
2026-10-08 The relay's tokens, and trust end to end (Q4, Q9). M4 keeps the relay's tokens, in a header or a 0600 file and never in a URL; the trust is end to end: the host key of the node and the allowlist of keys (T-087, T-107). Since Q63 (2026-11-02) the relay is also a role of podssh itself.
2026-10-08 podssh is a CLI (Q6). No GUI, no TUI, no emoji. The requests for a menu-bar app and for colored command blocks (reports in GitHub #24 and #25) are closed by this ruling, with no entry. The triage of an error by an AI is a hook of the CLI: podssh gives its report to a program that the user names (T-195).
2026-10-08 Devices and workloads are streams (Q7). A serial: address for podssh pipe (T-181); USB/IP as a tested recipe over podssh pipe, with no driver in podssh (T-182); a queue of detached remote jobs, with no GPU logic (T-197).
2026-10-08 The surface for agents comes after M3 and before M4 (Q8): T-049, T-050, T-012, T-052 and T-051 (GitHub #9 to #13).
2026-10-08 The way to a finished podssh (design.md). Milestone M3 comes first: relay failover, liveness, a fallback for no proxy and no DNS, doctor, keygen, and a run in a real sandbox. Then M4 (podssh-relay, the reverse road, podbox), M5 (podssh serve), and M6 (sessions that survive a drop). M6 builds two resumable layers, each the fallback of the other: podssh's own layer over the WebSocket relay, and iroh.
2026-10-08 iroh's relays are configurable, for connections between two podssh binaries. The default is n0's public relays until the operator runs an iroh relay on the operator's own Cloudflare account. Then the operator's relay is first and n0's relays are the fallback.
2026-10-08 The SSH client is russh with the aws-lc-rs backend, in the default build. The podssh binary links aws-lc.
2026-10-08 The repository is public. The git history was replaced by one commit (the old history is in a local bundle), and the old CI runs were deleted. Verified milestones are pushed without a question.
2026-10-08 Milestone 1 gives a usable default. The relay tcp.ssh.relay.ajam.dev is compiled in, and the user can override it. Mozilla's root store (webpki-roots) is the last trust fallback, after an explicit CA file and the system bundle.
2026-10-08 Tailscale is a feature that the user must select. podssh ts is the ts cargo feature, because the fork uses most of the build time, the memory and the binary size.
2026-10-05 Do not assume that the client has a privilege, a tool or a setup. The operator's words: "NEVER ASSUME OUR CLIENT HAS ANY PRIVS, TOOLS, SETUP." podssh probes what the host gives at runtime. What a host gives changes between sessions and images.
2026-10-05 Tailscale support (podssh ts, alias tailscale). "Vanilla" means a TUN device. The automatic chain is tun, then socks, then tcp, then relay. podssh ssh does not use the tailnet automatically in the first release. A wait for the relay's allowlist is used only on request; the default is to fail fast. A client-side DERP home pin is in the fork.
2026-10-05 Exit codes: 64 for a usage error, 78 for a configuration error (sysexits).
2026-10-04 IRC safety: podssh never executes something that it receives over chat, and never gets a peer's files on its own.
2026-10-01 No LD_PRELOAD, and no shims in other processes. A preload shim fails silently against a static binary and looks like an authentication failure. podssh supplies in its own process what the host does not have.
2026-10-01 The terminal must be usable after the connection. When no part gives echo, editing and history (no pty on either side, and no line discipline on the server), podssh supplies the line discipline. A server with no pty can have its own line discipline (podbox has one), so three inputs select the mode. See terminal.md.
2026-10-01 IRC is necessary. Two users in constrained hosts must be able to chat and share files. podssh supplies the protocol and the relay carries the bytes, as for SSH. Open items: the client sends plain text to public IRC servers, and the topology with no third-party server has no design yet.
2026-10-01, 2026-10-08 Tokens (Q12 and T-057 on 2026-10-08): one machine has one cached token for each relay deployment. The hosts of one deployment share it, and podssh never sends a token to a deployment that did not mint it (T-057). The cache is in the first usable directory of: a directory that the user names (T-243), the user's cache directory, the user's runtime directory, the temporary directory, the working directory. The temporary directory is TMPDIR; when it is not set, podssh resolves one at run time, and only then tries the fixed fallbacks (/tmp, /dev/shm). podssh probes each directory, and skips one that does not exist or refuses a write. The mode is 0600. If chmod fails, podssh continues.
2026-10-01 Command line: podssh with no subcommand never connects. chat is the IRC subcommand. The code generates the man page.

Work on the repository

Date Decision
2026-11-09 Priority: podvm, podnet and podssh first; cfb and githome wait (the owner, relayed by the podcrates session: "priority is first podvm, podnet, podssh projects, cfb & githome can wait"). The entries of cfb's asks (T-708 to T-731, COORD-014) stay filed and wait behind podssh's own work order: its defects, the issues of its testers (T-697 to T-707) and M10. Q105 (a cfb ask) waits with them and is not asked now. The new repositories of PodUtils (podrunner and the rest) get no work.
2026-11-09 Betas whenever they fit; no crates.io (the owner, PodUtils/podcrates docs/decisions.md, 2026-11-09, relayed by COORD-014, PodUtils/podcrates#38: "everyone can release betas whenever needed/fits, but no crates io release"). podssh may cut v1.1.0-beta.N again under the rules of a beta (AGENTS.md, "Releases"), before the migration ends; v1.1.0 itself still waits for the migration (T-446). podssh stays a beta until the owner calls it stable; publish.yml keeps crates-io = false (T-687).
2026-11-09 The owner's other rulings of 2026-11-09 that touch podssh (PodUtils/podcrates docs/decisions.md, relayed by COORD-014). pod-gsr is deferred, so GitHub #164 (the GSRN road) waits for it. Each new project gets its own repository: podkey is its own project, not podssh agent. podnet stays Linux only, so podssh serves each operator command on Windows and macOS, the Access TCP client included. No Cloudflare, Zero Trust or GitHub test account as a CI secret now: a live step that needs one waits, and its entry keeps offline tests and stand-ins.
2026-11-09 The product calls of the triage of GitHub #164 to #173 (the owner, 2026-11-09: "they are podssh product calls, not rulings: make them yourself"; made by the session, each recorded in its entry's ## Decision). doctor --tier reports the path probe of exec only, with no memfd probe (#166). The near-cap line of 80 % and 95 % is for sftp, not cp or scp, which rotate sessions (#167). serve's capability drop is an entry of its own that T-107 depends on (#168). relay serve takes limits above the compiled defaults, and the manual says that clients keep their own lower budgets; keepalive is not added to /relays.json, as that changes the pinned contract (#169). Each reader of an SSH peer's first line follows RFC 4253 section 4.2 (other lines before the version line, 4 KiB at most), with a read loop in place of T-363's one read, and no peek (#170). ssh node:// and --exit NAME refuse --relay-host and --relay-failover (64) and PODSSH_RELAY (78), and ignore a settings file's RelayHost with one -v line (#171). A cf:// session with the layer warns about the user's own keepalive interval and keeps it (#172). --quick wins over --full's edge probes; only the words change (#173).
2026-11-06 No release until the migration is done; podssh stays beta until the owner calls it stable; crates.io only after that, and only when asked again (the owner, answering Q102 and Q104). No v1.1.0 until each crate of PodUtils/podcrates that podssh shares is taken (T-684); the number of the next release is decided then. podssh stays a beta until the owner gives the go-ahead for stable. Once stable, a session asks the owner again whether podssh may publish to crates.io; until then publish.yml keeps its switch crates-io = false and no crate is let out (T-687).
2026-11-06 Q103: podvm takes the blocking facade from pod-relay (the owner). podvm takes pod-relay's feature blocking from PodUtils/podcrates by a tag, not a pin of podssh's revision; the test of two clients at once goes with the facade into pod-relay; T-085 keeps only its two boxes.
2026-11-06 T-689 closes as moot (the owner): the workspace builds on Rust 1.99, the gate's, and 1.97 is no target.
2026-11-06 A standing yes for COORD issues (the owner, answering podcrates' Q5). A podssh session may open and amend COORD-NNN: subject (to READER) issues in PodUtils/podssh, to talk to the sessions of podcrates and podnet when a message cannot go, and reads theirs through the read-only mirror (podcrates docs/maintenance.md, "Talk between sessions"), with no new yes each time.
2026-11-06 The migration session applies podcrates' patches; the next session does podssh's own work (the owner). The session that runs the migration takes each crate of PodUtils/podcrates on its release day. A session of podssh's own work leaves T-684 to it, changes a frozen module for fixes only (TODO/PROGRESS.md lists them), and does no work in a module that podssh took (Rule Q3).
2026-11-05 The lists and the measures of other parties' services go to a crate of PodUtils/podcrates (the owner, given to this session by the podcrates session: "they hardcode/measure many relays/third party services, it risks drift and staleness, so we move that code & related script here as a new crate"; T-684). Each list of hosts of another party that podssh holds (relay hosts and lists, DoH resolvers, STUN servers, DERP and iroh relays, piping, mailbox and ntfy servers, OpenTunnel and quick-tunnel hosts, Cloudflare and WARP endpoints, Tor's bridges and directory hosts, the others), each measurement or watch of them (code, scripts, the live and watch workflows) and the data files they read move to one new crate; the owner names it. Until podssh takes that crate, podssh adds no new hardcoded service of another party and changes these places for fixes only, each fix told to the podcrates session; podcrates imports from podssh's main, and podssh takes the crate on its release day. The crate is pod-services (the owner's name), one data file of one schema for each service, watch kinds as plugins. What moves, by the owner's ruling on podssh's inventory (PodUtils/podcrates a084f75, docs/research/services/podssh.md): the own relay's defaults (DEFAULT_RELAY_HOST, SEED, the ts relay, the limits of /relays.json and relay-limits.toml) as data, the rank, pool and health logic later to pod-relay; the facts of the probes (github.com:22 with GITHUB_KEYS, Let's Encrypt's directory) as data with a watch; the facts of quick/ (SRV, regions, names, the API host, cloudflared-pin.txt, cloudflare-roots.pem) as data that the protocol code reads. What stays: the release identity of podssh update; the cloud endpoints of the user's own accounts, with their features; the CI's supply chain; the relay contract's copy and relay-facts.toml go to pod-relay (T-047).
2026-11-05 The crates of PodUtils/podcrates: delivery, edits, names and publish (the owner, given to this session by the podcrates session's briefing of 2026-11-05; T-684). (Q1) podssh takes each crate by a git tag of PodUtils/podcrates, pod-x = { git = "https://github.com/PodUtils/podcrates", tag = "vX.Y.Z" }, with that URL in allow-git of deny.toml; not from crates.io. (Q3) From the import of a module into podcrates until podssh takes its crate, podssh changes that module only for fixes, and podcrates ports each fix; podssh takes a crate on the day of its release; after that a podssh session may change the crate in PodUtils/podcrates under podcrates' AGENTS.md with its checks green, or open an issue there. (Toolchain) Modern stable Rust, nightly where needed; podcrates is edition 2024 with rust-version 1.97, and the consumers move their toolchain to match. (Names) pod-* on crates.io is podcrates' alone; podssh's crates are podssh-*, each name reserved by the owner as a 0.0.0 placeholder before its first publish: an agent asks the owner and never publishes a new name with a token. (Publish) A workflow of its own, .github/workflows/publish.yml, apart from release.yml: on a tag v* (and by dispatch), permissions: {} at the top, the job in the environment crates-io with id-token: write and contents: read, rust-lang/crates-io-auth-action pinned to its commit and cargo publish with its token; no crates.io token is stored. Each run packs the workspace with cargo publish --workspace --dry-run; the publish runs only when [workspace.metadata.podssh] crates-io is true at the tag, which stays false until the owner rules that podssh publishes. The environment crates-io (deployable from tags v* only), the tag ruleset (a v* tag is never deleted or moved) and the read-only GITHUB_TOKEN are the owner's settings.
2026-11-04 Q98: v1.1.0-beta.2 now, and v1.1.0-beta.3 at the end of the night (the operator). The session of 2026-11-04 publishes beta.2 at once from the newest commit of main whose gate is green, and beta.3 with the night's work at its end, with the checks of Q88, even when both fall on one day by UTC: an exception, for that night, to "at most one beta a day".
2026-11-03 Q92: the entries on hold are released (the operator: "release all"). The holds of Q32 end: T-085, T-107 to T-113, T-117, T-118, T-213, T-222, T-245 and T-248 join the work order by their priority, M5's podssh serve among them, and so do T-328 and T-376, whose holds of 2026-11-01 end too; those two still wait for their inputs, which only the operator has (real exports of FinalShell and Termius under a license that the repository may hold; a throwaway GCP project and AWS account), and stay blocked on them alone.
2026-11-03 Q91: four more live test targets (the operator), each with short runs and throwaway keys or tunnels deleted after: the operator's SSH host of PODSSH_THROUGHPUT_SSH, for three minutes with nothing sent (T-463's silence on the relay's forward road); the services of Q77 and Q78 (Pinggy and the others that T-548 names); one onion service on the public Tor network, more than Q60's one bootstrap and one stream (T-578); and OpenTunnel from GitHub Actions each week (T-531's opentunnel-live.yml).
2026-11-03 Q88: move fast; the expensive checks wait until podssh is stable (the operator: "defer all expensive fancy checks etc after we are stable, right now focus on moving fast"). Until the operator calls podssh stable, an entry is done when its native tests pass and the gate (build.yml at the push) is green, and a release or a beta goes out when the gate is green at its commit, the release workflow passed by hand on it, and the short form of its binary passed (scripts/release-short.sh BINARY --net); after the tag, release-proofs.yml and update-live.yml. Deferred until then, each named in the ## Done it would close: the whole release check of T-251 before and after a tag (throughput, cp of 200 MiB, headscale here, the containers, IRC), the dispatch of each live and interop workflow at a candidate, the runs of plants.yml, planted defects in the build image, and live runs longer than a few minutes. A red gate is still repaired first; work does not wait on a gate that has not ended.
2026-10-31 The live relay is the operator's own: use it as much as the work needs (the operator). A live test of the relay (tcp.ssh.relay.ajam.dev, its pool and its loop) runs with its entry, as often and as large as the entry needs, here or in GitHub Actions; the bounds of Q42 and Q35 do not hold for it. Tokens are still minted and discarded in one shell, and no line prints one.
2026-10-31 Agents search and read the web through web.pkgforge.dev (the operator). A session's agent that needs the web uses the services listed at https://web.pkgforge.dev/llms.txt: it reads that page first, then the chosen service's own llms.txt (search, read, fetch, forge and the others), and no other way of searching.
2026-10-31 No immutable releases for v1.0.0 (the operator). GitHub's setting stays off; a tag never moves in any case.
2026-10-31 T-328 is a known gap (the operator). The import of FinalShell and Termius waits for an export that each program wrote and that is published under a license this repository can hold (Q41); v1.0.0 does not wait for it, and its notes name the gap.
2026-10-31 Q42: the relay's own loop is T-062's target (the operator). A forward session to tcp.ssh.relay.ajam.dev:443 carries a pair of the reverse road whose node sends 48 MiB, read slowly; podssh relay trace first shows that the relay dials itself. Here or in GitHub Actions, as often as needed: the live relay is the operator's own (2026-10-31).
2026-10-31 A headscale stand-in in place of the runs with the operator's tailnet (the operator; Q40 for v1.0.0). The live runs of podssh ts (T-100 to T-105, T-240, T-241) and the exit status of a login shell on a tailnet host (T-026) run against a local headscale with tailscaled in Podman. podssh ts may take a control URL, and the vendored fork a CA file beside its roots, TLS still checked (T-334). The runs with the operator's tailnet and its key in .env/ are not needed for v1.0.0; Tailscale's own control plane and DERP servers stay unmeasured.
2026-10-31 The throughput run's forward cell goes through the relay loop (the operator; Q38 for v1.0.0). The forward road's cell runs through the loop of Q42, at 100 MiB a direction, as a lower bound (its bytes cross the relay twice); the direct road's cell, which needs the operator's host, is named as not run.
2026-10-30 riscv64 is cross-compiled (the operator; T-218). cargo-zigbuild builds riscv64gc-unknown-linux-musl on a native runner, and the binary runs under QEMU in the release, as each target runs; the build under QEMU, measured past an hour, is dropped.
2026-10-30 Dependabot's updates reach main through a session (the operator). A session tests a pull request of Dependabot's; where it fails, the session finds why and repairs the code. It then commits the updates, with the repair, on main once the tests pass, and Dependabot closes its pull request. A session never merges or approves a pull request.
2026-10-30 Q46: GPL test data, beside its notice (T-327). A test file of another project under the GPL-2.0, written by that project's program, may be kept under crates/podssh-cli/tests/fixtures/, with its license and source in a notice beside it. No such file is linked into a binary.
2026-10-30 Cloud sessions (the operator). A session can run in a Claude Code cloud container. It works on main, never on a claude/* branch, and commits as the operator: the repository's own git identity is that of its newest commit by a person (scripts/cloud-setup.sh), and no co-author or session line is added (.claude/settings.json, attribution). The committed .claude/settings.json allows each tool with no prompt, as a cloud session cannot bypass permissions. sh scripts/cloud-setup.sh makes the container ready (T-310).
2026-10-30 Live tests from the session's own host, else GitHub Actions (the operator). A live test runs where the session runs when that host's network carries it; else in a job of GitHub Actions. The session measures podssh in its own host too: a cloud container behind its own proxy is a restricted host of its own kind (T-311).
2026-10-30 Q41: exports that their programs wrote, and that are published (T-292). Each format of podssh config import is tested on export files that the program itself wrote and that are published, each cited with its license. A format with no such file stays blocked.
2026-10-30 Q43: the quick tunnel's edge, measured from GitHub Actions (T-299). Short runs, a throwaway origin, and cloudflared as a reference only, never as a road. The operator decides T-300 after T-299's result.
2026-10-10 Longer throughput runs, and their targets (Q38; T-157, run in T-251). The live run of the throughput method may reach n0's public iroh relays, with about 500 MiB in each direction in each cell, for a measurement that means something; and a public SSH server of the operator's, named in PODSSH_THROUGHPUT_SSH where the run is made. Once podssh has the Cloudflare road (T-264, T-170), it is measured too. A run that long waits for the checks of v1.0.0 (Q35).
2026-10-10 The IRC client may be tested on public networks (Q39; T-091 to T-098, T-252). undernet, libera and OFTC, through the live relay: short runs, with a throwaway nick and a throwaway channel.
2026-10-10 Sessions use the files of .env/ (Q40; T-100 to T-105, T-240, T-241). A session passes a file of .env/ to podssh by its path, as the tailnet auth key to podssh ts --ts-auth-key-file, and never prints its contents; .env/ stays ignored by git.
2026-10-09 Live tests: short ones with each entry, long ones at the release (Q35). An entry runs the live tests of its Prove that take a few minutes and a few MiB. A live test of more than 5 minutes or 100 MiB waits for the checks of v1.0.0 (T-251), and the entry's ## Done says so; a live test of the relay does not wait (2026-10-31).
2026-10-09 A test that fails at random is repaired first (Q36). No quarantine: the session stays on it until it passes 20 runs in a row, and does other work only then (T-262).
2026-10-09 Each entry is tested natively until the release. An entry closes with the native tests (cargo test), python scripts/check-repo.py and cargo todo check. The other parts of its Prove (a run in the build image, sh scripts/dev.sh check, the interop against OpenSSH and Dropbear, a planted defect) wait for the checks of v1.0.0 (T-251), which run each of them; the entry's ## Done says what waits. CI still runs the gate on each push; a failed run may wait for T-251 too.
2026-10-08 The work record is the todo model of the operator's template (work-todo.md of Azathothas/TEMPLATE): TODO/PROGRESS.md holds the state and the only work order, TODO/INDEX.md each entry with the counts, and TODO/<area>.md the entries. Each GitHub issue, each open item of the roadmap and each known defect is an entry. The checker is Rust (crates/podssh-todo, cargo todo) and runs in the gate; no shell script is added for it. Nothing is closed as out of scope: a question of scope goes to the operator with a recommendation.
2026-10-08 The documents use ASD-STE100 (Simplified Technical English). They give the current state and procedures, not history; git keeps the history. The documents for agents send the reader to the exact document for each task (AGENTS.md).
2026-10-08 The box like the target sandbox uses Podman directly (scripts/test_in_box.sh). The build gate continues to use scripts/dev.sh.
2026-10-07 Repair a defect in the session that finds it. Do not defer it, and do not call it the work of another person.
2026-10-07 Redundancy and fallbacks from the first day are better than minimal code. Design for the first contact with a hostile host.
2026-10-07 Do not change the line endings of a file that a change does not otherwise touch.
2026-10-01 No source file has more than 500 lines. Split a longer file. Do not delete comments to make it fit. Documents are exempt. scripts/check-repo.py checks the Rust files under crates/ and the shell and Python scripts under scripts/.
standing Commits are attributed to the operator only. No co-author lines.
standing Agents never call wsl.exe, and never wsl --shutdown, --terminate or --unregister. These commands act on each distribution of the machine. Linux builds use sh scripts/dev.sh.
standing .tmp/ holds read-only copies of other projects. Do not change, commit or rebase in them. Make sure that the directory exists before you use it.

Superseded

Date Decision Replaced by
2026-11-06 In the row of the releases: "no beta until the migration is done" and "No v1.1.0-beta.N ... until each crate ... is taken". Betas whenever they fit (the owner, 2026-11-09). The rest of the row stays.
2026-10-08 In the row of the SSH client: "The rule "no C" applies to the library crates only: podssh-ws, podssh-relay, podssh-core, podssh-terminal, podssh-probe." With it, the library crates' Rust 1.89, for podvm (then podbox). Whatever is necessary, statically compiled (2026-11-05).
2026-10-30 In the row of Q45: "The endpoint waits for the relay's operator." podssh's own relay role serves it (Q63, 2026-11-02; T-326). The rest of the row stays.
2026-10-30 Q32 stays (the operator). The entries on hold stay on hold. Q92 (2026-11-03): the holds are released.
2026-10-09 Entries on hold (Q32). T-085, T-107 to T-113, T-117, T-118, T-213, T-222, T-245 and T-248 have the status blocked and a ## Blocker that names the operator. A session skips them until the operator lifts the hold, and the goal of AGENTS.md leaves them out, as it leaves out the relay's entries. Q92 (2026-11-03): each entry joins the work order by its priority.
2026-11-02 In the row of Q84: "their features wait for another go-ahead". The features go too (Q85, 2026-11-02).
2026-11-02 "The testers' entries wait for the operator's go-ahead": a session settles each open question first, makes each item an entry, and does none until the operator says go. The questions are settled (Q67 to Q83), and the testers' defects go now, their features later (Q84, 2026-11-02).
2026-10-08 In the row of Q4 and Q9: "The relay stays a separate project. Its contract is the boundary of podssh. A self-hosted relay (T-169), an endpoint that publishes a local HTTP service (T-180), and signed pairing grants that are used once (T-226, GitHub #35) wait for the relay's operator." The relay is a role of podssh itself, and those entries are podssh's work (Q63, 2026-11-02). The rest of the row stays.
2026-10-08 In the row "Sessions run unattended, toward v1.0.0": "until each entry of TODO/ is done except the entries that wait for the relay's operator (T-086, T-106, T-169, T-173, T-180, T-226, and each later entry that needs a change of the relay)". No entry waits for the relay's operator (Q63, 2026-11-02); after v1.0.0, the sessions build the expansion of milestone M10.
2026-10-08 "iroh is a road that the user must select". The iroh road is in the release binaries, and its public relays are an automatic fallback (Q50, 2026-10-31).
2026-10-31 In the row of Q42: "One short run, under 100 MiB, here or in GitHub Actions." Here or in GitHub Actions, as often as needed: the live relay is the operator's own (2026-10-31).
2026-10-09 In the row of Q35, for the relay: "A live test of more than 5 minutes or 100 MiB waits for the checks of v1.0.0 (T-251)". A live test of the relay does not wait (the operator, 2026-10-31).
2026-10-30 In the row "Q42 and Q32 stay": "T-062 waits for a target". The relay's own loop is T-062's target (Q42, 2026-10-31).
2026-10-30 "No immutable releases yet: GitHub's setting stays off; it is considered again before v1.0.0, after a test shows that the release workflow's upload works under it." No immutable releases for v1.0.0 (2026-10-31).
2026-10-08 In the row of v1.0.0: "Linux x86_64, aarch64, armv7 and riscv64 (static, where the gate's image family builds them)". armv7 with Alpine's own rust, as the gate's image has no arm/v7 (T-218), and riscv64 cross-compiled with cargo-zigbuild (the operator, 2026-10-30).
2026-10-09 In the row "Cloudflare: a client first": "A tunnel that podssh makes itself stays research (T-170)." podssh makes a quick tunnel itself (Q47, 2026-10-30; T-300).
2026-10-08 In the row of v1.0.0: "One release, at the end ... No beta, and no release between entries: a release run takes CI from the work." Betas v1.0.0-beta.N before v1.0.0, which stays the final release (the operator, 2026-10-30).
2026-10-08 In the row "The repository is public": "v0.1.0-beta.1 is tagged and released, with static Linux binaries and checksums, when M3 is complete." No tag of that beta remains on GitHub since the history was replaced; the betas are v1.0.0-beta.N (2026-10-30).
2026-10-01 In the row of the 500-line rule: "scripts/dev.sh (about 600 lines) is a known exception that must be split (T-207)." scripts/dev.sh is split in two, scripts/dev-wsl.sh beside it, and the size check reads the scripts (T-207, 2026-10-09); the rule is the same.
2026-10-08 The list of library crates in the row of the SSH client named podssh-transport too. The crate moved into podssh-relay, and is gone (T-082, 2026-10-09); the rule is the same.
2026-10-08 "The first beta waits for milestone M3", and a session publishes the beta v0.1.0-beta.1 after M3; a release candidate before v1.0.0. One release, v1.0.0, tagged once at the end (the operator, 2026-10-08: Q29, Q30).
2026-10-01 "One machine has one cached token. The cache is in the first writable directory of: the configuration directory, $TMPDIR, /dev/shm, the working directory." The code used the user's cache directory first. One cached token for each relay deployment (T-057), and the user's cache directory first, after a directory that the user names (the operator, 2026-10-08; T-243).
2026-10-08 "Light records: STATUS.md for the measured state, ROADMAP.md for the milestones in order, defects.md for the open defects, a short AGENTS.md, and topic pages." The todo model in TODO/ (2026-10-08). STATUS, ROADMAP (the milestones and their exit criteria), decisions, AGENTS.md and the topic pages stay; the rows of defects.md became entries.
2026-10-08 "CI runs only when the repository is public; until then the gate is local." The repository became public on 2026-10-08. CI runs the gate on each push.
2026-10-08 "Native SSH: evaluate russh first; the engine is open." russh with aws-lc-rs in the default build (2026-10-08).
2026-10-05 "Drop the no-C gate when the Tailscale adapter joins the workspace." Tailscale became the ts feature (2026-10-08). The same day, the no-C rule was limited to the library crates.
2026-10-05 Permissions for an earlier agent (unattended token mints, the operator's credentials and test machines, a commit and push for each change). Not standing permissions. Ask in the session that needs them.
2026-10-04, 2026-10-05 Work orders (relay, IRC chat, then SSH; E33, relay C1/C3/C4, E39, live IRC, E01). ROADMAP.md, 2026-10-08; the same day, the work order moved to TODO/PROGRESS.md.
2026-10-01 "There is no ssh on the sandbox." The probe of 2026-10-04 found /usr/bin/ssh. The rule of 2026-10-05: probe, never assume.
2026-10-01 "Use wsl-toolkit directly for testing." Linux builds use scripts/dev.sh; the sandbox box uses Podman directly (2026-10-08).