For the complete documentation index, see llms.txt.

Manage Chainguard Workspaces sessions

Open, detach from, return to, list, rename, and delete Chainguard Workspaces sessions, run several terminals in one, copy text out of one, and keep a session running while you're away.
  18 min read

A Chainguard Workspaces session lasts beyond a single terminal. You can detach from it and return later, keep it running while an agent works, run more than one terminal in it, copy text out of it, or open several sessions on the same repository. This page explains how to do each of these with chainctl develop.

The examples on this page use $ORGANIZATION for the name of your Chainguard organization. If you belong to only one organization, you can omit --parent.

How chainctl develop chooses a session

The chainctl develop command takes one optional argument, which tells it what to open:

ArgumentWhat chainctl develop does
None, or a directoryReattaches to the directory’s session if it has exactly one, and creates one if it has none. If the directory has several sessions, it asks which one you want.
A subdirectory of a checkoutOpens the whole repository with its history, and starts the shell in the subdirectory.
A repository URL, such as github.com/acme/appOpens the repository at its default branch, without syncing a local directory.
A session name, such as app/quiet-harbor, or a short ID, such as 3f9a2c1eReturns to that session.

Sessions you open from a directory and sessions you open from a repository URL are separate. Running chainctl develop github.com/acme/app doesn’t return to a session you opened from your local clone of that repository.

Choose the session’s repository

A session that you open on a directory is on one GitHub repository, and the session’s github credential is scoped to it. When the session can start from one of the repository’s commits, chainctl uploads only what differs from that commit. By default, the session is on the repository that your checkout’s origin remote points to. If the checkout’s remotes point to more than one GitHub repository, such as a fork and its upstream, chainctl lists them, starting with the one origin points to, and asks which one to use when it creates the session. Without a terminal, or with --non-interactive, it uses the repository that origin points to.

To choose ahead of time, pass --git-remote with the name of a remote. This example opens a session on the repository that the upstream remote points to:

chainctl develop --git-remote upstream --parent $ORGANIZATION

If the directory’s session is on a different repository than the remote you name, chainctl develop fails and tells you to add --new, as described in Open more than one session.

To open a session on no repository, pass --git-remote -, or choose Upload this directory on no repository when chainctl asks. The session then has no github service, so it gets no GitHub credential, and chainctl uploads the whole checkout with its history, instead of only what differs from a commit. Files that .gitignore excludes still stay on your laptop.

Whatever the session is on, chainctl doesn’t upload your checkout’s .git/config file or its .git/hooks directory, because they can contain credentials and commands that git runs. Chainguard adds an origin remote that points to the session’s repository, so origin is the session’s only remote, even if you chose another remote. A session on no repository has no remotes. Your other remotes aren’t in the session, and neither are hooks that you or a hook manager installed.

To fetch from or push to another of your remotes in the session, add it with its HTTPS URL:

git remote add upstream https://github.com/acme/app

The session’s github credential already covers your checkout’s other github.com remotes where the Chainguard App is installed, as described in What a session can reach by default. To run your hooks in the session, install them again there, for example with pre-commit install.

Detach from a session

While you’re attached to a session, press Ctrl+] to open the session menu. You can also type ~. at the session’s shell prompt, or as the first thing you type after you attach or press Enter. In a program that draws its own input line, such as Claude Code, ~. works only right after Enter, so use Ctrl+] there. The menu has the following options:

OptionKeyWhat it does
Detach — keep running while busyd or EnterReturns you to your laptop’s shell. The session keeps running while it’s busy and parks after it’s been idle for 10 minutes, as described in Keep a session running while you’re away. The option’s hint shows the latest time it can keep running, such as parks after 10m quiet, at most until 17:30.
Detach — keep running 4hReturns you to your laptop’s shell and keeps the session running for 4 hours, busy or not.
Detach — keep running for…Asks for a duration, such as 90m, or a time, such as 17:30, up to 24 hours from now.
Park nowReturns you to your laptop’s shell and parks the session right away, even one you’re keeping running. To resume it, run chainctl develop, which waits for the park to finish.
Restart the hostParks the session and resumes it on a new host while you stay attached. Running processes continue unless the snapshot can’t be restored.
Rename…nChanges the session’s name.
CredentialsShows and changes what the session can reach. Refer to Manage session credentials.
Share from laptopShares skills and Claude Code instructions from your laptop with the session, or removes ones you shared. Refer to Share skills and instructions from your laptop.
StatusShows the session’s name and state, its repository and commit or its directory, its branch, its credential endpoints, what serves its laptop credentials, and what happened to the last clipboard copy. Refer to Copy text from a session.
LogslShows every message chainctl has shown since you attached, newest last.
Helph or ?Lists every key.
BackEscCloses the menu and returns to the session.

The menu ignores what you type for a moment after it opens, so pause before you press a key.

The first option is what d or Enter does, and the detach options depend on the session. For a session you’re already keeping running, the first option is Detach — keeps running until followed by a time, with then while busy after it when the session can keep running while it’s busy after that time. A session created with --keep always has one detach option, which shows how long it keeps running. A session created with --keep park, or with chainctl 0.2.382 or earlier, has Detach — let it park instead of Detach — keep running while busy, and parks about 10 minutes after you detach, busy or not. The --keep flag also changes the first option, as described in Keep a session running while you’re away.

To send Ctrl+] to a program in the session, press it twice. To see every key, choose Help, or type ~? wherever ~. works.

If you exit the session’s shell, for example with exit, any other terminals you opened in the session close too. If the session isn’t already being kept running, chainctl asks whether to keep it running while you’re away. Press Esc to keep it running only while it’s busy, as Detach — keep running while busy does. In a session that has Detach — let it park instead, Esc lets it park.

If your laptop sleeps or loses its network connection while you’re attached, the session doesn’t detach right away. It counts as attached until Chainguard detects that the connection is gone, which can take several minutes. Then it keeps running or parks as described in Keep a session running while you’re away. After a brief network drop, or when your laptop wakes, chainctl reconnects on its own, resumes the session first if it parked, and brings your terminal up to date. If it can’t reconnect after a few tries, it exits with an error, and you can return to the session with chainctl develop.

Return to a session

To return to a session, run chainctl develop from the directory you opened it on, or pass the session’s name or short ID from anywhere:

chainctl develop app/quiet-harbor --parent $ORGANIZATION

If the session is parked, chainctl resumes it first, which usually takes a few seconds. If the session is still parking, such as right after you choose Park now, chainctl waits for the park to finish, as described in Returning to a session takes several minutes. The session’s memory is restored along with its disk, so running processes, such as a build or an agent, continue where they stopped, and files in /tmp, which is held in memory, are still there.

Network connections to hosts outside the session don’t survive a park. Programs that had one open, such as ssh, a database client, or a long download, find it broken after the session resumes and have to connect again. A park also discards the Claude and Google Cloud credentials that chainctl sent from your laptop, and chainctl sends them again when you return. Refer to How credentials reach a session.

Returning to a session doesn’t sync your directory again. Your directory syncs only the first time you attach, so move later changes between your laptop and the session with git.

Only one chainctl can be attached to a session at a time. If you attach from another terminal or laptop, the new one takes over, and the first one prints Detached from acme/app/quiet-harbor: another client attached. followed by the command that returns to the session.

Keep a session running while you’re away

A session that keeps running uses compute while you’re away, but an agent in it can keep working. It counts against your organization’s monthly allowance the whole time it runs, as described in Availability and limits.

When you detach, a session keeps running while it’s busy, and parks after it’s been idle for 10 minutes. A session is busy while any of the following is true:

  • Its CPU use averages more than a tenth of a vCPU over 5 minutes, or its network traffic, sent and received together, exceeds 256 KiB in that time.
  • Claude Code in the session is working on a turn.
  • A run-ci run that started in the session is running, as described in Run checks in a Workspaces session.
  • The busy command holds the session, as described in Mark a session busy.

Claude Code that waits at a permission prompt or for your input isn’t busy, so a session can park while an agent waits for you. Even while it’s busy, a session parks no later than 24 hours after you last detach. A chainctl develop check from your laptop counts as attaching. While your organization’s monthly compute allowance is used up, being busy doesn’t keep a detached session running.

To keep a session running whether or not it’s busy, choose Detach — keep running 4h or Detach — keep running for… in the session menu. The session keeps running until the time you choose, and then, unless you created it with --keep park or chainctl 0.2.382 or earlier, while it’s busy.

To decide ahead of time that a session keeps running when you detach, pass --keep with a duration of up to 24 hours when you open or return to it:

chainctl develop --keep 8h --parent $ORGANIZATION

With --keep 8h, the menu’s first option becomes Detach — keep running 8h, and exiting the shell keeps the session running without asking. chainctl places the 8-hour hold when you attach as well as when you detach, so the hold applies while your laptop sleeps or is offline, counted from when you attached. If chainctl then exits without a detach, such as when it can’t reconnect or another client takes over the session, it takes the hold back, and the session keeps running or parks as it would without --keep.

For a long-running task, create a new session with --keep always. That session keeps running instead of parking when you detach, busy or not, until 7 days after you last detach from it:

chainctl develop --keep always --name migration --parent $ORGANIZATION

Because it doesn’t park, a --keep always session can use up the monthly allowance in a few days. To stop it sooner, choose Park now in the session menu, or delete the session.

A session that you keep running still parks and resumes after 6 days, as described in Session lifecycle. A --keep always session that you leave for its full 7 days always reaches this point. Running processes continue, but their connections to hosts outside the session break, as at any park. Refer to Return to a session.

To create a new session that parks about 10 minutes after you detach, busy or not, pass --keep park:

chainctl develop --keep park --parent $ORGANIZATION

Claude Code can keep working after you close the terminal. With a Claude login that Chainguard holds for you, it keeps working for as long as the session runs. Refer to Keep Claude Code working while you’re away.

If you detach with Detach — keep running 4h, Detach — keep running for…, or a --keep duration, or from a session you’re already keeping running, the terminal you detached from keeps serving the credentials that come from your laptop, such as a cgr.dev login you switched to your laptop, until you press Ctrl+C or close the terminal. Once nothing serves the session, requests that need your laptop fail until you return to it. Refer to How credentials reach a session.

When a session parks and resumes after 6 days, its host discards the Claude and Google Cloud credentials that chainctl sent from your laptop, as at any park. A Claude login that Chainguard holds for you keeps working. If the terminal you detached from still serves the session, and serves only Claude’s credential, it sends that credential again. If it also serves other credentials from your laptop, it stops serving the session, and Claude Code’s requests fail until you return. chainctl sends your Google Cloud credential again only when you return.

Mark a session busy

Work that waits without using the CPU or network, such as a script that waits on a remote job, doesn’t make a session busy. To keep a detached session running while such work runs, use the busy command, which every session has at /usr/local/bin/busy:

CommandWhat it does
busy -- CMD [ARG...]Runs CMD and keeps the session busy until it exits. busy exits with CMD’s exit status.
busy --for DURATION [LABEL]Keeps the session busy for a duration, such as 90s, 30m, 2h, or 1d.
busy --clear [LABEL]Ends the busy --for hold with that label.

A label names a hold, so you can end one without the others. It has 1 to 32 lowercase letters, digits, and hyphens, and defaults to busy. For example, the following command keeps the session running until a release build finishes:

busy -- make release

Whatever busy holds, a session parks no later than 24 hours after you last detach.

Use several terminals in a session

When you run chainctl develop in an interactive terminal, it keeps a status row at the bottom of the window. The row lists the session’s terminals and shows each message from chainctl for a few seconds. It ends with ^] menu, or with a count of messages you might have missed, such as ^] menu · 2 new. To read them, choose Logs in the session menu.

A session can run up to nine terminals, for example Claude Code in one and your tests in another. chainctl also calls them PTYs. To manage them, press Ctrl+] to open the session menu, which lists the session’s terminals, and then press one of the following keys:

KeyWhat it does
cStarts a terminal and switches to it. The new terminal opens in terminal 1’s current directory.
1 to 9Switches to that terminal.
rRenames the terminal shown. Until you rename it, a terminal shows the title of the program running in it, or shell.
xAsks you to confirm, then closes the terminal shown and stops what runs in it.

On the status row, the name of the terminal shown is bold. A terminal you’re not looking at is marked when it has new output, or when its program rings the bell or sends a notification. When a terminal’s command exits, for example after you type exit in it, the terminal closes and the row shows the command’s exit status.

Terminal 1 is the session’s shell, so you can’t close it with x. Exiting it closes the other terminals and detaches you, as described in Detach from a session. When you detach from the session menu instead, every terminal stays open, and when you return from the same laptop, chainctl opens the terminal you were in when you left.

All the terminals share the session’s /work directory. To run several coding agents in one session, refer to Run several agents in parallel.

If the session menu doesn’t list the session’s terminals, the session was created before Workspaces supported more than one, and it has only its shell.

Copy text from a session

While you’re attached to a session, text you copy in it goes to your laptop’s clipboard. This works for Claude Code, for other programs that copy with an OSC 52 terminal escape sequence, and for the pbcopy, xclip, and wl-copy commands, which every session has. For example, this command copies the hash of the session’s current commit:

git rev-parse HEAD | pbcopy

How a copy reaches your laptop’s clipboard depends on where you run chainctl:

Where you run chainctlHow the copy reaches your clipboard
macOSchainctl writes it with pbcopy.
Linux, in a graphical desktop sessionchainctl writes it with wl-copy, xclip, or xsel, whichever is installed.
Anywhere chainctl can’t use these commands, such as a Linux machine over SSHchainctl passes it to your terminal without reporting it, and the terminal applies it only if it supports OSC 52 and allows it.

A copy can be up to 1 MiB, and chainctl drops a larger one. When chainctl writes your clipboard itself, it takes a burst of up to four copies and then one a second, and drops any copy beyond that. The status row reports each copy that chainctl writes or drops, and the session menu’s Status option shows what happened to the last one.

The session can’t read your laptop’s clipboard. In the session, the pbpaste, wl-paste, and xclip -o commands fail, and when a program in the session asks your terminal for the clipboard’s contents, chainctl removes the request. To get text into the session, paste it into your terminal.

While you’re attached, any program in the session, including a coding agent, can replace what’s on your laptop’s clipboard, and chainctl doesn’t ask you first. Before you paste copied text into a shell on your laptop, check it.

Open more than one session

With the free monthly allowance, each user can have one session at a time, including a parked one. To open another, delete the one you have first. To work on more than one thing at a time in a single session, open more terminals in it, as described in Use several terminals in a session.

If your organization’s allowance includes more sessions, such as one arranged with your Chainguard account team, you can run several sessions on the same repository, for example to give each of several agents its own copy of the code. To open another session on a directory that already has one, pass --new, and optionally --name to choose the second part of its name:

chainctl develop --new --name scratch --parent $ORGANIZATION

This command creates a session named app/scratch. Without --name, chainctl generates a name. Once a directory has several sessions, return to one by its name or short ID.

The --name, --env, --check, --cache, --keep always, and --keep park flags each open a new session, so you don’t need --new with them.

List sessions

To list your sessions, most recently used first, run chainctl develop list:

chainctl develop list --parent $ORGANIZATION

The output shows each session’s name, the branch checked out in it, its state, and when you last used it:

         NAME          |  BRANCH   |         STATE          | LAST USED
-----------------------|-----------|------------------------|-----------
 acme/app/quiet-harbor | main      | idle (parks in 6m)     | 4m ago
 acme/app/scratch      | fix-tests | parked, expires Oct 13 | 2d ago

The STATE column tells you whether a session is running, when it parks or stops running, and when a parked session expires. For a detached session, it also says whether the session is busy, such as running (busy: agent turn, as of 13:40; at most until Oct 21 13:35), or how long a hold you placed lasts, such as running, until 17:30, then while busy. A session shown as idle, such as idle (parks in 6m), is running but not busy, and the column shows when it parks. To add each session’s commit, environment, and directory, pass -o wide. To include expired sessions and sessions that you’re deleting, pass --expired. For what each state means, and the state value that -o json prints for it, refer to Session states.

To list every user’s sessions in the organization, with an OWNER column, pass --all. Only organization owners can use --all, and you can return to, rename, or delete only your own sessions.

Rename a session

To rename a session, pass its current name or short ID and the new second part of its name. The repository part stays the same:

chainctl develop rename app/quiet-harbor tests --parent $ORGANIZATION

This command renames the session to app/tests.

Delete a session

A parked session expires 5 days after it parks. Chainguard then discards its VM and snapshot, but the session stays in chainctl develop list --expired, and its name stays in use until you delete it. To delete a session, run chainctl develop delete with its name or short ID:

chainctl develop delete app/scratch --parent $ORGANIZATION

The command asks you to confirm, then destroys the session’s VM and its snapshot. Anything in the session that you didn’t push is lost. To skip the confirmation, for example in a script, pass --yes.

Use sessions from scripts

To use chainctl develop without a terminal, for example from a script or a coding agent on your laptop, pass --non-interactive. With it, chainctl never prompts or guesses. An ambiguous session reference is an error, and so is a repository with neither a .chainguard/develop.yaml nor a .chainguard/ci.yaml file, which would otherwise open on the default environment. To use the default environment, pass --env default.

To create a session and print its details as JSON without attaching to it, pass --output json:

chainctl develop --new --non-interactive --output json --parent $ORGANIZATION

The session’s /work directory stays empty until you attach with chainctl develop from the same directory, which uploads it. Without a terminal, a session can’t ask you to approve requests beyond its defaults, so it refuses them. To allow them ahead of time, pass --with, as described in Manage session credentials.

Last updated: 2026-10-20 14:50