Foundable: durable AI operator workers on Bun, Hono, and Postgres #35415
wesleyslin
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Disclosure: I'm a co-founder of Foundable. It is
currently in private early access. This is a first-party implementation note,
not a benchmark, customer result, or Bun endorsement.
Foundable helps people take an idea through validation, building, distribution,
pricing, and selling with an AI operator. Our API and background workers run on
Bun; Hono handles the HTTP surface, and Postgres holds the durable execution
state.
The main lesson has been simple: a fast runtime does not make an AI workflow
durable. The database has to own the work.
The job boundary
One operator turn becomes one durable job before the request returns. Its
minimal lifecycle is:
A worker claims eligible work with one conditional database update that:
queued(or stalerunning) torunning;to another execution lane.
Claim-sensitive settlement writes are fenced by the exact job ID, attempt
generation, and claim timestamp. A worker that wakes up late after losing its
claim cannot settle over the newer attempt.
Side effects need a second boundary
Job retries and tool retries are different failure modes. We keep a separate
operation receipt for side-effecting tool calls:
For replay-safe work, a completed receipt returns the stored result instead of
running the handler again. For an externally billed or otherwise ambiguous
operation, one contender must win a durable claim before the provider call.
If that worker disappears after the provider may have accepted the request,
the claim is reconciled rather than blindly stolen and replayed.
This is deliberately not described as blanket exactly-once execution. The
boundary only works where the application has a stable operation identity,
atomic admission, and a provider-specific recovery strategy.
Notifications are wakeups, not truth
Workers can use notifications to wake quickly, but they always reread durable
rows. Reconnects rebuild the current turn from stored events before following
new ones. Losing a notification changes latency; it should not lose the job.
One Bun-specific guardrail
Bun's automatic environment loading is convenient, but it also means a
checked-out
.envcan silently arm every script that reads it. Ourspend-capable development and evaluation commands therefore require two
independent signals:
The environment variable establishes that the checkout may spend; the
per-invocation flag establishes that this particular command may spend.
In those evaluation CLIs, provider-free modes reject
--live, and tests pinboth failure cases.
What I would like feedback on
We're keeping HTTP work and execution lanes in separate Bun processes as the
system grows, while sharing the same durable claim protocol. I would be
interested in how other Bun teams handle:
LISTEN/NOTIFYreconnects when routine queries use a transaction pooler;The patterns above are runtime-agnostic, but Bun has made the TypeScript worker
and test loop pleasantly small. Happy to share a more focused synthetic example
if one of these boundaries would be useful to the community.
All reactions