Skip to main content

frembench — dev benches

frembench is a Linux machine you rent by the minute and reach from your browser — a dev bench. It carries the same language toolchain your CI runners do, so something that builds on a bench builds in CI.

What it costs

A bench seat is €39 per month and includes 200 hours of runtime. Beyond the pool it is €0,20 per hour, and the total is capped outright — so your bill can never exceed €99 per bench seat in a month, however hard you run them. On a single seat the cap binds at 500 hours of runtime; past that the bench keeps running at no further charge. Every figure here is in EUR, which is what benches are charged in — as is the rest of your subscription.

Hours pool across bench seats, the same way CI minutes do. The allowance is 200 hours × seats, measured against everything your organisation’s benches ran, so two seats share 400 hours: one busy bench draws on a quieter colleague’s allowance rather than going straight into overage. The ceiling pools with it — the cap is €99 per bench seat, not per machine, so a single bench in a two-seat org can account for more than €99 while the org stays inside €198.

Two details worth knowing, because they change what you actually pay:

  • Time is measured in seconds and rounded once, at the end of the period. A bench suspends after 30 minutes idle and wakes when you attach, so a working day is many short intervals. Rounding each one up to a minute would bill twenty ten-second wakes as twenty minutes — charging you for our own suspend optimisation.
  • 200 hours is far more than a working month of attached time. The pool is sized against wall-clock time a bench is awake, not hours you spend at the keyboard; an agent left running a long job counts, an idle editor does not.

Seats are prorated when you add or remove them, like any other seat. See the pricing page for how it sits beside the rest of the plan.

You do not manage it. It wakes when you attach, and suspends itself when nobody is attached and nothing is running.

What survives, and what does not

This is the one thing worth understanding before you use a bench, because it shapes everything else.

Your bench has two disks. The profile volume, 50 GB, is yours and persists. The boot disk is rebuilt from a fresh image on every wake, which is how you get patched without a maintenance window and without ever being asked to reboot.

$HOME is on the profile volume. So:

WhatSurvives a suspend
Your code, your node_modules, your build outputyes
Dotfiles, shell history, git config, SSH keysyes
npm i -g, pip --user, uv tool, pipx, cargo install, go installyes
Your Claude Code sign-inyes
apt installno

apt is the deliberate exception: system packages are what the image is for. If you need one every morning, it belongs in the image — open an issue rather than scripting it into your shell profile, and the weekly rebuild stops it being your problem.

Taking your files with you

Your profile volume outlives a suspend, but not your bench. When an org admin removes your bench seat, the bench is removed with it:

  • It stops at once, and you cannot open it again, not in the browser and not with fremforge bench attach.
  • Its profile volume is kept for 30 days. For those 30 days an org admin can undo the removal on the organisation’s Benches admin page. That brings the bench back as it was, asleep, on the same disk, with your seat.
  • After 30 days the profile volume is deleted. It cannot be reattached or recovered, and a new bench always starts with an empty profile.

So take what you want to keep while you still have the bench, for example before you leave a project or the organisation. If it has already been removed, ask an org admin to undo the removal within the 30 days, copy your files off as described below, and let them remove it again.

There is no scp, rsync or SSH into a bench: it has no inbound network path, and fremforge bench attach gives you a terminal, not a file transfer. What you take with you leaves through frem.sh, in one of the three ways below.

Push your work

Code belongs in git, and the bench already has a git credential for frem.sh. To find every repository under your home directory with something not yet pushed (uncommitted changes, or commits on a branch that is on no remote):

find ~ \( -name node_modules -o -name .cache \) -prune -o -name .git -print 2>/dev/null |
while read -r g; do
  r=$(dirname "$g")
  if [ -n "$(git -C "$r" status --porcelain)" ] ||
     [ -n "$(git -C "$r" log --branches --not --remotes --oneline -1)" ]; then
    echo "not pushed: $r"
  fi
done

Commit and push each one. If you cannot push to the branch you are on, push to a branch of your own (git switch -c wip/<topic>, then git push -u origin HEAD). git stash list shows stashes, which a push does not take with it.

Everything else: a private repository

Dotfiles, notes and scratch files that belong to no repository can go into a private repository of their own. Create one in your organisation from the frem.sh web interface (New repository, visibility Private), for example bench-files-<your-login>. If you cannot create repositories in the organisation, ask an org owner. Then, on the bench:

mkdir ~/bench-files && cd ~/bench-files
cp -a ~/.bashrc ~/.tmux.conf ~/notes .   # whatever you want to keep
git init -b bench-files
git add -A
git commit -m "Files from my bench"
git remote add origin https://frem.sh/<org>/bench-files-<your-login>.git
git push -u origin bench-files

The branch is called bench-files rather than main so that your organisation’s branch protection, which covers main by default, does not refuse the push. On your own machine:

git clone -b bench-files https://frem.sh/<org>/bench-files-<your-login>.git

Two things not to copy:

  • ~/.git-credentials and ~/.config/fremforge/token. They are minted for the bench, revoked when it goes, and work nowhere else.
  • Secrets of your own, such as SSH private keys or cloud CLI credentials. Everyone with access to the repository can read them, including the organisation’s owners. Create new ones on your next machine instead.

Git is a poor fit for large binary files, so keep the repository to what you actually need.

A single small file: download it in your browser

For one small file there is a shortcut through Forwarded ports. Pack what you want into one file and serve its directory:

mkdir -p ~/outbox
tar -czf ~/outbox/dotfiles.tar.gz -C ~ .bashrc .tmux.conf
cd ~/outbox && python3 -m http.server 8000

Port 8000 appears under Forwarded ports. Open it, click the file to download it, then stop the server with Ctrl-c. A forwarded response is capped at 8 MB, so this works only for small files; use a repository for anything larger.

Running an agent

Codex and opencode are pre-installed. Both are open-source licensed, so we ship them.

Claude Code is not pre-installed, and the reason is a licence rather than an oversight: its terms are not an open-source licence, and this is a product we sell, so shipping it would be redistributing proprietary software. It is one command, under your own Anthropic agreement:

frembench-install-claude-code
claude

It installs from the public npm registry through the bench’s egress proxy, like any other package you install on the bench. You only do this once — the install and the sign-in are on your profile volume.

Running more than one thing at once

Your bench terminal is not a bare shell — it attaches to a tmux session called frembench:

tmux new-session -A -s frembench

That is what makes an agent survive a closed laptop, and it is also how you run several at once. The prefix key is the stock Ctrl-b, and the mouse is enabled.

KeysWhat it does
Ctrl-b cNew window — start a second agent here
Ctrl-b n / Ctrl-b pNext / previous window
Ctrl-b 0 … Ctrl-b 9Jump straight to a window
Ctrl-b wPick from a list of windows
Ctrl-b ,Rename the current window — worth doing, name them per task
Ctrl-b %Split into side-by-side panes
Ctrl-b "Split into stacked panes
Ctrl-b zZoom one pane to fullscreen, and back
Ctrl-b dDetach — everything keeps running

A second browser tab mirrors; it does not fork. Every attach goes to the same frembench session, so two tabs show the same window and will fight over it. Use windows and panes for parallel work, not more tabs.

If you do want two tabs showing different things, give the second one its own view of the same windows:

Ctrl-b :  new-session -t frembench -s second
Ctrl-b :  switch-client -t second

-t frembench groups the new session with the original, so both see the same window list but each can sit on a different window. A freshly opened tab always lands on frembench, so you re-issue switch-client per tab.

Two details that surprise people:

  • A new window is a plain login shell. ~/.frembench/startup runs only when the session is first created, so it does not re-run per window.
  • Different-sized tabs do not shrink each other. aggressive-resize is on, so a window is sized to the client actually looking at it rather than to the smallest one attached.

Opening a dev server in your browser

A bench has no inbound network path. Nothing on the internet can reach it, and that is not a limitation we plan to remove — it is why the attach surface is defensible.

Dev servers still work. Start one as you normally would:

npm run dev

Within a few seconds the port appears under Forwarded ports — a card of its own on the frembench page, below Holds; under the shell on the full-screen terminal page. Click it. The connection travels outbound over the same channel as your terminal; no port is exposed and no tunnel binary is involved.

The card names what is listening as well as where:

PortService
5173node
8000python3

Some things to know:

  • Only ports your bench is actually listening on are offered. You cannot ask for an arbitrary port, and neither can anything else holding a link.
  • The service column is the process’s own short name, read from the bench — node rather than vite, because that is what the process calls itself. It reads Unknown for a port whose process could not be identified, and on a bench running an image from before this shipped, which picks it up on its next wake.
  • A server bound only to the bench’s private address is not listed, because a forward to it would fail. Bind to localhost or 0.0.0.0.
  • SSH is never offered. Use the terminal.
  • Hot reload works. A forwarded page is served from your bench’s own origin, and a WebSocket opened from that origin is carried too — which is what a dev server’s live-reload channel needs. This note previously said it did not work; that was written before the per-bench origin existed and stayed here after it arrived.

This is also how you reach an agent web UI such as OpenChamber. It is installed on every bench, so there is nothing to set up: start it and open its port. Do not run openchamber tunnel — that is a Cloudflare tunnel, and the forward above is both the sovereign path and the one that already authenticates you.

The API credential on a bench

A bench authenticates to frem.sh with two credentials, and knowing which is which saves an afternoon.

CredentialWhereWhat it reaches
Git credential~/.git-credentialsgit clone / push / pull, and Forgejo’s own API at /api/v1/…
API credential~/.config/fremforge/tokenthe fremforge API at /_app/api/v1/…

Both are minted for you by the platform. You do not create them, paste them or renew them, and you should not replace them with a personal token.

It is minted per wake

The API credential is an ffp_… token minted when your bench starts, valid for about a day, and revoked when the bench suspends. The next wake mints a fresh one. That is why it is safe for it to sit on the profile volume: a token read off a disposed disk has already been revoked.

The practical consequence: a change to what your bench may do takes effect on the next wake, not immediately. If a capability was just enabled for you and the API still refuses, suspend and wake the bench before reporting a bug.

It acts as you, with a narrower reach

The token is bound to your identity, not to an org. Every request is still checked against your real org ownership, so a bench can never reach an organisation you could not reach from your laptop.

What it deliberately does not carry: billing, SSO configuration, seat changes, and the ability to mint further tokens. Those are the scopes whose consequences would outlive the bench being thrown away. Everything else a bench needs day to day — orgs, runners, findings, SBOMs, audit reads, dependency updates, deployment environments and secrets, and starting and downloading a data export — is included.

403 Token lacks required scope '…' from a bench means the endpoint exists and your bench credential does not carry that scope. It does not mean the feature is missing. The wording is easy to misread as “no such endpoint” — check the scope before concluding anything about the API.

Org owners can switch the bench API credential off entirely for the whole organisation in admin → Authentication policy. It is on by default.

Using it

curl -fsS https://frem.sh/_app/api/v1/orgs/<your-org>/environments \
  -H "Authorization: Bearer $(cat ~/.config/fremforge/token)"

Note the /_app prefix. The bare /api/v1/… path is Forgejo’s own API, which does not accept this token — and its rejection message quotes the token you sent, so avoid pasting the output of a failed call into a ticket or a chat.

Suspend, wake, and what you are charged for

You are charged for the minutes your bench is awake. A suspended bench costs only its profile volume, so what keeps it awake is worth knowing precisely.

What keeps it awake

Your bench stays up while any of these is true:

  • Someone is attached — a browser tab or an SSH session actually connected.
  • An agent is working. Not merely running: see below.
  • You pinned it, from the bench page.

When none of them holds, the bench suspends after 30 minutes.

What “an agent is working” means

An agent sitting at a prompt is not working, and this is the distinction that decides your bill. A Claude session left open overnight looks busy by every naive measure — it still ticks along using about 1% of a CPU — so we do not use CPU on its own to decide.

An agent counts as working when either is true:

  • it has written to its own session history in the last 10 minutes, or
  • it or anything it started is using at least 10% of a CPU core.

The second exists for long tool calls. If your agent kicks off a thirty-minute build, it writes nothing for thirty minutes while it waits — but the compiler it started is busy, so the bench stays up.

So a genuinely idle agent stops holding your bench about 10 minutes after its last activity, and the bench suspends about 30 minutes after that: roughly 40 minutes from your last interaction, if nothing else is holding it.

The one case you have to tell us about

A long job you started yourself — not through an agent — does not keep the bench awake. A make -j, a data import, a soak test: if you start it in a shell and close your laptop, the bench will suspend under it.

That is deliberate. If any busy process kept a machine awake, an idle bench could be kept billing forever by anything at all, and the cheapest thing to run on somebody else’s machine is a crypto-miner. So the rule is that we only count work we can attribute to an agent, and everything else is your call: pin the bench, and unpin it when the job is done.

Staying attached also works, since an attached client is itself a hold.

Waking is automatic — attach and it comes back. Your files are where you left them, and your terminal layout comes back with them: the windows you had open are recreated in the same directories, and Claude, Codex, opencode and OpenChamber are relaunched — each resumed into the session it was in, waiting at a prompt. Nothing is replayed for you; an agent does not act while you are away.

Two things do not come back. A session started without a resumable id returns as a fresh one in the right directory, and any other long-running process — your own dev server, a build — is not restarted. Declare it in ~/.frembench/restore-commands.txt, one command name per line, if you want it to be.

Pin a bench when you have a long job that our tracking would not see, from the bench page. A pin is not permanent: a bench that has gone too long without a rebuild is patched regardless, because an unpatched machine on the internet is not something a pin should be able to buy.

Getting one

Dev benches are enabled per tenant. If your org has them, the page is under Settings → Dev benches; if not, that page will say so. Your org admin sets how many benches your tenant may run at once.

One bench per organisation

A bench belongs to an organisation, not to you. If you are a member of several organisations that have bench seats, you get one bench in each — separate machines, separate profiles, billed separately to the organisation that granted the seat. You cannot create a bench in an organisation that has not bought seats, so no organisation can be charged for a machine it did not agree to.

Profiles do not follow you between organisations. Each bench has its own profile volume, so the tools, shell configuration and credentials you install on one bench do not exist on a bench in a different organisation. Set up each one you use.

This is a consequence of the tenancy boundary rather than a limitation we plan to remove: a shared profile would mean one organisation paying to store another organisation’s work, and would carry files across a boundary customers rely on.

Benches suspend independently after thirty minutes idle, so holding benches in several organisations does not keep several machines running — only the one you are actually using stays awake.

Why Claude Code is not pre-installed

The bench image ships codex (Apache-2.0) and opencode (MIT) ready to use. Claude Code is not pre-installed, and that is a licensing decision rather than an oversight: its licence is not open source, and frembench is a product we sell. Shipping it inside our image would redistribute it as part of something we charge for.

Installing it yourself is one command, and it then runs under your Anthropic agreement rather than ours — which is also why it survives a suspend: your profile volume keeps it, so you install it once per bench.

This is the same rule applied everywhere in the product: dependencies we ship are sovereign, current, and fully open-source licensed. Anything that fails that test is something you bring, not something we bundle.