/cli.md. The full docset is at /llms-full.md and the index is at /llms.md.CLI reference
curl -fsSL https://cthree.cloud/install.sh | sh # install (Linux, macOS)
c3 update # update to the latest release
c3 version
c3 docs prints every page of this site as plain text, for piping into an agent or a file.
Reconcile a persistent workspace
After a job writes to a persistent workspace, local edits may conflict with its new remote revision. Fetch the remote files into a new directory before merging those edits:
# Run in the workspace root containing .c3.
c3 workspace fetch my-workspace ../workspace-fetched --script job.sbatch
# Review both trees and copy your intended local edits into workspace-fetched.
cd ../workspace-fetched
c3 deploy job.sbatch --storage=juicefs --workspace my-workspace
--script is optional when .c3 already names the submission script. Fetch uses
the same script/ignore selection and sanitized config bytes as deploy for its
baseline, while retaining every exported file locally. It verifies the exact
resource revision, export manifest and file checksums before writing that baseline.
Fresh checkouts without a CLI sync record can also fetch an existing workspace:
the CLI finds it by the project in .c3 and the supplied workspace name within
your authenticated organisation. This reuses the resource without allocating
another workspace. An existing invalid or mismatched sync record still refuses.
It never overwrites the original checkout or an existing destination, uploads
files, creates a resource, or submits a job. Pending imports must be reconciled
first; a remote revision change during the download refuses the new baseline.
If another job writes remotely, repeat the fetch into another new directory
before reconciling further local edits, including Python lockfile updates.
Older workspaces may contain a Python environment from the previous runtime.
To retain that remote cache while fetching code, use:
c3 workspace fetch my-workspace ../workspace-code --preserve-python-cache
This omits exactly .c3-runtime/python from the new local checkout while keeping
the remote environment and its interpreter symlinks. Other regular files,
including .c3-runtime siblings, are fetched. Normal additive deploys preserve
the omitted remote cache; this is a code checkout, not a complete local copy.
Symlinks outside that cache still refuse. Without the flag, full exports retain
their strict symlink refusal. The JSON receipt includes every read_operations
ID and the exact preserved_remote_prefixes; inspect a reported retained read
operation before retrying after an uncertain response.
Do not edit .c3-runtime-sync-* records yourself. A failed local publication may
leave a partial new directory for inspection; its baseline is installed last.
This command reconciles files on the CLI host; hosted MCP/dashboard clients use the existing runtime resource export/download operations instead.
Jobs
c3 deploy
Submit a job from the .c3 in the current folder or any parent.
c3 deploy [script] [-f] [-v] [-p <provider>] [--json] [--dry-run] [--list-files] [--storage juicefs|legacy]
| Flag | Effect |
|---|---|
--storage juicefs|legacy | Storage pathway; defaults to JuiceFS. Legacy is an explicit compatibility choice. |
--environment-delivery nydus|oci | Container delivery; defaults to Nydus. OCI keeps persistent storage and downloads the ordinary image. |
--workspace <name> | Persistent workspace name within the project. Omitted, a fresh checkout gets its own private workspace and reuses it on every later deploy; name one explicitly to share a workspace between checkouts. |
--data-resource <name=id[:ro]> | Attach a persistent data resource; writable unless :ro is supplied. Repeat for multiple resources. |
-f, --follow | Stream logs until the job finishes |
-v, --verbose | With -f, include system and agent log streams |
-p, --provider <id> | Pin a provider for this run |
--json | Print the submitted job as one JSON object. Not with -f |
--dry-run | Show the route, rate and availability. Nothing is uploaded |
--list-files | Print the files that would be uploaded and exit. No sign-in needed |
[script] | A legacy .sbatch script instead of .c3 |
Plain c3 deploy publishes the workspace through owned storage (verified and published by the control plane) and runs it on persistent JuiceFS storage; c3 deploy --storage=legacy uses the existing presigned storage and execution path. There is no automatic fallback between them. The option applies both to .c3 projects and script arguments.
For legacy script submissions (c3 deploy job.sbatch), the CLI looks for a
project .c3 from the workspace folder upward, skipping directories named
.c3, such as the credential store at ~/.c3. If none exists, it writes a
generated project name. If it cannot read the project name from the workspace's
own .c3, it moves that file to .c3.broken-<unix time> and writes a starter
config with a new generated project name, script: run.sh, hardware: l40,
time: "01:00:00", and output: [./results]. It also sets python.project: ./
when both pyproject.toml and uv.lock exist; replace that with docker.image
while Python mode is disabled. The flight plan reports the
replacement and backup filename; inspect the backup to recover custom settings.
An unreadable parent config remains an error and is not replaced. Config-based
c3 deploy (without a script argument) still reports config-loading errors.
c3 squeue
c3 squeue [-n <count>] [--json]
c3 squeue <job-id> [--json]
Lists every active job plus the most recent finished ones (-n, default 10). With a job ID, shows one job in full: route, latest attempt and failure diagnosis.
c3 logs
c3 logs <job-id> [-f] [-v]
Prints setup progress and your script's stdout and stderr. -f tails until the job ends.
c3 cancel
c3 cancel <job-id>
Stops a pending or running job. Time already used is billed.
c3 pull
c3 pull [job-id] [folder] [--json]
c3 pull --all [--json]
Downloads a job's artifacts into ./<job-id>/. With no ID, downloads previously undownloaded jobs among the latest 100 succeeded jobs. folder limits the download to one artifact directory.
c3 init
Writes a starter .c3 in the current folder.
c3 migrate
c3 migrate job.sbatch
Converts a Slurm script's #SBATCH directives into a .c3.
Data
c3 data ls [-l] [-R] <path> # datasets, versions or files
c3 data cp <src> <dst> # upload or download; local paths one side, /datasets or /jobs the other
c3 data rm [-r] [-f] [-n] <path> # delete a version, or a dataset with -r; -n is a dry run
c3 data du [--dedup] <path> # storage used
c3 data log [-n <count>] <path> # version history
Path forms: /datasets/<name>/, /datasets/<name>/@<version>/, /datasets/<name>/@latest/, /jobs/<job-id>/, /projects/<project>/jobs/. See Datasets.
Hardware
c3 list [--all] [--class <class>] [--provider <id>] [--json]
Shows classes, profiles, providers, prices and availability. -al is short for --all. See Hardware and capacity.
Account
c3 login # sign in through the browser; credentials stored in ~/.c3/
c3 logout
c3 whoami [--json] # user, email, verification status, organisation
c3 verify-email # resend the verification email
c3 apikey create <name>
c3 apikey list
c3 apikey revoke <key-id>
See Authentication and API keys.
Billing
c3 balance # credit, plan and storage usage
c3 topup --verify-card # verify a card for £10 welcome credit (£1 hold, released); once per card/account
c3 topup [amount] # add credit, whole pounds, £10 to £10,000; opens checkout
c3 upgrade [tier] [-y] # free, pro or team; -y skips confirmation for scripts
See Pricing and plans.
Environment variables
| Variable | Effect |
|---|---|
C3_API_KEY | Authenticate with an API key instead of c3 login |
C3_AUTH_TOKEN | Raw bearer token, for CI |
C3_PROVIDER | Default provider pin, same as -p |
C3_API_ENDPOINT | Control plane URL. Leave unset for production |
JSON output
--json on deploy, squeue, pull, list and whoami prints one JSON document to stdout and sends progress and warnings to stderr. Field tables for deploy and squeue are on Project configuration.
Persistent deployment defaults
Both c3 deploy and c3 deploy run.sh use JuiceFS and Nydus. --dry-run
previews that same execution path without uploading or reserving a workspace.
Choose docker.image; python.project is currently disabled and refused with
PYTHON_DISABLED. Bash scripts run inside the chosen container.
Each fresh checkout automatically gets its own persistent workspace; repeated
deploys from that checkout reuse it. --workspace <name> explicitly selects a
named workspace for sharing. Existing checkouts with a local default
workspace binding keep it. Local .c3-runtime-sync-* state should not be
committed or copied into another checkout and is never uploaded.
Workspace writes persist between jobs. An unchanged checkout reuses remote
content. If a job's machine is lost mid-run, its automatic retry continues from
the files the interrupted attempt left in the workspace, like a Slurm requeue.
If another job or an upload changed the workspace after a job was queued, that
job fails with STORAGE_REVISION_CHANGED naming the writer; submit it again
with c3 deploy. If both a job and your local checkout have changed, the error gives a
c3 workspace fetch <name> <new-directory> command. Reconcile edits there before
redeploying. Prepared Python environments live in the container, so they do not
need to be fetched. For a legacy workspace cache, add --preserve-python-cache.
Idle workspaces count their stored bytes toward the runtime storage limit.
Active jobs reserve their allowed growth; imports reserve their upload bytes.
STORAGE_QUOTA_EXCEEDED reports the usage/reservations, requested growth and limit.
Free unused data or wait for active writes to finish before retrying.
Existing datasets: version mounts are not automatically converted into mutable
resources. Use c3 data resource create, c3 data resource upload, and
--data-resource name=id[:ro], or select --storage=legacy for the old workflow.
CPU and other offerings without the persistent runtime also require an explicit
compatible storage choice. Unavailable new-runtime capacity does not silently
switch your job to another execution or storage model.