Administer Ciresk dev
This page is for organization admins. It covers who may do what, the organization's dev policy, the kill switch, sharing, workers, secrets, logs and audit, data retention and export, and what happens when someone leaves.
You can do most of it with ciresk dev from any machine signed in with ciresk ci auth login (see quickstart.md). The dev console at dev.ciresk.com has pages for the policy, templates, usage, workers, the kill switch, the connection and egress logs, the audit log, and data export and erasure.
Who is an admin
Access to Ciresk dev is a set of dev: permissions (scopes). Your role in the organization decides which ones you hold:
| Role | Scopes |
|---|---|
| Owner, admin | every dev: scope |
| Member | dev:sessions.read, dev:sessions.write, dev:sessions.attach, dev:sessions.share, dev:policy.read, dev:templates.read, dev:secrets.read, dev:keys.read |
| Viewer | dev:sessions.read, dev:policy.read, dev:templates.read, dev:secrets.read, dev:keys.read |
Every person in the organization is a Member of dev by default. A custom role, or an API key, can be given single admin scopes. The exceptions are dev:org.export and dev:org.erase, which no API key can hold.
What each administrative operation needs:
| Operation | Command | Scope | Who may call it |
|---|---|---|---|
| Read the policy and your path view | ciresk dev policy show, ciresk dev doctor | dev:policy.read | people, agents, services |
| Change the policy | ciresk dev policy set, ciresk dev policy preset | dev:policy.write | people only |
| Read usage against the budget | ciresk dev usage | dev:policy.read | people, agents, services |
| List the budgets per group | ciresk dev budgets list | dev:policy.read | people, agents, services |
| Set or remove a group's budget | ciresk dev budgets set, ciresk dev budgets rm | dev:policy.write | people only |
| Kill sessions | ciresk dev kill | dev:sessions.kill | people and services, never an agent |
| Read the connection log | ciresk dev connections | dev:audit.read | people and services, never an agent |
| Read the audit log | the console, or the API | dev:audit.read | people and services, never an agent |
| Change how long audit records are kept | the console, or the API | dev:policy.write | people only |
| Share your session | ciresk dev share | dev:sessions.share | the session's owner, a person |
| Revoke a member of your session | ciresk dev share --revoke | dev:sessions.share | the session's owner |
| Revoke a member of anyone's session | the API | dev:sessions.kill | people and services |
| Read a session's egress decisions | ciresk dev egress | dev:sessions.read | the session's owner and members |
| List template versions | ciresk dev templates list | dev:templates.read | people, agents, services |
| Publish a template version | ciresk dev templates publish | dev:templates.write | people and services |
| Promote a template version | ciresk dev templates promote | dev:templates.write | people only |
| List session secrets | ciresk dev secrets list | dev:secrets.read | people, agents, services |
| Set or remove a session secret | ciresk dev secrets set, ciresk dev secrets rm | dev:secrets.write | people only |
| Enrol, drain or revoke a worker | ciresk dev admin enrol-worker, ciresk dev admin drain-worker | dev:workers.admin | people and services, never an agent |
| Export the organization's dev data | the console's Data page, or the API | dev:org.export | people only |
| Erase the organization's dev data, or cancel an erasure within 24 hours | the console's Data page, or the API | dev:org.erase | people only |
A refused call answers one of two errors. insufficient_scope means the scope is missing. principal_not_allowed means an agent, or an API key where only a person may act, called the operation. Every problem answer's type links to its code: Ciresk dev's own codes to troubleshooting.md, codes every Ciresk API shares (such as insufficient_scope, rate_limited, invalid_query) to the shared errors page.
An API key acts for the person who owns it. The sessions it starts belong to that person and count against that person's session limit and daily budget. An agent's sessions count against its owner's.
The policy
The organization has one dev policy, a versioned JSON document. An organization that never set one runs on the defaults below, as version 0. Every version is kept, and every change is an audit record that names each changed field (lifecycle.idle_max_secs).
Read it
ciresk dev policy show
ciresk dev policy show --jsonpolicy show prints the policy, its limits and budget in a few lines, and whether your path view hides or narrows any path. --json returns {policy, view}: the whole document and your evaluated path view. ciresk dev doctor shows the same policy as one of its checks.
The policy that applies to you is your effective policy: the organization's policy evaluated with your groups. An agent's effective policy is its owner's. You can read your own; reading another person's view is reserved for services.
Change it
Write the next version to a file and set it. The version must be the current one plus one; the first version you set is 1:
ciresk dev policy show --json # copy the "policy" object to policy.json, raise "version" by one, edit
ciresk dev policy set policy.jsonA file with the wrong version is refused with policy_version_conflict, which names the next version. A document whose fields do not hold together is refused with policy_invalid and the reason.
One preset is built in:
ciresk dev policy preset no-local-checkoutIt turns off local sync, downloads and the clipboard, and sets a 24 hour session lifetime unless you already set one. Everything else stays as it is.
When a change takes effect
- A narrowing of a switch,
sharing,weborweb_idereaches running sessions within 20 seconds. Every connection renews its ticket every 20 seconds against the session's starting policy and the current one, and gets the narrower of the two. - A widening applies to sessions started after the change.
- The lifecycle limits apply to sessions started after the change. The exceptions are the lifetime warning, the session counts and
auto_stop, which always read the current version. - A lowered budget applies within 15 minutes, the slice a session pays ahead.
- Egress ceilings, locked template fields,
models,workersanddotfilesreach a running session only when its owner runsciresk dev up --recreate. - Retention applies at the next background run, about twice an hour.
Default policy
{
"version": 0,
"local_sync": "allow",
"ssh": "allow",
"port_forward": "allow",
"download": "allow",
"upload": "allow",
"clipboard": "allow",
"ssh_exec": "allow",
"sharing": "view",
"web": "view",
"web_ide": "off",
"paths": [],
"lifecycle": {
"max_lifetime_secs": null,
"lifetime_warning_secs": 3600,
"idle_secs": null,
"idle_min_secs": 300,
"idle_max_secs": 28800,
"sessions_per_person": 10,
"sessions_per_org": 100,
"max_session_memory_bytes": null,
"max_session_cpus_milli": null,
"auto_stop": "pause"
},
"templates": "any",
"locked": { "images": [], "egress": null, "container_memory_bytes": null, "container_cpus_milli": null },
"budget": { "person_day_units": null, "org_month_units": null },
"egress": { "session": null, "agent": null, "agent_exit": true },
"models": { "allow": false, "default_model": null },
"retention": { "ended_sessions_days": 90, "connections_days": 30, "egress_decisions_days": 30 },
"workers": "cell",
"dotfiles": { "access": "allow", "hosts": [] }
}Any field you leave out takes its default, so a short file is enough. The only field you must always include is version.
Access switches
Each is allow (the default) or deny.
| Setting | What deny turns off |
|---|---|
local_sync | syncing a local worktree into a session; sessions start from the repository instead (ciresk dev up --remote) |
ssh | ciresk dev ssh, ciresk dev code and every other SSH connection, read-only ones included, and the terminal in the browser |
port_forward | SSH port forwarding to the session's ports |
download | copying files out of a session (sftp, scp), ciresk dev evidence bundles and saved logs |
upload | copying files into a session (sftp, scp) |
clipboard | copying to your clipboard from the session's terminals |
ssh_exec | SSH commands without a terminal (ssh host <command>), which Remote-SSH editors use; deny stops those editors from connecting but keeps interactive shells |
Lifecycle limits
| Setting | Default | What it does |
|---|---|---|
max_lifetime_secs | null (no limit) | stops a session this long after it was created, whatever it is doing; 3600 to 604800 |
lifetime_warning_secs | 3600 | the owner is told this long before that stop; shorter than the lifetime |
idle_secs | null (the template's, 60 minutes unless it says) | the idle stop a session gets when its owner names none |
idle_min_secs, idle_max_secs | 300, 28800 | the range every idle stop must fall in. A person's ciresk dev up --idle outside it is refused with idle_out_of_bounds; the template's and the organization's own values are pulled into the range |
sessions_per_person | 10 | live sessions one person may hold, their agents' included; over it, person_session_limit |
sessions_per_org | 100 | live sessions the organization may hold; over it, org_session_limit |
max_session_memory_bytes, max_session_cpus_milli | null (the host's limit) | the largest reservation one session's template may declare; over it, session_too_large |
auto_stop | pause | what an idle stop, a budget stop and the end of the lifetime do to a session that edits in the cloud (ciresk dev up --remote): pause keeps it with its checkpoint, so ciresk dev start resumes it; end ends it. A session synced from a laptop always ends |
Templates
| Setting | Default | What it does |
|---|---|---|
templates | any | any: a session may start from any repository's own template. published: only from the version the organization published and promoted for that repository; anything else is refused with template_not_published |
locked.images | [] | pinned image references that replace the template's image of the same repository |
locked.egress | null | when set, exactly the egress rows every session gets, whatever the template says |
locked.container_memory_bytes, locked.container_cpus_milli | null | caps on every container of every session |
Publishing and promoting versions is described in templates.md:
ciresk dev templates publish
ciresk dev templates promote <version_id> --repo github.com/acme/app
ciresk dev templates promote <version_id> --all
ciresk dev templates listTemplate parameters, and the choices, ranges and presets people pick from, are declared in the template itself (see templates.md). To control which parameters and choices your people get, publish the template and set templates to published. A policy setting that fixes a single parameter's value for the whole organization is not available yet.
Read access per path
paths limits who reads and writes parts of a repository inside sessions. Each rule names a directory prefix and the groups that may read and write under it:
"paths": [
{ "prefix": "crates/payments/", "read": ["payments"], "write": ["payments"] },
{ "prefix": "crates/payments/public/", "read": ["engineering"], "write": [] }
]- A prefix is a relative directory ending in
/, and each prefix appears once. The longest matching prefix decides, so a rule can reopen a subtree. - Write implies read.
- A path no rule matches is open to every member.
- Groups are the ones your organization's directory gives each person. An agent has its owner's groups.
- A path a person cannot read is not in their sessions at all: not synced, not in search results, not in diagnostics.
- A path they can read but not write is read-only in every container. A sync that writes it is refused for that path.
- A path rule change applies to sessions started after it.
- When a person's groups shrink so that a running session holds more than they may now read, the session stops with
access_changedat its next renewal.ciresk dev upthen starts one with the new view. - Delivering a path that falls under any rule is recorded in the audit log as
dev.path.read. A refused write is recorded asdev.path.refused.
Sharing and the browser
| Setting | Default | What it does |
|---|---|---|
sharing | view | the highest role an owner may grant on a session: off, view or use |
web | view | the session in the browser (previews, the diff, the terminal) for its owner and members: off; view, everything read-only; or use, where the owner and use members get a read-write terminal and editor |
web_ide | off | on adds VS Code in the browser. It is read-write for the owner and use members when web is use, and read-only otherwise |
Dotfiles
| Setting | Default | What it does |
|---|---|---|
dotfiles.access | allow | deny stops applying people's dotfiles repositories in new sessions; ciresk dev dotfiles set is then refused with dotfiles_off |
dotfiles.hosts | [] (any host) | when not empty, a dotfiles repository's host must be one of these: an exact host or *.suffix, up to 50 |
Nothing runs until a person names a repository with ciresk dev dotfiles set. Your egress ceilings also apply to the repository's host. How people set dotfiles is in editors.md.
Workers
| Setting | Default | What it does |
|---|---|---|
workers | cell | cell: sessions run on hosts Ciresk starts for your organization, and on any host you enrolled. customer: only on hosts you enrolled in your own network |
Budgets
Budgets are part of the policy (budget.person_day_units, budget.org_month_units) and are counted in vCPU-seconds. There is no budget by default. A new session that a budget cannot pay for is refused with budget_exceeded. A running session stops with the reason budget once nothing more can be paid. ciresk dev usage shows where you and the organization stand. The details are in budgets.md.
Budgets per group
A group of your organization (the platform console's Team › Groups) can have its own cap: what each member may use in a UTC day or a UTC month. It counts each member's own sessions, their agents' included, not the group's total. No group has one until you set it:
ciresk dev budgets set <group id> --vcpu-hours 10 --period day
ciresk dev budgets set <group id> --vcpu-hours 200 --period month --soft
ciresk dev budgets list
ciresk dev budgets rm <group id>The usage page of the dev console lists them and lets an admin set or remove one, with the groups named as Platform names them.
- A person in several groups is bounded by the strictest window: the one with the least left among the policy's two budgets and the hard budgets of their groups.
ciresk dev usageshows each group's window for you, and which one bounds your next session. - A hard budget (the default) behaves as the policy's: a new session is refused with
budget_exceeded(scope: group, with the group and its period), and a running one is warned, then stopped (or paused, for a cloud session underauto_stop: pause). - A soft budget never refuses or stops. When it is spent, each of the person's running sessions tells them once, in what needs them.
- A session reads its owner's groups when it starts. A change to a budget reaches running sessions within 20 minutes; a person added to a group is bounded by it from their next session.
What a session costs before it starts
ciresk dev up prints one line before it creates the session: what the template costs an hour, and how long your strictest budget pays for it.
kepler costs about 8,820 units (2.45 vCPU-hours) an hour; your budget for today pays 3 h 20 min of itThe console's new-session form shows the same as you pick the template, preset and parameters. The cost is in vCPU-hours, the unit budgets use; dev holds no price.
Egress
Nothing leaves a session unless every layer allows it. Model and tool vendors are always refused. After that come your organization's ceiling egress.session, then egress.agent for sessions started by an agent, then the template's rows (or locked.egress), and last the installation's allow-list. ciresk dev egress reads a session's decision log, and each refusal names the change that would let it out. The details are in egress.md.
Sharing
An owner shares a running session with another member of the organization:
ciresk dev share [email protected] --role view
ciresk dev share --list
ciresk dev share --revoke devm_...view: the app's web ports, logs, status, events, search and the read-only shell.use: everything an owner can do in the session except sharing, syncing and stopping it. That includes exec, reset, test logins, the browser and the read-write shell.- Only the owner shares, and only a person: an agent cannot.
- These grants are refused:
- a role above the policy's
sharingceiling, withsharing_ceiling - someone outside the organization, with
not_a_member - a member whose path view is narrower than the session's, with
member_view_narrower
- a role above the policy's
- Every member is checked again on every ticket renewal (20 seconds): still in the organization, sharing not off (a lowered ceiling lowers the role), and their path view still covers the session. A member who fails a check loses the grant.
- Revoking a member ends the grant and every device grant under it, and closes their connections at once.
ciresk dev shareacts on the current worktree's session. - A holder of
dev:sessions.killcan revoke a member of anyone's session through the API. The audit record names that person, not the owner.
The kill switch
ciresk dev kill devs_... # one session
ciresk dev kill --owner user:01H... --reason "offboarding"
ciresk dev kill --all --reason "incident 42" --yes # every live session of the organizationIt needs dev:sessions.kill, which owners and admins hold; an agent can never use it. For each session it reaches, in one step:
- The session is marked stopping, and every ticket issued for it before the kill stops working.
- Every connection to it closes at once.
- The host stops it at its next heartbeat and closes its egress. A host that lost contact closes the session's egress 30 seconds after its last renewal.
- The session's event log records the kill and the reason, and the audit log records
dev.session.kill.
--reason (up to 200 characters) is kept in both records. On a terminal, kill asks for confirmation; without a terminal it needs --yes. It prints the sessions it stopped.
Every kill sends an Idempotency-Key: the one you pass with --idempotency-key, otherwise a new one per run. A retry with the same key within 24 hours gets the first answer again instead of acting twice. Through the API, send the Idempotency-Key header on POST /v1/dev/kill yourself. The same key with a different body is refused with 422 idempotency_key_reused. To find a session id, ciresk dev connections lists which sessions people and agents connected to. The console's Kill switch page does the same.
Workers
Sessions run on workers. By default these are hosts Ciresk starts for your organization in its cloud. You can also enrol your own hosts and keep every session on them with workers: customer. Each enrolled worker serves only your organization.
ciresk dev admin enrol-worker acme-dc1-01 # issue (or rotate) the host's enrolment secret
ciresk dev admin drain-worker acme-dc1-01 # no new sessions; running ones go on
ciresk dev admin drain-worker acme-dc1-01 --undrain
ciresk dev admin enrol-worker acme-dc1-01 --revoke # cut the worker off at onceThese commands need dev:workers.admin. Revoking a worker stops its credentials at once and moves its sessions to a new epoch, so no old ticket or stream works any more. A session started from the repository with a checkpoint resumes on another of your workers. Any other session is marked lost, and its owner's next ciresk dev up starts it again. The audit log records dev.worker.enrol, dev.worker.drain, dev.worker.undrain and dev.worker.revoke. The console's Workers page lists every worker with its state. Requirements and the full enrolment steps are in hybrid.md.
Secrets
Session secrets let a session call an API with a credential the session never holds: the egress proxy adds the value on the way out, and only to the hosts you name. The value is read from stdin only. A secret with --purpose git is instead the credential ciresk dev push pushes with.
ciresk dev secrets list
ciresk dev secrets set npm-token --host registry.acme.com --repo '*' < token.txt
ciresk dev secrets rm npm-tokenEveryone in the organization, agents included, can list secrets' names, hosts and bindings, never a value. Only people with dev:secrets.write (owners and admins) set and remove them. Each change is recorded as dev.secrets.set or dev.secrets.delete. Each delivery of values to a session's worker is recorded as dev.session.secrets. The flags and the binding rules are in secrets.md.
Models
Models are off for every organization: models.allow is false by default, and agents inside a session have no model access. A session's egress never reaches a model vendor directly (see Egress).
Model access for a session's agents is not available yet. You can store models.allow and default_model, but today they give sessions no model access.
Logs and audit
The connection log
ciresk dev connections
ciresk dev connections --session devs_... --since 2026-10-24T00:00:00Z
ciresk dev connections --principal user:01H...This shows who reached which session, oldest first. There is one row per window of about a minute for each grant, stream kind, port and client. Each row names the person, the agent when one acted, and the device. It counts how many streams opened, closed and were active, the bytes each way, and why streams closed.
- Filter by
--session,--principal(user:<id>oragent:<id>),--sinceand--until. - Page with
--limit(up to 200, 50 by default) and--cursor. - It needs
dev:audit.readand covers the whole organization.
The audit log
Every change is written to the audit log in the same transaction as the change. Each record names the requester, the accountable person, the agent when one acted, and the session.
Read the log on the console's Audit log page, or through the API:
curl "https://api.eu.ciresk.com/v1/dev/audit/events?action=dev.session.kill&since=2026-10-24T00:00:00Z" \
-H "Authorization: Bearer $TOKEN"- Records come newest first.
- Filter by
actor(a subject such asuser:<id>),session_id,action,sinceanduntil. - Page with
limit(up to 200) andcursor. - A query string that does not parse (a malformed time, a
limitthat is not a number) is refused with400 invalid_query, a problem document like every other error. - It needs
dev:audit.read. A person or an API key holding it can read; an agent cannot. - Every read is itself recorded, as
dev.audit.searched. - There is no
ciresk devcommand for it.
Audit records are kept 12 months by default. An admin with dev:policy.write can choose 7 or 10 years on the same console page (see export.md).
The actions recorded:
| Action | When |
|---|---|
dev.policy.set | a new policy version, with each changed field |
dev.templates.publish, dev.templates.promote | a template version published or promoted |
dev.session.create, dev.session.update, dev.session.stop | a session started, changed or stopped |
dev.session.pause, dev.session.start | a session paused with its checkpoint, or started again from it |
dev.session.kill | the kill switch, per session |
dev.session.share, dev.session.unshare | a member granted or revoked |
dev.session.grant, dev.session.revoke | a device attached to a session or detached |
dev.session.web_handoff, dev.session.web_open | a browser link to a session made, and opened |
dev.tunnel.connect | a connection window that opened a stream |
dev.session.exec, dev.session.reset, dev.session.login, dev.session.ssh, dev.session.transfer | commands, resets, test logins, SSH, and files or evidence leaving a session |
dev.session.push, dev.session.ci_record | a session's workspace pushed as a commit, a CI run recorded on the session |
dev.session.need.raise, dev.session.need.resolve | a session asked its person for something, and the answer or dismissal |
dev.session.egress | a destination refused for the first time in a session |
dev.session.secrets | secret values delivered to a session's worker |
dev.secrets.set, dev.secrets.delete | a session secret set or removed |
dev.dotfiles.set, dev.dotfiles.delete, dev.dotfiles.credential.set | a person set or removed their dotfiles repository, or set its credential |
dev.schedule.set, dev.schedule.clear | an autostart schedule set or removed |
dev.path.read, dev.path.refused | a restricted path delivered, a write refused |
dev.clipboard.copied, dev.clipboard.denied | a copy from a session's terminal, allowed or refused |
dev.worker.enrol, dev.worker.drain, dev.worker.undrain, dev.worker.revoke | a worker enrolled, drained, undrained or revoked |
dev.worker.register, dev.worker.provision | a worker came online, a Ciresk host was started for your organization |
dev.org.export, dev.org.exported, dev.org.export.failed, dev.org.export.downloaded | an export asked for, built, failed or downloaded |
dev.org.erase, dev.org.erasure.cancelled, dev.org.erased | an erasure asked for, cancelled, or completed |
dev.org.suspend, dev.org.resume | Ciresk suspended or resumed the organization |
dev.retention.swept | old records deleted under the retention settings |
dev.rate_limited | an identity's first refusal in a rate-limit window (see rate-limits.md) |
dev.audit.searched | someone read the audit log |
Retention, export and erasure
Ciresk dev deletes old records under the policy's retention settings. By default, ended sessions with their journals are kept 90 days, and the connection and egress logs 30 days.
- An admin with
dev:org.exportcan export everything the organization still has as a signed archive. - An admin with
dev:org.erasecan have all of it erased after a 24 hour grace period, and cancel the erasure within that time.
A person does both on the console's Data page or through the API; neither is available to API keys or agents. The windows, the archive's contents and the erasure steps are in export.md.
Autostart schedules
A person, or their agent, can have a stopped remote session ready at a local time on chosen days (ciresk dev schedule set 08:30 --days weekdays), or every stopped session of theirs with one template name. A schedule belongs to its person and has no policy setting of its own. You govern what it does with the policy you already have:
- A scheduled start is admitted like
ciresk dev start: the session limits, the budget and the policy as it is now all apply. - In a suspended organization, or one being erased, nothing starts.
- Only a stopped (paused) session starts, so a session the kill switch ended is never started again.
Each schedule change is recorded as dev.schedule.set or dev.schedule.clear. Each start is recorded as dev.session.start by service:dev-api.
Suspension and emergency stop
These actions are taken by Ciresk, not by your admins. Ciresk staff can act on an organization in these ways:
- Suspend it. Every live session is killed with the reason
org_suspended. Every call by the organization's people, keys and agents then answers403 org_suspended("contact Ciresk support") until Ciresk resumes it. Resuming lets calls through again; the killed sessions do not come back. The audit log recordsdev.org.suspendanddev.org.resume. - Stop every session. Every live session is killed with the reason
staff_stop. Each kill is adev.session.killrecord with the staff member's reason. New sessions can start right away. - Revoke every worker. Every enrolled worker is cut off as if you had revoked it, each recorded as
dev.worker.revoke. You then enrol each host again (see hybrid.md).
In your audit log, these records name the requester service:internal. Ciresk staff read no session content through these actions.
When someone leaves
Each connection renews its ticket every 20 seconds, and every renewal checks the credential again. A revoked or expired credential gets no new ticket, and a member removed from the organization loses every session shared with them. To stop a person's running sessions at once, kill them by owner:
ciresk dev kill --owner user:01H... --reason "offboarding" --yes