Releases
A DevGrail release is one thing, not seven: it names the exact version of every container image and installer file it consists of, and they are published together or not at all. Older releases stay downloadable at their original bytes.
-
Development agreement and privacy previews now use short notices stating that DevGrail is not live yet.
-
Agreements, accepted once and kept current. Someone who has to accept sees a short "Before you start" step for each agreement (an administrator the Operator Agreement and then the User Agreement, anyone else the User Agreement) and then continues where they were going. When an agreement changes, one banner gives the date to accept it by, 30 days after the new version arrives; a wording fix asks nobody. Until then dashboard, API, MCP and new terminal access wait; running workloads continue and your files can still be exported. Agreements & privacy lists each document with where you stand on it, and installation reporting is now on Administration. There is no setting to turn acceptance off.
-
Every installation sends a daily heartbeat, and says exactly what is in it. Once an administrator accepts the Operator Agreement, DevGrail sends that acceptance and a heartbeat to the site it was installed from, at each start and then daily: random IDs, a sequence number, the release, the operating system and the CPU architecture, and nothing about users, workspaces or usage. It cannot be turned off. The installer, the Operator Agreement step and the Agreements & privacy page, which every user can open, list each field, and Administration shows the last heartbeat exactly as it was sent. If the installation's identity cannot be opened (a
config.yamlthat does not belong to the database, or a damaged instance file), only the heartbeat stops: Administration says why, and everything else keeps working. When reporting needs an administrator (that, or the DevGrail site refusing this installation's credential), Administration explains what happened and every administrator gets a notification when it starts and when it ends. When the identity cannot be recovered (the site has revoked or rejected its credential, or the config that can read it is lost), an administrator can start a new installation identity there, with their password and by accepting the Operator Agreement again for it. -
The server downloads its legal documents itself. Releases no longer carry them. The installer links the three documents, says that installing, unattended included, does not accept them, and waits up to two minutes for the server to have them. If it cannot get them, the install ends with an error, and DevGrail keeps running and trying.
-
IPv6 visitors are seen as themselves. On a server with IPv6, DevGrail now serves IPv6 visitors directly, so a route's IP addresses can name them, Add my IP adds a browser's own IPv6 address, and the sign-in lockout counts each IPv6 visitor separately instead of all of them together. Before, every IPv6 visitor reached DevGrail from one internal address, so none could be named and one could lock the others out. The installer turns this on when the server has a public IPv6 address and route, and decides again on every run;
--ipv6=nokeeps it off. Add AAAA records for your domain and the wildcard pointing at the server's IPv6 address: the installer prints them, and warns about an AAAA record that points somewhere else, since Let's Encrypt would use it. Workspaces stay IPv4-only. -
The installer needs Docker Engine 28.0 and Docker Compose 2.33.1 or newer. A server with an older Docker is stopped before anything changes, with the versions it found. A server without Docker still gets the current one installed.
-
Breaking: every app subdomain and custom domain now has one access policy, and upgrading resets them all to public. Who can reach a route and whether they sign in used to be four separate settings (project only, sign-in, the workspace bypass and allowed IP ranges). They are now one policy: the route is reachable by anyone or only by the sources you name, and optionally behind a sign-in that the sources you name skip. A source is the route's project, every workspace you have, chosen projects, chosen workspaces, or IP addresses and ranges with a note on each, and a route can now be limited to workspaces outside its own project. After the upgrade every route is reachable by anyone with its URL and has no sign-in: set its access again on its page, and check routes that held anything private first. Logins are kept but unused until the sign-in is turned back on. Snapshots taken before the upgrade restore their routes the same way, as public. Changing a route's access in any way now signs everyone out of it. A route's page edits the whole policy as one draft: who can reach it, whether they sign in and who skips the sign-in, with this project's workspaces, all of yours, chosen projects and workspaces, and IP addresses with notes as sources. It says in one sentence who gets in, offers a skip only from what can reach the route, and asks before a save that narrows a served route or lets more people in. The forms that add a route offer the same choices. Deleting a workspace or a project lists the routes on other workspaces that name it as a source (also
GET /workspaces/{id}/route-sourcesandGET /projects/{id}/route-sources). IP addresses never match a request from a Docker network or from the server itself, so a workspace is only ever admitted as a workspace source. Restoring a snapshot as a new workspace says when a route's source no longer exists and was left out. -
Named IP lists: keep addresses once and use them on any route. An IP list ("Office", "Home VPN") holds addresses and ranges with notes, and a route's access can name it as who can reach the route or who skips its sign-in, beside its own addresses. Editing a list changes every route that uses it at once: its page (Settings → IP lists) shows which routes use it, each linked, and asks before a save that narrows or widens one of them or leaves one with nobody who can reach it. An edit that would put a route over 300 IP entries is refused, naming the routes, and a list in use cannot be deleted until no route names it. On a route's page the lists are chosen beside the other sources, with their addresses shown read-only. API keys get an IP lists section, and agents can read the lists their key is granted with the new
list_ip_listsMCP tool and name them in a route's access (ip_lists), but never change one. Snapshots record which lists a route names; restoring as a new workspace keeps the ones that still exist. -
IP addresses can expire. Any IP address on a route or in an IP list can be set to expire in 1 day, 1 week or 30 days, or at the end of a date you pick. From then on it no longer gets in, but stays on the page marked expired, so you can see what lapsed and renew or remove it; a route left with nobody who can reach it says so. An address that expires soon is marked too. The API and the MCP tools take
expires_aton an entry and returnexpired. Expiry is applied by the DevGrail server, so while the server is down an expired address keeps getting in until it is back. -
Add my IP. Every list of IP addresses, on a route or in an IP list, has an Add my IP button that adds the address your browser comes from, noted "My IP". Where it lets you skip a sign-in it expires in a day, since everyone on the same phone network, café Wi-Fi or office shares it; change the expiry if you need longer. It adds nothing when the address is one DevGrail can never match, and says why: most often the browser connects over IPv6, which reaches DevGrail's proxy from a Docker network. The new
GET /api/v1/client-ipreturns that address; nothing about it is logged or stored. -
Agent tasks: hand a prompt to an AI agent in a workspace. A new Agent tasks page runs Claude Code (
claude -p) headless in the workspace you choose, with that workspace's own sign-in for Claude Code. Codex, OpenCode and the Antigravity CLI will follow on the page later. Each task shows whether it is running, succeeded, failed or was stopped, how long it took, and what the agent printed, live while it runs. A running task can be stopped, which ends the agent and everything it started; a finished one can be run again or deleted. By default a task runs with full access (no permission prompts, which a headless run has nobody to answer). A model, a reasoning effort and a starting directory are optional; the effort list shows the levels the chosen agent takes. Tasks stay in the history after their workspace is deleted. -
Claude Code sessions on the Agent tasks page, read from the workspaces. The page lists every Claude Code session in your workspaces, whether you started it on the page or in a workspace's terminal, with a badge that says which, and the title Claude Code gave it. Open one to read the conversation as Claude Code recorded it: your messages, Claude's answers rendered as markdown, its thinking, and every tool it ran with what it was given and what came back, live while a turn runs. Anything else in the transcript is kept and shown as it was written, collapsed, and each answer has a Raw view. Continue any session with your next message, or copy the
claude --resumecommand that picks it up in the terminal; a session continues in the workspace and directory it started in, one turn at a time. A stopped workspace's sessions are listed from when it last ran, and can still be read. The model is picked from a list (Fable, Opus, Sonnet, Haiku, or any other model id) and the effort by name, and the form warns before you send a prompt to a workspace where Claude Code is not signed in, until you have signed in. Deleting a session removes Claude Code's transcript from the workspace too, so it can no longer be resumed there; a session whose workspace was deleted can be removed as well. DevGrail no longer keeps a copy of your Claude Code prompts and answers: it records only how each of its turns ended, and the end of what Claude printed, which a failed turn shows to say why. The page remembers the prompts it sent while the tab is open, so a turn that failed before Claude recorded it offers its prompt to send again. Links in Claude's answers open in a new tab. A workspace that does not answer is listed as unavailable after a few seconds instead of holding up the list. -
The API keys page shows which keys are in use, and by what. A key used in the last few minutes is marked In use, and a key a connector such as Claude.ai holds access through names the connector. Opening a key lists who has used it in the last 30 days: the workspace an agent ran in, the agent's MCP client and version (Claude Code, Codex, …), the connector it came through, or that it came from outside DevGrail with the key's secret, each with when it was last seen. A connector that is connected but has made no requests is listed too. Last used now counts requests made through a connector as well, so a key used only by Claude.ai no longer reads Never used. Use is recorded at most once a minute per caller.
-
A terminal opened in its own tab names its workspace. The bar above it shows the workspace name before the session, and the browser tab title includes it too, so several such tabs can be told apart. On a phone the bar's Terminal quirks and Reconnect buttons show only their icons, which leaves room for both names.
-
Several images can be uploaded to the file explorer at once. Photos picked together on an iPhone all arrive named
image.jpg, and pasted images asimage.png, so uploading several left only the last: each replaced the one before. Such names are now numbered (image-1.jpg,image-2.jpg, …) and every image is kept. A batch reports its uploads in one message instead of one per file. -
Going to another workspace starts at the top of its page. Moving from one workspace's page straight to another's, from the sidebar or the switcher, kept the scroll position of the one you left. Back and Forward still return to where you were.
-
The Scripts page fits a phone. Its four buttons no longer push past the edge of the screen and squeeze the description into a sliver: they move under the title and wrap. Each script's action icons sit under its details when the list is narrow, and the filters stack one per line. Other pages whose header has buttons get the same wrapping.
-
The installer refuses a registry without release manifests. Every install and upgrade now comes from a signed release; resolving each file on its own, as installers did before releases existed, is gone. A release that lacks a file the installer needs is refused before anything on the host changes.
DEVGRAIL_REQUIRE_SIGNATURE=0no longer turns off the signature check:DEVGRAIL_ALLOW_UNSIGNED=1does.
- User accounts is easier to use, and works on a phone. Each account's controls now open under a Manage button as labelled buttons, grouped by sign-in, permissions, limits and removal, with each setting's current state beside it, in place of a row of unlabelled icons that only explained themselves on hover. New account sits beside the heading, and a search box appears once the list is long. Dialog buttons wrap on narrow screens instead of running off the edge.
- Administration is organized into tabs. Server holds updates, server health, the deployment, the push contact and open-source licenses; Workspaces holds the resource limits, file size limits and SSH port range; Domains & TLS holds app subdomains and both kinds of certificate. The links to System, Traefik routes, Container images and User accounts are now a compact row of tiles above the tabs. Workspace defaults explains each group of limits next to its fields, and a card whose setting fails to load shows the error instead of an empty form that would clear the setting on save.
- How to move to another registry. The Deployment card in Administration now explains that the registry changes by running the installer from the new site on the host, which also moves dashboard upgrades to that site.
- A clearer server health card. Administration's About card is now Server health. It opens with a single Ready or Degraded verdict, says in plain words what each check means and what a failure stops working, and shows when the checks last ran, with a button to run them again.
- Open-source licenses have a card of their own. Administration's new Open-source software card links to the server's license notices and says where the notices for workspaces and the desktop add-on are.
- Publish a release without opening the admin dashboard.
scripts/publish.sh --publish-draftmakes an uploaded draft live, every artifact at once, as the dashboard's Publish button does; add--envand a version for another environment. The Control Center's Release page offers it as Publish the draft, confirmed by typing the version. It refuses an unsigned draft and leaves a release that is already published alone, so rolling back stays in the dashboard. - A DNS provider for wildcard certificates. Administration's new
Wildcard certificates card, or
sudo devgrail dns-provider set <provider> <test-domain>over SSH, checks a DNS provider by issuing a test wildcard certificate from Let's Encrypt's staging CA, and only then hands it to Traefik. The card lists Loopia, Cloudflare, DigitalOcean, Gandi, GoDaddy, Google Cloud DNS, Hetzner, Hostup, Linode, Namecheap, OVHcloud, Porkbun and Vultr with the credentials each one needs, and takes any other provider lego supports except AWS Route 53. The card andstatus,test,removeandpruneon the command line show the provider, test more domains and remove it; a failedsetorteston the command line now exits non-zero. The card's domain to test starts as your default base domain, since a base domain's wildcard switch needs a passed test of that domain, and it lists the base domains that have not passed yet. Values are entered once, never shown again, and kept as root-only files on the host: not in the database and not in backups. - A wildcard certificate per base domain. Each base domain under App
subdomains gets a Wildcard certificate switch. Once the DNS provider has
passed a test of that domain (Test this domain runs one), switching it on
has Traefik order
*.<domain>with no restart; every new subdomain is then served at once, with no certificate of its own and no name in Certificate Transparency logs. Subdomains that already have a certificate keep it. Switching it off asks for your password: Traefik drops the wildcard and restarts, briefly disconnecting everyone, and each subdomain without a certificate of its own then orders one. A base domain with the switch on cannot be removed, nor can the DNS provider. - Wildcard certificates are watched. Every 10 minutes (every minute while
a switched-on base domain waits for its first wildcard) DevGrail reads the
certificate Traefik serves under each base domain, and App subdomains shows
it: ordering, active (in green) and valid until a date, renewal overdue or
expired. Admins get a notification when a switched-on wildcard becomes
active. If no wildcard arrives within 2 hours of switching on, or one has
under 20 days left, admins get a notification, at most once a day, naming
the command to test the provider. A first order that failed is retried on
its own. A wildcard 3 days from expiry is removed before it can block every
subdomain under it, and admins are told. A wildcard still served after its
switch went off shows as left-over, with Remove it. The System overview
lists these problems, and warns when the dashboard itself is served a
wildcard instead of a certificate of its own.
docs/RUNBOOK.mdhas a section on wildcard certificates: the Loopia API user and its permissions, the staging test, what switching off costs, what the alerts mean, and recovery over SSH, including after restoring onto a rebuilt host. - Why a wildcard certificate is worth having. While no base domain has one, Administration suggests it near the top of the page, and a workspace's Subdomains section suggests it to admins when its base domain has none: a new subdomain works over HTTPS at once, its name stays out of the public Certificate Transparency logs, and it no longer counts against Let's Encrypt's rate limits. Set up a wildcard certificate goes straight to the Wildcard certificates card, which now opens with the same reasons. Dismiss the suggestion once and it stays gone everywhere in that browser.
- Database upgrades keep migrations in numeric order. Migration 1000 and later run after 999, preserving the order of existing migrations.
- Limit app subdomains by IP address. In the Domains tab, allow individual
IPv4/IPv6 addresses or CIDR ranges when adding a subdomain or editing it.
Add an optional inline comment such as
203.0.113.10 #Kalles IPto remember who an address belongs to; comments are kept in saved lists and snapshots. The IP access panel shows the saved status; choose Edit IP access to make changes, then Save IP access or Remove IP restriction to apply them. Unsaved changes are clearly marked. Saving does not restart the workspace. The restriction also gates sign-in pages, workspace bypasses and HTTP redirects; excluded sources get 404. REST, MCP and snapshot restores preserve the same policy. - Manage deployment backups from Administration. Create, list, download and delete database/configuration archives; choose daily, weekly or monthly backups and optional retention. Downloads require your password. Restore remains a host command, and workspace files still need separate backups. The Manage backups link has a bordered button style and backup icon so it is easier to spot in the Deployment card.
- Dark mode applies before the dashboard first appears. Theme initialization now works with the installed dashboard's security policy, fixing a flash of the light theme and a blocked-script error in the browser console.
- Passwords stay hidden during installation. Typing an admin password and
its confirmation no longer echoes the characters when installing with
curl | bash.
- See how much disk a workspace uses. A workspace's General tab has a
new Disk row: the total, then the parts that make it up. Those are its
/workspacevolume, its Docker data if it is Docker-enabled, and its container, which covers everything else it has written, such as installed packages and the home directory. The card on the Workspaces page shows the total, with the parts when you point at it. Administrators get a Disk column for every workspace on the System page, stopped ones included. Snapshots are not counted here; they are listed where they always were. Measuring walks every volume on the server, so it happens in the background (at most every five minutes while someone is looking, and every half hour otherwise) and pages never wait for it. Each figure says how old it is. The API has it atGET /api/v1/workspaces/disk-usage. - The System page shows when it is still catching up. After you stop, start or resize a workspace, its row stays busy and Refresh spins until the list has reloaded. Before, the confirmation appeared while the row still showed the old state for a few seconds.
- The nested domain section is no longer only about DevGrail. The Domains
tab describes a nested domain as what it is: every name under it sent to a
server in the workspace that handles its own TLS, whether that is Traefik,
Caddy or another DevGrail. The form no longer previews an inner dashboard, and
a claimed domain no longer lists settings for an inner DevGrail or links to
one. The checklist says "Ready" once everything passes. The API and the MCP
tools follow suit: a nested domain and its check no longer carry
dashboard_url, and the check no longer carriesinner_settings.
-
Agents copy files between workspaces without spending tokens. The new
copy_filesMCP tool copies a file or a whole directory from one workspace to another, or within one, inside DevGrail: the content never passes through the agent, so a large or binary file costs nothing to move. Until now an agent read the file and wrote it back out withwrite_file, paying for every byte twice. It needs a read grant on the source and a write grant on the destination, and both ends run as the workspace user, so a copy reads only whatread_filecould and writes only wherewrite_filecould; setuid bits, ownership and anything that would land outside the copy are refused or dropped. The descriptions ofwrite_file,edit_fileandread_filenow say when to use which. Tools are granted one by one, so an existing API key getscopy_filesonly once you add it to the key. -
Desktop: move the controls tab out of the way. Drag the small tab that opens the desktop controls anywhere along the edges of the view, if it covers something in the desktop; this browser remembers where you left it. The controls now open as a panel floating beside the tab, instead of a drawer pinned to the left edge.
-
Desktop: see how much data the desktop uses. The new Connection section in the desktop controls shows what the browser tab receives and sends each second, and the round trip to the desktop. Click it for the picture and the sound apart, a graph of the last minute, the data used since the page opened, roughly how much an hour takes at that rate, how many pictures a second arrive, and how the sound's buffer is holding up. Turn on Show on the picture to keep the readout in the bottom left corner while you work. It measures only while it is on screen.
-
Desktop: the speakers cost nothing while the desktop is quiet. With Speakers on, the desktop used to send about 100 kbit/s of sound even in silence, some 40 MB an hour. Now it stops half a second after the sound does and starts again the moment anything plays, so an idle desktop with the speakers on uses next to no data.
-
Desktop: the microphone badge is a switch. The round microphone button in the bottom left corner is always there now and turns the microphone on as well as off: grey and struck through while it is off, red while the desktop hears you, amber while it is on but the sound is not through yet.
-
Nested domains: expose a DevGrail running inside a workspace yourself. The owner of a Docker-enabled workspace can claim every name under
*.<label>.<base domain>for it from the workspace's page, the API or the newset_workspace_nested_domainMCP tool. HTTPS is passed through untouched, so the inner DevGrail (or any other proxy) gets its own certificates, and plain HTTP is forwarded too; the ports default to the onesdev.shpublishes, 10443 and 10880. After the claim, the page checks every 10 seconds what is still missing (the DNS record to add, the operator's--acme-bypass=yes, something listening in the workspace), shows the settings for the inner DevGrail ready to copy, and links its dashboard once everything passes;check_workspace_nested_domaindoes the same for agents. A link with?nested_label=<label>opens the form filled in. An admin allows it per account on the Users page, and turning it off withdraws the account's claims. A label cannot clash with an app subdomain, a base domain, the dashboard or an admin route, in either direction, and the refusal says what holds it. Admins see and withdraw every claim on the Traefik routes page, which also says whether this server passes certificate challenges through. -
Workspace setup in the Control Center. A workspace domain profile sets up a nested DevGrail with TLS, public site hostnames and manual outer route instructions. Setup checks the public dashboard and can install an optional restart hook for the dev loop and Control Center. Public Control Center access is an explicit opt-in and always requires its access token; existing env values and startup hooks are preserved.
-
Correct dev-loop status URL.
./dev.sh stopkeeps the HTTP URL in.dev/status.jsonwhenDEVGRAIL_TLS=off, matching the running dev loop. -
Consistent test commands and VPS integration.
test.sh,make testand fast CI now run the same Go, dashboard, desktop, installer and repository checks, with a separate Linux Go race job.test/vps/vps.sh integrationcompiles tests locally and runs them against the existing disposable VPS installation. Live tests fail on missing prerequisites or cleanup errors, restore original MCP settings, and support staging certificate trust and configurable admin names. Binaries sent from Windows use VPS-local file ownership so the internal MCP probe works with Docker user namespace remapping. The installer shell tests run only on Linux, behind a Docker command that refuses any call a test has not faked. The test guides describe the current suites and their remaining coverage gaps. -
Workspaces come with Node.js 26. New and recreated workspaces have Node.js 26.11.1 instead of 22. Projects that need an older Node can install it inside the workspace, for example with nvm.
-
Security: the dashboard's router is up to date. The dashboard now uses React Router 7.18.4, which fixes an open redirect through links written with a backslash.
-
Notification cleanup. Notification timers are cancelled when dismissed or when leaving a page. Requests finishing after navigation no longer start notification timers on the page that was closed.
-
Copy error messages. Error notifications now include a copy button so you can copy the full message to the clipboard for troubleshooting or sharing.
-
Clearer subdomain visibility guidance. The workspace page now explains External (public internet access) and Internal (access from the workspace and its project's other workspaces) beside the subdomain controls, including how to view an internal app and why a browser outside those workspaces gets a 404.
-
Internal subdomains and custom domains. An app subdomain or custom domain can now be internal: the same
https://URL, with the same valid certificate, but it answers only requests from the workspace that publishes it and the other workspaces in the same project. Everyone else gets the same 404 as a hostname that does not exist, including your own browser outside those workspaces, so an agent's API can be shared with its project's other agents without putting it on the internet. To look at one in a browser, use a browser inside a workspace of the project, such as the desktop add-on's Firefox. Choose External or Internal when adding a subdomain or custom domain, on the create form, or switch an existing one from its row; the workspace keeps running. Over the API it isvisibilityon the subdomain and custom-domain requests and responses, plusPATCH /workspaces/{id}/subdomains/{label}andPATCH /workspaces/{id}/domains/{host}. For agents,add_workspace_subdomainandcreate_workspacetakevisibility, the newupdate_workspace_subdomaintool switches it, andget_workspacereports it. An internal name still has to point at this server in public DNS so its certificate can be issued, and like every certificate's name it appears in public Certificate Transparency logs. Snapshots keep each route's visibility. docs/RUNBOOK.md has a section on diagnosing an internal route that does not answer. -
Switch subdomains and custom domains off and on. Each subdomain and custom domain on the workspace page now has an on/off switch. Switched off, it is not served to anyone and its URL answers 404, but it stays on the workspace: the label or domain remains reserved, so no other workspace can take it, and switching it back on serves it again with the same port and visibility. New subdomains and custom domains are always on. The workspace keeps running either way. Over the API it is
enabledon the subdomain and custom-domain responses and on the samePATCHrequests that change visibility, each of which now changes only the fields it is given; agents passenabledtoupdate_workspace_subdomain, andget_workspacereports it. Snapshots keep a switched-off route switched off, and the Traefik routes page marks a custom domain its owner has switched off. -
Change certificate environments from Administration. Switch between Let's Encrypt production and staging while preserving both certificate stores. The page explains the temporary interruption, warns that staging can block browser access, and provides
sudo devgrail certificates productionfor SSH recovery. Traefik routes shows the active environment and links to the setting. Switching also works in the localdev.shloop, with a local terminal recovery command and no installer or systemd requirement. -
Docker-enabled workspaces. A workspace can now run Docker: its own daemon, containers,
docker buildanddocker compose, without access to the server's Docker and without being a privileged container. An admin allows it per account on the User accounts page, where a dialog explains what it changes first. Those accounts then see a "Docker-enabled workspace" option when creating a workspace (runtime_mode: "docker"in the API and the MCPcreate_workspacetool). It is chosen at creation and cannot be switched later. Inside, install Docker the usual way (sudo apt install docker.io, Docker's own instructions, or the official "Install Docker" script, run from Scripts or with plainsudoin the terminal); it starts by itself, and again after every restart.What to know before allowing it: these workspaces expose more of the Linux kernel to the code in them (they may mount filesystems, use FUSE and manage their own networking), so a kernel bug is likelier to let that code break out. A break-out still lands as an unprivileged user, and such a workspace runs under its own security profile that keeps it away from other workspaces' files. The inner daemon and its containers share the workspace's CPU, memory and process limits. Admins can now raise a single workspace's process limit from the System page, which warns when a Docker-enabled workspace is below 8192. Inner images live on a volume of their own: snapshots leave it out, deleting the workspace removes it, and it counts toward the account's disk usage. File-heavy work inside inner containers is several times slower than on the host, and a few image builds fail (replacing a dangling symlink over a file from a lower layer, as Ubuntu's
localespackage does). The host needs Docker's user namespace remapping and/dev/fuse; the installer enables the feature when both are present. DevGrail's own workspace image now builds inside such a workspace too, so DevGrail can be developed withdev.shin one. -
A DevGrail inside a workspace can have its own HTTPS certificates. A second DevGrail running in a Docker-enabled workspace can now serve its dashboard and app subdomains, for example under
*.inner.example.com, with Let's Encrypt certificates it orders itself. Re-run the installer with--acme-bypass=yesand add a TLS-passthrough Traefik route for those names to the workspace; the installer remembers the answer. Without the option, this DevGrail answered every certificate challenge itself, so the inner one could never get a certificate. The option is off by default and does not change how this DevGrail's own certificates are issued. The setup, with the route for plain-HTTP redirects, is indocs/RUNBOOK.md.dev.shnow also honoursDEVGRAIL_ACME_CASERVER, so a dev loop can use the Let's Encrypt staging CA. HTTP companion and TCP passthrough routes can share the same host, so the wildcard HTTPS route can coexist with its HTTP redirect route. -
Routes to your own workspaces, and a preset for a nested DevGrail. The admin route dialog can now target a port of one of your own workspaces by picking it, rather than typing its container name, and Expose a nested DevGrail creates both routes a DevGrail in one of your Docker-enabled workspaces needs: HTTPS passed through to it and plain HTTP for its redirects. A wildcard host on a route without passthrough is now served over plain HTTP with TLS switched off for you, since it could never get a certificate; the dialog says so for HTTP routes as well as passthrough ones.
-
Security: the database and certificates are closed to workspaces. Docker keeps DevGrail's database and its TLS certificates in the same place on disk as every workspace's files. If code in a workspace ever broke out of its container without gaining full control of the server, it could have read the database or deleted the certificates. Both are now readable by the server and Traefik only. Existing installs are fixed automatically when the upgrade restarts the stack, and nothing changes in how DevGrail works.
-
Security: workspaces are closed to each other at the host. Every workspace's files live in one place on the host, under one shared set of user IDs. Code that broke out of one workspace without taking over the server could have read or changed every other workspace. On hosts with AppArmor (Ubuntu and Debian have it), workspaces now run under a DevGrail security profile that blocks that place on disk, and the profile still applies after such a break-out. The installer loads it and keeps it across reboots. New workspaces get it right away. An existing workspace gets it when its container is next rebuilt, for example by restoring a snapshot. Inside a workspace the only difference is that
/var/lib/dockerand/run/dockercannot be used. Docker could not run in a workspace anyway; a Docker-enabled workspace (above) keeps its inner Docker elsewhere. -
Error messages are hard to miss. Pop-up messages at the bottom right of the dashboard now sit on a solid surface instead of a see-through tint, so the page under them no longer shows through. Each kind has its own icon and coloured edge: red for errors, amber for warnings, green for success. They slide in when they appear, show above an open dialog instead of behind it, and fit the screen on a phone. Screen readers announce errors right away.
-
Two people can give workspaces the same name. Creating a workspace no longer fails because another user already has one with that name. A workspace's address (its terminal URL, Docker container and network) now ends in a short tag tied to your account, such as
web-3f9a1c, so it never collides with someone else's. Names are unique among your own workspaces instead: creating or renaming one to a name you already use is refused with a clear message. Upgrading renames any duplicates you already have to "Web (2)" and so on. Existing workspaces keep their current addresses. -
A slow workspace create no longer breaks the next ones. If the browser or API client stopped waiting while a workspace was still starting, the container could keep running with no workspace attached to it, holding an SSH port. Every later create was then handed that same port and failed with "port is already allocated". A create now runs to the end once it has begun: it either finishes or cleans up after itself, whether or not anyone is still waiting for the answer.
-
More shared memory for development tools. Newly created workspace containers and containers recreated by snapshot restores have a 1 GiB
/dev/shm, up from Docker's 64 MiB default. Shared-memory usage still counts toward the workspace's memory limit. Existing containers keep their current size until recreated. -
Install apps and developer tools from desktop controls. Open Install apps & tools to search the catalog and install, update or uninstall VS Code, ChatGPT, Claude Desktop, Antigravity, ripgrep, GIMP and Inkscape. Downloads happen only when requested, from official signed repositories. The panel shows installed versions and progress; uninstall keeps settings and refuses to remove a package another installed package needs. Electron launchers use
--no-sandboxinside the workspace container. VS Code and Antigravity open directly from the terminal even when Docker shares a WSL2 kernel, without prompting to install the Windows app. Claude Desktop launchers use--disable-dev-shm-usageto prevent a white window and connection error caused by renderer crashes with Docker's default shared-memory limit. The panel scrolls to the top after actions to show confirmations and progress. -
Notifications from the desktop reach you. A notification from an app in the desktop add-on's desktop, such as
notify-sendat the end of a long build, now shows at the top right of the viewer, where you can click it or its buttons. Turn on Notifications › On this computer in the desktop's controls to get them as your computer's own notifications while the viewer is in the background; an installed desktop app also counts unseen ones on its icon. -
The browser tab names the window you are working in. The desktop viewer's tab, and an installed desktop app's window, now take the title of the window in front of the desktop, so you can tell desktops apart in the tab bar and the window switcher.
-
Everyday apps in the desktop. The desktop add-on now installs a text editor, Meld for comparing and merging files, an image viewer, an archive manager, screenshots, a task manager and a calculator, and PDFs open in Firefox. Upgrading an installed desktop adds them.
-
The desktop on a phone or tablet. A keyboard button at the bottom right of the viewer, or Show keyboard in its controls, brings up the on-screen keyboard to type into the desktop, with a row of the keys it lacks: Esc, Tab, Ctrl, Alt and the arrows. The desktop makes room above the keyboard, and its Help explains the touch gestures: tap to click, touch and hold to right-click, two fingers to scroll. On an iPhone, where a page cannot go full screen, the controls no longer offer it.
-
The desktop picks up where you left off. After the desktop restarts, is upgraded or its workspace restarts, its terminals reopen in the folders they were in, and Firefox reopens with its windows and tabs. The desktop saves what is open every minute and before it stops.
-
Help in the desktop add-on. A Help button in the desktop's controls opens a guide over the desktop: how copy and paste work between your computer and the desktop (and why a copy from the desktop sometimes waits for a click), dropping and downloading files, keyboard shortcuts the browser keeps, sound and the microphone, and what to know about a desktop everyone shares. The controls now also close when you click anywhere outside them.
-
Paste JPEG, WebP, GIF and other images into the desktop. Pasting an image used to work only for PNGs, such as screenshots. An image in any format your browser can show, such as an image file copied in Explorer or Finder, is now converted to PNG on the way in (an animation keeps its first frame).
-
Paste files into the desktop. Copy files on your computer and paste them into the desktop's file manager, or onto its desktop, and they land in the folder you pasted them in. Until now, pasting a copied file did nothing; only dragging it onto the desktop worked. A single image file still pastes as an image.
-
A progress bar for files copied into the desktop. Dropping or pasting a large file shows how much of it has arrived.
-
Find add-ons in the dashboard. A new Add-ons page in the sidebar lists what you can add to a workspace you already have, with a guide for each. The desktop add-on's guide gives you the install command for your DevGrail to copy into the workspace's terminal, and walks through adding its subdomain, signing in, its options, RDP, upgrading and removing it. Pick a workspace on the page to get links straight to its terminal and settings.
-
The desktop add-on moved on the DevGrail website. Its guide is now at
/addons/desktop, and the install command iscurl -fsSL …/addons/desktop/install.sh | bash. The upgrade command the desktop shows uses the new address once the add-on is installed or upgraded from it. -
Breaking: the desktop image and the dashboard's Desktop tab are gone. The desktop add-on replaces them: install it into any workspace, from the guide on the Add-ons page, and reach its desktop through an app subdomain. The installer no longer offers or downloads
devgrail-desktop, its--desktop-imageflag is gone, and Container images and Updates no longer list it. Before upgrading, move anything you want to keep out of workspaces created from the desktop image into a default workspace (add the desktop add-on there if it needs a desktop), then delete them. One left running keeps running, but its desktop no longer connects, and it cannot be recreated once the image is removed. After upgrading, remove the old image withdocker rmi $(docker images -q devgrail-desktop), and delete theDEVGRAIL_DESKTOP_IMAGEandDEVGRAIL_DESKTOP_IMAGE_DECLINEDlines from/opt/devgrail/deploy/.env, which nothing reads any more. -
Breaking: an agent can no longer read API keys and secrets it was never given. A script run puts the values of its
@[apikey:…]and@[secret:…]references into the workspace, where the agent can read them. Any API key holdingrun_scriptand write access to one workspace could therefore name any of your keys or secrets — including a full-access key with external access — and read it. A run an agent starts may now only use the agent's own key, a key with no more access than it (and without external access unless the agent's key has it), and secrets granted to its key. Anything else stops the run before it starts, with a message naming the key or secret. Runs you start from the dashboard and automations are unchanged. -
Breaking: secrets are granted to API keys. The key editor has a new Secrets section, with an "all my secrets" option like the ones for scripts and skills. Existing keys hold no secrets. If an agent runs scripts that use your secrets, or updates or deletes secrets over MCP, open its key under API keys and tick the secrets it should use. A secret an agent creates is granted to its key automatically. A secret whose value is a file in a workspace is usable through its grant alone, even by a key with no access to that workspace, so grant one only to agents that may read that file.
-
Breaking: an agent saving a script may only add references it could use itself. Creating, editing or cloning a script over MCP is refused when it would add a reference to a key or secret the agent's key may not use, so an agent cannot leave one for you to run. References a script already had do not block an edit.
-
Deleting a workspace shows that it is under way. The delete dialog closes as soon as you confirm, and removing a workspace can take a while. Its card on the Workspaces page now dims and its Delete button spins and reads "Deleting…" until the workspace is gone, instead of looking untouched.
-
Signing in to the desktop add-on works. In v0.4.54 the browser was told to leave out where a request came from, so the desktop turned away its own sign-in form with "This sign-in did not come from the desktop's own page". Once signed in, the picture and sound connections were turned away the same way, so the desktop kept showing "Desktop disconnected" and reconnecting. Run the install command again in the workspace to upgrade it.
-
Strangers can no longer keep you out of the desktop add-on. The desktop's sign-in allows thirty attempts a minute in all, to slow down password guessing from many addresses at once, and anyone who kept that limit used up could stop the owner from signing in. A browser that has signed in to the desktop before is now remembered and is not held up by that limit, only by its own five attempts a minute. Addresses on one IPv6 network now count as one address. Run the install command again in the workspace to upgrade.
-
Adding the Claude.ai connector can no longer be blocked from outside. DevGrail keeps at most 1,000 connector registrations, and registering needs no sign-in, since Claude.ai registers before you sign in. Anyone who kept registering could keep that table full, and adding a connector then failed with "temporarily unavailable". A new registration now pushes out the oldest one that never became a connection. Connectors in use are never touched.
-
Sign-in lockouts count an IPv6 network as one address. After five wrong passwords the dashboard holds back further sign-ins from that address for fifteen minutes. An IPv6 caller could previously move to the next address on its network and keep guessing; addresses on one IPv6 network (/64) now share one count. The connector registration limit counts them the same way.
- Add a desktop to any workspace. A new desktop add-on puts a full
desktop into a workspace you already have, with no desktop image and no
dashboard setting. In the workspace's terminal, run the command on the
DevGrail website's
/desktoppage (curl -fsSL …/desktop/install.sh | bash), add a subdomain for port 26080 under the workspace's Subdomains, open it and sign in with the workspace's SSH password. You get XFCE with Firefox, sound and microphone, clipboard and file drop, scaling and settings that follow your computer, and RDP to the same session through an SSH tunnel. The desktop can be turned off from its own menu to free its memory, and turned on again after signing in. Running the command again upgrades it, anddevgrail-desktop uninstallremoves it. The installer checks everything it downloads against the release signature, like DevGrail's own installer. - Workspaces run on Ubuntu 26.04 LTS. The workspace image moves from
Ubuntu 22.04 to 26.04, so the system tools inside a workspace are current:
Python 3.14, Git 2.53 and OpenSSH 10.2 among them. Existing workspaces keep
their files and get the new system when they are created again from the
updated image. One difference to know about:
pip installoutside a virtual environment now refuses to install into the system Python, as it does on every Ubuntu since 23.04. Usepython3 -m venv, or pass--break-system-packageswhere a system-wide install is really wanted. The SSH server now also slows down each address that keeps failing to log in. - Updates shows the desktop image. On a host with the optional desktop image installed, the Updates card under Administration now lists which release it came from and whether a newer build of it is published, next to the server and the workspace image.
- Desktop sound no longer stutters in a background tab. The desktop's sound used to break up when its tab was hidden, or while the picture kept the page busy, because the browser slows a hidden page down. The sound now runs apart from the page, so neither affects it. When the network is uneven, the viewer now buffers a little more sound instead of breaking up again and again, and goes back to the shortest delay once things settle. Gaps and restarts fade in and out instead of clicking.
- The microphone keeps its delay short in long calls. A voice chat in the desktop could fall further and further behind: any hiccup on the way added delay to the microphone that stayed until it was turned off. Now the delay stays under a fraction of a second. A desktop workspace created before this release gets this part once it is created again from the updated desktop image.
- Get back to your recent workspaces in one click. The sidebar lists the five workspaces you opened most recently under Workspaces, with a dot for each one's status. Click a name to open the workspace. The terminal icon beside it goes straight to its terminal. Opening a workspace's page, its full-screen terminal or its desktop window counts as using it. An agent working in it does not. The list is kept on the server, so it is the same on every device you sign in from.
- Jump to any workspace with Ctrl+K (⌘K on a Mac). Recent workspaces come first. Type to filter by name, use the arrow keys to move, and press Enter to open one. Shift+Enter opens its terminal and Alt+Enter its desktop. There is also a Find workspace button at the top of the sidebar, and a search button in the top bar on a phone. Ctrl+K typed in a terminal, the desktop or the file editor still goes to them.
- The browser's Back button works on a workspace's page. Switching tabs, opening a folder in the file explorer and opening a file each add a step to the browser history. Back retraces them: it closes a file preview, returns to the folder above, and goes to the tab you were on before. Before, it left the workspace. The folder and file you have open are part of the address, so a reload or a copied link opens the same place. The explorer also keeps your folder when you switch to another tab and come back.
- Deleting the last workspace in a project offers to delete the project. When you delete a workspace on the Workspaces page and its project has no workspaces left, you are asked whether to delete the empty project too. Cancel keeps it.
- Delete a project together with its workspaces. The delete dialog, on the
Projects page and on a project's own page, has a new option, off by default,
to also delete every workspace in the project. Only workspaces in that
project are deleted: the server checks each one as it removes it, so a
workspace moved to another project in the meantime is left alone. Their
volumes and snapshots are kept. If a workspace cannot be deleted, for
example because it is delete-locked, the project is kept with it. For API
callers,
DELETE /api/v1/workspaces/{id}takes a newproject_idparameter that refuses (409) to delete a workspace not filed under that project.
- The uninstaller asks what to remove. Run on a terminal without flags, it asks about one part at a time: the database and config, the workspaces, their snapshots, the TLS certificates, the images, the backup archives, whether to take a backup first, the changes install.sh made to the host, and Docker Engine. Pressing Enter always keeps the part. Removing everything except your workspaces is a yes to the first question and Enter for the rest. Before anything is deleted, it shows the resulting plan and asks once to go ahead.
- Keep the certificates, images or backups while removing the rest. New
flags
--keep-certs,--keep-imagesand--keep-backupskeep those parts through a--purge. A reinstall on the same hostname then reuses the certificates instead of ordering new ones from Let's Encrypt, skips downloading the images again, and can restore from the archives left in/opt/devgrail/backups.--remove-snapshotsremoves the snapshots of workspaces kept with--keep-workspaces. The existing flags work as before, and--yesstill runs unattended without asking anything. - Deleting a script removes it from your API keys. A key that had been
granted a script you later deleted kept a grant on nothing, and an agent
that sent the key's grants back through
update_api_keywas refused with "script not found". The same happened to keys granted the retired "Common CLI tools" built-in. Upgrading removes every grant left behind this way, for scripts and for deleted workspaces, projects and skills. - The OpenCode DevGrail controller setup script says workspaces in its description, in the help for its API key and in the next steps it prints, like the rest of DevGrail.
-
Containers are now called workspaces throughout the dashboard. The sidebar entry, the list, the create page and every page that mentions one say workspace, and the pages have moved from
/containersto/workspaces. Old links and bookmarks still work: they forward to the same page, tab included. Container is kept where it means the Docker container a workspace runs in, which a snapshot restore can replace while the workspace stays. The Workspaces page forgets whether you chose the grid or the list once, after the upgrade, and the Terminal tab forgets which terminals you had tiled in its grid; the terminals themselves keep running. -
Breaking: the API says workspace too. Every
/api/v1/containers…path is now/api/v1/workspaces…, and/api/v1/admin/container-domainsis/api/v1/admin/workspace-domains. The old paths are gone rather than aliased. Schemas and operation IDs follow (WorkspaceResponse,CreateWorkspaceRequest,listWorkspaces, …), and so do the fields that name a workspace:container_idisworkspace_id,container_nameisworkspace_name, andattached_container_*andsource_container_*areattached_workspace_*andsource_workspace_*. An API-key grant on a workspace hasresource_typeworkspace, notcontainer. Automations have acreate_workspaceaction (wascreate_container), aworkspaceparameter type, and aworkspace_idfield onrun_scriptandcreate_api_keysteps. Keys, saved automations and automation history are moved onto the new names when the server upgrades. Error messages follow: "workspace is not running", "workspace is delete-locked". -
Breaking: so do the MCP tools. The fourteen tools named after containers are named after workspaces:
list_workspaces,get_workspace,get_workspace_logs,create_workspace,update_workspace,delete_workspace,start_workspace,stop_workspace,restart_workspace,list_project_workspaces,add_workspace_to_project,remove_workspace_from_project,add_workspace_subdomainandremove_workspace_subdomain, in a tool group calledworkspaces. Arguments and results follow:container_idisworkspace_id,source_container_idissource_workspace_id, andlist_workspacesreturnsworkspaces. Keys granted the old tools, and automations that list them for a new key, are moved onto the new names when the server upgrades. A Claude.ai connector sees a new tool list and asks again before using the renamed tools. -
Breaking: a workspace's id is in
DEVGRAIL_WORKSPACE_ID. New workspaces, and any workspace a snapshot is restored onto, getDEVGRAIL_WORKSPACE_IDinstead ofDEVGRAIL_CONTAINER_ID. A workspace whose container was created before this release keeps the old variable until then, because a running container's environment cannot be changed; the instructions file DevGrail writes into it states the id either way. The agent-instructions placeholder is{{DEVGRAIL_WORKSPACE_ID}}: saved instructions are moved onto it when the server upgrades, and{{DEVGRAIL_CONTAINER_ID}}still renders. -
The database says workspace as well: the
containerstable isworkspaces,container_domains,container_snapshotsandcontainer_subdomainsareworkspace_domains,workspace_snapshotsandworkspace_subdomains, and everycontainer_idcolumn isworkspace_id. The tables are renamed in place when the server upgrades; no data is copied. This matters only if you query the SQLite file yourself. A backup taken after the upgrade restores only onto this release or a later one, as with any schema change. -
The server log says workspace too: a log line about a workspace names it under
workspaceorworkspace_id, notcontainerorcontainer_id. This matters only if you filter the server's logs by those keys. -
The MCP server now gives agents instructions when they connect: what a workspace is, and that
/workspaceis a directory inside one. The tool descriptions and the instructions DevGrail writes into each workspace say workspace too, so asking an agent for "the test workspace" no longer sends it searching/workspacefor files. Each workspace'sdashboard_urlnow points at/workspaces/<id>. -
The create page no longer says a volume lets a workspace be "recreated", or that an ephemeral workspace loses its files when recreated for a new image. Recreating was replaced by restoring a snapshot onto the current image. An ephemeral workspace's
/workspacegoes into its snapshots and is replaced when one is restored over it. -
Opening several terminals in a workspace now connects each pane to its own session. Before, every pane could display and control the same session.
-
sudo devgrail passwdandsudo devgrail add-userkeep spaces at the start or end of a password. Before, they dropped them, so the password saved was not the one you typed and you could not log in with it. A password set that way before this release still works without those spaces. -
Both commands also take the password from a pipe, so a provisioning script can run them:
printf '%s\n' "$PASSWORD" | sudo devgrail add-user alice. At a terminal they prompt twice as before. -
Secret names are limited to 64 characters, as the API reference always said. Before, the dashboard and the API accepted longer names and only the MCP tools refused them. A secret that already has a longer name keeps it, and you can still change its value without renaming it.
-
Creating a workspace with a launch script whose parameters are missing or wrong no longer leaves an unused volume behind. The volume was made before the parameters were checked, and was not removed when they failed. Volumes left this way by earlier versions are listed under the Volumes tab of the Containers page, where you can delete them.
-
A project's name and description are saved the same way wherever they come from: spaces at the start and end are dropped, and the documentation is kept exactly as written. Before, the dashboard kept spaces around a description, and a project an agent created lost the indentation at the start of its documentation, which could break a Markdown code block.
-
When the server cannot reach Docker, the System page and the Orphaned networks page load again: the one shows what it can, the other says Docker is unavailable. Before, the server dropped both requests without an answer, so the pages showed an error.
-
A folder or multi-file download that breaks partway through, for example because the workspace stopped, now shows as failed in the browser. Before, it could look finished and leave a zip that would not open.
-
The dashboard, the guides and the built-in agent skills now talk about Antigravity CLI (
agy) rather than Gemini CLI, which workspaces stopped shipping in v0.4.40. An agent helping you set up Antigravity is pointed at the Antigravity scripts, its sign-in token andagy mcp add, instead of atgeminicommands that no longer exist in a current workspace.
- Administration → About links to the open-source licenses of everything
DevGrail is built with: the Go libraries in the server and the JavaScript
packages in the dashboard, each with its license text in full. Workspace
images carry the same for their tools in
/usr/share/doc/devgrail/THIRD_PARTY_NOTICES. - The file explorer can download folders and several files at once. Select them and choose Download, or use the download button on a folder's row: they arrive as a single zip with everything inside, however large. A single file still downloads as itself.
- The Git client can squash commits that have not been pushed. The Log tab marks unpushed commits with an arrow and offers to squash them all at once, or tick a commit to squash it with every newer one. You get to edit the combined message first. Staged and modified files are left as they are, and commits already on a remote branch are never rewritten.
- The file explorer can move files and folders by drag and drop: drop onto a
folder, the
..row, or any folder in the path at the top. A move never overwrites, so a name already taken at the destination is reported instead. - The file explorer can select several entries at once, with the checkbox on each row, Ctrl/Cmd+click, Shift+click for a range, or Ctrl/Cmd+A. The toolbar then downloads or deletes the selection, and dragging a selected entry moves all of them.
- The Git client shows full file paths. File lists in Changes and in a commit's details put the file name on its own line with its folder beneath it, and wrap long paths instead of cutting them off. Each file has a colored status letter (added, modified, deleted, renamed), and in a commit its lines added and removed. Diff file headers wrap too.
- The Log tab puts commits beside their details on more screens, and the commit list scrolls on its own so the selected commit stays in view. The list shows up to two lines of each commit's subject and the details show all of it. Branch and tag names get a badge each. Clicking a file in a commit jumps to its diff every time, including the same file twice, and does so immediately even in large commits.
- The Git client's Changes tab can show the whole file: "Whole file" in the diff toolbar shows every line with the changes marked, instead of just a few lines around each change. The setting is remembered. Untracked files now show their contents too, instead of "No changes to preview".
- Deleting a workspace removes its repositories from the Git client's Recent list. Entries left behind by workspaces deleted earlier are cleared on upgrade.
- The Git client's diff view is easier to read. Syntax-highlighted lines no longer appear as dark boxes on the light theme, and code colours follow the light or dark theme. Added and removed lines are tinted green and red, with stronger line-number gutters and +/- markers. Each hunk header shows the enclosing function, and each file header shows a change bar plus a New file, Deleted or Renamed label. Words are highlighted within a line only when the old and new lines still resemble each other. The diff no longer shows an extra empty line after the last line of a file.
- The Git client's side-by-side panes can be resized. Drag the divider between the changes list and the diff, between the commit list and a commit's details, or between a commit's files and its diff. Widths are remembered in your browser. Double-click a divider to reset it, or focus it and use the arrow keys.
- Admins can upgrade DevGrail from the dashboard. When a newer release is
available, Administration > About has an "Upgrade to vX.Y.Z" button. It asks
for your password and hands the upgrade to a small helper the installer sets
up on the host. The helper checks the release signature and the new
installer's checksum, then runs the installer. It backs up first and rolls
back on its own if the new version doesn't come up healthy. The page follows
the upgrade through the server restart and shows the installer's output. It
can only upgrade: downgrades and rollbacks still happen from a root shell.
The helper needs systemd and is set up the next time you run the installer
from a shell. Turn it off with
--dashboard-upgrades=no. - The optional desktop image can be installed from Administration > Container images. When it isn't on the host, its row has an Install button. It uses the same host helper as dashboard upgrades.
- The installer asks whether to download the desktop image on an interactive
install of an amd64 host. The default answer is no, and the installer
remembers a no. Unattended installs still skip it unless you pass
--desktop-image=yes.
- The browser desktop now has a compact noVNC control tab for full screen, Ctrl+Alt+Delete, scaling, remote resizing, and view-only mode.
- Esc works inside the browser desktop in full screen. In Chrome and Edge, full screen now captures the keyboard, so Esc and shortcuts like Ctrl+W go to the desktop; hold Esc to leave full screen. Firefox and Safari always leave full screen on Esc, so the control tab adds a Send Esc button, and a Fill window mode that covers the browser tab without full screen. Combine Fill window with the browser's own full screen (F11) and Esc reaches the desktop there too.
- Pasting into the browser desktop works. Ctrl+V (also Ctrl+Shift+V and Shift+Insert) now hands your local clipboard's text to the desktop before the keystroke arrives, so the desktop pastes what you just copied rather than whatever its own clipboard held. On a Mac, Cmd+V hands the clipboard over, and Ctrl+V then pastes it inside the desktop.
- Copying out of the browser desktop works too. When you copy text inside the desktop, a notice shows a preview of it with a Copy to this computer button, and the control tab keeps the last copy. Text only lands on your own clipboard when you click: the workspace is not trusted, so it never gets to replace your clipboard silently. Highlighting text in the desktop no longer counts as copying it (desktop image change).
- Workspace owners can now set or generate a new SSH password from the container detail page. A running workspace uses it immediately; a stopped workspace uses it when next started. Desktop workspaces use the same password for RDP login. A chosen password needs at least 16 characters, with at least 8 different ones, because it guards an SSH port that is reachable from the internet.
- Workspace SSH now drops unauthenticated connections after 30 seconds and limits how many can be open at once, which slows password guessing. It applies to workspaces created after the upgrade.
- One workspace can no longer delay other workspaces from starting. A workspace that stalled while its SSH credentials were being applied used to hold up credential updates, and so starts, for every account. Each workspace now waits only on itself, and a stalled update gives up after 30 seconds.
- The terminal and desktop routes now always authorize the request Traefik is actually routing. They no longer accept forwarded-request headers a client could supply.
- Optional desktop workspaces. Install the signed desktop image with
--desktop-image=yes, then select it when creating a workspace. XFCE with Yaru opens in the workspace's Desktop tab; native RDP reaches the same session through an SSH tunnel. Firefox is included in the amd64 image. The regular workspace image remains the default. - Admins can now keep users off desktop workspaces. The desktop image is an ordinary entry on the Container images page, and it is there after upgrading, so nothing changes until you remove it. Once it is removed, new desktop workspaces cannot be created. Existing ones keep running.
- Restoring a snapshot as a new workspace in exact mode now needs the image the snapshot was built on to be permitted by the allow-list, the same rule as creating a workspace. Before, an exact restore skipped the allow-list.
- The desktop image now checks Mozilla's apt signing key against its published fingerprint and installs a pinned Firefox version, like every other download in the workspace images. Its display server is also explicitly kept off the network.
- The Desktop tab now warns that anything in the workspace, including an AI agent, can see and control the desktop. It also warns that the browser's cookies and saved logins are stored in the workspace and its snapshots.
- Fixed the Desktop tab on installs that serve plain HTTP (TLS off). Its route was missing its authorization gate there, so Traefik refused it and the desktop could not connect.
- Fixed terminals opened on the desktop not finding
claude,codex,agyor other tools installed in~/.local/bin. Desktop apps now also use the workspace's UTF-8 locale and time zone. This applies to desktop workspaces created after the upgrade, or to existing ones restored from a snapshot onto the new image. - Fixed the Desktop tab's noVNC connection, which was trying to open its WebSocket outside the workspace's desktop route and failing to connect.
- The Desktop tab now loads its noVNC client from the dashboard instead of the workspace. Desktop WebSocket connections require a short-lived token bound to that workspace, and the desktop route no longer serves workspace HTML.
- View and manage all workspaces without truncation. Accounts with more than 20 containers previously only saw the first 20 on the Containers page, because the dashboard requested a single page from the API. The Containers page and the API key editor now automatically fetch all containers across pages, displaying every workspace in both grid and list views grouped by project. Editing an API key also displays all containers in the grant picker and preserves existing grants on containers across all pages.
-
A snapshot a workspace was restored from is now marked, and held. An exact restore does not copy a snapshot: the workspace boots the snapshot's own saved image and keeps running from it. The snapshot is the floor that workspace stands on, so deleting it was never really possible — Docker refused it with a conflict naming an image ID, and only after you had asked for the deletion.
Both Snapshots lists now say so first. A snapshot a workspace is running from is marked In use, names the workspace holding it — as a link, on the account-wide list — and its delete button is disabled rather than offering something that cannot happen. The API refuses the same deletion with the same reason, and with the two ways out: restore that workspace onto its current image, which rebuilds it on a real base image and releases the snapshot, or delete the workspace. Deleting a workspace and its snapshots is refused in the same way when one of those snapshots is holding up a different workspace, and says to delete the workspace without its snapshots instead.
Restoring is unchanged. The restore dialog simply says what an exact restore leaves behind before you pick it.
-
Give an account a disk quota, and snapshots have to fit inside it. A snapshot saves a whole workspace, so it is the one thing on a DevGrail box that consumes disk on a tenant's say-so — and until now the only thing standing in front of that was a cap on how many an account could keep, which says nothing about ten snapshots of a 40 GB workspace.
Administration now has a Disk per account setting beside the snapshot count, and the Users page can give one account a quota of its own — larger, smaller, or
0for no quota at all. Both take the same sizes as a memory limit (50g). Leave them blank and nothing changes: a deployment with no quota set behaves exactly as it did before.Your dashboard shows what you are holding against yours, and the Snapshots tab will not offer to take one you have no room for. If a snapshot turns out to be bigger than the room left — which is only knowable once it has started measuring — it stops there and says so, before it has written anything. Nothing you have already saved is ever deleted to make space; a full quota is cleared by deleting a snapshot you no longer want, and the account-wide Snapshots tab is where you can see them all at once.
-
Save a whole workspace with a snapshot. A workspace page now has a Snapshots tab, and taking one saves the parts of a workspace nothing else ever has: everything under
/home/dev— the sign-ins your agents hold,~/.gitconfig, shell history, anything you installed withsudo apt— along with a copy of the files on the volume behind/workspace. Give it a label like "before the Node 22 upgrade" and it stays in the list until you delete it.Taking one stops the workspace for as long as it takes and starts it again afterwards, which is what makes the two halves a snapshot saves describe the same moment. If stopping is not acceptable there is a checkbox to snapshot the workspace while it runs; the list marks those, because a file being written while the copy runs can be saved half-updated. Either way the snapshot is taken in the background, so you can leave the page.
While one is being taken the list says what it is actually doing — stopping the workspace, measuring it, saving its image, copying
/workspace, starting it again — with the time so far beside it. Saving the image is nearly all of the wait, so it is nearly all of the bar, and it is the step that can report nothing at all about itself: its share fills on elapsed time, slowing as it goes and never running past the step it belongs to. Copying/workspaceis the short step at the end, and the one with a real measurement behind it — the bar moves on bytes actually copied, out of a total measured before the copy began. Once a workspace has one finished snapshot, the estimate stops being a guess: how long the bar takes to fill is paced by how long that workspace took last time. A snapshot that stopped your workspace keeps saying so for as long as it is down, and the list tells you how big the last snapshot of that workspace was and how long it took, which is usually the answer to "should this be taking this long".A snapshot of a workspace whose image the host no longer has is now refused up front, with a message that says so. Rebuilding the workspace image and pruning the old build leaves long-running workspaces on an image Docker has forgotten: they keep running, but they cannot be committed, and Docker's own answer to that is a missing content digest that names nothing you can act on. The refusal names the image, says the workspace itself is fine, and says what recreating it would cost.
A saved snapshot can now create a new workspace in either of two ways. An exact restore brings the whole saved machine back, including agent sign-ins, packages installed with
sudo apt, shell history and every other writable-layer change. A current-image restore starts on the image tag as it resolves now, keeps/workspace, and lets you choose which saved agent and shell settings to carry forward.~/.local/binis deliberately left on the current image so old copies cannot replace the agent binaries the new image shipped.The restored workspace is a copy: the snapshot and its source stay untouched, and a name or URL-path collision is resolved with a numeric suffix. Project membership and public routes return only when they are still available; API-key grants never copy to a new workspace. SSH and browser-terminal credentials are always issued fresh, and an SSH key revoked after the snapshot stays revoked after the restore.
A snapshot can also be restored over the workspace it came from, in either mode. The workspace keeps everything that identifies it — its name, URL path, SSH port, published routes, project and API-key grants — and everything inside it is replaced. That makes a current-image restore the way to move a workspace onto a newer build without losing your home directory, which is what rebuilding on the current image never could.
It is built so it cannot take your working files with it if it goes wrong: the saved copy of
/workspaceis poured into a brand new volume while the workspace is still running, and only once that has succeeded is the container replaced. If the rebuild fails, the workspace is put back on the volume and image it had. The restored copy then takes over the name of the volume it replaced, so the workspace goes on using a volume called what you called it, and the one it was using before the restore is kept, unattached, in the Volumes tab as “… (pre-restore <date>)” — so restoring the wrong snapshot is undoable, at the cost of holding both copies until you delete one. Tick “delete the previous volume” in the restore dialog to reclaim that disk instead; it is applied only after the restore has succeeded, so a restore that fails never deletes anything. Restoring over a workspace is refused while a script is running in it and for a delete-locked workspace, and an open browser terminal reconnects as a new session because its credential is reissued.A snapshot outlives the workspace it came from on purpose: delete the workspace and its snapshots stay, because "I deleted that by accident" is the case they most need to answer. The delete dialog says how many there are and roughly what they cost, with a checkbox to take them along — off by default, like the one for the attached volume. The disk a snapshot holds is freed when you delete it.
Those survivors now have a home: Containers → Snapshots lists every snapshot on your account, grouped under the workspace each came from, with the groups whose workspace is gone marked as such and one button to show only those. Restore one from there and the deleted workspace comes back as a new one; delete it and the disk is freed. Until now a snapshot whose workspace had been deleted was only reachable if you had kept its id, because the only list of snapshots was on the workspace page that went with it — so an account with no workspaces left still opens on this tab rather than the first-run panel.
Any snapshot in either list can now be inspected: what it holds, and where those bytes are. A snapshot is two ordinary Docker objects with names derived from its id — an image holding the container's writable layer and a volume holding the copy of
/workspace— and the panel names both, says whether the daemon still has them, and gives the path on the host each sits at. It also shows what the snapshot recorded about the workspace it came from (its URL path, CPU and memory limits, restart policy and published routes) and which workspaces are currently running from its image, which is what stops a delete until they are gone. A snapshot whose image someone removed outside DevGrail now reads as unrestorable here rather than at the moment you reach for it.For whoever runs the server: the System page now counts the snapshots on the deployment and what they add up to, since a snapshot's storage deliberately sits outside the volume figures beside it. Accounts are limited to ten snapshots by default, and Administration → Workspace defaults now has the field to change that (0 removes the cap). Snapshot images no longer appear on the images page as unpermitted images — the server made them, and no allow-list entry will ever name one.
uninstall.shaccounts for them in the report it prints before it asks anything, and removes them with--purge. -
"Update image" is gone, and so is the data loss it came with. Rebuilding a workspace on the current image kept the volume behind
/workspaceand threw away everything else — every agent sign-in,~/.gitconfig, shell history, every package installed withsudo apt. That is why the button needed a table explaining what survived, and why the note beside it ended by talking you out of pressing it.A snapshot restored onto the current image does the same job and keeps all of it, so the button and the
POST /containers/{id}/recreateendpoint behind it have both been removed. The Image row still says when a newer build of the tag exists, and now points at Snapshots below it; the Containers list still marks those workspaces. Nothing else about a workspace changes, and workspaces already running are untouched. -
Publish a workspace at your own domain. Until now the only public address a workspace could have was a subdomain of a suffix your administrator had set up —
shop.apps.example.com. A workspace page now has a Custom domains section beside Subdomains, where you can point a domain you own at a port inside that workspace: putwww.myrestaurant.com's DNS A record on this server, add it with port 3000, and the site your workspace serves on port 3000 answers there. The Containers list shows each workspace's custom domains beside its subdomains, so a published address is one click away from the overview.The HTTPS certificate is arranged for you. Traefik obtains one from Let's Encrypt the first time somebody visits, so that first load can take a few seconds and every one after it is immediate — there is nothing to upload and nothing to renew. Visitors who type the bare address without
https://are now redirected to it rather than meeting a 404, which is also true from now on for app subdomains and for the routes an administrator adds under Traefik routes.Adding or removing a domain leaves the workspace alone: nothing restarts, nothing is rebuilt, and everything running inside it keeps running. The route is live about a second after you add it. As with a subdomain, the process inside the workspace has to listen on all interfaces rather than only on localhost, or the address will answer with a gateway error.
Two things are refused, both with a reason. A domain can be published only once on a server, so a name another workspace already holds is turned down — and a name that falls under one of this server's own base domains is turned down too, because that is what the Subdomains section above it is for. Wildcards are not accepted: the way this server proves it owns a domain works for one exact name at a time, so a wildcard could never be given a certificate browsers trust.
Administrators can see every custom domain on the server, with the workspace it serves and who owns it, at the bottom of the Traefik routes page, and withdraw one from there. That is deliberately the only thing an operator can do to somebody else's domain: it takes the route off the shared proxy without touching the workspace, which keeps running, and its owner can publish the domain again.
-
Your open terminals are still there when you come back. The terminal tab used to forget its layout the moment you looked at anything else: switching to another tab or reloading the page closed every pane and dropped you back on the session list, even though the sessions themselves had never stopped running. The workspace now remembers which terminals you had open, in what order and at what size, and puts that grid back — reattached to the same shells — the next time you open the terminal tab in that browser. Terminals you close stay closed, and a session that has since ended is simply left out.
-
A terminal you open is ready to type in. Opening a session from the terminal list, from a chip beside the toolbar, or with "New terminal" now puts the cursor in that terminal, instead of leaving you to click into the pane first. Terminals that come back on their own — restored from your last visit, or opened by a link — leave the keyboard where it was.
-
Agent commands now get ten minutes by default. MCP's
exec_commandno longer cuts ordinary builds and test suites off after two minutes, and when a command does reach its deadline the timeout result returns promptly instead of waiting for that command to finish. Commands that need longer can run in a persistent terminal session.
-
Videos can now be watched directly in the file explorer. MP4, WebM, Ogg, M4V and QuickTime files open in a native browser player with playback, volume and full-screen controls, while the original file remains available from the preview's download button.
-
Two of your projects can no longer share a name. A project is picked by name everywhere it is used — the project list, a workspace's project field, and the project tools an agent calls over MCP — so two of your own answering to "site" made every one of those a guess. Creating or renaming a project onto a name you already use is now refused, and the New Project dialog says so while you type rather than after you submit. Names are yours alone: another user having a "site" has never had anything to do with yours, and still does not.
If this install already had duplicates, the upgrade keeps the oldest of each set under its name and appends "(2)", "(3)" to the others; the server log names every project it renamed, and you can rename them to whatever you like from the project page.
-
The beta terminal is now the standard terminal. All terminal panes and full-screen sessions use it, with reconnect, text selection, and mobile input controls. The old terminal and beta switch have been removed.
-
The dashboard has a new colour scheme. Gold and navy are gone; the whole interface is now warm charcoal, ivory and a soft lime, in both the light and the dark palette. The lime is spent where it means something — the primary button on a page, the navigation row you are on, a link, the focus outline, the logo — and nowhere else, so ordinary capacity bars are a neutral warm grey rather than four green bars competing with each other for your attention. Running/stopped/failed still read green, amber and red, and code in the script editor keeps its own colours.
Several things are easier to see than they were. Links, the selected navigation row and the focus outline are all darkened in light mode instead of being the pale lime they would otherwise be; input borders are a step stronger; the on/off switch's knob now shows against a switched-on track; and the terminal's chrome, its input bar and the container log viewer are warm charcoal like everything else instead of the cool grey they used to be. The colour the browser paints its own chrome with follows the palette too.
Nothing moved, and nothing changed what it does. If your dashboard is installed as an app, it picks the new palette up the next time it updates.
-
DevGrail has a new icon. The gold-and-navy grail has been replaced by an angular soft lime DG monogram — in the browser tab, in the sidebar beside the wordmark, and on the home screen if you have installed the dashboard as an app. It is drawn on a transparent background, so it takes the colour of whatever it sits on. Nothing but the artwork changed; an installed dashboard picks the new one up the next time it updates.
-
The terminal tab now shows your running sessions instead of hiding them. They used to be small grey chips at the end of the toolbar, next to the buttons they looked exactly like — easy to miss entirely, and easy to think a session you had left running was gone. With no terminal open, the panel now lists every session in full, each with a dot showing whether something is attached to it, and opens one on click. As soon as a terminal is open that space goes back to the terminal: up to three remaining sessions ride along as chips beside the toolbar buttons, and beyond that — or on a phone — the whole set folds into one "N sessions not open" button that expands to a single row when you want it. Every session in the list can also be killed from where it sits, so clearing out one you are done with no longer means opening a terminal onto it first. It still asks before destroying anything.
-
A session no longer claims something is attached to it after you have left. The dashboard asked the workspace for its sessions when the tab opened and then only when you pressed Refresh, so a session you had just closed a terminal on — a pane, or a full-screen tab — kept the "attached" mark it had at that moment, sometimes for as long as the page stayed open. The list now re-reads itself while it is on screen, when you come back to the window, and shortly after a pane closes, so what it shows is what the workspace currently reports.
-
Workspaces now come with Google's Antigravity CLI instead of Gemini CLI. Google has retired Gemini CLI in favour of Antigravity, so
geminiis gone from the workspace image andagytakes its place — the same account, a different command. Typeagyin the terminal to start it. It is a compiled binary rather than an npm package now, so it installs into your own~/.local/binand keeps itself up to date from there. Existing workspaces are untouched until you recreate them —geministays where it is, for as long as Google keeps answering it. -
Guides now start by saying what DevGrail is. Every guide so far assumed you already knew what a workspace, a project, a script, a skill and an API key were — the words the rest of the dashboard is written in. A new What DevGrail is guide defines those five in one page, alongside what the product actually runs and what an agent holding one of your keys can reach. It sits first under Guides, hands you on to the get-started walkthroughs, and is offered beside them on the panel a brand-new install lands on — where the walkthroughs now say plainly that they are the agent route. It also answers the question a developer arrives with — why run an agent here rather than on the laptop — in a paragraph, and then at length: a workspace per branch instead of two agents fighting over one checkout, port 3000 and one local database; approvals asked once instead of at every step of a forty-file change; the dev server reachable on a real HTTPS URL you can send to someone; test suites and builds that carry on after you close the lid; a toolchain that is a script rather than a machine that drifted, on the same Linux the code deploys to; and an agent whose reach is the key you handed it rather than every repo and credential on your disk.
-
DevGrail now describes itself without borrowing another product's name. The sign-in page, the guides and the instructions every workspace hands its agent all led with “cPanel for AI developers” — a description that only lands if you happen to have run cPanel. They now say the same thing in plain words: a machine of its own for every coding agent, self-hosted on a single server you control.
-
A brand-new install now sees one thing, and the same thing everywhere. Before you have a workspace, the dashboard led with meters of an idle host and two empty "heaviest workspaces" lists, and the invitation to make your first one sat underneath them. That invitation is now the whole page — the capacity cards, the usage lists and the header's "New Container" button stay away until you have something to measure. Containers shows the same panel, pointing at the getting-started guide rather than only offering a bare container: the tab strip, the view toggle and that page's own header button all stand aside for it, so a first install has one thing to read and one thing to do. Volumes and Re-add come back with your first workspace, along with everything else.
-
An agent now hands you a workspace link that works. The workspace details an agent reads over MCP carried a
terminal_urlthat looked like a link you could open, so agents offered it as one — and it always failed, because the browser terminal is reachable only with a short-lived token the dashboard mints per connection, which no field in a tool result can carry. That field is gone, replaced by adashboard_urlpointing at the workspace’s own page in DevGrail: the page that opens a terminal and mints the token itself. The tool descriptions and the MCP guide now say so too, so an agent asked for a terminal sends you there rather than inventing an address. The dashboard terminal is unchanged, and so is what the REST API returns — itsterminal_urlis only described more honestly, as the base path it has always been. -
An automation can ask which workspace to work on. A step that runs a script in a workspace had to name that workspace when the automation was written, so doing the same thing to a second workspace meant a second copy of the whole automation. An input can now be a Workspace: the run form offers the workspaces you have, by name, and every step that was pointed at that input gets the one you picked — one automation, whichever workspace you are setting up today. It is checked like everything else a run names, so a workspace deleted since the last run is said so on the form, and the run is refused before it makes anything rather than stopping halfway through.
-
An automation is checked before it runs, not while it runs. An automation stops at its first failing step and does not undo the ones before it, so a workspace refused for its image used to cost you a project and a volume to find out. The run form now checks first and creates nothing: it resolves the inputs you have filled in, then says whether the projects, workspaces, scripts and skills each step names still exist, whether the tools are real ones, whether the script it installs is in the official registry, and whether the image and the CPU and memory sizes are ones this deployment will actually admit. What it finds is listed above the button, by step and field, in the same words the run would have failed with — and when the check finds nothing, it says so, because a warning you only see when something is wrong is one you learn to ignore. Nothing is blocked: a check that could not reach the registry is not proof the script is missing, so Run still runs, after asking once. The check keeps up with you: fixing what it named and saving clears it without a reload, it says how long ago it looked, and Check again re-asks — a workspace deleted in another tab is not something this page is told about. While the definition above it has unsaved edits it says so, because what it checked is the version the server has.
-
A built-in automation can be a starting point. Built-ins ship with DevGrail and cannot be edited, so wanting the OpenCode onboarding with one step changed meant retyping all four by hand. Every automation — built-in or your own — now has a Clone, which makes a copy you own and opens it ready to change. The copy carries the same inputs and the same steps, references between them included, and the original is left exactly as it was.
-
Running an automation caught up with running a script. A run now records the inputs it was given, so the run form keeps them instead of emptying itself the moment the run starts, and fills itself from the last run when you open the page — running the same automation again with one value changed no longer means retyping the rest. Each history entry has a Run again beside it that repeats that run as it was, behind a dialog naming the values it will use, with API keys and secrets shown by name rather than as reference tokens. Credentials are recorded the way script runs record them: as the reference, never the plaintext. And Run on an automation's card opens the page at the run form instead of above the definition, so pressing it no longer means scrolling past the whole automation to find the button you just asked for.
-
A run in progress updates as it happens, and can be stopped. The run history refreshed on a two-second timer, which was most visible in exactly the case that needed it least — a step that is itself a script run, where output is arriving the whole time. Progress now arrives over the same live connection the notification bell uses, and a run that is still going has a stop control: the step in flight is cancelled and nothing after it starts. Stopping undoes nothing, the same as a failure — the run log still names everything that was created — and the step that was interrupted says so, since whatever it had already begun may well have finished. A run left saying "running" because the server restarted under it can finally be closed the same way.
-
The ids a run reports are links. A step's outputs rendered as monospace
key=valuetext, which after a half-finished run meant copying each id into a search box to find out what to clean up. Projects, workspaces, scripts, skills and keys now link to their own pages. -
An automation step no longer asks you to type things it could offer. The "Create a workspace" step used to take its image as free text, and its CPU and memory caps as strings like
0.5and512m: an image the deployment does not admit, or a size above its ceiling, was accepted by the editor and then refused when the run reached Docker — after the project and the volume had been created, which nothing undoes. All three are now chosen from a list, and the sizes on it are the ones this account may actually create, each labelled with what it means, including what leaving the field alone applies. "Download an official script" asks which script rather than for the last segment of a URL copied off another page, with a search box once the registry runs long. And the two yes/no fields — whether the workspace is ephemeral, and whether a download may update copies you already hold — are checkboxes with their explanation beside them. Automations already saved keep working untouched: the words they store still mean what they meant. -
Choosing skills and scripts for a key an automation mints is a list you tick. The "Create an API key" step used to ask for its skills and its scripts as comma-separated ids typed by hand, with a
*for "all of them" buried in the field's description. Both are now the same checkbox list the key editor itself uses: everything you own, built-ins marked as such, and an "all, including ones created later" row above them. A value an earlier step or a run parameter supplies can still stand in the list, and an id whose skill or script has since been deleted is kept and shown rather than quietly dropped. -
The automation editor got out of the way. Steps now collapse to a line that says what they do, so a six-step automation reads as six lines rather than six stacked forms. A new step picks its action from the catalog laid out under its group headings, with each description visible before the choice is made rather than after, and is then named after what it does — renaming it afterwards rewrites the references later steps make to it instead of silently breaking them, and a move that would carry a step above something it reads is refused. A field naming a project, workspace or script asks where its value comes from, listing an earlier step's output by what produces it and offering a way back to the list of ones that already exist. And what the server would refuse — a duplicate step id, an empty required field, a reference to a parameter nobody declared — is now said beside the step it is about and on the Save button, instead of arriving as a toast once the page has scrolled away.
-
Script libraries are easier to navigate. The scripts page can now search by name, description, or interpreter; filter by any combination of editable, official, and built-in scripts or focus on main scripts and building blocks; and sort by name or recent activity. Bulk export follows those filters and shows how many matching scripts it will export. The view is remembered in the current browser, including its search, filters, and sort order. Built-in and downloaded official scripts cannot be exported, either directly or indirectly through another script's include chain.
-
Notifications now arrive as they are sent. An agent that reports a finished build over
send_notificationused to sit in the database until the dashboard next asked for it, which was every fifteen seconds. The bell now holds an open connection to the server and is told the moment a notification is written, so the badge and the toast appear at once.The old poll has not been removed, only slowed to five minutes, so a browser that cannot hold the connection — an old one, or a proxy that severs it — still keeps up on its own. This is separate from browser push notifications and does not replace them: push is what reaches a phone with the app closed, and it is configured, and can fail, independently.
-
Updating a workspace's image now asks first, and says what it costs. The button on a container's page no longer rebuilds on the click: it opens a confirmation describing what a rebuild keeps and what it starts over.
It keeps
/workspace— that is on a volume and gets remounted — along with the name, SSH port and password, subdomains and restart policy. It starts over the rest of the container: processes are killed rather than asked to stop, so an agent session, a dev server or a script mid-run stops where it is, and the home directory goes with it, which means agent sign-ins,~/.gitconfig, shell history and anything installed withsudo aptare gone. The old wording mentioned only that/workspaceis preserved, which reads as "nothing is lost".It is also offered in one place now, and more quietly. The warning banner across the top of the page is gone; a workspace running an older build says so on its Image row, next to the tag it was built from, with the button beside it. The container list used to carry the same one-click rebuild on every drifted workspace, where there is no room to explain any of this — it now marks them and leaves the action to the workspace's own page. A new build of the image is a routine event, not a problem with the workspace.
-
A workspace now says which API keys its agents were given. Open a container and the General tab lists the keys the agents in it were configured to reach the MCP server with, each named alongside the script that set it up. Agents are wired up one at a time — Claude Code, Codex, Gemini CLI and OpenCode each have their own setup script — so a workspace often holds several different keys, and until now the only way to find out which was to go into the container and read an agent's configuration file.
The list is built from what DevGrail handed the workspace, not from what is in it: setup scripts take the key as a parameter, and a run records that as a reference rather than a value. So the page can name a key without a secret ever being read out of the container, which matters because that is exactly where the key sits in cleartext, in a place the workspace itself controls. Nothing is revealed here — the key's name links to where it is managed, and copying its config stays on the API keys page.
Two consequences of reading the history: clearing a workspace's run history clears this list, and a key whose setup run failed is still listed, marked, because it did reach the workspace and the setup may be half-applied. A key deleted since is listed too, without its name.
-
Re-running a script now really repeats it. A run in a workspace's history remembers the parameter values it was started with, and two buttons put them back to work: the existing one loads the run into the form to be edited first, and a new Run again repeats it as it was, after showing you what it will use.
Reuse used to reconstruct a run's parameters from the environment it recorded, which could only ever approximate them. That environment is keyed by variable name and mixes the script's own settings in with what you supplied, so the two could not be told apart afterwards, and it covered the top-level script alone — so anything you filled in for a building block it includes was quietly replaced by that block's default. Secrets came off worst: a secret can be referenced either by itself or by the slot an official script names, and the picker only ever knew the first, so the field came back blank while the form still held the old value. Runs now record the values themselves, per script, across the whole chain, and reuse replays exactly those.
The credential pickers also stop hiding a value they cannot show. A secret referenced through a slot is named for what it is, and a reference to a key or secret that has since been deleted is flagged in the field instead of leaving it looking empty while the run would still have used it.
As before, nothing about this stores a credential: what a run remembers is the reference, resolved fresh on each run and redacted from the output. Runs from before this release have no record to replay, so they keep the old behaviour and say so in the confirmation.
-
The getting-started skill now leads with the login that doesn't go stale. Ask an agent what DevGrail can do and it will, before the rest of the tour, offer to point a secret at the credentials file of a workspace you have already signed in — after which every new workspace gets a current login with nothing pasted, and nothing to redo when the token rolls over. It was already possible; it was not what you were told to do first, which is the only moment it is cheap to set up.
-
The coding-agent setup guide now ships with DevGrail. Getting Claude Code, Codex, OpenCode or Gemini CLI running in a workspace — signing in without a browser, wiring up the DevGrail and Context7 MCP servers, where each agent keeps its config, and what the failures actually mean — was expertise you had to find in the official catalog and download before an agent could use it. It is now built in, like the getting-started tour beside it, so an agent on a fresh install can simply load it. Each of the four agents has its own page within the skill, so what an agent acts on is that agent's params and paths rather than a remembered average of all four.
A step that creates an API key now grants every skill you own by default, instead of only the getting-started one. An agent handed a new key can reach both built-in guides, and anything you have written yourself, without you listing ids on the step — narrow it back to specific skills when a key should see less.
-
An automation can create a workspace without a project. The "Create a workspace" step insisted on one, so an automation that only wanted a workspace had to create a project to put it in, or name an existing one. The Project field is now optional: leave it empty and the workspace is created on its own, the same as one created from the dashboard, ready to be filed into a project whenever there is a reason to. Naming a project still does what it always did, including refusing one that is not yours.
-
The API keys page can be read at a glance. Every key printed its full detail at once — the same paragraph about internal and external connections under each one, and five grant headings of which four usually said "None" — so a handful of keys filled the screen with repetition and none of them stood out. A key is now a single line: its name and prefix, whether it is disabled or flagged for external access, one summary of what it can reach ("12 tools · official registry · all containers (write)"), and when it was last used, or that it never has been. Editing what a key may reach is on that line too, so it no longer costs an expand. Open a key for the config buttons, its switches and the full grant list, where the groups it holds nothing in are simply absent. Granted skills and scripts are named there rather than listed as ids, and the guidance on which config to hand an agent now sits once at the top of the page.
-
A secret can now be a file in one of your workspaces. Plenty of credentials already live as files inside a container — a
~/.netrc, a service-account JSON, a PEM key some tool wrote. Copying one into DevGrail made a second copy that went stale the moment the original was rotated. A secret can now point at a workspace and a path instead, and the file's contents are its value.It is read fresh every time, not copied: rotate the file and the next script run picks up the new value, with nothing to update in DevGrail. Trailing newlines are trimmed, because almost every token file ends with one and almost no program wants it; nothing else about the file is touched, so a PEM key or a
.envblock keeps its shape. The workspace does not have to be running. The file is read once when you save, so a mistyped path is refused in the editor rather than surfacing days later as a failed run — and if the workspace is later deleted or the file moved, the run says which, by name.Everything else about a secret is unchanged. It is still referenced as
@in a script, still injected server-side, still redacted from the run output, and still fills the same reference slots an official script names. Pick a file with the new Browse button on the secret editor, which walks the workspace's filesystem.Agents can manage these too, and still cannot read one:
create_secretandupdate_secrettake a container and a path,list_secretsandget_secretreport which file a secret points at, and no tool returns the contents — reading that file still takesread_fileand a grant on the container it lives in. Naming a workspace as a secret's source requires the same read grant, so a key cannot use a secret to reach a container it was never given. -
A second getting-started guide, for Codex. It builds the same thing the OpenCode guide does — a project, a workspace container, and an API key scoped to just those two — and then points Codex at DevGrail using the official "All agents: connect to DevGrail (MCP)" script, downloaded from the registry as part of the run. Codex still needs a sign-in of its own, which happens in a terminal rather than in DevGrail, so the guide says so, names the option that finishes from a container ("Sign in with Device Code", against a ChatGPT subscription you already have) and hands you a button that opens a full-screen terminal in a new tab with
codexalready starting in the new workspace. Both guides sit under "Get started with your favorite coding agent" on the Guides page, which now lists sections rather than individual guides. The original guide is called "Get started for free with OpenCode", and says outright that OpenCode is open source and can be pointed at models that cost nothing — so a first workspace need not cost you anything either. -
Automations can install from the official registry. A new "Download an official script" step fetches a script by its slug, installs it into your library and publishes its id, so the next step can run it — which is how the Codex automation gets its setup script without the binary carrying a copy. A script you already have is wired to rather than copied again, and nothing you hold is quietly moved to a newer version unless the step asks for it.
-
The built-in "Get started with OpenCode" automation now issues a key the agent can actually build with. Alongside the new project and workspace it was always scoped to, the key reads every skill and script in your account — the ones you have now and the ones you add later — and the official registry, so the agent can install a ready-made skill or script instead of writing one from scratch. It can also list your secrets and API keys by name, which is how it references them from a script; a secret's value and a key's plaintext stay unreadable, and it can neither create, change nor delete either. Keys minted by earlier runs are untouched — re-run the automation, or widen the key on its own page.
-
Automations of your own can grant the same. A "Create an API key" step now has Scripts and Official registry fields beside Skills, so a key an automation mints can reach your script library and download from the official catalog. Both grant nothing unless you ask, so every automation you already have mints exactly the key it minted before.
-
An agent can now pass on its access to the official registry. The key-creation tools accepted every kind of grant except that one, so an agent holding the registry could browse and download from it but could not hand a helper key the same reach — and a key that held it could not be renamed, disabled or deleted over MCP by anyone. Both work now, under the rule the other grants already followed: an agent can only pass on what it holds, and read (browse) does not become write (download).
-
The get-started guide tells you when your workspace is ready. The automation runs on the server, so it used to finish in silence — four checkmarks filling themselves in while you were reading the step below. Now the end of a run is announced, the browser tab says so if you have looked away, and step 1 keeps a note naming the workspace it built and linking to it, so the page still tells you where things stand when you come back to it.
-
The guide's second step no longer points at a workspace that is not there. "Open OpenCode" now says what it is waiting for while step 1 has not run, and when the run finishes the page scrolls to that button rather than leaving you to notice it. Reopening the guide after deleting the workspace it built offers to build a new one instead of opening a container that is gone.