Agbrte  ·  design concept

The window is not where the work lives

Agent Bridge Terminal, said /ɛɡɯbɯɾɯtʰɯ/ or /ˈæɡbərt/ — a contraction rather than a word, so there is nothing here to get wrong.

An agent workbench built around one decision: a session belongs to a process that owns its log, not to the interface watching it. Everything else in the system is a consequence of that.

Close the app and the run continues. Open it on another machine and you are in the same session, mid-turn. The transcript is the truth, not a cache of it.

The shape

Three processes, and only one of them owns anything

Most agent tools put the loop inside the application. That makes closing the window a decision about the work. Here the application is a client: it renders and commands, and holds no session state at all.

Electron window Browser, over your network Terminal (CLI) Session host — one per workspace event log · append-only permission gate sessions, scheduler Agent host (forked) agent loop tools session protocol · v7 forks SURVIVES THE APP CLOSING
The app is a client. Everything a session is — its log, its gate, its scheduler — lives inside the marked boundary, in a process the app did not have to stay alive for. Three clients are drawn because all three speak the same protocol; none of them is privileged.

Why the host owns the log

Detaching a process is not enough on its own. If the app still owned the log, a running agent's events would have nowhere to go the moment it quit — the work would continue and the transcript would not, which is worse than stopping.

Why that makes a second client free

Two devices are two connections to one owner, not two copies of a session. They see the same transcript because there is only one, and turns are ordered by arrival at the host — the only ordering that exists when neither client can see the other.

Remote

The loop runs on the machine it is changing

A remote session does not stream tool calls across the network. The host is installed on the target, so a file read is a local file read, and the link carries only the transcript. Which is also why the link is allowed to drop.

your machine the app — a window, or a tab the target machine session host · the log lives here agent host the workspace bootstrap over ssh ssh -L → control channel events only network file reads, shell, git — all local to here
Nothing is installed system-wide on the target: a private Node is unpacked under ~/.agbrte and the host bundle is copied in version-stamped. Because only events cross the link, a dropped connection is not an interrupted run — reconnecting asks for everything after the last sequence number seen, which loses nothing and repeats nothing.

What the bootstrap actually does

Nothing is installed system-wide and nothing needs a package manager on the far side: a private Node runtime is unpacked under ~/.agbrte, the host bundle is copied in version-stamped, and the process is started detached so it survives the ssh session that started it.

Windows needed a second answer to the same question. A Windows host cannot listen on a unix socket, so it listens on loopback and proves who it is with a bearer token — and the file holding that token is given an explicit ACL, because chmod 0600 is close to a no-op there and inherits whatever the parent directory allows.

Where to run it

Local works. A machine that stays awake works better.

Nothing here requires a server. Attach a folder on the laptop you are typing on and everything above is true of it: the host is still a separate process, the log is still durable, closing the window is still a non-event.

What a laptop cannot give you is the half of the promise that depends on time. A detached host survives the app closing; it does not survive the lid closing. Sleep, a reboot, a battery — each of those ends the process, and a run that was going to take four hours is over at the first of them. The design's durability is bounded by the machine's uptime, and on a laptop that bound is short and arrives without warning.

So the shape this is built for is a computer that stays on — a spare desktop, a home server, a cheap VPS, an old laptop with the lid open and sleep disabled. Sessions live there and keep working; your laptop and your phone are windows onto them, and closing either costs nothing. That is also when the rest of it starts paying: a run continues while you are asleep, and the machine you pick it up on the next morning need not be the one you started it from.

Reaching it without a fixed address

Most home and office connections have no static IP, and often no way to open an inbound port at all. Three shapes work, and they differ in one thing: which side dials.

your laptop, phone your laptop, phone your laptop, phone the agent computer the agent computer the agent computer jump host rendezvous one private network · both ends keep a stable address you dial it dials on you dial mesh VPN jump host reverse tunnel it dialled out first
Only the third changes who initiates, and that is what makes it work where nothing inbound is possible at all: the agent computer opens the connection outward and keeps it, so the network it sits behind never has to accept anything.

A mesh VPN — the intended answer

Tailscale, WireGuard or similar gives every machine a private address that follows it between networks. Both ends keep that address on café wifi and at home, so ssh works unchanged and the browser client can bind to it directly.

A jump host — free if you already have one

The ssh alias is handed to your own ssh untouched, so whatever ~/.ssh/config already says — ProxyJump, a proxy command, a key, a port — is what happens. If you reach the machine today, this reaches it too, with nothing new configured.

A reverse tunnel — when nothing inbound is possible

The agent computer runs ssh -R out to any host with a stable address and holds it open. You attach to that host's forwarded port. Nothing listens on the agent computer's side of the NAT, and nothing has to.

Dynamic DNS — the weakest of the four

A name that follows a changing IP still needs an inbound port, still needs the router to cooperate, and still exposes that port to the internet. It solves the address and none of the rest.

Do not put the browser client behind a public tunnel. The link carries the token that admits a client, and the address still decides who can reach it at all — which is why it binds to loopback unless told otherwise. A private network has already established who is connecting; a public URL has established nothing, and a bearer in a link is not a reason to put a shell on the internet.

Many sessions

A tree that cannot outspend its root

Work decomposes. The risk in letting an agent decompose it is a tree nobody can see the bottom of, spending money nobody agreed to. Both are handled by structure rather than by watching.

root session child child grandchild — waiting on a person budget reserved at spawn a child on another machine — refused, not faked depth 3 · 8 children · 24 open blockage bubbles up, carrying the path to it
Two things travel, in opposite directions and by different rules. Budget goes down, reserved out of the parent's remainder at the moment of spawn — a tree cannot outspend what its root was granted, because the reservation happens before the child exists. Blockage comes up, from any depth to the root, carrying the breadcrumb: "something below needs you" is not actionable unless you can get there. Structure itself does not travel — a parent holds references to its own children and nothing deeper.
LimitValueWhat it protects
Tree depth3A deeper tree almost always means the split was wrong, not that the work is deep
Children per session8Keeps one node reviewable by a person
Open descendants24The whole tree's concurrent sprawl
Budgetreserved at spawnTaken from the parent's remainder before the child exists — checking at spend time is a report, not a limit
Localities

Eight kinds of target, two of them real

The transport table is a promise about where work can run. Six of these are not built — and the point of the table is that asking for one is refused by name rather than quietly running the work on the laptop instead.

TargetStatusWhat is missing
This machineobserved
Linux, macOS or Windows over SSHobserved
WSL distributionnot builtthe runner; the control channel it needed now exists
Docker / Podman containernot builtits control port must be published when the container starts
Kubernetes podnot builtkubectl port-forward held open; a pod can be rescheduled mid-run
Dev containernot builtthe container transport, plus reading devcontainer.json
Hosted agent servicenot builta locality with no transport — a different path, not a harder one
Customnot builtthere is no plugin API to register with
Declared where the refusal is written, so a target that does not work says which capability is the reason. An error is something a person can act on; a session badged docker:… that is actually writing to their own home directory is a wrong belief they have no reason to question.
Staying current

Two things go stale: the app, and what it is running

A workbench you leave running for weeks drifts in two directions at once. The application ages, and so does the machine on the far end — which is a separate copy of the same software, on a computer you may not be sitting at. Neither updates behind your back.

The app, when you say so

A new release is announced, not applied. The banner appears, the download happens in the background, and the restart waits for the button. Nothing interrupts a turn that is running.

The far end, from either end

The remote host is a second copy of the software, and an old one speaks an older protocol. Each attached machine gets its own Update control, and the same thing is available from a terminal on either side as agbrte update /srv/api.

Versions negotiate rather than assume

Host and client agree on a range — currently up to v9 — so a newer client drives an older host instead of stranding it. That is what makes updating the two ends independently a safe thing to offer.

Refused by name, again

Three builds cannot update themselves: a checkout with nothing to replace, an unsigned macOS bundle, and a Linux build that is not an AppImage. Each says which, before the download rather than after it.

Models

Choosing a model should not require knowing its exact tag

Local models are the reason to own the machine in the first place, and installing one meant knowing that the thing you wanted was spelled qwen2.5-coder:14b and not qwen2.5:14b-coder. The list is a dropdown now, and the entries are checked rather than remembered.

A catalogue that was verified, not typed

Twelve models — Llama, Qwen, Gemma, DeepSeek, gpt-oss and others — with sizes read from their published manifests. A build step re-checks every tag against the registry, so an entry that stops existing fails the build instead of failing a user.

Installed where the model will run

The pull happens on the machine that serves the model, which for a remote host is that machine's disk and bandwidth, not yours. Progress is summed across layers, because a model is several and the last one is often the smallest.

Only where installing is a real operation

Ollama can fetch a model while running. vLLM, llama.cpp and NIM take theirs at launch, so there the button is absent and a sentence explains it — an install that 404s teaches you to look for the bug in the wrong place.

Still nothing bundled

No runtime ships inside the app. It detects what is already serving models and offers to use it, which is the same rule as the browser, the CLIs and the icons.

In use

What it looks like

One accent colour on a neutral ground, spent only where a person has to act. On the dashboard that mark appears exactly once — on the heading of the group that needs them.

The dashboard: sixteen session cards under a Needs you heading, with a sidebar listing the attached machine
Every session on every attached machine, ranked by who needs a human. Cost and tokens per card; no progress bars, because nothing writes a checklist yet and a bar reading 0/0 forever is worse than no bar.
An open session: transcript set to a readable measure, roster and composer below
An open session. The transcript is set to a 72-character measure rather than the width of the window, and the state badge keeps the accent here because it is the only indicator on the screen.
The same dashboard at phone width, one pane at a time
The same app served to a browser over your own network — a phone on the sofa, driving a run on a build box.
Scenarios

Six things that follow from the one decision

Close the laptop

Quit the app mid-turn. The host keeps working and keeps writing. Reopen and you rejoin the same session at the same pid, mid-turn if it still is.

Move to another machine

Attach from a second device and you are in the same session, not a copy of it. Both windows show the same transcript because there is one.

Watch without touching

A client asks for a role and the host decides. Read-only means read-only: enforcement is with the owner, because a client that can still send is not read-only.

Drive it from a terminal

agbrte run . "…" is scriptable and exits with a code. The same host, the same log — the GUI is not privileged.

Work on a server

Attach a workspace over ssh. The agent's file reads and shell commands happen there; only the transcript crosses the network.

Answer a prompt from a phone

Serve the interface over your own network. A permission request that appeared on your desk can be answered from the sofa — and the other client is told who answered.

Honesty

What is not built, said out loud

The recurring failure in this project has a name: correct code behind a seam nobody crosses. A module that passes its own tests, is reached by nothing, and reports a capability the product does not have. Several of the rules above exist because that happened and was caught.