CodeHerderSearch⌘KRequest access →

Self-hosted departure checklist

Retire a self-hosted install in order. Each step names a command or route and its expected result. A blank rehearsal record follows.

Use this checklist when your organization leaves CodeHerder, or retires an install. Your operator runs it. Rehearse it once on a test install first. Your support agreement says whether the support contact takes part.

Run Self-hosted exit and data preservation first. Do not start step I1 until every export passes its completeness check. A step marked irreversible cannot be undone.

Record each step in the rehearsal record. A step is done when you see its expected result.

Identity

# Step Command or route Expected result
I1 List the people ch member list Every human in the workspace.
I2 Deprovision each human POST /v1/workspaces/{id}/members/{memberId}/deprovision, from the member action in the web app. With SCIM, deactivate the user in your identity provider. The member is disabled. Its roles, API keys, MCP grants, CLI installations and device links are revoked. See Offboard a person.
I3 Revoke every credential of each human (belt and braces) ch human revoke-all <human> --yes. Instance operator only. The reply shows the revoked counts and boundSeconds. Every server process refuses the credentials within that bound.
I4 Revoke the SCIM token DELETE /v1/sso-connections/{id}/scim-token Your identity provider’s next SCIM call fails with 401.
I5 Revoke each agent key ch member keys list <agent>, then ch member keys delete <hash> for each key. Then ch agent disable <agent>. The key list shows no active key. The agent is paused.
I6 Disable the Cognito app client, then the pool AWS console, or aws cognito-idp delete-user-pool-client. To delete the pool, first aws cognito-idp update-user-pool --deletion-protection INACTIVE, then aws cognito-idp delete-user-pool. Irreversible. A sign-in at the hosted UI fails. See Self-hosted Cognito sign-in.

The Cognito pool is not part of the database. A database restore does not change it.

Devices

# Step Command or route Expected result
D1 List the devices ch device list Every linked device.
D2 Unlink each device from each workspace ch device workspaces delete <device> --workspace <workspace> The device drops the workspace from its served set. On its next reaper cycle it removes the repo mirrors, pool directories, shared knowledge folder and transcripts for that workspace.
D3 Revoke each device token ch device revoke-token <device> <tokenId> The tunnel closes. A reconnect with that token gets 401.
D4 Archive each device ch device archive <device> The device leaves every list and placement. All its tokens are revoked.

Do the next steps on each device host.

# Step Command Expected result
D5 Stop the device server, then delete the session root Stop the process that runs ch device-server. Then delete $CH_SESSION_ROOT (default ~/codeherder-sessions). ls finds no session root.
D6 Sign out, then delete the local roots ch logout, then delete ~/.codeherder (or $CH_CONFIG_DIR) and the credential store root if you moved it with CH_CLAUDE_CREDENTIAL_STORE. ls finds no directory.
D7 Delete harness transcripts kept by Docker stages Delete ~/.claude/projects/. ls finds no directory.
D8 Remove the tool-cache volumes docker volume ls, then docker volume rm <name> for each /opt/ch-tools volume. docker volume ls shows none.

ch logout revokes the CLI credential on the server and removes the cached credential. It does not delete the local store, the shared-document cache or the local tasks. Step D6 deletes them. This is a known gap.

What a device keeps

Every row below is a store the device server or ch writes. The last column names the step that removes it.

Store Path Removed by
Session worktree and sandbox <root>/<sandboxId>/ (checkouts, session files) Step D5: delete the session root.
Per-session knowledge copy <root>/<sandboxId>/<leaf>/knowledge/ Step D5: delete the session root.
Agent CLI config redirect <root>/<sandboxId>/<session area>/isolated-config/ (aws, docker, kube, gh, glab) Step D5: delete the session root.
Repo mirrors <root>/.mirrors/<workspaceId>-<repoHash>/ (bare clones) Step D5: delete the session root.
Warm worktree pool <root>/.pool/<workspaceId>-<repoHash>/ Step D5: delete the session root.
Shared knowledge folder <root>/.knowledge/<workspaceId>/, with .staging/ and .retired/ beside it Step D5: delete the session root.
Transcript archive <root>/.transcripts/<anchorId>/<harness>/ Step D5: delete the session root.
Reclaimed work bundles <root>/.reclaimed/ Step D5: delete the session root.
Harness config area under the sandbox directory: the relocated harness config dir (transcripts, harness settings) Step D5: delete the session root.
Provision parameters <root>/.provision-params/<sessionId>.json (repo URLs, branches) Step D5: delete the session root.
Spawn cwd pins <root>/.spawn-cwd/<sessionId>.json Step D5: delete the session root.
Foreign-path attestations <root>/.foreign-attest/<sandboxId>.json Step D5: delete the session root.
Foreign-path anchors <root>/.foreign-anchor/<sandboxId>.json Step D5: delete the session root.
Git common-dir anchors <root>/.git-common-dir-anchors/<sandboxId>/ Step D5: delete the session root.
Agent, git-credential and device-local sockets the agent socket dir, <runtime dir>/git-credential/ and the device-local bridge dir (unix sockets, no data at rest) Stopping the device server removes the sockets. Nothing to delete.
Empty git hooks directory ~/.codeherder/empty-git-hooks/ (empty; core.hooksPath target) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Session state files ~/.codeherder/sessions/*.json (PTY holder state) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Execution receipts ~/.codeherder/sessions/receipts/ and receipts/refused/ Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Egress denial outbox ~/.codeherder/sessions/egress-outbox/<runId>/<reportId>.json (destination host, port, transport, denial category, counts) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Docker stage-run files ~/.codeherder/stage-runs/<runId>/ (env file, bearer dir, pi auth, transcript mount) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Host harness transcripts ~/.claude/projects/<cwd slug>/ (Docker stages; unrelocated fallback) Step D7: delete ~/.claude/projects/.
Tool-cache Docker volume Docker volume mounted at /opt/ch-tools, one per task worktree Step D8: docker volume rm.
Credential store and pool <credential store>/credentials/<harness>.env and <harness>.d/ (pool entries) Step D6: delete the credential store root. Then revoke the credential at its provider (I14).
Local launch directories ~/.codeherder/launches/<launchId>/ (status, registration, runtime log) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Device-server log ~/.codeherder/logs/device-server.log (+ .1) and last-trim.json Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
ch local store ~/.codeherder/local.db (+ backups); sync outbox, bases, cursors Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Local attachment blobs ~/.codeherder/blobs/ Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Shared-document cache <CLI config home>/shareddocs/<endpoint>/<workspaceId>/collection.json Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
CLI endpoint credentials <CLI config home>/endpoints/<endpoint>/ (CLI tokens) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Cost markers ~/.codeherder/cost-markers/ Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).
Held cost evidence ~/.codeherder/cost-held/<transcriptHash>/<turnIndex>.json Before departure, resolve pending usage evidence. Step D6 then deletes this root, including expired and quarantined entries; normal workspace unlink and sandbox teardown retain it.
Stage-tools template ~/.codeherder/stage-tools/ (injected stage config and gitconfig) Step D6: delete ~/.codeherder (or CH_CONFIG_DIR).

Integrations

# Step Command or route Expected result
I7 Delete each integration GET /v1/workspaces/{id}/integrations, then DELETE /v1/integrations/{id} for GitHub, GitLab, Slack and PagerDuty. In the web app: Settings → Integrations. The list is empty.
I8 Revoke the token or uninstall the app at the provider The provider’s console. The provider shows no CodeHerder token or app.
I9 Delete each outbound webhook ch webhook list, then ch webhook delete <id> ch webhook list shows none.
I10 Delete each inbound endpoint ch inbound-endpoint list, then ch inbound-endpoint delete <ref> The list is empty. A sender’s next POST gets a 404.
I11 Delete each device git token On each device host: ch device git-token list, then ch device git-token delete <label> The pool is empty.
I12 Delete each device credential pool entry On each device host: ch device credential list, then ch device credential delete <label> The pool is empty.
I13 Delete variables ch variable list, then ch variable unset <KEY> at each scope ch variable list shows none.
I14 Revoke each AI key at its provider The Anthropic console, Bedrock IAM, or your gateway. A call with the old key fails.

Backups

# Step Command or route Expected result
B1 State the retention Read your snapshot and dump retention. A written number of days for the database, the bucket and the revocation journal.
B2 Keep the revocation journal Keep revocation-journal/ or CH_REVOCATION_JOURNAL_DIR at least as long as the longest backup. The journal is older than the oldest backup.
B3 Restore a pre-departure snapshot into a scratch environment See Recover. Start the server fenced. The server starts and refuses to send email or to accept devices.
B4 Try a revoked API key, and read a revoked device token’s row curl -H "Authorization: Bearer <key>" <addr>/v1/workspaces. Then, in the restored database: SELECT revoked_at FROM device_tokens WHERE id = '<tokenId>'; Before replay, the key may work and revoked_at may be NULL. Note both results.
B5 Replay the revocations ch instance restore-fence replay --yes The command succeeds.
B6 Check that no entry is left ch instance restore-fence show No pending journal entry.
B7 Try the same key again, and read the same token row Repeat B4. The key gets 401. revoked_at shows a time.
B8 Promote ch instance restore-fence promote --yes The server leaves the fence. Never promote before B7 passes.

Erased and revoked data stays in each snapshot until that snapshot expires. See Erased data in backups. The restore fence replays revocations and erasures. It does not delete a Cognito identity. Delete it by hand if you also rolled the pool back.

A device token does not sign in to a REST route, so B4 reads its row instead. A fenced server refuses every device tunnel, so a tunnel test cannot run before B8. After B8, you can try the revoked token on a device. Expect the server to refuse the tunnel.

CodeHerder has no automated test that a restored database never revives access. Use B3 to B7 as your proof until that test exists.

Retire the workspaces

Do this only after every export passed. Irreversible.

  1. Archive: ch workspace archive <workspace>. Expect the workspace to leave every list.
  2. Delete: ch workspace delete <workspace>. Only an owner can run it. Expect the workspace to be gone.

Rehearsal record

Your operator and a witness fill this table during a rehearsal on a test install. Leave a row blank until the step runs. Evidence is the command output, a screenshot, or a ticket id.

Area Step Date Who ran it Evidence Result (pass, fail)
Exit Export and completeness check
Identity I1 to I3
Identity I4 to I6
Devices D1 to D4
Devices D5 to D8
Integrations I7 to I10
Integrations I11 to I14
Backups B1 to B2
Backups B3 to B8
Retire Archive and delete

Signed off by (operator): ______________________ Date: ____________

Witnessed by: ______________________ Date: ____________

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