</aethereum>
A shared brain for AI coding agents: contracts sync before anyone commits.
0.9.9
live on npm · CLI, hosted MCP and desktop app
115
npm downloads · week to 29 Aug 2026
6
primitives · the whole protocol fits in your head
degrades
silently · a dead room never blocks your agent
SPECIFICATIONS
| ROLE | FOUNDER |
|---|---|
| YEAR | 2026 |
| TYPE | CLI · MCP · DESKTOP · WEB |
| STATUS | LIVE · v0.9.9 on npm |
| STACK | typescript · mcp · a2a · pnpm · turborepo · tauri · rust · next.js +4 more |
| LINKS | [live ↗][github ↗] |
| AVAILABILITY | aethereum.dev |
“Teams running AI coding agents have no shared context across developers and machines.”
Aethereum is a coordination layer for AI coding agents. Agents on different machines, running different tools, publish intent and interface contracts into a shared room over MCP, so a teammate's agent sees a breaking change seconds after it is declared instead of at merge time. Live at aethereum.dev, shipping as aethereum@0.9.9 on npm with a Tauri desktop app delivered through four per-platform packages.
Teams running AI coding agents have no shared context across developers and machines. One agent changes an interface while another, on a different laptop and often a different vendor's tool, keeps building against the old shape. Nothing is committed, so git shows nothing, and the mismatch only surfaces at merge time after both agents have produced code against incompatible assumptions. The agent-team features shipping inside the tools themselves coordinate agents inside one session on one machine, which leaves the cross-machine, uncommitted-contract case open · and that case is the expensive one, because it is invisible until it is already a merge conflict spread across two branches.
Give the agents a shared room without changing how anyone works. `npx aethereum init` detects which agents are configured on the machine and writes the correct MCP config for each of eight tools, so developers keep their own editor and their own agent. Underneath, agents publish intent and versioned interface contracts into one room-scoped append-only ledger and read everything back through a single cursor-based context call, with a long-poll primitive for live push. The core is held to six primitives and 29 MCP tools, with the tool count pinned by a test rather than by prose. Everything else · the web dashboard, the desktop app, the skills marketplace, the A2A surface, the git contract gate · is a view over that ledger. Two hard constraints shape the rest: the MCP layer must never block or break the agent, so every handler degrades to a one-line note and never throws, and only what an agent explicitly publishes is stored, never source code, which is enforced by the shape of the wire format rather than by a setting.
role & context
Founder and sole builder. I own the product decisions, the code, the release pipeline and the npm identity.
Aethereum started from a specific friction on any team running AI coding agents: two developers, two laptops, two different agents, and no shared context between them. One agent changes a function signature while another, on a different machine, keeps coding against the old shape. Nothing is committed yet, so git has nothing to show, and the mismatch only surfaces at merge time after both agents have written a pile of code against incompatible assumptions.
Aethereum closes that pre-commit, cross-machine window. Agents publish intent and interface contracts over MCP into a shared room, and every other agent in the room sees the change plus a collision alert within seconds. Developers keep the agent they already use · Claude Code, Cursor, Codex, Windsurf, Cline, Zed, opencode, Gemini CLI · and `npx aethereum init` detects which of them are configured and writes the right MCP config for each, plus Claude Code hooks and a merged AGENTS.md. No new editor, no wrapper around the terminal.
The wedge is the part git and same-machine agent frameworks cannot reach. A vendor's own agent-team feature can coordinate several agents inside one session on one machine; it cannot see a contract that exists only in another developer's working tree, and it cannot gate the git commit of a different teammate's different-brand agent on a different laptop. `aethereum check` runs as a pre-commit hook and does exactly that, and the same check ships as a composite GitHub Action that runs `npx aethereum check` with no model call and no per-run cost.
At 0.9.9 it is four surfaces over one core: a hosted zero-install MCP endpoint, the `aethereum` npm CLI, a Next.js web app with the dashboard and docs, and a Tauri desktop app that hosts real terminals beside the live room view. The desktop app is the newest piece and the one that changed distribution the most · it ships as four per-platform npm packages declared as optionalDependencies of the CLI, so `npm i -g aethereum` puts a launchable 6.2 MB Aethereum.app on disk with no postinstall step and no network fetch.
Most of the hard-won engineering in this project is not in the coordination model. It is in the distribution and the release discipline around it, which is where the two worst incidents happened: eleven consecutive production deploys sitting in ERROR while I kept saying the site was deployed, and an afternoon spent on a macOS Gatekeeper problem that measurement showed did not exist.
Everything is a way of writing to, deriving from or rendering one room-scoped, append-only ledger. The core is deliberately held to six primitives · declarations, conversations over declarations, derived signals, sealed payloads, numbers, views · with an explicit test written into the architecture doc: which primitive is this feature, and if it needs a seventh, reject it or redesign it. The doc names the one place the test genuinely fails, gates, and flags a second as benign rather than smoothing either over. On the wire that ledger is a closed zod discriminated union of 23 RoomEvent variants, which is what makes the privacy claim structural instead of a policy: there is no variant that could carry source code.
The MCP surface is 29 tools registered unconditionally across 9 groups, rising to 34 only when room view, code sharing and session handoff are all enabled. That whole matrix is asserted in one test, because docs and marketing both cite the number and it had already drifted once. Reads ride a single rail: `get_team_context` returns teammates' intent, current contracts with versions and dependencies, changed-contract alerts, direct messages and pinned state, and it consumes a cursor so each event is delivered exactly once. `await_team_events` long-polls up to 28 seconds under the hosted function's 30-second budget, which gives cross-agent live push with no hooks and no tmux. Three MCP resources expose room state, contracts and presence. An A2A agent card advertises four skills mirroring the four core MCP tools one-to-one, though only `message/send` is implemented; streaming and task lifecycle are an open TODO.
The hosted rail is a Next.js 16 route handler that resolves a hashed bearer token to a room and member, rate-limits at 120 tool calls per minute per token, and records a per-tool audit row. Durability is Supabase Postgres with RLS, Realtime on the events, rooms, room_members and room_chat_events tables, and Postgres full-text search behind `search_memory`. RLS is not asserted against mocks: `packages/db` boots PGlite in-process, installs the minimum Supabase environment the policies depend on, applies every real migration file in order, and runs the policy tests under `set role` with JWT claims set, so the tests run against the exact SQL that ships.
The repo is a pnpm and Turborepo monorepo of six workspace projects: packages/{shared,cli,db}, apps/{web,desktop}, and an experiment package. The CLI is built with tsup and carries a forked local room server, Claude Code hooks, a tmux push bridge, a statusline, a `listen` delivery daemon, E2EE code sharing and session handoff, git worktree allocation and the pre-commit contract gate. The desktop app is Tauri 2 with real PTY terminals in Rust and a plain-TypeScript Vite frontend, and it renders the real web dashboard as a child webview rather than an iframe: the web app sends X-Frame-Options DENY on every path but /demo, and a child webview is a top-level browsing context, so the header does not apply and auth cookies stay first-party. The single most expensive build trap is written into both README and CLAUDE.md · the CLI and web app resolve the shared package through its dist build output, not its source, so the shared build must run before any test result is trusted.
Cross-machine contract sync before anyone commits
An agent declares an interface contract and the version bumps atomically through a Postgres RPC; every agent that depends on it gets a collision alert within seconds, on a different machine, in a different tool, with nothing committed. A no-op redeclare of the same shape raises nothing.
One context rail, 29 MCP tools
get_team_context returns intent, contracts, alerts, messages and pinned state, and consumes a cursor so each event is delivered exactly once. await_team_events long-polls up to 28 seconds for live push with no hooks and no tmux. The 29-to-34 conditional matrix is asserted in a test, not documented in prose.
The desktop app arrives with the CLI
Four per-platform npm packages are declared as optionalDependencies, so npm's os and cpu fields select one at install time and a 6.2 MB Aethereum.app is on disk before aethereum app runs. No postinstall, no download, no progress UI to get wrong. The dashboard inside it is a Tauri child webview rather than an iframe, which is what keeps auth cookies first-party.
Shared panes, redacted before they are encrypted
The CLI streams a tmux pane end-to-end encrypted with one ephemeral key wrapped per watcher; the desktop app shares its own portable-pty panes through the same proven pipeline rather than a second one. Sharing is an act, never a setting: a click plus a consent sheet, with a global Stop. --allow-control is permission to ask, not permission to type, and every remotely typed byte is logged locally before it is applied.
A contract gate at the git boundary
aethereum check installs as a pre-commit hook and flags staged files touching a contract that is mid-negotiation or frozen, plus mechanically checkable team rules. Fail-soft is non-negotiable: offline, unconfigured or erroring, it prints nothing and exits 0, so the service being down can never block someone's commit. The same check ships as a composite GitHub Action with no model call and no per-run cost.
Daemon, statusline and guarded autopilot
aethereum listen delivers teammate events live with nothing executing. The statusline reads Claude Code's piped stdin verbatim and appends collision alerts and pending approvals off a peek rail with a hard 2500ms abort, never exiting non-zero. Managed runs sit behind five local layers enforced on the operator's machine, and autopilot ships on by default only for auto tickets you wrote yourself: a teammate's ticket parks with evidence until you name them with `listen --enable --author`. The full run report stays on disk and the server sees a one-line summary, and the login service runs listen with no flags so it can never switch autopilot on.
A marketplace that executes nothing
A skill is a bundle of instructions whose only delivery mechanism is inclusion in the team's shared context. No webhook, no external delivery, nothing runs on anyone's server. Submissions are moderated, installs are team-scoped and read through RLS, and aethereum distill mines local sessions for candidates behind a consent gate.
DECISION 01
A successful git push is not a deployment, and I learned that from eleven consecutive production builds sitting in ERROR. The site served a stale build all day while every claim I made that day about having deployed was false. The mechanism: a web test imported a file from the desktop frontend, which is not a pnpm workspace member because the workspace glob is one level deep, and tsc resolves node_modules by walking up from the importing file, so the shared package was in none of the directories above it on a clean checkout. It passed on my machine because an earlier desktop install had left those node_modules behind. CI could not catch it, because the suite was green throughout and the failure only appears under `next build`'s type check, which nothing ran locally either. The first fix I tried was wrong and is recorded so nobody retries it: a tsconfig paths mapping satisfied tsc and then broke the runtime, because Turbopack honours those paths for bundling too. The shipped fix excludes that one test from the web type program, and the cost is stated plainly · the file loses its type check, and the honest structural fix is logged and still open. This was the second such outage; an earlier js-yaml ESM interop failure ran about 20 ERROR deploys and left production 17 days stale.
DECISION 02
Measure the premise before building the fix. I was about to build a guided macOS installer because I believed Gatekeeper would warn users on other Macs, so I measured a real `npm i -g aethereum` install first and the premise was wrong. The bundle carries no com.apple.quarantine, only com.apple.provenance, which is not a Gatekeeper gate, and the unidentified-developer dialog is driven by quarantine · npm is a tar extraction and does not set it. `spctl` does reject the ad-hoc bundle, but assessment only gates a quarantined launch, so the rejection is inert, and the kernel execs the app fine: it ran to the probe's own 8-second timeout and exited on SIGTERM, where a signature refusal would have been SIGKILL. Measuring did find a real defect, which is the point: the published tarball carries zero _CodeSignature entries, because Tauri ad-hoc signs at build time and npm's packing drops the directory, so `codesign --verify` fails. Notarisation is deferred with the trade written down so it stays re-decidable · it buys nothing for npm-only distribution and becomes mandatory the moment a direct download, a .dmg or a Homebrew cask ships, because all three are quarantined delivery paths. What shipped instead was an app-doctor with 9 tests whose central rule is a negative one: when the app is not quarantined, never blame macOS and never mention xattr.
DECISION 03
Publish order is load-bearing, and the lockfile exclusion takes bare names, which I only know because I measured both forms. The CLI lists the four platform packages as exact optionalDependencies, so publishing the CLI first makes a fresh install resolve a 404, and a pin without its lockfile entry fails `pnpm install --frozen-lockfile` with an outdated-lockfile error that cannot be fixed forward, because you cannot regenerate a lockfile for a package that 404s. That deadlock broke every workflow for hours. Separately, pnpm 11 rejects packages published in the last 24 hours as a supply-chain guard, and pnpm writes its own exclusions in name@version form · but only the resolver honours that form, and the separate lockfile verification pass ignores versioned entries. Measured both ways: versioned form fails the lockfile check on all four packages, bare names pass. The tradeoff is deliberate and narrow · the guard stays on for roughly 750 other lockfile entries and is relaxed only for the four first-party packages, which are published minutes before being pinned, and bare names mean a future bump needs no new line for anyone to forget. One more thing: npm read replicas lag writes, so a package can be published and still 404 for a reader, and pinning before all four resolved is what left three lockfile entries for four pins earlier the same day. So the registry gets polled with `npm view` until every package actually resolves before the lockfile is touched.
DECISION 04
Version the whole surface in lockstep, which at the 0.9.1 release meant repacking rather than rebuilding. A test requires every app package pin to equal the CLI version exactly, and the release workflow enforces the same rule per matrix leg, so a CLI-only patch cannot ship without its four siblings moving with it. The macOS launch diagnosis was stranded on main because 0.9.0 was already taken, and rebuilding four Tauri targets was not available at 100% of the GitHub Actions quota, where one four-platform run costs around 230 billed minutes and 3 of the last 60 runs succeeded. So the four platform packages were republished at 0.9.1 from their 0.9.0 tarballs, same bytes, only the version field changed. That is a real cost and I would rather state it than dress it up. Every guard ran before publishing, because npm versions can never be reused: the payload path must exist, exceed 500 KB (a hollow package publishes fine and leaves `aethereum app` silently dark with nothing red anywhere), and keep its executable bit through the tar round-trip, checked against CFBundleExecutable read from Info.plist rather than a guessed filename.
DECISION 05
Redact on the sharer's machine, in plaintext, before the frame is sealed, because end-to-end encryption cannot help here. Terminal sharing is the one place the product ships a whole screen rather than the contracts an agent chose to publish, and a screen is where credentials sit in the clear. The frame is sealed to the watcher, so a flawless handshake hands them any secret intact; the only place that can fix it is the producer. Frames therefore pass redaction first, then a cap, and the bounds were read off the server rather than guessed: the frames route allows 120 requests a minute while the producer's 120ms timer pushed about 500, and on the control route, which allows 30 a minute, that rate exhausted the allowance in about four seconds, so remote control could effectively never be granted. The shipped sender-side bucket sits at 90 frames a minute with a burst of 12, deliberately under the server's limit. The token bucket is sender-side, so a throttled tick defers output into the next frame · throttling costs latency, never content. The consent warning is generated beside the rule table so it cannot drift from what the code actually masks, including the admission that a shapeless secret, a bare password on its own line, cannot be caught. A measured hole is still open and written down: on the desktop path the stream scrubber releases a held partial line after 180ms of quiet while the pane producer ticks every 120ms, so a token split across a mid-line pause reaches the watcher verbatim with the sharer's masked counter reading zero. The CLI producer, which scrubs whole snapshots, is unaffected.
A green test suite is not a green build. Both production outages on this project were invisible to CI by construction, because the failure only appeared under next build's type check and the test script never ran a build. The lesson generalises past this repo: a gate that cannot observe the artifact users receive is not watching production, and neither is a successful git push.
Production telemetry found a bug that code review had not. /api/sync/presence returned 403 exactly 399 times in 24 hours and /api/sync/events 376 more, with beats spaced exactly 30 seconds apart. A metronome is a machine looping; ragged timestamps are humans. The cause was void fetch(...).catch(() => {}) never inspecting res.status, and a 403 is a resolved fetch, so the catch never ran and a permanently revoked token was indistinguishable from a working one at 2,880 requests a day per stuck project. Fail-soft is not the same as fail-blind, and the fix had to distinguish transient from terminal rather than treat every non-2xx as fatal.
A guard that asserts a false invariant is worse than no guard, because people trust it. One security docblock claimed the desktop webview held no durable private key, which terminal sharing had made false; two CI guards the same week grepped a directory that did not exist and printed OK having inspected nothing. Guards now assert a floor instead of trusting a zero exit: 50 executed tests before reporting green, and exit 1 when the build output they are meant to inspect is missing.
Holding the core to six primitives is worth more than any feature it refused. A tool-surface audit applied one rule (if the daemon, the hook or the diff could have produced the argument, it is not a tool) and deprecated five tools in place rather than deleting them, so no agent broke and the public count stayed at 29. Two of the five were later un-deprecated because the reasoning turned out to be factually wrong, which is the argument for writing the rule down where it can be re-checked.
Make the desktop frontend a real pnpm workspace member so the type-check exclusion can go, close the measured 180ms scrubber hole in terminal sharing, and put the web build into the preflight so a broken production deploy cannot go unnoticed again.
typescript · mcp · a2a · pnpm · turborepo · tauri · rust · next.js · supabase · postgresql · e2ee · npm
LIVE · v0.9.9 on npm · aethereum.dev