CLI reference
Faithful to coreutils in shape, unfaithful in scope — and never misleading about the difference.
Available now
| Command | What it does |
|---|---|
one nodes | Inventory: name, datacenter, rack, state, cores, load, memory. |
one top | The cluster as one host, then the per-node breakdown. |
one free | Cluster memory, plus the largest single node. |
one df | Cluster storage, plus the largest single node. |
one ps | What this node is running, with the mobility column. |
one artifact … | The content-addressed store: ingest, ls, show, materialize, scrub, gc. |
one run / stop / start | Declare a workload, take it down, bring it back. --cluster declares it for the fleet; --secret delivers an operator secret to it as a credential. |
one scale / exec | Change an instance count; run a one-off command in a workload’s slice, limits and identity — as the job’s user, never as the agent. |
one logs <job> | Its journal from this node, or --all for every node holding an allocation. |
one alias … | set, rm, ls. Repointing an alias is the rollback primitive. |
one node … | drain, undrain, cordons, upgrade, remove. |
one metrics | This node in the Prometheus text format, over the agent socket. No port. |
one secret … | put, ls, rm. Sealed in the cluster, delivered as a systemd credential. No get. |
one backup / restore | The three unreconstructable things, sealed to an operator key. --inspect needs no key. |
one bootstrap / token / join | Create the trust material and the founder's keyring; mint a single-use join token for one node and one role (--role control|worker, default worker); redeem one, which writes the keyring for that role. |
one snapshot | The replicated state as this node holds it, for reading. Not a backup. |
one regions | Regions heard from, and where a request would go (--route). |
one cluster … | plan and create: order the machines and form the cluster from a laptop. |
one datastore … | plan, apply, endpoints for SoliDB, SoliES and SoliKV. |
one import | Read soli-proxy’s state and show what the cluster would see. Writes nothing. |
one reload | Re-read desired state next pass; --force lifts the mass-deletion guard for one pass. |
--host <node> runs any of them on a cluster node over SSH, so
the cluster reads as one machine from a laptop. There is no control port: the
agent’s only interface is a unix socket whose permissions are its
authentication, and SSH is already on every one of these machines.
Every command takes -o json. The output carries a
"schema": "one/v1" field from the first commit, because
-o json gets scripted and an unversioned format becomes an
accidental public API the moment somebody writes a monitoring check on it.
one top
$ one top
VIRTUAL HOST 3 nodes up / 3
CPU 48 cores load 1.32 3.66 5.40 2%
MEM 82G total 42G used 39G available
DISK 294G total 27G free
NAME DC RACK STATE CPU LOAD MEM
rk-01 par1 r1 alive 16 0.44 14G/27G
rk-02 par1 r2 alive 16 0.44 14G/27G
rk-03 par1 r3 alive 16 0.44 14G/27G
3 nodes 3 alive 0 not alive
Degrading honestly
When a node dies, its capacity leaves the totals immediately. It is still listed — an operator most wants to see a node at the moment it stops answering — but it stops counting as something you could schedule onto.
$ one top
VIRTUAL HOST 2 nodes up / 3
CPU 32 cores load 1.42 63.74 70.22 4%
MEM 54G total 25G used 30G available
NAME DC RACK STATE CPU LOAD MEM
rk-01 par1 r1 alive 16 0.71 12G/27G
rk-02 par1 r2 alive 16 0.71 12G/27G
rk-03 par1 r3 down 16 0.97 12G/27G
3 nodes 2 alive 1 not alive
! 1/3 nodes not alive — results are incomplete
$ echo $?
3
Exit code 3 means partial. A monitoring check that cannot tell "everything is healthy" from "I could not see a third of the fleet" is worse than no check at all.
Facts also expire. A node that stops reporting keeps its row but drops its numbers after thirty seconds, rather than showing hour-old memory as current.
one free — the last line is the point
$ one free
total used available
Mem: 82G 45G 37G
Largest single node can allocate: 12G (rk-03)
Summing available memory across the fleet is arithmetically true and useless for placement: you cannot put a 400 GiB workload into two hundred nodes with 10 GiB each. Printing both numbers is what separates a useful illusion from a lie, and it heads off a whole category of confused bug report.
one ps
$ one ps
JOB SLOT STATE MOBILITY RESTARTS AGE
acme.soli.app single running mobile 0 4m
billing-worker single running pinned 0 4m
solidb single running replicated 0 4m
3 allocations on rk-01 3 running
The MOBILITY column is the point of the table. It
is the one fact that decides what happens when this machine dies:
mobile is rescheduled, pinned waits for the node to
come back, replicated fails over on its own. A table that hides
it is how an operator discovers at 3am that their database was never going to
move.
The JOB column shows the id the operator typed. The slug that
ends up inside the systemd unit name is an implementation detail nobody
should have to recognise — -o json carries the full unit for
when you do need to paste it into systemctl.
Degrading honestly, again
Break a workload file and the running jobs are untouched, but the view says so:
$ echo '[[job]' >> /etc/soli-one/workloads.d/apps.toml
$ one ps
acme.soli.app single running mobile 0 4m
billing-worker single running pinned 0 4m
solidb single running replicated 0 4m
3 allocations on rk-01 3 running
! desired state is stale: apps.toml: TOML parse error at line 21, column 7
$ echo $?
3
A typo must never stop production, so each file keeps its last good content. But serving stale desired state as if it were current would be worse than the typo, so the view is marked degraded and the exit code says 3. The same applies to an orphan — a unit in our namespace the agent cannot account for, which is reported and never killed.
one artifact
The store is node-local, so these talk to disk directly and need no running agent — which is exactly the situation in which you most want them.
one artifact ingest <path> # chunk a tree in, print its id
one artifact ls # what this node holds
one artifact show <id> # entries, modes, chunk counts
one artifact materialize <id> <dest> # write it back out as a tree
one artifact scrub # re-hash everything, quarantine the bad
one artifact gc --keep <id> [--dry-run]
materialize reports how each file got there. If nothing was
reflinked it says so, because a store on a filesystem without
FICLONE silently costs a full copy per deployment and this is
the only place that surfaces.
scrub exits 1 when it finds corruption, so a timer driving it
does not have to parse stdout.
What this section used to say
This was a list of ten unimplemented commands. All ten exist, and so does
everything added since: one metrics, one secret,
one backup, one restore and
one node upgrade. The table above is generated from nothing —
it is maintained by hand — so scripts/check_docs_sync.sh fails
the build if any page on this site calls a shipped command unbuilt. That
check exists because this page was wrong for months in the direction nobody
checks: it undersold the thing it documents.
one snapshot reads the local state machine, so it costs no quorum
round trip and works on a node that has lost its leader — which also
means it can be behind, so the revision and the deterministic clock are
reported beside the data. A snapshot with no revision next to it cannot be
told from a current one.
Its values are opaque bytes to the store, and it renders one as text only when
it reads as text. Valid UTF-8 is not the test: a postcard frame often
is valid UTF-8, and one rendered as a string came out as
\u0006websvc\u0004demo — which reads as corruption to
someone looking at a snapshot to find out whether something is corrupt.
Two distinctions the design already fixes. one run submits work to
the cluster and lets the scheduler place it; one exec runs a command
inside something already running. And there is no one shell giving a
prompt "on the cluster" — that would promise something meaningless.
one exec is a root-equivalent remote execution API on every machine in
the fleet. Operator identity and a per-invocation audit record ship with it, not
after it.
Since this page was written
| Command | What it does |
|---|---|
one run --name X -- cmd | Declares a workload. Writes a declaration under the data directory; the reconcile loop starts it. --image makes it a container, --schedule a batch job. With --cluster, placement reserves --memory-mb and --disk-mb on the chosen node, above its free-disk floor. |
one exec <job> -- cmd | Runs a one-off command in the job's slice, so it inherits the job's limits rather than escaping them. Output goes to the journal. |
one reload --force | Lets the next reconcile pass ignore the mass-deletion guard. One pass only. |
one import | Reads soli-proxy's state read-only and shows what the cluster would make of it. Exits 3 on a degraded view. |
The reconcile loop is the only thing that starts a workload. A CLI that started one directly would produce something the loop does not know about, and the very next pass would stop it as an orphan.
/etc/soli-one/workloads.d belongs to the operator — the
agent's unit is ProtectSystem=strict and cannot write there
even by mistake. one run writes under
/var/lib/soli-one instead. Without the split, a CLI
invocation would silently overwrite a file configuration management
maintains, and the next config run would silently overwrite that. A job id
declared in both is reported, not resolved by whoever wrote last.