vibeD v0.6.0: Fewer Moving Parts, Fewer Sharp Edges
v0.6.0 is a consolidation release. The legacy orchestrator is gone, the API lives entirely under /v1, the hot paths got a performance pass, and we fixed eight reported vulnerabilities. It is also the release where we stopped and asked an uncomfortable question: when a deploy fails, does vibeD actually tell you why?
It did not. That turned out to be the most useful thing we fixed.
The orchestrator is gone
This is the breaking change. When the two-lane rewrite landed in v0.3.1, the old build-and-deploy orchestrator stayed behind the /api/artifacts* REST surface while the real work moved to /v1/apps. Two paths, one of them dead weight.
v0.6.0 deletes it: the orchestrator plus the deployer, environment, and source-storage packages, roughly four thousand lines. The artifact lifecycle now lives entirely under /v1/apps, with pagination (?limit=, ?offset=) and a deploy-latency histogram so the sub-two-second contract is something you can measure rather than something we assert.
Every removed route and MCP tool maps to a /v1 equivalent in the migration guide. If you drive vibeD through MCP, your agent already used the new path.
Failures that explain themselves
Here is the bug that started the most interesting thread of this release.
Deploy a Node app to a dev cluster that has no Node warm pool. The deploy fails. You ask for logs, and get nothing, because the app never got a pod, so there was never anything to log. An agent looking at that sees a bare "failed", finds no logs, concludes the failure was transient, and retries. Forever. The failure was permanent and one line of configuration would have fixed it.
The information existed the whole time. The controller wrote a perfectly clear explanation into the app's Ready condition:
Reason: TemplateMissing
Message: no SandboxTemplate for template "node-24" (no warm pool configured for it)
Nothing surfaced it. The API returned the phase and dropped the condition; the MCP tools returned a status string; the dashboard drew a red badge.
So reason and message now flow through every surface: GET /v1/apps/{id}, the deploy_artifact MCP result, get_artifact_status, and the dashboard next to the status badge. The MCP tool description now also tells agents to read the reason before retrying, because most of these failures are configuration gaps that will fail again identically.
One subtlety worth knowing: healthy apps carry a reason too (Running, "user process listening"), so clients only present it as an error when the phase is Failed. Otherwise every running app would sprout a spurious error message.
JS and Python deploy out of the box
The other half of that story: the dev install only ever ran the static-nginx warm pool, so the very first Node or Python deploy on a fresh cluster hit exactly the failure above.
node-24 and python-313 are now enabled by default. The general lane needs no Kata or nested virtualization on Kind, because runtime.defaultClass is empty there and those pods schedule normally, which was true all along and better documented now. The heavier go-123 and base-al2023 slots stay opt-in behind make enable-go-pool and make enable-base-pool.
Security: eight fixes, two critical
A full-codebase audit produced eight vulnerabilities that are fixed in this release. Two were critical.
Deploying under someone else's app name took over their app. POST /v1/deploy looked up the existing app, saw it existed, and redeployed it. It never compared owners. You could replace another user's source and serve your code on their URL.
The interesting part was the second half. vibeD does consult an authorizer on every deploy, and enterprise installs run a full RBAC one, so why did nothing catch this? Because the authorization request described the resource as Owner: <the caller>. Every request looked self-owned. No authorizer, however sophisticated, could see a takeover, because the question it was being asked already contained the wrong answer. A redeploy now requires ownership (admins keep break-glass), and the request carries the app's real owner.
The sandbox agent handed its own credentials to user code. The in-sandbox agent copied its entire environment into the untrusted user process, including VIBED_AGENT_TOKEN, the bearer token for its own control API on port 9000. That token is issued per template rather than per app, so a deployed app could call /inject on itself and on other sandboxes it could reach. A deployed app was one os.environ read away from running code in its neighbours. The agent's own variables are now stripped before the user process starts.
The six high-severity issues, briefly:
- Source blob keys were derived from the app name and version, so any authenticated caller could guess another user's key. They now carry 128 bits of entropy, which makes the reference a capability: holding it is the authorization. This matters because the endpoint authenticates callers but cannot authorize them per app, since the agent fetches with one shared token and carries no per-user identity.
- Share links resolved by app name at request time and were not revoked on delete, so a stale link re-bound to whatever app next took that name.
- The chart's warm-pool images were pinned by hand to
0.4.4and had drifted two releases behind, shipping agents that predated the v0.5.1 fetch and extract hardening. Image references now derive from the chart's ownappVersion, so they cannot silently lag again. - Caddy's admin API, which can repoint any hostname at an arbitrary upstream, was reachable by any pod in the cluster.
- The server's ClusterRole granted cluster-wide read on all Secrets, plus write verbs on Deployments and Jobs that no code path had used since v0.4.1.
- Both CI workflows granted
packages: writeat workflow level, so jobs building pull-request code held a token that could push to the registry.
Two of these are worth naming as a pattern rather than a bug: the ownership one and the image-pin one were both cases where the mechanism was correct and the input was wrong. Fixing the specific instance is easy. We tried to fix the shape instead, by passing the real owner and by deriving tags from a single source, so the same class of mistake cannot recur quietly.
Browser SSO actually works now
Uncomfortable finding: browser SSO for the dashboard had never worked end to end, in any version.
The SAML assertion consumer returns a session token in the URL fragment, and no version of the dashboard ever read it. The dashboard also had no way to know which auth mode the server was running, so on any 401 it fell back to an API key prompt, which cannot authenticate in SAML mode. Our tests all passed, because they speak HTTP directly and extract the token themselves. Nobody had opened the page in a browser against a SAML server.
A new public GET /api/auth reports {enabled, mode, loginUrl}, the dashboard consumes the token handoff, and a session flag breaks the redirect loop that this kind of fix usually introduces. There is a lesson in there about what "tested" means.
Also in this release
Three stability defects from the same audit: deploy request bodies are bounded before multipart parsing, since oversized uploads previously spooled to the node's disk unchecked; the runner-agent client shares one HTTP transport, having previously built one per call and leaked sockets and goroutines with no idle timeout; and the wait after SIGKILL is bounded, because an unbounded wait held the agent's mutex and wedged its entire control API when a process descendant escaped the process group.
vibeD also stops lying about its own version. The MCP handshake advertised 0.1.0 for five consecutive releases. It is now injected at link time, and unreleased builds report dev so they cannot masquerade as a release.
Upgrading
Read the migration guide if you call the REST API directly. If you use MCP or the dashboard, there is nothing to change.
One upgrade note on the source blob fix: sources uploaded before v0.6.0 keep their old guessable keys. Each app's next deploy mints a fresh one, but the previous blob stays until version retention evicts it. To close that window immediately, delete the legacy <name>.v<N>.tar.gz objects from your blob store after upgrading. You forfeit rollback to those specific versions, which is usually the right trade.
