ETA open-source agent workbench

OPEN-SOURCE GROK BOT / MUSE · NO VENDOR LOCK-IN · 100% BYOK

Who holds the AI that knows you? Your machine. Your models. Your data.

Grok Bot says every agent gets its own computer — inside their datacenter. Muse reads your mail and photos; Today watches your calendar and meetings. All of them learned to know you somewhere else, and they remember you somewhere else too. eta is the open-source version of that: the one that lives at your house.

$ git clone \
    https://github.com/0xfe-project/eta.git
$ cd eta
$ bun run setup
$ bun run dev

Open http://127.0.0.1:3000. Data lives in ~/.eta — the same directory the installed one uses.

One data directory holds all of it: conversations, memory, tasks, usage. Copy it and you have everything.

Chat · Maintainer Idle
  1. You Checkout still has no e2e tests. Take it, and hand it back for review when it is done.
  2. list_projects · list_files checkout/
  3. draft_task #57 · mac-mini · new worktree
  4. tasks.confirmDraft #57 → queued
  5. Maintainer Done — #57 is open and I am on it in a fresh worktree on the mac-mini. I will open a PR and hand it back for review; merging is your click.
Tasks board
To do 1
#54 Tidy release notes maintainer
In progress 1
#55 Bump Caddy to 2.11.7 maintainer
In review 0
Completed 2
#57 Checkout e2e tests mac-mini
#56 CSV export for orders linux-box
Pending
  • #57 Checkout e2e tests In review · PR #214 ← Press it to continue ✓ PR merged; task completed
  • Release watch Routine · 1 new report
  • #55 Bump Caddy to 2.11.7 Draft · waiting for you
Events sample log
  1. 09:00:00.002 INFO routine finished name=release-watch report=1
  2. 09:41:02.114 INFO turn started agent=maintainer
  3. 09:41:03.004 INFO list_files project=checkout paths=42
  4. 09:41:04.870 INFO task drafted id=57 machine=mac-mini workspace=worktree
  5. 09:41:05.032 INFO task confirmed id=57 status=todo
  6. 09:41:05.410 INFO run started task=57 machine=mac-mini branch=eta/task-57
  7. 09:52:40.006 INFO pr recorded task=57 pr=214 tool=open_pull_request
  8. 09:55:12.771 INFO comment added task=57 by=you
  9. 09:55:13.020 INFO run started task=57 run=2 reason=comment
  10. 10:01:03.090 INFO run finished task=57 run=2 pr=214 updated
  11. 10:01:03.512 INFO task status=in_review task=57 pending=+1
  12. 10:03:20.418 INFO pr merged pr=214 method=squash head=unchanged
  13. 10:03:20.950 INFO task done task=57 worktree=kept branch=kept
Done
12/12

Session replay · scripted · time compressed · not live The replay stops wherever eta would wait for you.

Compare

Grok Bot, Muse and Today are all doing the same thing: moving into your life and acting before you ask. The only difference is how many apps each one plugs into. The cost is the same: the thing that knows you lives at their house.

Why not just use Grok Bot

same thing, two kinds of ownership
Grok Bot / Muse / Today eta
Where it lives Their datacenter. Stop paying and the team is gone Your machine, your own rack
Who answers Dynamic routing — you do not know who answered this time You decide. 100% BYOK: ChatGPT, Grok, Gemini, or the private model in your own rack
Memory A black box you have to trust One SQLite file you can read, change, delete, copy
What it did A summary handed to you Every step on the timeline: which file it read, which command it ran, which line it changed
Who sets the rules Price rises, rate limits, a ban — you take it None of that reaches your machine
Can you change it No The source is right there

Five opposites

  • Rented 24/7 or your own 24/7. They sell you a team by subscription and take it back when you stop. eta is a team you keep, because you installed it once.

  • It remembers you. Do you remember what it remembers? Memory is one SQLite file on your disk: readable, searchable, deletable line by line. Deleted is really deleted.

  • One subscription, or your own models. 100% BYOK: a subscription account, an API key, the private model in your own rack — all first class.

  • A black-box memory, or a file. Go offline, pull the plug, move house: copy one directory and it comes with you.

  • The day you get banned. A ban on their side makes your agents disappear. eta has no such switch — only you do.

Claude Code is good. It just is not yours.

the best coder, in a rented terminal
Claude Code eta
Session Close the terminal and it is gone Agents stay, remember the last turn, start on their own schedule
Models One vendor; they set the price and the limits 100% BYOK, including the box in your rack
Data Conversations and code flow into their service One SQLite file on your disk; copy it and you have everything

People who need 100% transparency and 100% control.

Willing to hand your life to an AI — not to one company.

  • People whose company will not let code leave the intranet.

  • People who have been rate limited, quietly model-swapped, or banned.

  • People who have to tell an auditor exactly what an agent read.

What it is

eta is a workbench for always-on agents. It runs on your own machine and you use it in the browser, on desktop or phone. Not a session — a team that stays.

  • Always-on agents Each has its own name, instructions, persona card and model. A restart loses nothing, and it remembers where the last turn stopped.

  • Group chats and a steward Several agents in one conversation; whoever you @mention answers, anything unmentioned goes to the steward.

  • Task board An agent proposes a task, you confirm it, the agent does it in its own session and hands it back for review.

  • Routines A short session on a schedule that leaves a report — and says nothing when there is nothing new.

  • Memory It remembers what was said; you can browse, search and delete. Memory is rows in a database, not a mood.

  • Artifacts Agents publish static pages: reports, charts, prototypes. Preview, share, revoke.

  • Your own models 100% BYOK: a subscription account, an API key, the private model in your own rack — all of it counts.

Fig 1. process: no cluster, no message queue
1
Fig 2. SQLite file holds conversations, tasks, memory, usage and settings
1
Fig 3. external services required — it starts without a network
0
Fig 4. of the source is in your hands, short enough to read
100%

Agents

Agents are who you talk to and hand work to: a name, instructions, a persona card, a model, and which bundles it may use. Not a session, and not a machine.

Each face grows out of the agent's own id: rename it and the face stays, add an agent and you get another. These ones are actually breathing.

One identity that lasts

global config

Name, instructions, persona card, model and thinking level live on it. Conversations, tasks and routines all use that identity — it does not become a different person each time.

Its own sessions

one per conversation

Every (agent × conversation) pair has a session that remembers where it stopped, found again by the same row after a restart. It runs in one working directory.

Group chats and a steward

wake budget ≤ 5 per message

Several agents in one room; only the @mentioned one answers, otherwise the steward. A woken agent gets only the lines it has not seen — never the whole history again.

Memory

one SQLite file

It recalls what is relevant before a turn and records what is new afterwards. Browse, search, add and delete in the UI; memory is a SQLite file in the data directory.

Artifacts

sandboxed · revocable

Agents publish static pages: reports, charts, prototypes. Share any version by link; revoking is permanent. Pages run in an opaque origin and cannot read your cookies.

Changes wait for the turn

next turn

Rename it, rewrite its instructions, swap its model or bundles: the reply being written finishes first, the next one uses the new settings.

Status

colour only ever reports
Idle no reply running
Working replying, or using a tool
Waiting for you stopped on a question or an approval
Model unavailable log in again or change the key; it continues once it recovers

Tasks

An agent proposes a task in the conversation; once you confirm it, the agent runs it — a worktree in the project, then back to you for review. You see every step, not a "done".

A task card carries

Assignee
the agent that proposed it — never handed over
Workspace
a new worktree (branch eta/task-<id>) or the project directory
Origin
results go back to the conversation that proposed it
Status
backlog, to do, in progress, in review, done, blocked, cancelled
Comments
saying something on the task wakes that same session once more

The one who writes, the one who reads

Hand it to an agent and have it hand the result back: every comment on the task wakes it once, in the same session. Nothing merges a pull request by itself, and nothing marks a task done by itself.

Tools it has inside the task session
· list_tasks
· get_task
· update_task
· comment_task
# report done and hand it back for review
· finish_task
# merging is a click you make, not it
· open_pull_request

Status

the yellow line is where you come in
  1. 01 Backlog
  2. 02 To do
  3. 03 In progress
  4. 04 In review
  5. 05 Done
  • Blocked
  • Cancelled

A session runs on its machine, as your user, with whatever the tools are allowed to do. Only attach machines you keep for work.

Routines

Something that does one thing on a schedule. There is no agent behind it — only a model, a brief and a clock. Each run either reports or says nothing.

Schedule

each with its own timezone
Daily 09:00 Release watch · read the upstream release notes
Mon 10:00 Weekly digest · collect the week’s public activity
Every 6 h Mentions on X · search for discussion of your project
0 */2 * * 1-5 Custom cron · every two hours on weekdays

Brief

  1. v1 Read the upstream releases each day and write a paragraph.
  2. v2 + only what affects self-hosting. ← your feedback
  3. v3 + breaking changes only, with links. ← your feedback

A handoff note

Each run leaves a note for the next one, and can read the runs and reports before it.

Feedback becomes the brief

Tell a report “breaking changes only” and that goes into a new version of the brief, from the next run on. Old versions can be restored.

It has edges

A run lasts at most 15 minutes, one at a time, and missed slots are not caught up — after a restart the clock starts from now.

Control

Control first. You can see what an agent is doing, stop it, change it. Anything that needs your OK waits as long as it takes.

A confirmation try it
Needs confirmation · Bash command Maintainer
$ git push origin fix/checkout-clock

rule confirm-push · from a bundle’s akhook.yml

✓ Allowed · this command only · valid 10 minutes · used once ✕ Denied · it was told not to route around it

Commands, file changes and tool calls all become the same card, answered in the chat or in Pending. The wait has no timeout.

Tool-call rules akhook
# built in, shipped with the source
- id: gh-pr-create
  on: shell_exec
  checks:
    - argv: [gh, pr, create]
  message: Push the branch, then open the PR with open_pull_request.

# brought by a bundle’s akhook.yml
- id: confirm-push
  on: shell_exec
  action: ask
  checks:
    - argv: [git, push]
  message: A push needs your OK.

Every shell command and every file change passes the rules first: either blocked with a better way to do it, or sent to you. Bundles can bring their own akhook.yml; the built-in layer cannot be turned off. This is a guardrail against slips, not a security boundary.

Pending

counted on the tab and in the page title

Everything waiting on you, in one place:

  • Questions and approvals from every session
  • Task drafts work it wants to start
  • Tasks in review or blocked waiting for your call
  • Failed runs the last attempt did not finish
  • Routine reports unread reports and failures

Cut in at any time

Steer the reply
change direction while it is writing
Stop everything
stop every reply in the room at once
/compact
compact the context and carry on

Bundles

A bundle is a directory: skills, MCP servers, hook rules, and optionally a service. Whatever an agent ticks, its sessions carry on whichever machine they run. No repackaging, no install scripts.

Bundles that ship

chosen per agent
github
git and gh as your GitHub App, pull requests and issues. Off by default; --github loads it.
built in
grok
Images and video with Grok Imagine, plus web and X search.
built in
oomol
The apps you connected in OOMOL, through the oo CLI: mail, calendar, docs.
built in
Yours
One directory: plugin.json, mcp.json, skills/, akhook.yml. Edit it and it applies, no restart.
One bundle a directory; edit it and it applies
my-bundle/
├── plugin.json       # name, description, requires, config, service
├── mcp.json          # stdio or streamable HTTP
├── skills/*/SKILL.md
└── akhook.yml        # tool-call rules
Skills
the bundle’s skills/ joins the session’s skill directory, discovered as usual
MCP servers
mcp.json is one config source; stdio processes start on the session’s machine
Hook rules
akhook.yml applies only to sessions of agents that loaded it
Config and secrets
AES-256-GCM in SQLite, handed only to that bundle’s own processes
Sources
built in, git (pinned to a commit), or a local directory — hot reloaded
Service
optional loopback child process, proxied by the host

Models

The models are yours. Pick one: link a ChatGPT (Codex) or Grok subscription with a device code, paste a Google AI Studio key, or point it at any OpenAI-compatible service — including the one in your own rack.

Model accounts illustrative
  • ChatGPT work available
  • ChatGPT personal failing for now · only this model cools down
  • xAI grok needs a new login
Usage per agent · kept 30 days
Maintainer 5 hours 24 hours 7 days
Device code no keys to copy and paste
WDJB - MJHT

Type this code on the provider’s page and eta has the login.

Only the failing model cools down

When one model fails on one account, only that model is parked; the account keeps working.

Refreshing costs nothing

“Refresh status” checks every account without spending quota.

State you can see

Needs a login, out of quota, failing for now — written next to the account. Replies continue once it recovers.

A private model is first class

Anything OpenAI-compatible in your own rack is an ordinary model here, not a fallback.

Architecture

Everything is in one process: the web, product data, running sessions, model credentials. No control plane, no message queue.

You Browser one WebSocket: /ws, or plain HTTP
WebSocket · the token gate turns on for non-loopback addresses
One process apps/eta
Web
the built static pages, served by itself
Product
agents · conversations · tasks · artifacts · routines
SQLite
eta.sqlite: conversations, tasks, usage, settings
Sessions
local sessions run inside the process
LLM gateway
a Go library loaded over ffi: subscriptions and keys
↓ SQLite · 文件 ↔ /machines/connect
One data directory $HOME/.eta, no flag to change it
  • eta.sqlite
  • sessions/ · agents/
  • worktrees/ · artifacts/
  • bundles/ · persona/
Other machines eta-daemon, a single file
  • sessions run over there
  • keys are made locally; the primary stores only a fingerprint
  • it only dials out to the primary
Memory one SQLite file
  • recall per conversation
  • no dependencies, works offline
  • on your own machine
Off this machine
  • OpenAI · xAI (your account)
  • Google AI Studio
  • Your own model service
  • GitHub (optional)

How a message travels

  1. 01 A message from the browser enters the process; it and the work it asks for are written into SQLite.
  2. 02 The session runs on whichever machine holds it, given only the lines it has not seen.
  3. 03 Model requests go through the gateway to your account or key — never through us.
  4. 04 Events come back to the process and out to the browser; every tool call stays on the timeline.

Boundaries

  • — The token gate turns on when the host listens beyond loopback; browsers use a cookie, scripts use Bearer.
  • — Previews and shares use signed or one-shot addresses, and the page runs sandboxed with no access to cookies.
  • — A daemon only trusts its own device credential; the primary never holds its private key.

Data

Conversations, tasks, memory, artifacts and agent workspaces all live on your machine, in one directory. The only thing that leaves is what you send out.

Stays on the machine

■
  • One directory: $HOME/.eta
  • eta.sqlite: conversations, tasks, usage, settings, bundle config (encrypted)
  • sessions/, agents/, worktrees/, artifacts/: sessions, workspaces, artifacts
  • Memory is a SQLite file too, in the same directory
  • Errors and logs stay on your machine; with no external service configured, nothing is sent
  • Delete an agent and the lines it wrote stay on the timeline

Leaves the machine

→
  • Model requests → the account you linked, or the key you pasted
  • GitHub: only repositories where you installed the App, and it is off by default
  • OOMOL: if you configured it
  • git-sourced bundles: the repository you pulled

Up front

  • Sessions run on the machine as your user, with whatever permissions the tools have.
  • Rules and approvals are guardrails, not a security boundary.
  • The token gate turns on for non-loopback addresses; behind a proxy, do not leave the port bare.
  • Bundle config is stored encrypted, but the process that loads it can read it.

Pages you share

Public link
bound to one version; revoking kills it for good
Private preview
a signed address, valid for an hour
Sandbox
CSP sandbox, no same-origin — it cannot read your cookies or storage

Deploy

One machine is enough. One process, one data directory, one SQLite file. Decide before you start which machine the data lives on.

  1. 01

    Run the process directly

    recommended
    git clone \
      https://github.com/0xfe-project/eta.git
    cd eta
    bun run setup
    bun run --cwd apps/web build
    HOME=/var/lib/eta bun apps/eta/src/main.ts \
      --web-dir apps/web/dist \
      --host 0.0.0.0 --port 8787

    The data directory is always $HOME/.eta — no flag moves it: change HOME instead. In systemd that is Environment=HOME=/var/lib/eta.

  2. 02

    It closes the door on its own

    token gate

    Listening beyond loopback turns the token gate on: every request except /up needs a token. Open /?token=<token> once in a browser and it sets a cookie (HttpOnly, SameSite=Strict, 30 days); scripts use Authorization: Bearer. Behind a proxy, pass --allow-origin with the page’s public address.

  3. 03

    Link your own models

    Settings → Model services

    A subscription account (device code), a Google AI Studio key, or any OpenAI-compatible service — pick one, or all three. A model you host only needs an address and a key.

  4. 04

    Add another machine

    optional
    # Settings → Machines → Add, copy the command it gives you
    eta-daemon setup <primary-url> <code>
    eta-daemon run
    eta-daemon install-service

    The daemon is a single file with no web and no database: sessions run there, product data and model calls stay on the primary. The code works once and expires in 24 hours; you approve the pairing after checking the fingerprint. install-service registers a systemd user service or a launchd agent.

What it needs

Machine
Linux (amd64 / arm64) or macOS; Windows through WSL
Runs as
one process plus the built-in model gateway
Data
one directory, $HOME/.eta by default
Not needed
Kubernetes, a message queue, object storage

FAQ

What is eta?

An open-source workbench for always-on agents that runs on your own machine: they remember, use tools, run on a schedule, and anything needing your OK stops and waits. Grok Bot, Muse and Today do the same thing — they just live in someone else’s datacenter.

Is there a hosted version?

No. eta runs on your machine and the data stays there. We do not touch your conversations, and we do not resell model capacity.

Can several people use it?

No. eta is a single-user service with no accounts or roles. It locks itself with a token gate that turns on when it listens beyond loopback.

Which models work?

Any. Subscription accounts (ChatGPT Codex, Grok), a Google AI Studio key, any OpenAI-compatible service — and a model you host just needs an address and a key. A model is provider:model-id, and the default provider is Anthropic.

How is this different from Claude Code?

Claude Code is a rented terminal: close it and it is gone, the models are theirs, the conversations flow into their service. eta is a team that stays, with your own models and your own disk. It is excellent at writing code — that part is not the argument.

Do agents merge code by themselves?

No. The pull request a task delivers waits for you to merge it on the task page; the head is checked first, the merge uses a method the repository allows, and no admin rights are used to bypass protection. The GitHub chain is off by default.

Is attaching another computer safe?

The daemon only dials out to the primary, its key is made locally and the primary stores only a fingerprint; you approve the pairing after checking it, and the code works once and expires in 24 hours. Sessions there run as your user, so only attach machines you keep for work.

How big a machine?

One process, a few hundred MB to start, plus whatever each session needs. No Kubernetes, no message queue.

Where is the data?

One directory, $HOME/.eta by default: one SQLite file holds conversations, tasks, memory, usage and settings; sessions and artifacts are files. Copy that directory and you have everything.

Principles

eta’s interface is built on these rules. So is this page.

  1. 01

    Control first

    Using it should feel like things are in your own hands. Calm is there only so nothing knocks you out of that state.

  2. 02

    Stay in flow

    The aim is to keep you in flow — and let you stay inside the work.

  3. 03

    Ablate

    Try removing every element. If nothing is lost in understanding or use, it doesn’t stay.

  4. 04

    Dashboard dense

    Use space like a car’s dashboard. Density is respect, not clutter.

  5. 05

    Right angles

    Square corners everywhere. Every colour goes through a variable; light and dark both.

  6. 06

    60ms linear

    Interaction transitions are 60ms linear: no trailing, no bounce. Reduced motion is respected.

  7. 07

    Plain words

    Agent and technical parts use everyday words so you know what’s going on. No piles of jargon.

  8. 08

    Finish it

    Polish means alignment, spacing, states, feedback and motion are all done — not a more “professional” skin.

Put your agents on your own machine.

See the deploy steps Source