All releases

v0.4.61

Signedpublished October 19, 2026

What changed

  • 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-sources and GET /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_lists MCP 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_at on an entry and return expired. 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-ip returns 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 --resume command 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 as image.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=0 no longer turns off the signature check: DEVGRAIL_ALLOW_UNSIGNED=1 does.

Container images

registry build 43
amd64921 MB
sha256:810b760266f7a92d10a28fb5499b6f2d7545a833b3a78d9fa6cb407f1eedac73
Download
arm64786 MB
sha256:217a3f2121efabe7a9aca5d8f9a40b7a2ac5d5ec347d5357b18be6ba0cd91fda
Download
registry build 87
amd6447 MB
sha256:6e73a94fc17370222eb6f32c217aa6865e7c97dbb2e3460eb37fd9968f9ca0c9
Download
arm6446 MB
sha256:9719aa631cb03947436255ab3b95b656f73cd71168a3bb7e5def80c027814b18
Download

Installer files

devgrail-desktop-files.tar.gz31 KBsha256:44dc54d035e3262da2df136dce5e3aac0a1b53b4c31775ee0a2f3024d5d38308
devgrail-desktop-install.sh46 KBsha256:c2f31ebdcb1c09c7278f8d1073d6b99c3b50586922c9d1ca0fe8451e798e4187
devgrail-desktop-linux-amd6411 MBsha256:6ba9ee78163d9ba0151357301f4751839762524809f2dfa1673408db9ed1f676
devgrail-desktop-linux-arm649.9 MBsha256:21986575c7a02bc82c394569014d7f1c17a4af8d2740615e67c9550df5e8ed1a
docker-compose.yml30 KBsha256:6533fcaae65ad8535bb1d27764d342fe695eaa69d644d728f71dfa441eb57dce
install.sh268 KBsha256:7957148660ade79de8c597832a8a849facb1cfbab2f54620acb6392cf829542b
traefik-dynamic-http.yml7 KBsha256:726f358caacf8625fcc3ab27ae3d8d811308029389bf01c6d93efacf8b60fe60
traefik-dynamic.yml8 KBsha256:db7d4c5afe08ae9eb5f0c8227e9ac632776fff4b8929ba30484c36b85924054a
uninstall.sh83 KBsha256:b5fa5df04d471097ceb8d59a0745dd681e21715937c66cf0fdecc945c5076ea7

Signature

ecdsa-p256-sha256key f1f1eaf642454d65

One signature covers every artifact above. It is made off this host, and neither this site nor the registry behind it can check it — that is the point, and it is why verifying it catches things a checksum from the same server cannot. How to verify it.

Verify devgrail-container (amd64)
curl -fsSL https://web-dev.trixibot.com/api/registry/releases/v0.4.61 -o release.json

# -j, not -r: -r appends a newline the signer never saw, and the check fails.
jq -j '.signature.payload'   release.json > payload
jq -r '.signature.signature' release.json | base64 -d > payload.sig

openssl dgst -sha256 -verify devgrail-release.pem -signature payload.sig payload
# -> Verified OK

# The payload must name this release, so an older signed one cannot be replayed.
head -2 payload

# ...and it must name this exact file. -qxF: whole line, literal.
grep -qxF 'image devgrail-container 43 amd64 810b760266f7a92d10a28fb5499b6f2d7545a833b3a78d9fa6cb407f1eedac73' payload && echo "digest is covered by the signature"
The signed payload — shown for reading, not for verifying

Copy this from the manifest with jq -j, never from this page: the signature covers exact bytes, and rendering here drops the trailing newline the signer included.

devgrail-release-signature-v1
release v0.4.61
asset devgrail-desktop-files.tar.gz 10 44dc54d035e3262da2df136dce5e3aac0a1b53b4c31775ee0a2f3024d5d38308
asset devgrail-desktop-install.sh 10 c2f31ebdcb1c09c7278f8d1073d6b99c3b50586922c9d1ca0fe8451e798e4187
asset devgrail-desktop-linux-amd64 10 6ba9ee78163d9ba0151357301f4751839762524809f2dfa1673408db9ed1f676
asset devgrail-desktop-linux-arm64 10 21986575c7a02bc82c394569014d7f1c17a4af8d2740615e67c9550df5e8ed1a
asset docker-compose.yml 71 6533fcaae65ad8535bb1d27764d342fe695eaa69d644d728f71dfa441eb57dce
asset install.sh 71 7957148660ade79de8c597832a8a849facb1cfbab2f54620acb6392cf829542b
asset traefik-dynamic-http.yml 70 726f358caacf8625fcc3ab27ae3d8d811308029389bf01c6d93efacf8b60fe60
asset traefik-dynamic.yml 71 db7d4c5afe08ae9eb5f0c8227e9ac632776fff4b8929ba30484c36b85924054a
asset uninstall.sh 71 b5fa5df04d471097ceb8d59a0745dd681e21715937c66cf0fdecc945c5076ea7
image devgrail-container 43 amd64 810b760266f7a92d10a28fb5499b6f2d7545a833b3a78d9fa6cb407f1eedac73
image devgrail-container 43 arm64 217a3f2121efabe7a9aca5d8f9a40b7a2ac5d5ec347d5357b18be6ba0cd91fda
image devgrail-server 87 amd64 6e73a94fc17370222eb6f32c217aa6865e7c97dbb2e3460eb37fd9968f9ca0c9
image devgrail-server 87 arm64 9719aa631cb03947436255ab3b95b656f73cd71168a3bb7e5def80c027814b18