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:

RoleScopes
Owner, adminevery dev: scope
Memberdev:sessions.read, dev:sessions.write, dev:sessions.attach, dev:sessions.share, dev:policy.read, dev:templates.read, dev:secrets.read, dev:keys.read
Viewerdev: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:

OperationCommandScopeWho may call it
Read the policy and your path viewciresk dev policy show, ciresk dev doctordev:policy.readpeople, agents, services
Change the policyciresk dev policy set, ciresk dev policy presetdev:policy.writepeople only
Read usage against the budgetciresk dev usagedev:policy.readpeople, agents, services
List the budgets per groupciresk dev budgets listdev:policy.readpeople, agents, services
Set or remove a group's budgetciresk dev budgets set, ciresk dev budgets rmdev:policy.writepeople only
Kill sessionsciresk dev killdev:sessions.killpeople and services, never an agent
Read the connection logciresk dev connectionsdev:audit.readpeople and services, never an agent
Read the audit logthe console, or the APIdev:audit.readpeople and services, never an agent
Change how long audit records are keptthe console, or the APIdev:policy.writepeople only
Share your sessionciresk dev sharedev:sessions.sharethe session's owner, a person
Revoke a member of your sessionciresk dev share --revokedev:sessions.sharethe session's owner
Revoke a member of anyone's sessionthe APIdev:sessions.killpeople and services
Read a session's egress decisionsciresk dev egressdev:sessions.readthe session's owner and members
List template versionsciresk dev templates listdev:templates.readpeople, agents, services
Publish a template versionciresk dev templates publishdev:templates.writepeople and services
Promote a template versionciresk dev templates promotedev:templates.writepeople only
List session secretsciresk dev secrets listdev:secrets.readpeople, agents, services
Set or remove a session secretciresk dev secrets set, ciresk dev secrets rmdev:secrets.writepeople only
Enrol, drain or revoke a workerciresk dev admin enrol-worker, ciresk dev admin drain-workerdev:workers.adminpeople and services, never an agent
Export the organization's dev datathe console's Data page, or the APIdev:org.exportpeople only
Erase the organization's dev data, or cancel an erasure within 24 hoursthe console's Data page, or the APIdev:org.erasepeople 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

Terminal
ciresk dev policy show
ciresk dev policy show --json

policy 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:

Terminal
ciresk dev policy show --json     # copy the "policy" object to policy.json, raise "version" by one, edit
ciresk dev policy set policy.json

A 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:

Terminal
ciresk dev policy preset no-local-checkout

It 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, web or web_ide reaches 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, workers and dotfiles reach a running session only when its owner runs ciresk dev up --recreate.
  • Retention applies at the next background run, about twice an hour.

Default policy

policy.json
{
  "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.

SettingWhat deny turns off
local_syncsyncing a local worktree into a session; sessions start from the repository instead (ciresk dev up --remote)
sshciresk dev ssh, ciresk dev code and every other SSH connection, read-only ones included, and the terminal in the browser
port_forwardSSH port forwarding to the session's ports
downloadcopying files out of a session (sftp, scp), ciresk dev evidence bundles and saved logs
uploadcopying files into a session (sftp, scp)
clipboardcopying to your clipboard from the session's terminals
ssh_execSSH commands without a terminal (ssh host <command>), which Remote-SSH editors use; deny stops those editors from connecting but keeps interactive shells

Lifecycle limits

SettingDefaultWhat it does
max_lifetime_secsnull (no limit)stops a session this long after it was created, whatever it is doing; 3600 to 604800
lifetime_warning_secs3600the owner is told this long before that stop; shorter than the lifetime
idle_secsnull (the template's, 60 minutes unless it says)the idle stop a session gets when its owner names none
idle_min_secs, idle_max_secs300, 28800the 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_person10live sessions one person may hold, their agents' included; over it, person_session_limit
sessions_per_org100live sessions the organization may hold; over it, org_session_limit
max_session_memory_bytes, max_session_cpus_millinull (the host's limit)the largest reservation one session's template may declare; over it, session_too_large
auto_stoppausewhat 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

SettingDefaultWhat it does
templatesanyany: 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.egressnullwhen set, exactly the egress rows every session gets, whatever the template says
locked.container_memory_bytes, locked.container_cpus_millinullcaps on every container of every session

Publishing and promoting versions is described in templates.md:

Terminal
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 list

Template 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:

JSON
"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_changed at its next renewal. ciresk dev up then 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 as dev.path.refused.

Sharing and the browser

SettingDefaultWhat it does
sharingviewthe highest role an owner may grant on a session: off, view or use
webviewthe 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_ideoffon 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

SettingDefaultWhat it does
dotfiles.accessallowdeny 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

SettingDefaultWhat it does
workerscellcell: 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:

Terminal
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 usage shows 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 under auto_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.

Code
kepler costs about 8,820 units (2.45 vCPU-hours) an hour; your budget for today pays 3 h 20 min of it

The 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:

Terminal
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 sharing ceiling, with sharing_ceiling
    • someone outside the organization, with not_a_member
    • a member whose path view is narrower than the session's, with member_view_narrower
  • 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 share acts on the current worktree's session.
  • A holder of dev:sessions.kill can revoke a member of anyone's session through the API. The audit record names that person, not the owner.

The kill switch

Terminal
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 organization

It 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.

Terminal
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 once

These 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.

Terminal
ciresk dev secrets list
ciresk dev secrets set npm-token --host registry.acme.com --repo '*' < token.txt
ciresk dev secrets rm npm-token

Everyone 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

Terminal
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> or agent:<id>), --since and --until.
  • Page with --limit (up to 200, 50 by default) and --cursor.
  • It needs dev:audit.read and 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:

Terminal
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 as user:<id>), session_id, action, since and until.
  • Page with limit (up to 200) and cursor.
  • A query string that does not parse (a malformed time, a limit that is not a number) is refused with 400 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 dev command 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:

ActionWhen
dev.policy.seta new policy version, with each changed field
dev.templates.publish, dev.templates.promotea template version published or promoted
dev.session.create, dev.session.update, dev.session.stopa session started, changed or stopped
dev.session.pause, dev.session.starta session paused with its checkpoint, or started again from it
dev.session.killthe kill switch, per session
dev.session.share, dev.session.unsharea member granted or revoked
dev.session.grant, dev.session.revokea device attached to a session or detached
dev.session.web_handoff, dev.session.web_opena browser link to a session made, and opened
dev.tunnel.connecta connection window that opened a stream
dev.session.exec, dev.session.reset, dev.session.login, dev.session.ssh, dev.session.transfercommands, resets, test logins, SSH, and files or evidence leaving a session
dev.session.push, dev.session.ci_recorda session's workspace pushed as a commit, a CI run recorded on the session
dev.session.need.raise, dev.session.need.resolvea session asked its person for something, and the answer or dismissal
dev.session.egressa destination refused for the first time in a session
dev.session.secretssecret values delivered to a session's worker
dev.secrets.set, dev.secrets.deletea session secret set or removed
dev.dotfiles.set, dev.dotfiles.delete, dev.dotfiles.credential.seta person set or removed their dotfiles repository, or set its credential
dev.schedule.set, dev.schedule.clearan autostart schedule set or removed
dev.path.read, dev.path.refuseda restricted path delivered, a write refused
dev.clipboard.copied, dev.clipboard.denieda copy from a session's terminal, allowed or refused
dev.worker.enrol, dev.worker.drain, dev.worker.undrain, dev.worker.revokea worker enrolled, drained, undrained or revoked
dev.worker.register, dev.worker.provisiona worker came online, a Ciresk host was started for your organization
dev.org.export, dev.org.exported, dev.org.export.failed, dev.org.export.downloadedan export asked for, built, failed or downloaded
dev.org.erase, dev.org.erasure.cancelled, dev.org.erasedan erasure asked for, cancelled, or completed
dev.org.suspend, dev.org.resumeCiresk suspended or resumed the organization
dev.retention.sweptold records deleted under the retention settings
dev.rate_limitedan identity's first refusal in a rate-limit window (see rate-limits.md)
dev.audit.searchedsomeone 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.export can export everything the organization still has as a signed archive.
  • An admin with dev:org.erase can 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 answers 403 org_suspended ("contact Ciresk support") until Ciresk resumes it. Resuming lets calls through again; the killed sessions do not come back. The audit log records dev.org.suspend and dev.org.resume.
  • Stop every session. Every live session is killed with the reason staff_stop. Each kill is a dev.session.kill record 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:

Terminal
ciresk dev kill --owner user:01H... --reason "offboarding" --yes