the Solid take-home

Remote bash execution built to a resilience spec: Postgres as the only clock, failure as a client-side verdict.

solid-takehome is my solution to Solid's backend interview take-home: a distributed remote-bash-execution system in TypeScript with Effect — API servers, a CLI client, an executor daemon, Postgres. The repo is public, and I work at Solid now. It records no score.

The assignment is preserved verbatim in-repo, and it is a resilience spec: commands to 1KB, output to 1MB; survive latency, packet loss, and network, database, or server outages up to 60 seconds; the client exits failed within 120 seconds; no conceptual races — "we may add 100ms sleep statements anywhere." I committed a constitution of fourteen invariants in the first PR, before implementation, and worked a 10-PR roadmap with per-PR specs.

The load-bearing decision: Postgres is the only clock. Every lease and session expiry compares against NOW(); no machine clock is consulted; the schema gives sessions no status column, because connectivity is a predicate over timestamps; servers hold no state that correctness depends on. The alternative, in-memory server state, dies with the server. Timestamps do not.

Signaling splits along the same line. LISTEN/NOTIFY wakes workers as a best-effort hint; dropped notifications are tolerated because a tick-based catch-up fetch guarantees progress. Correctness comes from FOR UPDATE SKIP LOCKED claim transactions and clients replaying output from a cursor. Output is an append-only log with a per-attempt monotonic sequence number, so duplicates and reconnects are harmless by construction. The alternatives were per-chunk NOTIFY, or pure polling.

failed is a client-side verdict only. The 120-second detector lives in the CLI and resets only on genuine progress, and the database never records failed for a command a client gave up on. I built the alternative first, a server-side timeout column, and removed it as out-of-spec, migration and all.

Numbers: 29 commits, 2025-12-29 to 2026-01-04, 22 of them merged PRs; 113 test cases across 15 files, each vitest worker in its own throwaway Postgres schema; 5 runtime dependencies; 5 tables in 100 lines of SQL; 7 chaos scripts covering partition, packet loss, latency, DB outage, server restart, and executor kill; zero setInterval calls in src.

What is wrong with it. The repo's own coverage matrix marks 24 requirements "needs test," including the 1KB and 1MB limits. The constitution still forbids the client NOTIFY channel that PR #20 shipped. Chaos testing is manual scripts against live VMs, and there is no CI. The final commit, four days after the rest, removes pubsub timeouts that had been silencing streams — my own bug outlived the deadline.

The design held under iptables. The documentation did not keep up with it.