CodeHerderSearch⌘KRequest access →

Managing your devices

How to manage your devices — names, load, concurrency, and usage limits.

A device is a machine running ch device-server for CodeHerder. The device runs the actual agent processes — checking out branches, executing the configured AI binary, and reporting the results. Every session happens on a device.

This page covers day-to-day device management. For the first-time steps — connecting a device and linking it to a workspace — see How do I add a device? for the focused version, Launch a device on AWS if you’d rather not run one yourself, or the Quickstart for the full path from a fresh account.

Online and Offline

A device shows Online when its ch device-server process is running and connected to CodeHerder. Stop or kill that process and the device goes Offline. The engine places no new work on an Offline device; tasks that were waiting for a slot on that device queue until the device comes back Online.

The current state of each device is visible under Set up → Devices in the sidebar.

Naming a device

By default, a device is identified by its hostname — whatever its operating system reports. That works fine for a laptop or a named server, but it’s not much help on a --docker fleet device (see Docker container (trusted mode)), where the hostname is just a generated container ID.

Give a device a name of your own choosing at any time — it’s safe to do even while the device is busy. Renaming only changes the label: the device keeps its ID, its connection, its workspace links, and anything already running on it keeps running without interruption.

ch device edit <deviceId> --name "prod-runner-1"

The name replaces the hostname as the label a device is shown under — the devices list, a device’s detail page, session and sandbox listings, and anywhere else ch or the web app prints a device. It replaces the hostname as a CLI reference too: once a device has a name, use that name (not its hostname) with ch device show, ch agent placement, and anywhere else you’d point at it by name. See Referring to things on the command line — and keep names distinct, since two devices sharing a name makes that reference ambiguous the same way any other duplicate name does.

The Devices page’s search box is the one place both still work: it matches on either the name or the hostname, so a device stays findable by its old hostname even after you’ve named it. If you can’t see host details (see Who sees host details), search matches the name only.

An unnamed device is labelled with its hostname, so everyone who can see the device sees that label. If you don’t want that, give the device a name.

A name is trimmed of surrounding whitespace and capped at 128 characters. Clear it to go back to displaying the hostname:

ch device edit <deviceId> --name ""

Web app: open the device’s detail page (Set up → Devices → [the device]) and edit the Name field directly. The Hostname field below it always shows the raw reported hostname, named or not.

Only a workspace owner or admin can rename a device — the same rule that applies to changing its capacity, pausing (draining) it, or deleting it. Registering the device yourself carries that same permission, but only for as long as you still hold at least admin rank; a plain member who registered a device doesn’t otherwise get a bypass. A member outside that role sees the device’s name and capacity as plain text rather than editable fields, and doesn’t see the Enable/Disable control on the device’s row. See Why a control is missing.

Device health and readiness checks

In addition to the Online/Offline connection state, CodeHerder continuously checks whether a device is actually ready to run work. These readiness checks run on the device every few minutes and report back to the platform.

Health states

Each device has one of three health states, shown as a badge in the Health column in Set up → Devices:

State Badge colour What it means
healthy green All checks are passing — the device is ready for new sessions.
degraded amber At least one check is in a warning state, or has failed in a way that doesn’t block work. Nothing here prevents the device from running tasks.
unhealthy red At least one check has failed in a way that blocks new work. The engine will not assign new sessions to this device until it recovers.

When a device can’t be staffed, a Not staffable badge appears next to the health badge in the devices list. Both badges show together — they are two separate facts, so one never replaces the other. The badge means at least one check is blocking new work on that device. It is tied to the unhealthy state and disappears as soon as the failing check is resolved.

Viewing the health snapshot

Click a device’s row in Set up → Devices to open that device’s page. The Health panel on that page shows:

  • The current health state and when the snapshot was last taken.
  • Any failing or warning checks, each with a short summary, detail text, and a remediation tip.
  • Every passing check, in the same flat list. A passing check shows its status and summary only, with no detail text and no remediation tip.

Only the device’s owner and workspace admins see the checks. Everyone else sees the health badge and the notice “Check details are visible only to the device owner and workspace admins.” Ask the owner or an admin to read the checks for you. See Who sees host details.

Who sees host details

Only the device’s owner, and admins and owners of a workspace the device is linked to, see the device’s hostname, operating system, boot time, launcher version, check details, probe-derived capabilities, and acknowledged secrets. In the web app, each hidden field reads “Visible only to the device owner and workspace admins.” In the CLI, ch device show prints host details: host details are visible only to the device owner and workspace admins, and ch device list prints — under HOSTNAME and OS.

Everyone else still sees the device’s name, online state, health badge, CPU, RAM and disk readings, version, drain state and capacity. The full list of limited details is in What only admins can see.

If no health snapshot is available yet (the device has just been registered and has not reported its first check), the panel shows a message to that effect — the device is not unhealthy, it just has not reported yet.

Each row in the snapshot carries one of four outcomes:

Outcome What it means
OK The check ran and confirmed everything is fine.
Warn The check ran and found a non-blocking issue worth a look.
Fail The check ran and found a blocking issue — see the “Blocks new work?” column below.
Skipped The check did not run this round, so it verified nothing. Treat a skipped row the same as an unknown, never as a pass — it is not the same as OK.

Readiness checks

The device server runs the following checks and reports the results to the platform. The Check column uses the exact label shown in the Health panel.

Check What it verifies Blocks new work?
Clock skew The device’s clock stays close to CodeHerder’s. No — warns, but does not block work.
Device registration The device’s device token is present, valid, and accepted by CodeHerder. Yes
Disk headroom Free space on the session-root filesystem. Warns when free space is low; fails when critically low. Only when critically low
Docker ready Devices that run each stage in its own container (how to enable) only: the Docker daemon is reachable and the task image is available locally. Yes
GitHub auth The GitHub CLI (gh) is installed and signed in. No — warns if missing or not signed in, but does not block work.
Git credentials Git can authenticate to each repository the workspace uses (one row per repo). Yes
Git tokens The device holds at least one git token added with ch device git-token create, and reports how many it holds per host. It counts tokens — it does not test whether one works. No — this check only ever reports OK; it does not warn or block work.
Harness auth Each installed harness is actually signed in, not just present — one row per harness. Warns instead when a signed-in credential is close to expiring. Yes, for work that needs that harness
Harness ready At least one of the five harnesses — Claude, Codex, Cursor, OpenCode, or Pi — is installed. See Choosing the coding-agent CLI your agents run for the full list. Yes
PID capacity The device has enough headroom left to start new processes. Only when that headroom is critically low
Session capacity How many session-worktree slots — and, if a disk budget is configured, how much session disk — are in use against the device’s limits. Yes, once a limit is reached
Session root The session-root directory is configured, exists, and is writable. Yes
Spawn probe A brief test process launches cleanly through the same machinery real sessions use. Yes, on failure
Tunnel connected The device’s live connection to CodeHerder is up. Yes
Version current The running ch version matches the latest published release. No — warns if an update is available, but does not block work.
Worktree slots Whether the slots counted as in use are still tied to a running session, or left over from one that has already ended. Only if left-over slots persist for a long time despite automatic clean-up — see Reclaiming stuck worktree slots below.

A few of these only appear once a condition is met — their absence isn’t a problem, it just means the condition hasn’t happened yet:

  • Docker ready shows up only on devices that run each stage in its own container; a device that runs sessions as host processes has no Docker row at all.
  • Clock skew and Spawn probe appear only after the device has connected to CodeHerder and completed its first check of that kind — before that, the row is simply not present yet.
  • Harness auth appears only once at least one harness is installed; if none is, Harness ready is already failing and covers the problem.
  • Git tokens appears only once you’ve added at least one token on that device with ch device git-token create; a device with none added has no row at all.
  • PID capacity appears only on devices that report their process usage to CodeHerder, which is most common on devices that run inside a container. A device that does not report it simply has no row. A device that reports its process usage but has no limit set still shows the row, reporting OK with no limit configured.
  • Harness ready and Harness auth both cover Pi along with the other four harnesses, so a device with only Pi installed and signed in passes both checks — see Choosing the coding-agent CLI your agents run for how Pi is signed in, since it has no login command of its own.

If a harness binary is installed on the device for reasons unrelated to CodeHerder — say, you use it directly yourself — and you don’t want to sign it in just to satisfy Harness auth, exclude it from the check entirely with ch device-server --harness-exclude <name> (or CH_DS_HARNESS_EXCLUDE=<name>, comma-separated for more than one). The excluded harness is then treated exactly as if it weren’t installed at all — it no longer blocks staffability. --harness-include/CH_DS_HARNESS_INCLUDE does the opposite: probe only the named harnesses, ignoring every other one.

Checks run automatically on the device; you do not need to trigger them manually. Most checks re-run roughly every five minutes (or immediately on restart), so a fixed issue is usually reflected in the platform within a few minutes. Spawn probe is the exception — it’s a heavier canary that runs about once an hour, so give it a bit longer to pick up a fix.

Starting the device server

The device server is the long-running supervisor that accepts agent work and keeps CodeHerder informed of the device’s health:

ch device-server

You don’t have to run this by hand just to use ch start on your own machine — it starts a device server for you whenever one isn’t already running. See Start a session in your own checkout for that path.

Run this command once per device in a dedicated terminal, or configure it as a persistent background service that starts at login and survives reboots — see Running the device server as a service for step-by-step instructions for Linux (systemd) and macOS (launchd). To run the device server inside Docker instead — keeping your host files out of its reach — see Docker container (trusted mode) in the same guide. That mode is not a boundary against a hostile agent. By default it also reports device metrics — CPU, RAM, and disk load — automatically every 30 seconds. Pass --no-metrics if you prefer to opt out of metrics reporting entirely.

Leave the server running for as long as you want the device to be available for agent work.

The device server also checks for updated versions of ch automatically — roughly once an hour — and restarts itself with the new binary when a newer release is found. By default, running agent sessions are not interrupted during an auto-update. See Updating the CLI for how to tune the cadence, disable auto-update, change that session behavior, or troubleshoot a device that’s stuck on an old version.

Inspecting your devices

List all devices

ch device list

This shows all devices in your workspace with their current Online/Offline state, name and hostname (hostname and OS show — unless you are the device’s owner or an admin), CPU and RAM readings, and the team they belong to.

Show one device in full

ch device show <deviceId>

Prints every field for a single device you can see — name and hostname (if you can’t see host details, a notice replaces them), workspace and team, drain state, CPU/RAM readings, and the last time metrics were reported.

Check recent load

ch device metrics
ch device metrics <deviceId> --minutes 60

Shows a min/avg/max/now breakdown of CPU, RAM, and disk load over a recent window (default: 30 minutes). With no target it reports your own device. Useful when the device is Online but tasks seem slow or are queuing unexpectedly.

Changing a device’s settings

An owner or admin of the device’s workspace can change six device settings from the Settings section of the device’s detail page. The settings cover isolation and input timing. You can also read them with ch device show. See Changing a device’s settings.

Concurrency and capacity

CodeHerder runs more than one task on a device at the same time, up to a configurable limit. The effective number of concurrent sessions on a device is the smaller of two values:

  • The per-device limit — set on the device’s detail page: Set up → Devices → [the device] → Capacity → Max concurrent sessions, or from the CLI with ch device edit <deviceId> --max-sessions 12 — by the device’s owner or a workspace admin (see Naming a device above for who that is). If nobody has ever set this value, CodeHerder assumes 8. Raising this allows more parallel work on that machine; lowering it frees resources for other processes. The CLI applies the same permission rule and writes the same value the detail page shows — it’s another way to set the number, not a different number.
  • A server-wide ceiling controlled by whoever operates your CodeHerder server, via the CH_MAX_DEVICE_SESSIONS setting. The shipped default is 4, but an operator commonly raises it for their deployment — including on the hosted cloud, which does not run the shipped default. There is no single number that holds for every deployment.

Because the effective limit is the smaller of the two, raising the per-device value above the server ceiling has no practical effect until the operator also raises the ceiling. Read both live numbers, and the effective limit they produce, from:

  • Set up → Devices → [the device] → Capacity, which prints a line like “Effective limit: N sessions — min(this value, the server’s global ceiling of M, set via CH_MAX_DEVICE_SESSIONS)”; or
  • ch device show <deviceId> --json, whose effectiveMaxConcurrentSessions and sessionCeiling fields carry the same two numbers. (The human-readable ch device show output does not print them.)

For example, a device with a per-device limit of 8 on a server whose ceiling is 50 has an effective limit of 8 — the per-device value binds. A device with a per-device limit of 12 on a server whose ceiling is 4 has an effective limit of 4 — the ceiling binds instead.

When a device reaches its effective limit, additional tasks queue automatically and start as soon as a running agent finishes. No manual intervention is required.

This running-session limit is a different axis from disk capacity. If you run a self-managed device server, it also enforces a separate local cap on how many session worktrees it holds on disk — --max-session-worktrees (or the CH_MAX_SESSION_WORKTREES environment variable), default 8. Where those worktrees are stored is a third, independent setting, --session-root. See Running the device server as a service for all three flags. A device can be at its session-concurrency limit while nowhere near its worktree-slot limit, or the reverse; the two numbers do not track each other.

Reclaiming stuck worktree slots

Each running session on a device occupies one of its session-worktree slots. A slot normally frees within moments of its session ending, and the next queued task claims it right away — most of the time there is nothing to do here.

Occasionally a slot does not free automatically — for example, if the device restarted while a session was still running. When enough of these build up, the device can look fully busy even though nothing is actually running on it, and tasks that should start just sit queued.

To tell which situation you are in, expand the device’s row in Set up → Devices and check the Worktree slots check in the Health snapshot panel (see Device health and readiness checks above). Its summary reports how many slots are orphaned or reclaimable:

  • None reported — the device is genuinely busy. Tasks queue and start as soon as a running session finishes; no action needed.
  • Some reported — those slots belong to sessions that have already ended. Force an immediate clean-up instead of waiting for the next automatic pass:
ch device reclaim <deviceId>

This is fire-and-forget: the command only confirms CodeHerder accepted the request, and the device clears the affected slots in the background. Check the Worktree slots check again a few minutes later to confirm the counts dropped.

A few things to know before you run it:

  • The device must be Online and connected — see Online and Offline above. Reclaim has nothing to act on when the device is Offline.
  • Only the device’s owner or a workspace admin can trigger a reclaim; other members are refused.
  • It is a CLI-only action — there is no equivalent button in the web app.

AI limits

When a device runs agents on a Claude credential — a subscription plan (Max, Pro, Team) or an enterprise/organization API credential — the device server probes it for current usage every few minutes and reports the data to CodeHerder. A device can hold more than one Claude credential at once — see AI credentials on a device for adding a second. The AI limits section appears on the device detail page when usage data has been received.

Viewing AI limits

Open Set up → Devices, click on the device row to open its detail page, and look for the AI limits section. It shows one card per credential — labelled with the name you gave it, if it’s not the device’s only one. For each card, the section shows:

  • The provider name with a plan badge (for example, claude with a max badge).
  • A usage meter for each rate-limit window — the percentage consumed and, when available, how long until the window resets. See AI usage limits for the full list of meters and which ones pause the device.
  • A Capped badge, with the cap shown alongside each capped window’s meter, if you’ve set a per-window usage cap on that credential — see AI credentials on a device.
  • A Not reporting badge, with the meters hidden, if that credential’s usage probe hasn’t reported in a while — the row instead reads “Usage probe stopped reporting. Last probed …”.
  • Otherwise, a “Probed …” timestamp for the most recent successful check.

Meter values refresh as the device server probes the provider, approximately every five minutes by default.

The section only appears when the device has reported usage data. If you do not see it, the device server may not have signed-in credentials for any AI provider on that machine.

How usage limits affect task placement

CodeHerder stops routing new tasks to a device once every fresh Claude credential on it has a capacity meter — a subscription pool, a spend cap, or the account-blocked verdict — at 100%, with none of them yet reset. A device with a single credential pauses as soon as that one hits its limit; a device with several keeps taking work as long as at least one still has headroom. A per-minute throughput meter at 100% never triggers this. The device stays Online and healthy; sessions already running are not interrupted. Placement resumes automatically once a meter resets and the next probe reports headroom.

When more than one device is eligible for a task, CodeHerder also leans toward whichever has more Claude headroom left across its credentials — so a device nearing its limit gradually takes less new work even before it pauses outright.

If tasks aimed at a device stop starting and nothing else seems wrong, look for a Claude exhausted badge next to the device’s health badge on the devices list (or Claude subscription exhausted on its detail page) — that’s the answer to “why isn’t this device being used”. For the full picture — every meter, why the pause needs every credential exhausted, why switching launch configs doesn’t help, and how to keep work moving with a second credential or a second device before the reset — see AI usage limits and AI credentials on a device.

Disabling or tuning the probe

The usage probe runs automatically alongside the device server. To disable it entirely, set the environment variable CH_AI_LIMITS_PROBE=0 before starting the device server. To change how often it checks, set CH_AI_LIMITS_PROBE_INTERVAL to a Go duration (for example, CH_AI_LIMITS_PROBE_INTERVAL=10m for every ten minutes). See Running the device server as a service for how to apply environment variables to a service-managed device server.

Running more than one device

A workspace can have several devices registered at once. When the engine places a new task, it picks any Online device that:

  1. is linked to the task’s workspace (or an ancestor group) and has a free slot, and
  2. has the tooling the work requires (such as the git-host CLI for code and merge stages).

Adding more devices (or raising per-device limits on existing ones) increases the number of tasks that can run in parallel. The engine spreads work across all eligible devices automatically — you do not need to route tasks to specific machines manually.

Sharing a device across workspaces

A workspace admin can run code on any device linked to the workspace. See Who can run code on your device.

A device is owned by the person whose ch device-server first registered it and can be linked to several workspaces at once — useful when you own more than one workspace and want them all to share the same hardware instead of running a separate device server per workspace.

A device serves a workspace when it is linked to that workspace. If CH_WORKSPACE_ID (or --workspace) is set when ch device-server self-registers on first run, that workspace becomes the device’s first — and primary — link; self-registering with no workspace set leaves the device unlinked until you link it explicitly. Either way, you add further links yourself with the commands below. A device you launch on AWS from a workspace’s Devices page is linked to that workspace from the start; the commands below add further links if you want it to also serve another workspace.

See which workspaces a device serves

ch device workspaces list <deviceId>

Prints every workspace the device is linked to — its primary link plus any extra ones. A bare ch device workspaces <deviceId> (no op) lists too. The device’s detail page in the app shows the same list.

Use the ch CLI. This command needs the device’s full UUID from ch device list — the device isn’t in the target workspace yet, so there’s no name there to match. Refer to the workspace by its name, slug, slug path, or UUID (from ch workspace list):

ch device workspaces create <deviceId> --workspace <workspace>

For example, to let a device also serve the FreeTier workspace:

ch device workspaces create 019e58e6-3f9a-7b12-a845-2c3d4e5f6789 --workspace FreeTier

Once linked, the engine treats the device as a candidate for that workspace’s tasks exactly as it does for any other linked workspace — placement, staffing, and the tunnel all honour the link. The device then appears in ch device list --workspace <workspace> for the newly-linked workspace, and the device’s detail page in the app lists every workspace it is linked to.

You do not need to restart or reconfigure ch device-server — linking takes effect immediately on the server side.

ch device workspaces delete <device> --workspace <workspace>

You cannot unlink a device’s last remaining workspace — it must always stay linked to at least one. Any other workspace link can be removed at any time.

What a shared device shares

The device owner decides whether a device serves more than one workspace. CodeHerder does not decide it.

The list of what workspaces on one device share is in Self-hosted device isolation.

What else a shared workspace needs

Linking the device is only the hardware half. For the engine to actually run a workspace’s tasks on a shared device, that workspace also needs:

  • An agent with capabilities matching the work (agents are created per workspace — see the agents guide).
  • The repository registered in that workspace, and the device set up with git credentials and the host CLI — see What the device needs in Connecting repositories.

When all three are in place — a linked device, a capable agent, and a reachable repo — tasks filed in the shared workspace start running on the device automatically.

Pausing a device

Sometimes you want to stop new work landing on a device without touching anything already running on it — before a reboot, a maintenance window, or while you look into an issue. Draining a device does exactly that: new placements stop immediately, while any sessions already running there keep going until they finish. The device’s Online state and health badge do not change; only new placement pauses.

Pausing needs the device’s owner or a workspace admin — the same rule as renaming a device or changing its capacity (see Naming a device, above).

Web app: In Set up → Devices, click Disable on the device’s row. A disabled chip appears on the row once it takes effect. Click Enable on the same row to resume placement.

CLI:

ch device disable <deviceId>
ch device enable <deviceId>

ch device disable prints a confirmation and, since the whole point is to leave running work alone, also lists anything still in flight there:

ch device disable <deviceId>
Device my-device (019e58e6-3f9a-7b12-a845-2c3d4e5f6789) enabled=false.
no live sessions on this device

To check whether a device is currently paused, look at the STATE column in ch device list — it reads drained for a paused device — or run ch device show <deviceId>, which prints drained: yes (since <when>). ch device list --disabled narrows the whole list to every device that’s currently off, drained and personal alike — see Listing enabled and disabled records.

Plain disable applies to every workspace. To turn a device off for one workspace only, see Turning a device off for one workspace below.

Pausing is fully reversible: run ch device enable <deviceId> (or click Enable) whenever you’re ready, and the device resumes taking new work. Archiving (below) is reversible too, but it goes further — pausing just stops new placements, while archiving takes the device out of the fleet entirely and revokes its tokens.

Turning a device off for one workspace

Plain disable pauses a device in every workspace that uses it. Sometimes you only want it gone from one. A device that a group shares with its workspaces is the usual case: one workspace wants to keep its work off that machine while the others carry on. For that, turn the device off for just that workspace:

ch device disable <deviceId> --workspace <workspace>
ch device enable <deviceId> --workspace <workspace>

<workspace> is a workspace name, slug, slug path or ID. The command confirms both the workspace setting and the fleet-wide one:

Device my-device (019e58e6-3f9a-7b12-a845-2c3d4e5f6789) enabled=false in workspace 019e5a01-7c2d-7e40-b1f3-5a6b7c8d9e01 (fleet-wide enabled=true).

A few things to know:

  • CLI only. The web app has no control for this yet.
  • Admins and owners only. You need to be an admin or owner of that workspace. Being the device’s owner is not enough, and an agent session can’t run it.
  • New work only. The workspace stops placing new tasks, stages and dev sessions on the device. Sessions already running there keep going.
  • It reaches workspaces below. The setting covers the workspace and every workspace nested under it. If you set it on a group, all of its descendants skip the device.
  • Nothing else changes. The device stays linked, the fleet-wide pause stays as it was, and every other workspace keeps using the device.

To see it, run ch device list in the workspace. The STATE column reads excluded here for a device the workspace has turned off, including when a group above it did so. ch device show and ch device list --disabled look only at the fleet-wide pause, so they don’t show it.

To turn the device back on, run ch device enable <deviceId> --workspace <workspace> against the workspace or group that turned it off. Running it from a workspace below a group that excluded the device doesn’t lift the group’s exclusion. Run it against the group instead.

This differs from Remove from workspace. Removing a link only works where the device is linked, so it can’t stop a workspace from using a device it inherits from a group. The Placement report shows an excluded device as device_excluded_by_workspace; see The Placement report.

Personal devices

Running ch start on a machine that has no device registered yet registers one automatically, as a personal device — see Start a session in your own checkout. A personal device is registered disabled, exactly like a paused one, so the engine never hands it someone else’s tasks; it’s there to run your own ch start sessions.

A personal device looks like a paused device almost everywhere, with two tells. The STATE column in ch device list reads personal instead of drained, and ch device show <deviceId> prints a personal: line in place of drained:. In the web app, the device’s row and its detail page carry a Personal badge instead of the disabled chip.

Sharing a personal device with the rest of the workspace uses the same command as resuming a paused one:

ch device enable <deviceId>

This clears the personal mark for good, not just for now — once you enable a personal device, it’s an ordinary device from then on. Disable it again later and it comes back as a plain paused device, not personal again. The permission rule is the same as pausing any other device: the device’s owner, or a workspace owner or admin.

Retiring a device

When you stop using a device in a workspace — or want to decommission the machine entirely — CodeHerder gives you two distinct removal paths. Choose based on how completely you want to remove it.

Remove from workspace

Removes the device from one workspace while leaving its links to any other workspaces intact. The device is not archived, and the removal is reversible — you can re-add the device to the workspace at any time with ch device workspaces create.

Web app: In Set up → Devices, click Remove on the device’s row. On the device’s detail page you can also click the × next to any workspace chip in the Workspaces row.

CLI:

ch device workspaces delete <device> --workspace <workspace>

If the workspace you are removing is the device’s last remaining link, the operation is blocked — a device must always stay linked to at least one workspace. To fully retire the device, archive it instead (below).

Archive the device

Archiving takes the device out of the fleet entirely, across every workspace it’s linked to: it drops out of every default device list and out of placement, and every one of its device tokens is revoked in the same step. Any live tunnel connection to the device is closed. It’s reversible — restoring brings the device back, though it needs a fresh token to reconnect (see below).

Web app: Open Set up → Devices, click on the device to open its detail page, then click Archive at the top of the page. The confirmation names the consequence — tokens revoked, re-enrolment needed — before you commit. Click Restore on the same button once you’re ready to bring it back.

CLI:

ch device archive <deviceId>
ch device restore <deviceId>

ch device delete is retired. It used to be a frozen alias of archive — soft-archiving the device instead of destroying anything — but that made delete ambiguous with kinds where it really does destroy. ch device archive above is the one spelling now; typing ch device delete prints Did you mean: ch device archive ? instead of running anything.

Two things to check before you archive:

  • Stop any live sessions first. CodeHerder refuses the archive while sessions are running on the device; the refusal tells you sessions are still live, but not which ones. Pause the device first with ch device disable <deviceId> — it lists the sessions still running there — then archive once they’ve finished.
  • You need workspace owner or admin. Registering the device yourself doesn’t grant this on its own — it takes admin rank too. Members without owner or admin rank cannot archive a device, and only a human caller can (an agent’s own session credential is refused).

After you restore, re-enrol the device. Archiving revokes every device token the device held, and restoring does not bring them back — a restored device needs a fresh token before it can reconnect:

ch device rotate-token <deviceId>

Save the printed token on the device (the command prints the exact command to run there), then start or restart ch device-server on it. Nothing about the device’s identity, workspace links, or history changes — only the credential.

Finding an archived device

Archived devices are hidden from the regular list by default. To see only the archived ones:

ch device list --archived

ch device list --all shows every device instead, active and archived together, if you want the wider view. Either way, the STATE column reads archived (taking priority over drained) so you can tell an archived device apart from one that’s merely paused. See Listing archived records for how this flag works across CodeHerder. ch device show <deviceId> still resolves an archived device and prints its archived timestamp.

Web app: in Set up → Devices, pick Archived (or All) from the Live / Archived / All picker — archived rows appear dimmed with an Archived badge.

When a device goes offline during a task

Sometimes a device loses its connection while an agent works on a task. CodeHerder recovers the task without doing the work twice. The times below are defaults. They count from the last message CodeHerder got from the device.

  1. The device shows Offline. This takes up to 105 seconds if the network drops with no warning. It is immediate if the device closes the connection.
  2. The agent run shows Lost. This takes about 90 to 120 seconds more. The task stays with the device.
  3. CodeHerder retires the session. This takes about 5 minutes after the last sign of life. The session key stops working at that moment. The old agent cannot change the task.
  4. CodeHerder moves the task to another device. It waits 5 minutes first, in case the device comes back. Set CH_DEVICE_AFFINITY_GRACE to change this wait. Set CH_SESSION_LEASE_TTL_SECONDS to change step 3.
  5. The device comes back. CodeHerder tells the device to stop the old agent process. Only one session works on the task.

The old agent can still push to the git host while the device has no link to CodeHerder but can reach the git host. CodeHerder does not control the git host. It cannot stop these pushes. Check the task branch if a device was offline for a long time.

Troubleshooting

The device never appeared at all — not even Offline. It was never registered. Registration only happens the first time you run ch device-server in a terminal and confirm the one-time prompt — a background service or container started before that first interactive run just idles, with no error and nothing to show. See How do I add a device? for the registration prompt and Running the device server as a service for the register-first order when using a service.

The device shows Offline. Start or restart ch device-server on that machine. The device switches to Online as soon as the server connects.

The device used to be there and is gone from the list now. It’s most likely archived, not deleted. Find it with ch device list --archived (or pick Archived from the Live / Archived / All picker in Set up → Devices), then restore it, rotate a fresh token, and restart ch device-server on it — see Archive the device above.

The device is Online but no agent is picking up work. Check the following:

  1. Is the agent eligible on this device? An agent is eligible on any device linked to its workspace (or an ancestor group) whose capabilities the device covers — no separate assignment step. Run ch agent placement <agentId> to see whether this device is eligible and, if not, why — see The Placement report for the full reason list and what to do about each one. See also Quickstart and Agents and the CLI for the full setup steps.

  2. Is the device at its capacity limit? Open the device’s detail page (Set up → Devices → [the device]) and check the Capacity panel for its per-device Max concurrent sessions limit — see Concurrency and capacity above for how that combines with the platform-wide ceiling. If the device is full, tasks queue until a slot opens — that is normal, and no action is needed while sessions are genuinely running there. If it stays full with nothing actually running, some slots may be stuck rather than in use — see Reclaiming stuck worktree slots below.

  3. Does the device have the tooling the work requires? Some workflow stages need specific host tools — for example, the code and merge stages need the git-host CLI (gh for GitHub, glab for GitLab) to open and land pull requests. If the device is missing a required tool, the engine cannot run that stage there. Install the missing tool on the device; the device picks it up on its next connection. For a full explanation of stage capability gaps and how to fix them, see Agents and the CLI and Why isn’t my task moving?.

  4. Is the device paused (drained)? A paused device stays Online and healthy, but the engine skips it for new placements. Check the STATE column in ch device list — it reads drained for a paused device — then resume placement with ch device enable <deviceId> or the Enable button in Set up → Devices. See Pausing a device above.

    If the state reads excluded here, the workspace turned the device off for itself. See Turning a device off for one workspace.

Last updated

CodeHerder

Round up your herd.

Bring every human and every agent onto one table. Watch the work move. Costs update as it happens.

Try "pricing", "connect a device", or "who reviews the code"

↑↓ move · ↵ open · esc close