Changelog

What's new.

The notable, user-visible changes to the workbench, newest first. For the full per-version technical detail, see the GitHub Releases.

Your features are current after every apply

Every monoceros apply now brings the features in your workbench up to date before you get a shell. Until now they came out of the cached image layer, so a workbench you set up weeks after your first one started on the version that was current back then, and a re-apply could take away a self-update you had already been working with.

Each feature checks itself before anything is downloaded, so the usual apply costs a version lookup and no transfer, and apply lists what it found at the end. With no network it says so and keeps what is in the image. A feature you pinned to a version in the yml is left alone, as are your languages, your apt packages and the runtime image.

Service images are the deliberate exception, because your data lives there. They are pulled on the first apply of a workbench, and after that only when you run monoceros upgrade.

A workbench you already have needs one monoceros upgrade to pick this up: the refresh runs from inside the container, and it gets there when the feature layers are rebuilt.

Read monoceros apply for what moves when.

Your Claude plugins come with the workbench

Claude Code plugins can now be named in the yml, and a fresh workbench comes up with them installed. Until now every rebuild meant typing the two /plugin commands again, and nothing in the yml said which plugins the workbench was supposed to have.

The claude feature entry takes a plugins: block with the marketplace and the plugins to install from it. Every workbench that has the feature already carries a commented example, so monoceros init and monoceros add-feature claude show you the shape in the file: uncomment it, put your own repository in, and run monoceros apply. Monoceros hands the work to Claude Code’s own plugin commands inside the container, so the marketplace ends up registered under the name it gives itself and the plugins are actually enabled.

Later applies keep the plugins current: the marketplace gets pulled again and the plugins updated on top of it, so a fix someone pushes reaches your workbench the next time you apply.

A marketplace is a full HTTPS git address, the same form repos: uses, or a path to a folder in your workspace. GitHub, GitLab and Bitbucket are recognised by their host; for anything else, name the provider. A private marketplace needs the same access token as a private repository, and apply tells you before it builds anything.

Read Claude Code for the full block, including a self-hosted marketplace and a local one.

Upgrade brings the services along

monoceros upgrade now moves your services forward too. A service added from the catalog kept the image tag it was created with, so a fix upstream never reached a workbench that already existed. Upgrade now writes the tag the catalog ships today into your yml, names every change in its output, and re-pulls the images, so a fix inside an unchanged tag lands as well. It leaves an image you chose yourself untouched.

Keycloak moves to 26.7 with this release, which closes a critical account-takeover flaw in the reset-credentials flow (CVE-2026-18963). The 26.6 line will not be patched, so if you run Keycloak, run monoceros upgrade.

Read monoceros upgrade for what it refreshes and what it leaves alone.

One address for your app and its services

monoceros share now takes the workbench’s HTTP services with it, so a phone that opens your app also reaches the Keycloak it logs in against, the Mailpit inbox and the RustFS console. Which services those are is a line in the yml, httpPort, filled in by the catalog where it makes sense and absent on every database, because sharing a Postgres over HTTP would only return nonsense. Delete the line to keep a service to yourself.

Caddy joins the curated services as a reverse proxy for the case where one origin is the point: app and login server behind a single address, so cookies stay scoped and the OIDC issuer keeps matching the page. Its configuration is a Caddyfile you keep in your repo, and saving it is enough to apply a change.

On the host the same line earns a permanent address, acme-keycloak.localhost and the like, routed by the proxy with nothing to keep running. monoceros status prints it on the service’s row.

Read Caddy for the mount and an example Caddyfile, and monoceros share for what goes out and how to move a busy port.

One role set per workbench, and removing it clears up after itself

claude-code-roles and opencode-roles ship the same role names, and OpenCode reads Claude Code’s skills directory as well as its own. In a workbench with both, an OpenCode run picked up a skill written for Claude Code and ran it on its default agent, so neither the role nor its permission guard applied and the run edited a file it should not have touched. monoceros apply now refuses a workbench that has both, before anything is built, and names the command that drops one.

Dropping a role feature from your yml also takes its files out of the container now. Until this release the agents, skills and commands stayed where an earlier apply had put them, so the roles kept working in a workbench that no longer declared them and you had to delete them by hand. Skills and agents from other features are left alone, and your plans are never touched.

Give the agent a map of your code, not a grep

The new graphify feature parses your workspace into a knowledge graph the agent can query. Instead of opening five files to work out what calls what, it asks one question and gets back the part of the graph that answers it. Add it with monoceros add-feature acme graphify.

Parsing runs in the container with tree-sitter, which means no API key and no account, and your code stays where it is. If Claude Code or OpenCode is in the same workbench, graphify finds it and registers itself there as a skill on every start, so /graphify . in the agent is the whole setup. On the shell, graphify . --code-only builds the same graph without a model.

The results land in graphify-out/, which is build output and belongs in your .gitignore.

Paste a screenshot into the agent in your workbench

Copy a screenshot on your desktop and paste it with Ctrl+V into Claude Code, OpenCode or anything else running in a workbench. The image arrives, so you can ask what is wrong with the layout you just captured instead of describing it.

Copying out of a workbench already reached your clipboard; now the way back carries images too, in every terminal and in an IDE attached over SSH. Run monoceros upgrade and start a fresh session to pick it up.

MCP servers for your agents, from the yml

The workbench yml has an mcpServers: block now, so the agents in a container get their MCP servers from the config instead of from you registering them inside. monoceros add-mcp-server acme rovo adds one.

Seven connectors are curated: Context7, Atlassian Rovo, Atlassian Forge, Notion, Linear, Figma and Microsoft Learn. Any other server goes in the same block with the config its provider publishes. MCP servers has the catalog, the credentials, and the ones you sign in to.

Rovo Dev needs its own Atlassian token

Rovo Dev only accepts a token scoped to its own app, not the one twg and Forge use. The atlassian feature has a rovodevToken option for it, filled from ATLASSIAN_ROVODEV_TOKEN, and a token it rejects now warns instead of leaving the container half built.

The feature page has the steps for all three Atlassian tokens.

Each role gets its own effort dial

The planner, the implementer and the reviewer could already each take their own model. Now each takes its own effort level too, with plannerEffort, implementEffort and reviewEffort.

Planning and reviewing are thinking work. The implementer works from a plan that already names the files and the command that decides pass or fail, so that is the cheapest place to turn the dial down.

What the values are and how to check yours arrived: Claude Code roles and OpenCode roles.

Claude Code gets the three roles

The plan, implement and review split existed only for OpenCode. The new claude-code-roles feature brings the same three roles to Claude Code: a planner that turns your task into a plan file and writes no code, an implementer that follows that plan and cannot change it, and a reviewer that runs the acceptance command itself and compares the result against the plan.

The questions happen in your own session rather than inside the planner, because a Claude Code subagent has no way to reach you. /monoceros-plan asks one thing at a time, each with the answer it will assume if you say nothing, and hands your answers to the planner. You read the plan, then /monoceros-ship runs the implementer and the review, gated on the acceptance command coming back green.

Each role takes its own model, so plannerModel: opus and implementModel: sonnet put the reasoning and the typing in different places. The limits that keep the roles apart hold in every permission mode, including the Auto Mode a Monoceros container turns on by default: the planner cannot touch source, the implementer cannot edit the plan it is measured against, and the reviewer cannot write at all.

Add it with monoceros add-feature acme claude-code-roles. Details on the Claude Code roles page.

Starting in a project that does not exist yet

monoceros run acme --in projects/shop -- opencode died on cd: projects/shop: No such file or directory when the directory was not there yet. That is exactly the first run for a new app: the agent is supposed to create the project, and you have to start it somewhere.

It now asks whether to create it, with yes as the default. Answer no and nothing runs, so a typo still does not start a command in the wrong place. In a script there is nobody to ask, so a missing directory stays an error there; pass --yes to skip the question. Details on the run page.

Starting one level up is not a substitute, which is why this was worth fixing: a language server looks for its markers in the directory it was started in, and OpenCode keys its session list by that directory too.

Two more things OpenCode brings along

A theme you picked with /theme lives in that one container, so every new workbench started over in the default colours. The opencode feature takes a theme option now, written into ~/.config/opencode/tui.json on apply, and each workbench comes up the way you like it. system follows your terminal’s background, which is usually what you want over SSH.

The second one is worth more than it looks. OpenCode can pass a language server’s diagnostics to the agent, so it sees a type error or an unresolved import the moment it writes one instead of when the tests run. It ships with servers for TypeScript, Go, Python, Java, Rust and about thirty more, and they are off by default. lsp: true switches them on; a server whose toolchain is not in the container does not start, and the ones that download themselves now survive an apply instead of coming back every time.

TypeScript needed one extra step. The container ships TypeScript 7, the native port, and OpenCode’s built-in TypeScript server cannot launch it, so the switch would have done nothing for the language most people write here. It registers TypeScript 7’s own protocol support instead. The bash server got a companion it was missing too: shellcheck is now in the image, without which that server starts and reports nothing. Details on the OpenCode page.

Every container can commit

A workbench without repositories could end up with no git identity, and nothing said so. The container started, git ran, and the first commit failed with “Please tell me who you are”, usually inside an agent that had been told to commit its work, where it reads like the agent’s fault.

Put GIT_USER_NAME and GIT_USER_EMAIL in monoceros-config.env once and every workbench you build from then on has them, whether or not it clones anything. A single workbench that should commit under a different name gets them in its own acme.env. No entry in the yml is needed for either: a name and an address are personal data, the env files are gitignored, and the yml is the part you share.

Removing them works the same way. Take the identity out of the file you put it in, apply, and the container no longer has one, which monoceros check then reports. That sounds obvious and was not: the identity was also read back from the file apply writes inside the container, so it survived the removal of everything that declared it. Details on Git and repos.

The three OpenCode roles use this. When a plan scaffolds a new project, its first step is now git init plus a .gitignore, so the implementer can commit each round and the reviewer reads a diff instead of every file in full. In a measured run that cut the review from 18 minutes to under two. A project that was already there is left alone.

Three roles instead of one agent

When a single agent plans a change, writes it and then checks it, it is checking its own work. If it misunderstood the task at the start, it will still misunderstand it at the end, because nothing in between looked at the task again.

The new opencode-roles feature splits that across three agents. A planner turns your task into a plan file and writes no code, asking you about anything the task leaves open before it starts. An implementer follows that plan and cannot change it. A reviewer runs the acceptance command itself and compares the result against the plan. Whichever of the three commands you call leads the rest, so you can start with a fresh task or with a plan from yesterday.

Each role runs on its own model, so you can put a model that reasons well where the thinking happens and a cheaper one where the code gets written. Add it with monoceros add-feature acme opencode-roles. Details on the OpenCode roles page.

Check finds a launch target that cannot start

A launch config can be wrong in a way that only shows up when you start the server: the entry runs npm run dev while your project calls that script start, and you find out as an npm error at the moment you wanted to look at the app.

monoceros check now compares the target’s command against your project and reports a script that does not exist, along with the ones that do. It does the same for a working directory that is missing. Both are decided from the files on your machine, so you can fix them before you start anything. Details on the check page.

Copying in the workbench now reaches your clipboard

Copying inside a workbench used to go nowhere, and nothing told you so. You copied an error message out of your agent, pasted it into a chat window, and got whatever you had copied a minute earlier.

Monoceros now carries the copy to your desktop clipboard. It works in monoceros shell, in monoceros run and in an IDE attached over SSH, in every terminal including Apple Terminal, with nothing to switch on. Run monoceros upgrade and start a fresh session to pick it up.

Go builds reuse their cache across rebuilds

A Go project used to pay for every monoceros apply: both caches sat outside the workspace, so a mid-sized project re-downloaded more than a gigabyte and recompiled its whole dependency tree afterwards.

The build cache and the module cache now live in a volume shared by every workbench on the machine. A dependency is downloaded and compiled once and then reused, by the next build and after the next apply. Tools you install with go install stay per workbench, so two projects can pin different versions of the same linter. Details on the Go page.

Apply a changed Keycloak realm while you work

Editing your realm.json used to have no effect until the next apply recreated the service, because Keycloak only imports a realm into an empty database. That made every change to a client or a role a rebuild.

The new .monoceros/bin/keycloak-realm applies a changed file to the running Keycloak instead. The Keycloak page explains what that replacement costs.

Ready-made compose blocks for your pipeline

Your services run one way in the dev container and another way in a pipeline, and until now an agent asked to write the pipeline had to work out the difference on its own. The results were unusable: dev credentials as fallback values, and a Keycloak that loses everything on restart.

Every curated service now hands over a finished block for the pipeline. Each apply writes .monoceros/deploy.md for the services you configured, so the agent copies a verified block instead of inventing one. Values in those blocks are required rather than defaulted, so a variable you forget stops the pipeline by name instead of quietly starting a reachable database on credentials from our catalog. Details on the agent briefing page.

Service data now lives in a Docker volume

The data of a service is no longer stored under container/<name>/data/ but in a Docker volume, which behaves the same way on macOS, Linux and Windows. Existing workbenches move their data across on the next monoceros apply, and backups still contain plain files, so monoceros restore works as before.

What changes for you is how you look at the live files. The Services overview has the one-line Docker command that opens a volume.

Share works even when your IDE forwards the same ports

monoceros share no longer trips over your editor. An attached IDE (VS Code, Codium, JetBrains over Remote-SSH) auto-forwards the container’s ports to localhost on your machine, and that forwarding cannot be reliably switched off - so share, which needs the same ports on 0.0.0.0 to reach other devices, used to collide and stop.

Now it explains the collision instead of failing cryptically. When a port is already taken, share names the real cause - your IDE is holding it on 127.0.0.1 - and offers two ways past it: right-click the port in the IDE’s PORTS panel and “Stop Forwarding Port” (it stays gone across reconnects), or publish the busy port on a different host port with the new --forward-ports flag, as in monoceros share acme web --forward-ports 15173:5173.

It reads in Docker -p order (host:container), takes a comma-separated list, and only remaps the busy ports - everything else keeps its number. The bind stays on 0.0.0.0, so your phone and tablet reach the app exactly as before.

Tab completion that works on Windows PowerShell

Press Tab in PowerShell and monoceros now finishes what you are typing, instantly.

It completes the things you actually reach for. Subcommands (monoceros ap<Tab>), flags and their values (--with-features=<Tab>, --open <Tab>), and - read straight from your %USERPROFILE%\.monoceros - your container names, the apps inside a container (monoceros start <name> <Tab>), workspace folders for run --in, and a launch config’s targets. Nothing calls back into the Linux side on each keystroke, so suggestions appear with no lag, even with the container stopped.

It is set up for you. The Windows installer writes the completion into your PowerShell profile automatically. If you are already on Windows, re-run the installer and open a new terminal to pick it up - see Installation. Completion inside monoceros shell, and on macOS and Linux, keeps working as before.

Share to your phone over HTTPS - logins and installable PWAs included

monoceros share puts your app on your phone in one command - and now it does so over HTTPS, which is what a modern web app actually needs once it leaves your desk.

Logins and installable PWAs work on the phone now. A browser only unlocks sign-in cryptography, service workers, offline mode and “add to home screen” on pages it trusts - and a plain http:// address on your network is not one. share now serves HTTPS, so a Keycloak login goes through and a PWA installs on your phone, just like it already did on your desktop.

The certificate is yours and stays local. Monoceros creates a small certificate authority on your machine; its private key never leaves the host and nothing is sent anywhere. Each device trusts it once - see Trusting the local certificate for the per-platform steps (macOS, Windows, Linux, iOS, iPadOS, Android).

Backends behind share get the scheme right. share tells your app it is being reached over HTTPS, so an OIDC issuer or any other absolute URL comes out as https://… and the login round-trip completes.

Run monoceros share <name> <app> and open the printed https:// URL on any device on your Wi-Fi.

Your repos, cloned and signed in with one token

Working with private repositories inside a workbench used to mean wiring up git credentials by hand. Now it is declarative like everything else.

Point a workbench at your repos and they clone themselves. List HTTPS repos

  • GitHub, GitLab or Bitbucket - in the yml, and apply clones each one into the workspace. No git tooling on your host.

One Personal Access Token per provider does it all. The token that clones your repos also signs in the provider CLIs inside the container, so gh and glab - and the AI agents that lean on them - are authenticated the moment the workbench comes up. Add a provider CLI without a repo and Monoceros still picks the right token.

The token never touches the yml. It lives in a private, gitignored env file next to the config, so the yml stays safe to commit and share.

See Git and repos for the token model and how to create a PAT for each provider.

A first-class Windows installer

Windows is now a one-command install, with none of the WSL setup you would otherwise do by hand.

The PowerShell installer sets up its own managed monoceros WSL distro, turns on Docker Desktop’s WSL integration for it, and adds the monoceros command to your PATH - so you run monoceros straight from PowerShell while it executes in Linux under the hood. Your %USERPROFILE%\.monoceros is symlinked in, so configs and containers land where you expect.

You never run wsl --install, pick a distro, or flip an integration toggle. Re-running the installer reuses the distro and preserves your config. See Installation for the one line to paste.

One-command uninstall, everywhere

Installing Monoceros is now cleanly reversible. A matching uninstaller ships for macOS, Linux and Windows.

One command tears down every workbench, purges the runtime images, and removes the CLI and its shell wiring - on Windows, the managed distro’s Docker integration, the monoceros shim and the %USERPROFILE%\.monoceros link with it. It asks whether to keep your configs and backups, so a later reinstall resumes where you left off, or wipe everything.

Only Monoceros’ own objects are touched: images and volumes from other tools are left alone, and it is safe to re-run. See Uninstall.

See your apps, keep them across applies, and share them to your phone

Three changes make the apps you run inside a workbench easier to live with.

monoceros status is now a whole-stack overview. One glance shows the container, its services and its apps - which are up, which are not ( / ·), the URLs they answer on, and what the yml built in (languages, features, ports). Pass an app or service name to narrow it to just that.

Your running apps come back after monoceros apply. Applying a yml change recreates the container, which used to kill every dev server you had running. Now the ones that were up are restarted automatically - the same “comes back unless you stopped it” behaviour the rest of the stack already has.

monoceros share <name> <app> puts an app on your phone. It exposes the app’s ports to your local network, so any device on the same Wi-Fi can open it - with hot-reload working. It runs in the foreground; Ctrl+C stops sharing and nothing stays exposed in the background.

The status app-state and the apply-restore need runtime 1.6.0 - upgrade an existing workbench to pick them up. See monoceros status, monoceros apply and monoceros share.

Start and stop long-running app servers by name

Long-running servers you run inside a workbench - a web app, an API - are now first-class. Each app declares its servers once in a launch config, and anyone can start, stop or tail them by name from the host, while the build agent does the same from inside the container. Monoceros brings the container up if needed, runs the server detached so it survives the session, and waits until its port is actually listening before reporting it up.

An app that needs several servers - an API and a web frontend, say - marks them all, and one monoceros start brings them up together, in declared order, each ready before the next.

Needs runtime 1.5.0 - upgrade an existing workbench to pick it up. See Long-running app servers for the details.

Monoceros is now Apache-2.0

Monoceros is now licensed under Apache-2.0 - fully open source, free to use privately and commercially, with an explicit patent grant. Take your setup and walk away anytime; there is nothing proprietary locking you in.

Connect the Claude Code desktop app

The Claude Code desktop app can now attach to a running workbench through its “Connect to SSH host” flow - Windows included, with stable host keys so it connects cleanly every time instead of tripping over a changed key. The agent runs inside the container, not on your host. See the Claude Code guide for the setup.

A Docker restart no longer leaves the workbench half-up

After a Docker Desktop restart or a reboot, a workbench used to come back in a half-up state - services running without their workspace, or the shared proxy and the SSH endpoint gone so port routing and IDE attach quietly broke. Now the whole container group stays down together until you run monoceros start, while the proxy and the SSH endpoint survive a restart so ports and editor attach keep working.

Existing containers still carry the old restart: unless-stopped line on their services in the yml. Remove it from those services and re-apply (monoceros apply <name>) to adopt the new behaviour; a per-service restart stays available as a deliberate opt-in.

Attach JetBrains IDEs

IntelliJ, WebStorm and the other JetBrains IDEs attach to a running workbench over SSH through the Toolbox App. Your IDE state - indexes, settings - persists across rebuilds via a shared backend, so you are not re-indexing the project every time you build. See the JetBrains guide for the setup.

Tab-completion for run --in

monoceros run --in <TAB> now completes the workspace directories inside the container, so you can target a subfolder of your workspace without typing the path by hand.

Keycloak as a curated service

Add a local Keycloak to your workbench like any other service. It starts once the workspace is up and can import a realm from a file you mount in, so your clients, users and roles come up provisioned instead of hand-clicked through the admin console every time. See the Keycloak guide for the setup.

Attach any IDE over SSH

Open the running workbench in your editor over plain SSH - no proprietary container plugin required. monoceros open <name> launches VS Code or VS Codium for you; JetBrains IDEs (IntelliJ, WebStorm and the rest) and a plain terminal attach to the same endpoint, and the Claude Code desktop app can connect too. Windows included. You work inside the container while your host stays untouched.

Per-editor setup on the Use an IDE pages.

See every version's technical detail in the GitHub Releases →