Export your organization's data
An organization admin can export everything Ciresk dev holds for the organization into one signed archive. That covers sessions, their journals, every policy version, templates, the connection and egress logs, usage and the audit trail. This page explains how to start an export and download it, what the archive contains and how to check its signature. It also covers how long dev keeps each kind of record, and how to erase the organization's dev data.
Who can export
Starting an export and downloading it need the dev:org.export permission. Organization owners and admins hold it. A custom role can be given it too.
Only a person signed in as themselves can export:
- An API key cannot be given this permission.
- An agent is refused with
principal_not_allowed. - There is no
ciresk devcommand and no MCP tool for it.
Export from the dev console, or call the API as yourself, with your own sign-in token, not an API key.
In the console
Open Data in the dev console at dev.ciresk.com. The page shows to anyone who holds dev:org.export or dev:org.erase. Each half of the page shows only to the holder of its own permission.
- Export lists the organization's exports, newest first. For each one it shows the status, who requested it, when, the size and how long it is kept.
- Request export starts one. It is built in the background; select Refresh to see it change.
- Download on a ready export makes a download link at that moment, valid for 15 minutes. Each click is recorded in the audit log.
The erasure half of the page is described in Erase your organization's dev data.
Start an export through the API
curl -X POST https://api.eu.ciresk.com/v1/dev/exports \
-H "Authorization: Bearer $TOKEN"The request answers 202 Accepted with the export:
{
"id": "eu1_devexp_0192f3c4a5b67d8e9f0a1b2c3d4e5f60",
"state": "pending",
"requested_by": "user:01H...",
"requested_at": "2026-10-25T09:12:03Z",
"completed_at": null,
"expires_at": null,
"bytes": null,
"sha256": null,
"rows": null,
"error": null,
"download": null
}An organization has at most one open export at a time. A request while one is pending or running answers that export instead of starting a second one.
List the organization's exports, newest first, to find an id again:
curl "https://api.eu.ciresk.com/v1/dev/exports?limit=20" \
-H "Authorization: Bearer $TOKEN"The answer is a page, {"data": [...], "next_cursor": null, "has_more": false}. limit is 20 by default and at most 50, and there is no cursor: the list holds only the newest exports. A listed export never carries download; read the export itself for that.
The archive is built in the background. A build starts within about 15 seconds. If a build fails, it is tried again, up to three times in all.
| State | Meaning |
|---|---|
pending | waiting to be built |
running | being built |
ready | built; bytes, sha256 (of the zip), rows (row count per file) and expires_at are set |
failed | not built; error says why |
expired | the archive was deleted 7 days after it was built |
Download it through the API
Read the export:
curl https://api.eu.ciresk.com/v1/dev/exports/eu1_devexp_0192f3c4a5b67d8e9f0a1b2c3d4e5f60 \
-H "Authorization: Bearer $TOKEN"Once it is ready, the answer carries download, a URL valid for 15 minutes:
"download": {
"url": "https://...",
"expires_at": "2026-10-25T09:30:41Z"
}Fetch the zip from that URL with a plain GET. It needs no Authorization header. Each read of a ready export creates a new URL and adds a dev.org.export.downloaded record to the audit log. If the URL has expired, read the export again.
What the archive contains
A zip file in the format ciresk-dev-export/1. The archive holds every record the organization still has, meaning everything retention has not deleted yet (see Retention). Each data file is JSON Lines: one JSON object per record, with that record's fields as stored.
| File | What it holds |
|---|---|
orgs.jsonl | the organization's dev record |
sessions.jsonl | every session, live and ended |
session_events.jsonl | every session's journal |
writers.jsonl, device_grants.jsonl, members.jsonl | who wrote to each session, the devices attached to it, the people it was shared with |
needs.jsonl, ci_records.jsonl | what sessions asked their people, and the CI runs recorded on them |
web_handoffs.jsonl | sessions opened in the browser |
org_policies.jsonl | every version of the dev policy |
template_versions.jsonl, template_promotions.jsonl, template_files.jsonl | published templates, what each repository runs, and their files |
egress_decisions.jsonl | the egress decision log |
connections.jsonl | the connection log |
usage_ledger.jsonl | usage, in vCPU-seconds |
workers.jsonl | the organization's workers |
demand_audits.jsonl | when each service last polled for waiting sessions (bookkeeping for the audit log) |
secrets.jsonl | each session secret's name, hosts and bindings, never its value |
dotfiles.jsonl | the dotfiles repository each person set, never the value of a private repository's credential |
schedules.jsonl | autostart schedules people and their agents set |
idempotency_keys.jsonl | the answers kept for 24 hours to stop, pause, start and kill requests sent with an Idempotency-Key |
org_erasures.jsonl, org_exports.jsonl | past erasure requests, cancelled ones included, and exports |
audit.jsonl | every dev audit record of the organization, as the audit log shows it |
manifest.json | the organization, region, export id, creation time, and each file's name, row count and SHA-256 |
manifest.sig | the signature over manifest.json |
No secret is ever exported:
- Session secret values and dotfiles credentials stay in the secret store, which an export never reads.
- Worker and enrolment token hashes are left out.
- The single-use credential a session starts with is left out.
- The hash that redeems a browser link is left out.
Verify the archive
manifest.json lists each file with its row count and the SHA-256 of its contents. Check each file against it.
manifest.sig is a single line dvx_<payload>.<signature>:
<payload>is base64url (no padding) of a JSON object holdingkid,exp,org_id,export_idandmanifest_sha256. The last one is the SHA-256 ofmanifest.json, in hex.<signature>is base64url (no padding) of an Ed25519 signature over the textdvx_<payload>.
To check it:
The signing keys rotate. A downloaded archive can be verified only while its key is still in that list, about seven weeks. If you need proof for longer, keep a copy of the key along with the archive.
Limits and errors
| Answer | When |
|---|---|
503 export_unavailable | the installation has no object store for exports (only on a self-hosted installation set up without one) |
failed, error: "export_too_large: ..." | the archive passed 512 MiB compressed |
failed, error: "export_failed: three builds did not finish" | three builds did not finish; start a new export |
404 export_not_found | no export of your organization has that id |
403 insufficient_scope, 403 principal_not_allowed | see Who can export |
A problem answer's type links to the code's entry: https://docs.ciresk.com/docs/dev/troubleshooting#<code> for Ciresk dev's own codes, such as export_unavailable, and https://docs.ciresk.com/errors/<code> for codes every Ciresk API shares, such as insufficient_scope.
Retention
Ciresk dev deletes old records on its own. The windows are part of the organization's dev policy, under retention (see admin.md):
| Setting | Default | What is deleted |
|---|---|---|
retention.ended_sessions_days | 90 | a stopped or failed session this many days after it ended, together with its journal, device grants, members, needs, CI records, usage and browser links |
retention.connections_days | 30 | connection log windows this many days after they ended |
retention.egress_decisions_days | 30 | egress decision windows this many days after they ended |
"retention": { "ended_sessions_days": 365, "connections_days": 90, "egress_decisions_days": 90 }- Each value is a whole number of days from 1 to 3650.
- A change is a new policy version, set with
ciresk dev policy setlike any other field. - Deletion runs in the background, about twice an hour, and each run records
dev.retention.sweptin the audit log. - A paused session is not deleted.
- A session that a later session resumed from is kept as long as that later session is.
- Connection and egress windows go on their own windows, whatever their session's state.
Two kinds of data follow their own rules:
- Audit records are kept 12 months by default. An admin with
dev:policy.writecan choose 7 or 10 years instead, on the Audit log page of the dev console at dev.ciresk.com, or throughPUT /v1/dev/audit/retentionwith{"months": 84}(12, 84 or 120).GET /v1/dev/audit/retentionreads the setting, withdev:audit.read. - Checkpoints in Ciresk's cloud are deleted 30 days after they were taken. That includes the checkpoint a paused session resumes from. A self-hosted installation sets its own rule.
Erase your organization's dev data
An admin with dev:org.erase can have every record Ciresk dev holds for the organization deleted. Only a person can do this; API keys and agents cannot, and neither the CLI nor the MCP tools offer it. Export first if you want a copy.
In the console, open Data and select Erase organisation data under Danger zone, then type the organization's id to confirm. While the erasure waits out its grace period, the page shows it as scheduled with a Cancel erasure action. Afterwards it shows the erasure under way, then done with its counts, or cancelled.
Through the API, type the organization's id back to confirm:
curl -X POST https://api.eu.ciresk.com/v1/dev/erasure \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"confirm": "org_01H..."}'At once:
- Every live session is killed, with the reason
org_erasure. - The answer is
202with the erasure:state: "erasing",requested_at,erase_after(24 hours later) andkilled, the number of sessions stopped. - Until the erasure finishes or is cancelled,
ciresk dev upis refused with409 org_erasing. - A second request while one is pending answers the pending one.
- A
confirmthat is not your organization's id is refused with422 validation_failed.
Cancel within the 24 hours
curl -X DELETE https://api.eu.ciresk.com/v1/dev/erasure \
-H "Authorization: Bearer $TOKEN"- Within the grace period this answers
200with the erasure instate: "cancelled"andcompleted_atset. Nothing is erased, and new sessions can start again. - The sessions the request killed stay ended.
- The audit log records
dev.org.erasure.cancelled. - Cancelling an erasure that is already cancelled answers it as it is.
- Once
erase_afterhas passed, the cancel is refused with409 erasure_started: the erasure may already be deleting. - With no pending or cancelled erasure, it answers
404 erasure_not_found.
After the 24 hours
- Every dev record of the organization is deleted: sessions, journals, policies, templates, logs, usage, workers, schedules and exports.
- Its checkpoints and export archives are deleted from storage.
- Its session secret values and its people's dotfiles credentials are deleted from the secret store.
- Each person and agent named in its audit records is erased. The audit records themselves stay, with the names behind them removed.
- The erasure becomes
erasedand its counts are filled in:rows,subjectsandcheckpoints.
GET /v1/dev/erasure reads the latest erasure (erasing, erased or cancelled), with the same permission. After an erasure finishes, the organization can use Ciresk dev again, starting from the default policy.