The runtime that refuses to die.
HamsterBunker keeps an AI agent running when the machine under it dies. It checkpoints the agent's state (encrypted), replicates it, and runs the agent under a lease so exactly one node is active at a time. Kill that node and a standby in another region resumes from the last checkpoint.
Introduction
An agent you run today has a single point of death: one process, one host, one region. Any one of them ends it, and you find out hours later, mid-task, with whatever it was holding gone.
HamsterBunker turns that agent into something that survives. The core is small, real, and covered by a survival test you can run in ten seconds: it spawns three nodes across three regions, SIGKILLs the active one, and a standby resumes the agent from its last checkpoint with zero state loss.
Status
We write our docs from the code, not ahead of it. Here is exactly where things stand.
✓ Working today
- Encrypted checkpoints — AES-256-GCM, authenticated
- Pluggable store for checkpoints, heartbeats, lease
- Node runtime: agent loop + lease-based active/standby election
- Survival test: kill the active node, a standby resumes, 0 state loss
◷ Roadmap
- Production stores with atomic CAS (Redis, DynamoDB, etcd)
- Real cross-region deployment (demo runs nodes locally)
- Transport hardening — mTLS between nodes, optional Tor
- Threat detection beyond missed heartbeats
- Token / payments layer on Robinhood Chain
How it works
Every node runs the same loop. The lease decides who is active.
// each tick (default 400ms)
1. write a heartbeat // "I'm alive, in region X"
2. try to hold the lease // am I the single active node?
3a. just became active → restore the last checkpoint and RESUME
3b. just lost the lease → yield: become a warm standby
4. if active: step the agent, seal + publish the new checkpoint
The lease has a TTL (default 1.6s). The active node renews it every tick. If it dies, it stops renewing, the lease expires, and a standby acquires it within a tick or two and resumes. Every takeover bumps a monotonic epoch — a fencing token — so a resurrected old leader knows it was superseded and steps down instead of double-running the agent.
State never travels in the clear. Each checkpoint is AES-256-GCM sealed before it touches the store, so a checkpoint replicated to a peer in another region is useless without the network secret.
Quick start
The runtime is on npm: hamsterbunker.
npm install hamsterbunker
Or clone the repo and run the survival test — 3 nodes, kill the leader, watch it live:
git clone https://github.com/millennium-v/hamsterbunker
cd hamsterbunker/runtime
npm run demo
Expected tail:
active node: hbnk-3 (ap-south) · agent at count=7
!! killing active node hbnk-3 (SIGKILL) — the server is gone
↳ hbnk-2 (us-east) RESUMED at count=7 · epoch 2
✓ SURVIVED — agent resumed from checkpoint, 0 state loss.
Writing an agent
An agent is three functions. The runtime owns the survival; you own the logic.
import { Runtime, FileStore } from 'hamsterbunker';
const myAgent = {
id: 'price-watcher',
init() { return { seen: 0 }; }, // first-run state
step(state) { return { seen: state.seen + 1 }; }, // one unit of work → next state
};
const node = new Runtime({
nodeId: 'node-eu-1',
region: 'eu-central',
store: new FileStore('/var/lib/hamsterbunker'),
agent: myAgent,
secret: process.env.HB_SECRET, // the network key — same on every node
});
node.start();
Start the same code on three machines in three regions, pointed at a shared store, and you have a bot that refuses to die. Only one runs at a time; if it dies, another resumes from the last checkpoint.
API
new Runtime({ ... })
| field | meaning |
|---|---|
nodeId / region | identity of this node |
store | any backend implementing the store contract |
agent | { id, init(), step(state) } |
secret | shared network key, same on every node (≥ 8 chars) |
tickMs / leaseTtlMs | loop interval (400) / lease lifetime (2000) |
onEvent | hook: node.online, agent.started, agent.resumed, agent.step, agent.yielded, node.standby… |
.start() begins the loop. .stop() halts it and drops this node's heartbeat.
Store contract
Swap FileStore for any backend that implements:
putCheckpoint(agentId, sealed)/getCheckpoint(agentId)— atomic, last-writer-winsheartbeat(nodeId, region)/peers(staleMs)/dropHeartbeat(nodeId)readLease()/tryAcquireLease(nodeId, region, ttlMs)→{ ok, holder, epoch }
The included FileStore is correct for a single machine — that's what the demo uses. For real regions, back the lease with a store that has atomic conditional writes. The file version uses atomic rename + read-back, which demonstrates the mechanic but is not a substitute for real compare-and-set under contention.
The honest guarantee
What the survival test proves: with a shared store and a valid network secret, killing the active node results in a standby resuming the agent from its last checkpoint, with no state rolled back past that checkpoint. State loss is bounded by one tick — the work since the last checkpoint.
What it does not yet prove: correctness under a partitioned network, two nodes racing for the lease across machines against a non-CAS store, or real cross-region operation. Those need the production store backend on the roadmap. We'd rather say that plainly than ship a claim we can't back.
Roadmap
- Production store adapters (Redis / DynamoDB / etcd) with real CAS election
- Cross-region reference deployment + a public status page
- Node-to-node mTLS and an optional Tor transport
- Richer threat signals (provider flags, latency, degradation) beyond missed heartbeats
- A permissionless compute + payments layer settling in $HBNK on Robinhood Chain