Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
233 commits
Select commit Hold shift + click to select a range
8cc940b
feat(daytona): add Daytona config, credentials, client, service and s…
ibetitsmike Oct 20, 2026
9403abf
refactor(daytona): split client request, startAndWait and credential …
ibetitsmike Oct 20, 2026
4869c28
feat(daytona): add DaytonaSSHRuntime, reconciler, agent-home seeding …
ibetitsmike Oct 20, 2026
71726bd
refactor(daytona): split runtime, seed and archive-hook functions und…
ibetitsmike Oct 20, 2026
abebb17
fix(daytona): check dirty work before archive-delete, name the runtim…
ibetitsmike Oct 20, 2026
0ca603c
feat(daytona): add Daytona to Settings > Runtimes and new-workspace c…
ibetitsmike Oct 20, 2026
3be8da4
fix(daytona): never stop or delete the parent's sandbox when archivin…
ibetitsmike Oct 20, 2026
a5da99b
docs(daytona): add the Daytona runtime page and fix the remote user-h…
ibetitsmike Oct 20, 2026
137ac97
fix(daytona): satisfy lint and format checks for the runtime controls…
ibetitsmike Oct 20, 2026
436ecd9
fix(daytona): keep the draft default runtime stable under React Compiler
ibetitsmike Oct 20, 2026
fae3a1a
refactor(daytona): split the Daytona UI and archive paths under the C…
ibetitsmike Oct 20, 2026
b218f5a
fix(daytona): use interfaces for two object types (lint)
ibetitsmike Oct 20, 2026
c96c3de
fix(daytona): keep the runtime badge current and stop UI status check…
ibetitsmike Oct 21, 2026
e124802
fix(daytona): report invalid settings and HTML error pages as short, …
ibetitsmike Oct 21, 2026
9d114f3
fix(daytona): fix UAT round 1 data-loss, host-key, token and lifecycl…
ibetitsmike Oct 21, 2026
9213b57
fix(daytona): reject an API URL with an empty host
ibetitsmike Oct 21, 2026
da988ae
fix(daytona): fix UAT round 2 orphaned sandbox, token and delete find…
ibetitsmike Oct 21, 2026
ce322cf
fix(daytona): check the sandbox state before replacing a refused ssh …
ibetitsmike Oct 21, 2026
a873c96
refactor(daytona): reuse shared lock, sleep and abort helpers; drop d…
ibetitsmike Oct 21, 2026
24537ce
test(daytona): make three runtime tests and the schema round-trip fai…
ibetitsmike Oct 21, 2026
26fb323
fix(daytona): satisfy type-aware lint rules
ibetitsmike Oct 21, 2026
7a68b68
fix(daytona): keep strict gateway host key checking when ssh config s…
ibetitsmike Oct 21, 2026
a038b0b
fix(daytona): revoke a token minted across a settings change
ibetitsmike Oct 21, 2026
478b2b2
fix(daytona): delete a sandbox after a failed create only when it is …
ibetitsmike Oct 21, 2026
2219020
fix(daytona): keep every token within the retention window redactable
ibetitsmike Oct 21, 2026
c49c5b2
fix(ssh): expand an Include only when its enclosing block applies to …
ibetitsmike Oct 21, 2026
7d5aef0
fix(daytona): reload creation state after Daytona access is saved
ibetitsmike Oct 21, 2026
fa92cf6
fix(daytona): pin the gateway host key for integrated terminals
ibetitsmike Oct 21, 2026
6a3aede
fix(daytona): drop a token minted across a release of ssh access
ibetitsmike Oct 21, 2026
99920fc
fix(daytona): ignore a stored API URL that does not validate
ibetitsmike Oct 21, 2026
44a7292
fix(ssh): serialize managed ssh config block writes
ibetitsmike Oct 21, 2026
8b656c8
fix(daytona): check each checkout within the ready cache and keep unt…
ibetitsmike Oct 21, 2026
7bc3be0
fix(daytona): check a seed file's size before reading it
ibetitsmike Oct 21, 2026
1c151b1
fix(daytona): never seed files a symlink brings in from outside a skill
ibetitsmike Oct 21, 2026
4afe905
fix(daytona): bring the sandbox up before a terminal opens
ibetitsmike Oct 21, 2026
bfd6a5d
fix(daytona): check an attached sandbox's state before probing its jobs
ibetitsmike Oct 21, 2026
476ac25
fix(daytona): keep sandboxes alive under jobs that survived a Xum crash
ibetitsmike Oct 21, 2026
c91c927
fix(daytona): keep a draft's Daytona choices across runtime switches
ibetitsmike Oct 21, 2026
f1ecfa0
fixup! fix(daytona): check a seed file's size before reading it
ibetitsmike Oct 21, 2026
94cad17
fixup! fix(daytona): never seed files a symlink brings in from outsid…
ibetitsmike Oct 21, 2026
745ce52
fixup! fix(daytona): bring the sandbox up before a terminal opens
ibetitsmike Oct 21, 2026
fddee2d
fixup! fix(daytona): keep a draft's Daytona choices across runtime sw…
ibetitsmike Oct 21, 2026
40016c5
fix(daytona): bind the gateway to the mint's settings and refresh it …
ibetitsmike Oct 21, 2026
dcf4245
fix(daytona): save the API URL only when the user edits it
ibetitsmike Oct 21, 2026
6b51403
fix(daytona): refresh the runtime status after a terminal brings a sa…
ibetitsmike Oct 21, 2026
a2ba06a
fix(daytona): match sandbox references by both the sandbox's id and name
ibetitsmike Oct 21, 2026
ce706b1
fix(daytona): delete a failed setup's sandbox in the account that cre…
ibetitsmike Oct 21, 2026
0d4c467
fix(daytona): read workspace references after the sandbox list for th…
ibetitsmike Oct 21, 2026
ea0a98e
fix(daytona): keep a sandbox alive only for a job proven to run
ibetitsmike Oct 21, 2026
3618bee
fix(daytona): skip an oversized seed file and copy the files that sti…
ibetitsmike Oct 21, 2026
1329759
fix(runtime): log a failed init's error message, not the serialized E…
ibetitsmike Oct 21, 2026
f40013c
fix(daytona): refuse a settings change while a Daytona operation runs
ibetitsmike Oct 21, 2026
8cadb56
fix(daytona): retire a sandbox's pooled ssh connections when its gate…
ibetitsmike Oct 21, 2026
02b7a40
fix(daytona): refuse a plain-http API URL unless its host is loopback
ibetitsmike Oct 21, 2026
6f5a5ad
fix(daytona): build the seed script without spreading every base64 line
ibetitsmike Oct 21, 2026
4ba7387
fix(daytona): delete a sandbox Xum created with its last workspace, a…
ibetitsmike Oct 21, 2026
5bba730
fix(daytona): never start a stopped sandbox for a forced removal, and…
ibetitsmike Oct 21, 2026
33d48fa
refactor(daytona): keep deleteWorkspaceAndSandbox and isLoopbackHost …
ibetitsmike Oct 21, 2026
c800ffa
fix(daytona): never delete an attached sandbox, even one Xum created;…
ibetitsmike Oct 21, 2026
c038ca0
fix(ssh): expand an Include's wildcards in every path segment, as Ope…
ibetitsmike Oct 21, 2026
bcb192b
fix(daytona): refuse a saved API URL that no longer validates instead…
ibetitsmike Oct 21, 2026
7918813
fix(ssh): expand bracket expressions in an Include path, and match it…
ibetitsmike Oct 21, 2026
9d0214f
fix(daytona): probe for a surviving job before a model-driven archive…
ibetitsmike Oct 21, 2026
18eb45f
fix(daytona): tie a recorded workspace removal to its pendingRemoval …
ibetitsmike Oct 21, 2026
764b086
fix(daytona): replace a refused ssh token only if the command used th…
ibetitsmike Oct 21, 2026
6b8324f
fix(daytona): refuse every DAYTONA_API_URL that does not validate
ibetitsmike Oct 21, 2026
275925a
docs(daytona): say that archive-delete does not protect files git ign…
ibetitsmike Oct 21, 2026
2d0f644
fix(daytona): drop a cancelled readiness waiter's starting callback
ibetitsmike Oct 21, 2026
7a748b0
fix(daytona): never offer, create or attach a Windows sandbox
ibetitsmike Oct 21, 2026
d38d5a2
fix(ssh): never reuse a connection that was still connecting when its…
ibetitsmike Oct 21, 2026
51c5a48
fix(daytona): refuse a snapshot Daytona no longer has before persisting
ibetitsmike Oct 21, 2026
9054b6b
fix(daytona): refuse a Windows deployment default snapshot before cre…
ibetitsmike Oct 21, 2026
979ce24
fix(daytona): repoint every sandbox alias when the gateway changes
ibetitsmike Oct 21, 2026
25f3846
fix(daytona): drop the install label from a handed-off sandbox so no …
ibetitsmike Oct 21, 2026
a539e23
fix(daytona): close the bash AI proxy forward when a sandbox alias is…
ibetitsmike Oct 21, 2026
83ec9fe
fix(daytona): narrow the default snapshot name before reporting it
ibetitsmike Oct 21, 2026
b5d3f5d
Merge remote-tracking branch 'origin/main' into mike/daytona-runtime
ibetitsmike Oct 21, 2026
7625504
fix(daytona): retire a released sandbox's ssh connections and forwards
ibetitsmike Oct 21, 2026
c47225f
fix(daytona): release an archived workspace's alias that no other wor…
ibetitsmike Oct 21, 2026
3eb8fa4
fix(daytona): register saved ssh tokens before restoring bash AI prox…
ibetitsmike Oct 21, 2026
f24a517
fix(daytona): install a moved gateway's ssh setup before retiring old…
ibetitsmike Oct 21, 2026
d4f4e77
fix(daytona): say where a forced removal left the checkout on a stopp…
ibetitsmike Oct 21, 2026
b67bda7
fix(daytona): leave an unreported sandbox disk size alone when resizing
ibetitsmike Oct 21, 2026
ff1f14f
test(daytona): give the restart test's writer stub repointGateway
ibetitsmike Oct 21, 2026
9b33500
test(daytona): give the shadowed-config test's writer stub repointGat…
ibetitsmike Oct 21, 2026
3bca315
refactor(daytona): split the access release out of applySandboxFate
ibetitsmike Oct 21, 2026
17ac602
fix(daytona): resize unreported cpu and memory on a stopped sandbox
ibetitsmike Oct 21, 2026
bf08d89
fix(daytona): warn and release access when a forced removal cannot ha…
ibetitsmike Oct 21, 2026
8c91a35
fix(ssh): expand variables, local tokens and named glob classes in co…
ibetitsmike Oct 21, 2026
d5f856f
refactor(ssh): split named-class parsing out of the Include bracket m…
ibetitsmike Oct 21, 2026
026a9ae
fix(daytona): never forward the local agent, X11 or config port forwa…
ibetitsmike Oct 21, 2026
fe4e5b4
fix(ssh): split Include and Host arguments as OpenSSH's argv_split does
ibetitsmike Oct 21, 2026
98072c6
fix(daytona): accept an IPv6 SSH gateway, refusing zone ids
ibetitsmike Oct 21, 2026
b10c05f
fix(daytona): keep walking full snapshot pages when Daytona omits the…
ibetitsmike Oct 21, 2026
41a39a2
fix(daytona): hold a cross-process lock across a credentials save
ibetitsmike Oct 21, 2026
51e0804
fix(daytona): hold a cross-process lock across every Daytona ssh conf…
ibetitsmike Oct 21, 2026
43299a1
fix(daytona): apply the archive policy once only archived workspaces …
ibetitsmike Oct 21, 2026
7c7be57
fix(daytona): neither offer nor attach a sandbox in an error state
ibetitsmike Oct 21, 2026
de10c3e
fix(daytona): neither offer nor accept a snapshot that is not active
ibetitsmike Oct 21, 2026
c78e60c
fix(daytona): refuse an inactive deployment default snapshot like a c…
ibetitsmike Oct 21, 2026
e98b3d8
fix(daytona): redact Daytona tokens from terminal output
ibetitsmike Oct 21, 2026
5f7ec5a
fix(ssh): restore the includer's scope after an Include returns
ibetitsmike Oct 21, 2026
3e44307
fix(daytona): seed only skill directories of a skills root
ibetitsmike Oct 21, 2026
ff9c9b3
fix(daytona): keep a possible token start masked after the quiet period
ibetitsmike Oct 21, 2026
a092216
fix(daytona): settle the sandbox when a forced removal cannot delete …
ibetitsmike Oct 21, 2026
e326c41
fix(daytona): start a stopped source sandbox before forking
ibetitsmike Oct 21, 2026
27cc140
fix(daytona): tell the user a sandbox a failed setup could not delete…
ibetitsmike Oct 21, 2026
d96f3dc
fix(daytona): drop cached SSH access when another process changes the…
ibetitsmike Oct 21, 2026
b6b48c7
refactor(daytona): split the settlement after the SSH checkout delete
ibetitsmike Oct 21, 2026
c5c17d8
fix(ssh): lock the main ssh config across processes while writing a m…
ibetitsmike Oct 21, 2026
2e25786
fix(daytona): refuse a Xum root with % before writing Daytona ssh config
ibetitsmike Oct 21, 2026
a67047e
fix(workspace): report archive warnings from bulk archive of merged w…
ibetitsmike Oct 21, 2026
02b1a9f
refactor(workspace): keep archiveMergedInProject complexity flat
ibetitsmike Oct 21, 2026
da146e5
style(app): keep PageArea's doc comment on PageArea
ibetitsmike Oct 21, 2026
075a8ad
fix(daytona): leave a resolve during this process's own settings save…
ibetitsmike Oct 21, 2026
93a838b
fix(daytona): seed only skill directories of a skills root, not its f…
ibetitsmike Oct 21, 2026
dd7be06
fix(daytona): never count a borrowed checkout as a sandbox reference
ibetitsmike Oct 21, 2026
b97e9b1
test(daytona): lock in that multi-project workspaces refuse a Daytona…
ibetitsmike Oct 21, 2026
28ca6d8
fix(daytona): recognize Daytona's deleting and deleted sandbox states
ibetitsmike Oct 21, 2026
daf8304
fix(daytona): share one gateway refresh between concurrent lookups
ibetitsmike Oct 21, 2026
d0b2996
fix(daytona): keep an account lease on the credentials it started with
ibetitsmike Oct 21, 2026
85f3759
fix(daytona): neither offer, create nor attach GPU sandboxes
ibetitsmike Oct 21, 2026
30c2746
fix(daytona): refuse a Xum root with ssh glob characters before writi…
ibetitsmike Oct 21, 2026
1ef4b72
fix(ssh): keep spaces in a quoted UserKnownHostsFile path
ibetitsmike Oct 21, 2026
1947523
fix(daytona): allow only ssh-literal characters in the Daytona ssh co…
ibetitsmike Oct 21, 2026
c24e86c
fix(ssh): restore the includer's scope after every file an Include reads
ibetitsmike Oct 21, 2026
fbd1823
fix(daytona): check every directive of a Daytona host block against s…
ibetitsmike Oct 21, 2026
0b57469
fix(daytona): apply the archive policy when an attached workspace is …
ibetitsmike Oct 21, 2026
0998dcc
fix(daytona): let an archive join an in-flight readiness before relea…
ibetitsmike Oct 21, 2026
956d64b
fix(daytona): redact the request's secrets from Daytona error diagnos…
ibetitsmike Oct 21, 2026
91625e4
fix(daytona): run no inherited ProxyCommand, ProxyJump or LocalComman…
ibetitsmike Oct 21, 2026
fdf994e
fix(daytona): let a cached readiness check restart a sandbox that sto…
ibetitsmike Oct 21, 2026
62308a2
fix(daytona): read the saved account before an idle operation pins it
ibetitsmike Oct 21, 2026
8bed052
fix(daytona): run no Daytona ssh before the earlier process's tokens …
ibetitsmike Oct 21, 2026
374724a
refactor(daytona): keep spawnRemoteProcess under the CRAP complexity …
ibetitsmike Oct 21, 2026
95f28ca
fix(daytona): delete only sandboxes that carry this install's label
ibetitsmike Oct 21, 2026
0596fdd
fix(daytona): follow the shared include file another backend edits
ibetitsmike Oct 21, 2026
52c984c
fix(daytona): resolve Daytona aliases from Xum's include file alone i…
ibetitsmike Oct 21, 2026
ca57219
refactor(daytona): drop the ssh -G shadowing check now that nothing c…
ibetitsmike Oct 21, 2026
ffb6e7b
fix(daytona): refetch a gateway that moved within the config cache an…
ibetitsmike Oct 22, 2026
83ce12d
fix(terminal): shell-quote every argument of native-terminal ssh comm…
ibetitsmike Oct 22, 2026
7281c19
docs(daytona): describe the -F isolation and the label check before d…
ibetitsmike Oct 22, 2026
4873b20
fix(daytona): prove ownership before a removal or archive changes the…
ibetitsmike Oct 22, 2026
3a991bd
fix(daytona): never bring up a sandbox without this install's label f…
ibetitsmike Oct 22, 2026
b805476
fix(daytona): check ownership before the ready cache and expire the p…
ibetitsmike Oct 22, 2026
575f0fb
fix(daytona): apply an archived owner's deferred policy when the last…
ibetitsmike Oct 22, 2026
c8e9b1f
fix(daytona): revoke another backend's first token for a sandbox once…
ibetitsmike Oct 22, 2026
10d01df
refactor(services): keep the perf recorder's comment on its method
ibetitsmike Oct 22, 2026
8524ac7
refactor(daytona): name the removal's archive step for what it applie…
ibetitsmike Oct 22, 2026
41b6253
fix(daytona): keep the saved settings when an update cannot read them
ibetitsmike Oct 22, 2026
9a97eb4
fix(daytona): never pin the old account for a lease after unusable se…
ibetitsmike Oct 22, 2026
d23785b
fix(ssh): end a reverse forward an earlier process left when its host…
ibetitsmike Oct 22, 2026
81eaf9c
fix(ssh): open the unescaped literal name of an Include segment witho…
ibetitsmike Oct 22, 2026
28ded5d
test(ssh): skip the escaped Include glob test on Windows without test…
ibetitsmike Oct 22, 2026
5181738
fix(daytona): show no previous account's resources while Daytona sett…
ibetitsmike Oct 22, 2026
ecebbfd
fix(daytona): narrow the loaded Daytona resources before reading them
ibetitsmike Oct 22, 2026
bf53066
fix(daytona): prepare the sandbox before opening an external editor
ibetitsmike Oct 22, 2026
04935ad
fix(daytona): put the hardened Daytona ssh block first in ~/.ssh/config
ibetitsmike Oct 22, 2026
4040f70
chore(docs): regenerate built-in skill content for the Daytona SSH se…
ibetitsmike Oct 22, 2026
86d4af1
fix(daytona): refuse opening a sandbox in an editor from a remote bro…
ibetitsmike Oct 22, 2026
3419717
fix(daytona): put back the ~/.ssh/config block on ssh access while th…
ibetitsmike Oct 22, 2026
69eb986
fix(daytona): load Daytona settings and resources again when the conn…
ibetitsmike Oct 22, 2026
9979097
fix(daytona): start the sandbox for an editor only once the editor us…
ibetitsmike Oct 22, 2026
af312d5
fix(daytona): settle the sandbox when the last borrower of a removed …
ibetitsmike Oct 22, 2026
eeb5633
fix(daytona): fail a credentials resolve when the settings file canno…
ibetitsmike Oct 22, 2026
ed4ead5
fix(daytona): refuse an editor open while a user RemoteForward reache…
ibetitsmike Oct 22, 2026
b30a619
fix(daytona): restore the editor's ssh setup when sandbox readiness i…
ibetitsmike Oct 22, 2026
36c57f1
fix(daytona): never follow a symlink when seeding the sandbox home
ibetitsmike Oct 22, 2026
2785b21
fix(daytona): probe every workspace on a shared sandbox before an arc…
ibetitsmike Oct 22, 2026
9803d69
fix(ssh): match dot files with an escaped leading dot in an Include glob
ibetitsmike Oct 22, 2026
284ca63
fix(daytona): read each sandbox's state once per runtime status refresh
ibetitsmike Oct 22, 2026
ac6ed1c
chore(daytona): keep StoredSandboxRef's doc comment on it
ibetitsmike Oct 22, 2026
5956abc
fix(ssh): resolve a checkout path under ~ on the host before an edito…
ibetitsmike Oct 22, 2026
5aea16d
fix(daytona): accept an absolute DNS name for the SSH gateway
ibetitsmike Oct 22, 2026
717ea3c
fix(daytona): remove the old account's aliases before retiring its co…
ibetitsmike Oct 22, 2026
8dbb7c4
fix(daytona): change only the include blocks this backend owns
ibetitsmike Oct 22, 2026
c6c2f32
fix(daytona): refuse ssh to an old account's alias until it is removed
ibetitsmike Oct 22, 2026
dd50e62
fix(daytona): report a failed sandbox setup only to the workspace who…
ibetitsmike Oct 22, 2026
9038946
fix(daytona): put back the gateway's pins after a host key failure
ibetitsmike Oct 22, 2026
eb39bf3
fix(daytona): gate Daytona access actions on the live API client
ibetitsmike Oct 22, 2026
5f5ccf4
test(daytona): compare the seeded directory's entries in a fixed order
ibetitsmike Oct 22, 2026
622aee9
refactor(daytona): assume one Xum process per data folder
ibetitsmike Oct 22, 2026
b881e16
refactor(daytona): make a settings save only revoke tokens and clear …
ibetitsmike Oct 22, 2026
80e4a55
refactor(daytona): move the agent home copy to a follow-up
ibetitsmike Oct 22, 2026
4ccb7f0
refactor(daytona): stop checking the snapshot state before create
ibetitsmike Oct 22, 2026
dabcd5d
test(daytona): keep the token out of an ssh error that gets no retry
ibetitsmike Oct 22, 2026
2cac8ed
refactor(daytona): race shared requests with the existing abort helper
ibetitsmike Oct 22, 2026
fd0891c
refactor(daytona): use the shared not-found check
ibetitsmike Oct 22, 2026
d079667
refactor(ssh): share the workspace parent directory setup with Coder
ibetitsmike Oct 22, 2026
25126d0
refactor(daytona): drop the existing-sandbox picker
ibetitsmike Oct 22, 2026
ba89432
refactor(daytona): one owner per sandbox; archive deletes it
ibetitsmike Oct 22, 2026
b741f8f
refactor(daytona): drop an unused import after the rebase
ibetitsmike Oct 22, 2026
e4acd11
docs(daytona): drop the attach mention from the draft comment
ibetitsmike Oct 22, 2026
6ec64fa
refactor(daytona): split DaytonaService into account, ssh, sandbox an…
ibetitsmike Oct 22, 2026
887d407
fix(terminal): expand a ~ workspace path when a native SSH terminal o…
ibetitsmike Oct 22, 2026
aa427bf
fix(daytona): remove the old account's aliases from the include file …
ibetitsmike Oct 22, 2026
fa74933
fix(daytona): keep alive only sandboxes that carry this install's lab…
ibetitsmike Oct 22, 2026
0e212ad
style(daytona): format the reconciler's service type
ibetitsmike Oct 22, 2026
d9edf76
test(daytona): drop tests of the removed attach flag and borrower set…
ibetitsmike Oct 22, 2026
3c17aee
refactor(daytona): bring the new UI helpers under the CRAP limit
ibetitsmike Oct 22, 2026
e7da6a3
fix(ssh): start the remote bundle writer before git, so a slow ssh st…
ibetitsmike Oct 25, 2026
6402b9f
refactor(daytona): drop the unowned-sandbox, setup-failure and delete…
ibetitsmike Oct 25, 2026
c2d79aa
test(daytona): one fake control plane, and table tests for snapshot a…
ibetitsmike Oct 25, 2026
495b9ac
refactor(daytona): name the removal reachability rule
ibetitsmike Oct 25, 2026
e0a5d35
refactor(daytona): API key only in Settings, no project defaults, one…
ibetitsmike Oct 25, 2026
78ba90f
refactor(daytona): drop the event-driven runtime status refresh
ibetitsmike Oct 25, 2026
fa777ff
refactor(daytona): map sandbox states onto the existing runtime statu…
ibetitsmike Oct 25, 2026
f783964
refactor(daytona): trim DaytonaClient error and pagination hardening
ibetitsmike Oct 25, 2026
35bea14
refactor(daytona): drop the remote-browser refusal for Daytona editor…
ibetitsmike Oct 25, 2026
a47ea68
refactor(daytona): keep the touched Daytona UI functions within the C…
ibetitsmike Oct 25, 2026
e2f58bf
docs(daytona): describe the Coder-like removal and the logged kept sa…
ibetitsmike Oct 25, 2026
1e30f44
refactor(ssh): drop Include expansion from the ssh2 config parser
ibetitsmike Oct 25, 2026
742bb7e
refactor(daytona): stop retiring ssh connections and discarding mints…
ibetitsmike Oct 25, 2026
01eda07
refactor(ssh): drop the cross-process lock on managed ssh config writes
ibetitsmike Oct 25, 2026
e23885d
refactor(daytona): redact ssh output chunk by chunk, and turn off pro…
ibetitsmike Oct 25, 2026
44309a0
fix(daytona): share one /config refetch between ssh failures that rac…
ibetitsmike Oct 25, 2026
7f3c6f0
refactor(daytona): move the settings connection test into its own hoo…
ibetitsmike Oct 25, 2026
a15df13
test(ssh): use an optional chain in the bundle upload test
ibetitsmike Oct 25, 2026
c70e6e2
refactor(daytona): drop the editor RemoteForward refusal
ibetitsmike Oct 25, 2026
18d60a5
refactor(daytona): drop the account lease around Daytona operations
ibetitsmike Oct 25, 2026
a1d7073
refactor(daytona): one ssh repair path, and fetch the gateway config …
ibetitsmike Oct 25, 2026
328b7fe
fix(daytona): redact a token that a pipe or PTY splits across chunks
ibetitsmike Oct 25, 2026
bd8c2a8
fix(daytona): use only labelled sandboxes, and keep a setup on one ac…
ibetitsmike Oct 25, 2026
1c26b34
fix(daytona): quiet terminal ssh, and fail a setup the account change…
ibetitsmike Oct 25, 2026
07c785d
fix(daytona): drop a token whose mint spans a settings save
ibetitsmike Oct 25, 2026
dcc2630
fix(daytona): delete the sandbox id the ownership check saw
ibetitsmike Oct 25, 2026
270e373
fix(daytona): open no bash AI proxy forward to a sandbox
ibetitsmike Oct 25, 2026
bdcf0f0
fix(daytona): stop, resize and start the sandbox by its checked id
ibetitsmike Oct 25, 2026
ebe4781
fix(daytona): keep your own ssh clients quiet for sandbox aliases
ibetitsmike Oct 25, 2026
ca859b4
Merge remote-tracking branch 'origin/main' into mike/daytona-runtime
ibetitsmike Oct 27, 2026
69803bc
fix(daytona): record the sandbox id and send every request to it
ibetitsmike Oct 27, 2026
8b8f113
docs(daytona): say when Xum replaces the ssh token
ibetitsmike Oct 27, 2026
f0fec18
chore: regenerate the built-in docs skill for the Daytona token wording
ibetitsmike Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/config/providers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Scripts that the agent runs in the bash tool (test harnesses, `make bug-bash`, S
- Xum sends the calls with the provider keys from this page. The command never sees the real key.
- Local and Worktree commands reach Xum directly. SSH and Coder commands reach it through a reverse SSH forward (`ssh -R`) that Xum opens to each host, on the host's `127.0.0.1`. The first turn on a host can wait up to 10 seconds for it. The forward uses its own SSH connection without the forwards in your `~/.ssh/config`. A host that refuses forwarding (`AllowTcpForwarding no`) gets no variables, and Xum tries it again after 5 minutes. After a connection failure Xum tries again after 30 seconds. A host that asks for a password or a key passphrase gets no variables: the forward does not prompt, so load the key into `ssh-agent`.
- With `XUM_ALLOW_MULTIPLE_INSTANCES=1`, or while a `xum server` of another process uses the same Xum home, SSH and Coder commands get no variables: a host keeps one forwarded port per Xum home, and two backends would each pick their own. Xum does not detect a desktop app started with `XUM_NO_API_SERVER=1` next to `xum server`.
- Docker and devcontainer commands keep their environment: they have no route to Xum.
- Docker, devcontainer and Daytona commands keep their environment: they have no route to Xum (Daytona's SSH gateway refuses `ssh -R`).
- Bash commands in `xum run` and `xum workflow` sessions do not get the variables.
- Commands in untrusted projects, and every command while `XUM_DISABLE_PROJECT_AUTOMATION=1` is set, get no variables: Xum blanks provider keys there, so repo code cannot spend through the proxy. Revoking a project's trust also refuses calls from its commands that are still running.
- A provider without a Xum API key (for example OpenAI with Codex OAuth only) gets no variables. A provider that your [project secrets](/config/project-secrets) configure keeps the secret values. This includes `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID`.
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"runtime/worktree",
"runtime/ssh",
"runtime/coder",
"runtime/daytona",
"runtime/docker",
"runtime/devcontainer"
]
Expand Down
2 changes: 1 addition & 1 deletion docs/hooks/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Xum searches for each hook file in this order:

This applies to `tool_pre`, `tool_post`, and `tool_env`.

For SSH workspaces, hooks execute **on the remote machine**.
For SSH workspaces (including Coder and Daytona), hooks execute **on the remote machine**, and the user-level location there is `~/.mux/<hook>`, not `~/.xum/<hook>`. Docker workspaces read user-level hooks from `/var/mux/<hook>` inside the container.

</Accordion>

Expand Down
82 changes: 82 additions & 0 deletions docs/runtime/daytona.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: Daytona Runtime
description: Run agents in Daytona Cloud sandboxes
---

Xum can run workspaces in [Daytona](https://www.daytona.io/docs) sandboxes. Each new Daytona workspace gets its own sandbox, and Xum connects to it over SSH through Daytona's SSH gateway. Builds, tests, and other agent commands use the sandbox's CPU and memory, not your machine's. Under the hood a Daytona workspace is an [SSH workspace](/runtime/ssh), so `XUM_RUNTIME` is `ssh`.

## Requirements

- A Daytona account and an API key from the Daytona dashboard
- `ssh` installed on the machine running Xum
- A Linux snapshot (or Daytona's default) with git and your toolchain. Xum runs Linux shell commands over SSH, so it does not offer Windows snapshots, and refuses one before it creates a sandbox.

## Set up access

1. Open **Settings > Runtimes** and find the **Daytona** row.
2. Paste your API key and click **Save**.
3. Click **Test connection**.

Xum stores the key in `~/.xum/daytona.json` (under `XUM_ROOT` when it is set) with file mode `0600`. The settings page never shows a saved key again: to change it, enter a new one, or click **Remove key**.

Without a saved key, Xum uses `DAYTONA_API_KEY` from its own environment. A saved key wins over the environment.

For a self-hosted (BYOC) Daytona install, set `DAYTONA_API_URL` in the environment of the Xum server. It must use `https://`, so that no one on the network can read the API key; `http://` works only for a loopback host (`localhost`, `127.0.0.1` or `::1`). A `DAYTONA_API_URL` that is not accepted keeps Daytona unavailable until you fix it; Xum never sends the key to another deployment instead. `DAYTONA_TARGET` picks a default region; without it, Daytona uses its default.

<Warning>
Do not put your Daytona API key in **Secrets**. Xum passes secrets to agent turns and terminals,
so an agent could read the key and create or delete sandboxes in your organization.
</Warning>

## Sizes

| Size | vCPU | Memory | Disk |
| ------ | ---- | ------ | ------ |
| Small | 2 | 4 GiB | 10 GiB |
| Medium | 4 | 8 GiB | 20 GiB |
| Large | 8 | 16 GiB | 40 GiB |

Small is the default. Xum offers only these sizes, which keeps costs predictable.

## Create a workspace

1. In the new-workspace controls, choose **Daytona** as the runtime.
2. Pick a snapshot and a size.
3. Xum creates the sandbox, waits until it starts, clones the project, and runs the [init hook](/hooks/init).

Xum labels each sandbox it creates with `xum.instance`, `xum.workspaceId`, and `xum.project`. If Daytona rejects the size for a snapshot, Xum creates the sandbox at the snapshot's size and then resizes it.

Xum uses only sandboxes it creates: a workspace cannot use an existing sandbox.

## Lifecycle

| Xum event | Daytona action |
| ----------------------------------- | ------------------------------------------------------------------------------------- |
| A turn starts | Start the sandbox if it is stopped or archived (waits up to 3 minutes) |
| An agent is streaming or a job runs | Report activity every minute, so Daytona does not stop the sandbox |
| Idle | Daytona stops the sandbox after 30 minutes and archives it after 1 day stopped |
| Sub-agent with a forked workspace | A new worktree in the same sandbox, not a second sandbox |
| Archive | Delete the sandbox at once, without a check for uncommitted work |
| Delete | Delete the sandbox; a running one is first checked for uncommitted or unpushed work |
| Opening a workspace | Background git and PR status checks do not start the sandbox; they wait until it runs |
| Sandbox no longer exists | The workspace reports it; Xum does not recreate it |

Each sandbox belongs to the workspace that created it. Archiving or deleting that workspace deletes the sandbox, even while forks of it (sub-agent worktrees) still use it; **Settings > General > Remote box on archive** does not apply to Daytona. Unarchiving a workspace whose sandbox was deleted does not bring the sandbox back, so push or copy any work you need before you archive. An agent cannot archive the workspace that created a sandbox: it asks you to do it. Archiving or deleting a fork removes only its worktree and never stops or deletes the sandbox. Xum records the id of each sandbox it creates in the workspace's settings, and every Daytona request about the sandbox uses that id, so a sandbox that later gets the same name is never touched. Before Xum starts a sandbox, opens SSH access to it, or deletes it, it also checks that the sandbox carries this install's `xum.instance` label. A sandbox without it (for example one whose label was removed) is never used or deleted, and Xum notes it in its log.

A forced delete removes the workspace even when Daytona refuses to delete the sandbox; it then shows a warning that names the sandbox, which you delete in the Daytona dashboard.

Xum does not stop or delete sandboxes that no workspace uses (for example one whose delete failed); Daytona stops them after 30 minutes idle. Delete them in the Daytona dashboard.

## SSH setup

Xum writes the SSH settings for its sandboxes to `~/.xum/ssh/daytona.conf` and adds a managed block to the top of `~/.ssh/config` that includes it for hosts named `<sandbox-name>.xum--daytona`. The gateway's access token is the SSH user name; Xum keeps it in memory and gets it for one hour. When Xum brings a sandbox up for a turn or a terminal, it replaces a token that has less than 15 minutes left. Xum does not replace tokens in the background, so your own SSH clients can be refused after the hour until Xum uses the sandbox again. Xum revokes the token when it replaces it, when you change the Daytona settings, and when it deletes the sandbox. At startup, Xum takes over the tokens that an earlier run left in `daytona.conf`, so it can filter and revoke them too. Before each SSH command it reads the file again, in case that read failed at startup. When an SSH command to a running sandbox fails (for example the gateway refuses a token revoked outside Xum, or Daytona moved its gateway or changed its host keys), Xum reads the gateway settings from Daytona again, rewrites the SSH settings, gets a new token, and runs the command again once. Xum replaces any token it finds in SSH errors and logs with `<daytona-token>`; output of commands that you or an agent run in a terminal is not filtered.

Xum pins the gateway's host keys in `~/.xum/ssh/daytona_known_hosts` and refuses a gateway whose key does not match, with OpenSSH and with the built-in SSH2 transport alike. This holds for `xum server` too, which otherwise skips host key checks for SSH hosts.

Xum's own SSH connections to sandboxes read only `daytona.conf` (OpenSSH runs with `-F`), so nothing in your `~/.ssh/config` or in `/etc/ssh/ssh_config` applies to them: a `Host *` entry's `User`, `ProxyCommand`, `KnownHostsCommand`, forwards or `Match exec` never run for a sandbox, and neither does a system-wide proxy or jump host. The managed block in `~/.ssh/config` is for your own SSH clients, such as an editor's Remote-SSH, which do read your config. OpenSSH uses the first value it finds for each setting, and Xum keeps its block first in the file, so its settings win for sandboxes in those clients too: strict host key checking against the pinned keys, the token as the user name, no proxy or jump host, no agent or X11 forwarding, and `LogLevel QUIET`, because an ssh error message would print the token. Your other entries still apply to every other host. A `KnownHostsCommand` or `Match exec` line in your config still runs for sandboxes in your own clients, with the token as `%r`; scope such lines to the hosts that need them. OpenSSH adds up `RemoteForward` lines instead of keeping the first, so a `RemoteForward` in a `Host *` entry also opens a tunnel from the sandbox to your machine in those clients; scope it to the hosts that need it.

## Notes

- **Costs:** Daytona bills running sandboxes by size and time. Archive or delete workspaces you no longer need, and pick the smallest size that works.
- **CPU count:** `nproc` inside a sandbox reports the host's CPUs, not the sandbox's, so builds may start too many parallel jobs. Set job counts (for example `MAKEFLAGS=-j4`) in your snapshot or in the project's [`.xum/tool_env`](/hooks/tools).
- User-level hooks and skills in a sandbox live under `~/.mux`, the same as on other SSH hosts.
1 change: 1 addition & 0 deletions docs/runtime/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Runtimes determine where and how Xum executes agent workspaces.
| **[Local](/runtime/local)** | All workspaces share the project directory | Quick edits to your working copy |
| **[Worktree](/runtime/worktree)** | Each workspace gets its own directory | Running multiple agents in parallel |
| **[SSH](/runtime/ssh)** | Remote execution over SSH | Security, performance, heavy parallelism |
| **[Daytona](/runtime/daytona)** | Cloud sandbox per workspace, over SSH | Remote compute without managing hosts |
| **[Docker](/runtime/docker)** | Isolated container per workspace | Full isolation, reproducible environments |
| **[Dev Container](/runtime/devcontainer)** | Container from devcontainer.json | Projects with existing devcontainer configs |

Expand Down
12 changes: 7 additions & 5 deletions src/browser/components/ChatPane/WorkspaceFooterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import { cn } from "@/common/lib/utils";
import { stopKeyboardPropagation } from "@/browser/utils/events";
import { updateUserPreferences, useUserPreferences } from "@/browser/stores/AppConfigStore";
import { isDevcontainerRuntime, type RuntimeConfig } from "@/common/types/runtime";
import { isDevcontainerRuntime, isSSHRuntime, type RuntimeConfig } from "@/common/types/runtime";
import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
import type { ProjectConfig } from "@/common/types/project";
import { getDevcontainerStatusChip } from "@/browser/utils/runtimeUi";
import { getRuntimeStatusChip } from "@/browser/utils/runtimeUi";
import { formatTokens } from "@/common/utils/tokens/tokenMeterUtils";
import { hasWorkspaceRepository } from "@/browser/utils/workspaceCapabilities";
import { isMultiProject } from "@/common/utils/multiProject";
Expand Down Expand Up @@ -138,7 +138,7 @@
);
}

type DevcontainerChip = ReturnType<typeof getDevcontainerStatusChip>;
type DevcontainerChip = ReturnType<typeof getRuntimeStatusChip>;

function WorkspaceBranchControls(props: {
workspaceId: string;
Expand Down Expand Up @@ -425,12 +425,14 @@
const { canInterrupt, isStarting, awaitingUserQuestion } = useWorkspaceSidebarState(
props.workspaceId
);
const isWorking = (canInterrupt || isStarting) && !awaitingUserQuestion;

Check warning on line 428 in src/browser/components/ChatPane/WorkspaceFooterBar.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: || -> &&

No unit test failed when WorkspaceFooterBar had || -> &&. Add a test that tells them apart, or say why they are equivalent.

Check warning on line 428 in src/browser/components/ChatPane/WorkspaceFooterBar.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: && -> ||

No unit test failed when WorkspaceFooterBar had && -> ||. Add a test that tells them apart, or say why they are equivalent.

Check warning on line 428 in src/browser/components/ChatPane/WorkspaceFooterBar.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: delete !

No unit test failed when WorkspaceFooterBar had delete !. Add a test that tells them apart, or say why they are equivalent.

const runtimeStatus = useRuntimeStatus(props.workspaceId);
const devcontainerChip = isDevcontainerRuntime(props.runtimeConfig)
? getDevcontainerStatusChip(runtimeStatus)
: null;
? getRuntimeStatusChip(runtimeStatus, "Devcontainer")
: isSSHRuntime(props.runtimeConfig) && props.runtimeConfig.daytona != null

Check warning on line 433 in src/browser/components/ChatPane/WorkspaceFooterBar.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: && -> ||

No unit test failed when WorkspaceFooterBar had && -> ||. Add a test that tells them apart, or say why they are equivalent.

Check warning on line 433 in src/browser/components/ChatPane/WorkspaceFooterBar.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: != -> ==

No unit test failed when WorkspaceFooterBar had != -> ==. Add a test that tells them apart, or say why they are equivalent.
? getRuntimeStatusChip(runtimeStatus, "Sandbox")
: null;
// One JS source for the phone layout, so the hidden drift label and the tap-to-toggle diff
// numbers can never disagree about the width.
const isNarrow = useNarrowViewport();
Expand Down
48 changes: 48 additions & 0 deletions src/browser/components/RuntimeBadge/RuntimeBadge.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, expect, it } from "bun:test";
import { getSshRuntimeInfo } from "./RuntimeBadge";

describe("getSshRuntimeInfo", () => {
it("shows a Coder workspace by name, or by host before it has one", () => {
const named = getSshRuntimeInfo({
type: "ssh",
host: "coder-host",
srcBaseDir: "~/src",
coder: { workspaceName: "my-ws" },
});
expect(named.type).toBe("coder");
expect(named.label).toContain("my-ws");
const unnamed = getSshRuntimeInfo({
type: "ssh",
host: "coder-host",
srcBaseDir: "~/src",
coder: {},
});
expect(unnamed.label).toContain("coder-host");
});

it("shows a Daytona sandbox by id, or by host before it has one", () => {
const created = getSshRuntimeInfo({
type: "ssh",
host: "sb-1.xum--daytona",
srcBaseDir: "~/src",
daytona: { sandboxName: "sb-1" },
});
expect(created.type).toBe("daytona");
expect(created.label).toContain("sb-1");
const pending = getSshRuntimeInfo({
type: "ssh",
host: "daytona-placeholder",
srcBaseDir: "~/src",
daytona: {},
});
expect(pending.type).toBe("daytona");
expect(pending.label).toContain("daytona-placeholder");
});

it("shows a plain SSH host without its user", () => {
const info = getSshRuntimeInfo({ type: "ssh", host: "me@box.example", srcBaseDir: "~/src" });
expect(info.type).toBe("ssh");
expect(info.label).toContain("box.example");
expect(info.label).not.toContain("me@");
});
});
33 changes: 23 additions & 10 deletions src/browser/components/RuntimeBadge/RuntimeBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,33 @@

type RuntimeType = keyof typeof RUNTIME_BADGE_UI;

/** SSH runtimes split by their managed backend (Coder, Daytona) or plain host. */
export function getSshRuntimeInfo(runtimeConfig: Extract<RuntimeConfig, { type: "ssh" }>): {
type: RuntimeType;
label: string;
} {
if (runtimeConfig.coder) {
const coderWorkspaceName = runtimeConfig.coder.workspaceName;
return {
type: "coder",
label: `Coder Workspace: ${coderWorkspaceName ?? runtimeConfig.host}`,

Check warning on line 82 in src/browser/components/RuntimeBadge/RuntimeBadge.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: ?? -> ||

No unit test failed when getSshRuntimeInfo had ?? -> ||. Add a test that tells them apart, or say why they are equivalent.
};
}
if (runtimeConfig.daytona) {
return {
type: "daytona",
label: `Daytona sandbox: ${runtimeConfig.daytona.sandboxName ?? runtimeConfig.host}`,

Check warning on line 88 in src/browser/components/RuntimeBadge/RuntimeBadge.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: ?? -> ||

No unit test failed when getSshRuntimeInfo had ?? -> ||. Add a test that tells them apart, or say why they are equivalent.
};
}
const hostname = extractSshHostname(runtimeConfig);
return { type: "ssh", label: `SSH: ${hostname ?? runtimeConfig.host}` };

Check warning on line 92 in src/browser/components/RuntimeBadge/RuntimeBadge.tsx

View workflow job for this annotation

GitHub Actions / Quality / Mutation

Mutation survived: ?? -> ||

No unit test failed when getSshRuntimeInfo had ?? -> ||. Add a test that tells them apart, or say why they are equivalent.
}

function getRuntimeInfo(
runtimeConfig?: RuntimeConfig
): { type: RuntimeType; label: string } | null {
if (isSSHRuntime(runtimeConfig)) {
// Coder-backed SSH runtime gets special treatment
if (runtimeConfig.coder) {
const coderWorkspaceName = runtimeConfig.coder.workspaceName;
return {
type: "coder",
label: `Coder Workspace: ${coderWorkspaceName ?? runtimeConfig.host}`,
};
}
const hostname = extractSshHostname(runtimeConfig);
return { type: "ssh", label: `SSH: ${hostname ?? runtimeConfig.host}` };
return getSshRuntimeInfo(runtimeConfig);
}
if (isWorktreeRuntime(runtimeConfig)) {
return { type: "worktree", label: "Worktree: isolated git worktree" };
Expand Down
20 changes: 20 additions & 0 deletions src/browser/components/icons/RuntimeIcons/RuntimeIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ export function CoderIcon({ size = 10, className }: IconProps) {
);
}

/** Cloud icon for the Daytona-backed SSH runtime (a managed cloud sandbox) */
export function DaytonaIcon({ size = 10, className }: IconProps) {
return (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-label="Daytona Runtime"
className={className}
>
<path d="M4.5 12.5 A3 3 0 0 1 4.2 6.5 A4 4 0 0 1 11.8 6 A3.25 3.25 0 0 1 11.5 12.5 Z" />
</svg>
);
}

/** Container icon for Docker runtime */
export function DockerIcon({ size = 10, className }: IconProps) {
return (
Expand Down
Loading
Loading