Quickstart

From a repository with no manifest to a first ciresk ci check in five minutes.

Every command on this page is ciresk ci <command>. This page assumes the CLI is installed and logged in; if not, start with Install the CLI.

What you need

ItemDetail
An organization with Ciresk CISign in at platform.ciresk.com (the sign-in page also creates an account) and start your own organization. Ciresk turns CI on per organization: write to [email protected] with your organization's name. Until then ciresk ci auth login answers that none of your organizations has ci enabled.
A folder with code in itgit is optional: with an origin remote the repository is identified by its URL, otherwise the first run registers the folder as local/<folder name> and writes that slug to .ciresk/repo; commit the file so every clone runs as the same repository. The repository registers itself on its first run; private repositories work the same, since the CLI uploads the working tree.
A credentialA person logs in with ciresk ci auth login. A machine or an agent sandbox exports CIRESK_TOKEN with a csk_live_eu_... token from platform.ciresk.com under API tokens and runs ciresk ci --pass-token .... See API keys.
WorkersTasks run on workers that belong to your organization: a Linux host, a PC under WSL2 or a cloud instance running ciresk-ci-worker, enrolled with ciresk ci workers add. Without an online worker a job stays queued; ciresk ci doctor says so and ciresk ci status <job> names the reason. See Run your own worker.

Check the set-up before the first run:

Terminal
ciresk ci doctor

Every line is ok or fix with the command that repairs it, and the last line is the next command to run.

Create the manifest

Terminal
ciresk ci init

init writes .ciresk/ci.yaml with check and test for the toolchain it detects, and lint where it finds a linter (clippy for Rust; a lint script or an eslint config for JavaScript). For Rust it also writes .config/nextest.toml, which makes nextest write the JUnit file the test task reports. A package.json gets the package manager its packageManager field names, else the one whose lockfile is there, else npm. Edit the files and commit them; the full reference is Manifest. For a single Rust crate init writes this (without the comments it adds):

.ciresk/ci.yaml
version: 1

toolchains:
  "cargo:cargo-nextest": "0.9.148"
  rust: "1.95.0"

globals:
  inputs: [".config/nextest.toml", ".ciresk/*.sh"]
  env: { CI: "true" }
  pass_env: []

workspace:
  warm: true
  keep: ["target"]

tasks:
  check:
    run: "cargo check --all-targets"
    inputs: ["src/**", "Cargo.toml"]
    affected: { strategy: cargo, targets_arg: "-p {crate}" }
    timeout: "10m"
  test:
    run: "cargo nextest run"
    inputs: ["src/**", "Cargo.toml"]
    affected: { strategy: cargo, targets_arg: "-p {crate}" }
    timeout: "10m"
    report: { junit: "target/nextest/default/junit.xml" }
  lint:
    run: "cargo clippy --all-targets -- -D warnings"
    inputs: ["src/**", "Cargo.toml"]
    affected: { strategy: cargo, targets_arg: "-p {crate}" }
    timeout: "10m"

ciresk ci lint runs the lint task. Every tool a run line calls comes from toolchains: cargo nextest works because of the cargo:cargo-nextest entry. For Node, the TypeScript example is the pnpm version to start from.

Run a check

Terminal
ciresk ci check --follow

check runs the check task on your working tree (on the targets you pass with -p, else the whole run command), waits and prints the summary. --follow streams the task's log to stderr while it runs.

Output
tree: 612 entries, 1,070,702 bytes, root 1d6ccb175d0e; walk 9 ms, hashed 0 (reused 612) 1 ms, plan 0 ms: 0 blobs, 0 bytes: 0 inline bodies, 0 presigned puts, 0 multipart parts
timings: locate 4 ms, index 1 ms, walk 9 ms, hash 1 ms, plan 0 ms, upload 0 ms, submit 41 ms
budget: session 1296/72000 units, day 1296/720000, resets 00:00Z
job_01a158eea7347770a076e6b4ab7955d9: queued
job_01a158eea7347770a076e6b4ab7955d9: leased
job_01a158eea7347770a076e6b4ab7955d9: running
[ciresk] toolchains installed with mise in 45.9s
[ciresk] running `cargo check --all-targets` in .
[ciresk] exit Some(0)
job_01a158eea7347770a076e6b4ab7955d9: succeeded
succeeded job_01a158eea7347770a076e6b4ab7955d9 check@main  8m 19s  14080 units  cache 0 hit / 1 miss / 0 write
trust: trusted_interactive (warm workspace, read-write cache)
next: ciresk ci status job_01a158eea7347770a076e6b4ab7955d9

With ciresk ci watch running, the tree: line reads tree: watcher live, nothing to upload. Progress, the timings and the budget line go to stderr; the data goes to stdout. With --json, stdout is exactly one JSON document.

Read the result

The exit code is the verdict: 0 is a pass, 1 is a failed task or test, and 2 to 8 name a reason that is not a code failure (usage, auth, policy, budget, cancelled, timed out, infrastructure). The full table is in Exit codes. A failed task ends with the failing tests, the last lines of the first failed task and the next command:

A failed task
failed job_01a158e79d4874dcb4d5ca3a263a7f9f test@main  1m 12s  960 units  cache 1 hit / 1 miss / 0 write
trust: trusted_interactive (warm workspace, read-write cache)
tasks:
  test: failed, exit 1, 1m 8s
failing tests: 1
  FAIL manifest::parses_nested_manifests
       assertion `left == right` failed
tail of test (last 3 lines):
  test manifest::parses_nested_manifests ... FAILED
  error: test run failed
  [ciresk] exit Some(1)
next: ciresk ci tests job_01a158e79d4874dcb4d5ca3a263a7f9f

Human output ends with the exact command for the obvious drill-down, and every --json envelope carries it as next:

Afternext
A failed test runciresk ci tests <job>: failing tests first, with file:line and the first message lines.
A failed taskciresk ci logs <job> --task check (failed-only by default): the last 200 lines, at most 256 KiB.
A passed jobciresk ci status <job>.
More than 50 failing testsciresk ci tests <job> --cursor eyJ....
A job still running without --waitciresk ci wait <job>.

Run next rather than ask for a whole log: logs returns failed-only, the last 200 lines, at most 256 KiB per call, and says in band when it truncated.

After the first run

WantCommand
Tests for one crate or packageciresk ci test -p <crate>
What would be uploaded, without uploading itciresk ci run check --dry-run
Every run one requestciresk ci watch in a second terminal, or ciresk ci watch --once at session start
A job in the background, then waitexport CIRESK_JOB=$(ciresk ci run test --json | jq -r .data.id); ciresk ci wait
Your spend and limitsciresk ci budget; see Limits and spend
Claude Code, Codex and Cursor set-up, the hooks, MCPClaude Code, Codex, Cursor
The same check in GitHub Actions, no secretGitHub Actions
Run your own workersRun your own worker
Delete a repository's data, or leaveLeaving Ciresk CI

Limits and spend

Every job is metered in units, and every response carries the budget line you saw above: the units spent in this session and today against the caps, and the time the day resets (00:00 UTC). A new organization starts with these caps:

CapDefault
Units per session72,000 (a soft budget at 36,000 runs further jobs at a lower priority)
Units per day, per person or agent720,000 (soft at 360,000)
Units per job36,000
Jobs per session, per day200, 2,000

A job that would pass a hard cap is refused with exit code 5 and the time the window resets. ciresk ci budget prints where you stand, ciresk ci policy get every limit with its source, and an organization admin changes one with ciresk ci policy set hard_units_per_day=50000. Usage per month, and a monthly budget once a unit price is set for your organization, are on platform.ciresk.com under Billing.

Good to know

  • A folder that was never git init-ed works the same: the first run registers it under a slug of its own and uploads the tree once; from the second run only changed files travel. See Caching and tree sync.
  • Identical requests from two people or two agents are one job, and a retry after a timeout returns the same job. See How it works.
  • A job started inside Claude Code or Codex is attributed to agent:claude-code or agent:codex. The actor is attribution only: the trust tier comes from your credential, the ref and your policy, and a policy with agent_branches_untrusted=true runs agent:* jobs as untrusted. CIRESK_ACTOR or --actor overrides the detected actor.