Resource verbs
The verbs every resource kind carries: get, describe, create, edit, copy, delete, and wait, plus the cross-cutting apply.
Every resource kind is a command group named after the
kind, and each group carries the same set of generic verbs: rl <kind> <verb>. So
you list workstations with rl workstation get, inspect one with
rl workstation describe, and remove it with rl workstation delete. The verbs
mirror kubectl: you describe state in YAML and apply it, then get/describe
to observe and wait to synchronize.
Most kinds have a short alias you can use in place of the full name, e.g. ws
(Workstation), wsc (WorkstationConfig), lb (LocalBinding), ns (Namespace),
ci (CloudIdentity), ca (CloudAccount), od (OrgDomain). rl ws get is the same
command as rl workstation get.
rl apply -f <file>
Parse a (multi-document) YAML manifest and create or update each resource. This is
the one cross-cutting verb — it is not tied to a single kind. -f takes a local
file, an http:// or https:// URL, or - for stdin.
rl apply -f workstation.yaml
rl apply -f https://example.com/manifests/dev-box.yaml # fetched over HTTP
cat manifest.yaml | rl apply -f -
rl apply -f manifest.yaml -o yaml # echo the written objects| Flag | Description |
|---|---|
-f, --filename <file> | Manifest file, http(s):// URL, or - for stdin. Required. |
-o, --output <format> | Echo written objects: yaml, json, or jsonpath='{.path}'. |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
A URL is fetched and handed to the same pipeline a local file is, so applying a
manifest someone shared by link needs no curl … | rl apply -f - workaround. The
fetch is bounded: it times out, and an oversized body is an error rather than a
silent truncation. rl delete -f <url> works the same way.
Apply is declarative and safe to re-run: applying the same file again brings
the resources to match, reporting unchanged where nothing differs.
Applying over a field a command owns
Some spec fields are written by a command rather than by you: rl workstation stop
sets spec.stopped, rl workstation restart sets spec.restartNonce,
rl binding enable/disable sets spec.enabled, and so on. Apply is
last-apply-wins, so a manifest captured before one of those commands ran will quietly
undo it — starting a workstation you stopped, or triggering a real power cycle.
rl apply -f warns before it does, naming the field, both values, and the command
that owns it:
warning: workstation/my-box: the live workstation has spec.stopped=true (set by
rl workstation start/stop) but your manifest does not set it — applying will clear itThe warning is not fatal — the apply proceeds. Use rl diff -f
first if you want to see the whole change before committing to it.
rl diff -f <file>
Preview what rl apply -f <file> would change, without changing anything. Each
document in the manifest is compared against the object the apply would actually land
on, using the same routing apply uses — so a workstation pinned to a cloud provider is
compared with the copy at the control plane, not a stale local one.
rl diff -f workstation.yaml
rl diff -f manifest.yaml -o json
cat manifest.yaml | rl diff -f -An object that would be created shows as an all-new document; one that already exists shows a unified diff of what the apply would replace. Fields an apply cannot change are left out: status, ownership and finalizer bookkeeping, the server-assigned identity fields, and the annotations the server derives on every write.
Secrets are compared by key name only — which keys the apply adds, removes, or writes. No secret value, and nothing derived from one, is ever printed in any output mode.
| Flag | Description |
|---|---|
-f, --filename <file> | Manifest file, http(s):// URL, or - for stdin. Required. |
-o, --output <format> | text (default), json, or yaml. |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
Exit codes differ from the rest of the CLI, so rl diff can drive a script:
| Code | Meaning |
|---|---|
0 | No differences. |
1 | Differences found — not an error. |
2 | Usage error. |
3 | The preview could not be produced (unreadable manifest, unreachable origin, invalid document). |
rl <kind> get [name]
List resources of a kind, or get one by name. Defaults to a table; supports YAML,
JSON, and JSONPath, and can watch for changes. list is an alias for get.
rl workstation get
rl workstation get my-box -o yaml
rl ws get -A # all namespaces
rl workstation get -w # watch
rl secret get db -o jsonpath='{.metadata.name}'| Flag | Description |
|---|---|
-n, --namespace <ns> | Namespace. |
-A, --all-namespaces | List across all namespaces. |
-o, --output <format> | table (default), yaml, json, jsonpath='{.path}'. |
-w, --watch | Stream changes as they happen. |
--reveal | Reveal a Secret’s values (requires the access right). |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
-w streams changes as they happen. Watching workstations additionally gives
each row a STATUS and an AGE — the same status label the table and the TUI show —
so you can watch a workstation advance to Ready (or settle into Stopped) live:
$ rl workstation get -w
EVENT NAMESPACE NAME STATUS AGE
ADDED local my-box Configuring 32s
MODIFIED local my-box Ready 1m4sOther kinds stream EVENT, NAMESPACE, NAME, and GEN.
JSONPath
-o jsonpath= evaluates kubectl’s JSONPath grammar. An expression copied from
the kubectl documentation behaves here as it does there — including its limits.
# A field, plus a trailing newline literal.
rl workstation get my-box -o jsonpath='{.spec.stopped}{"\n"}'
# Filter a conditions list — the idiom a readiness model invites.
rl ws get my-box -o jsonpath='{.status.conditions[?(@.type=="Configured")].status}'
# Each attached config, one per line.
rl ws get my-box -o jsonpath='{range .spec.configs[*]}{.name}{"\n"}{end}'What is available:
| Form | Example |
|---|---|
| Field / nested field | {.metadata.name} |
| Index, negative index | {.spec.configs[0].name}, {.spec.configs[-1].name} |
| Wildcard | {.spec.packages[*]} |
| Filter | {.status.conditions[?(@.type=="Ready")].reason} |
| Existence filter | {.status.conditions[?(@.reason)].type} |
| Range | {range .spec.configs[*]}{.name},{end} |
| Recursive descent | {..name} |
| Text literals | {"name="}{.metadata.name}{"\n"} |
| Bracket keys | {.metadata.labels['tier']} |
| Whole object | {$} |
Filters compare with ==, !=, <, >, <=, >=.
Mind the shape of what you are walking: a {.name} inside a {range} needs the
list to hold objects. Ringleader accepts packages as either bare strings or
{name, version} entries, so {range .spec.packages[*]}{.name}{"\n"}{end} prints
blank lines against a list of bare names — walk {.spec.packages[*]} directly
instead. A path that does not match renders empty rather than complaining.
Over a list, the template is evaluated once per object and each result gets its own line. A missing key renders empty rather than erroring; a malformed template is an error.
Three traps worth knowing
&&and||do not work in a filter. They belong to label selectors, a different grammar. Write one comparison per filter.- A numeric comparison needs a decimal literal:
[?(@.seq>1.0)]works,[?(@.seq>1)]errors. - A key containing a dot must escape it.
{.metadata.annotations['ringleader\.dev/subject']}resolves; the unescaped form silently means a nested path and renders empty. Bracket keys are single-quoted only.
rl <kind> describe [name]
Print a human-friendly summary of one or more resources: metadata, phase and message, conditions, and spec. Good for a quick “why isn’t this Ready?” glance.
rl workstation describe my-box
rl ws describe -A # every workstation you can see| Flag | Description |
|---|---|
-n, --namespace <ns> | Namespace. |
-A, --all-namespaces | Describe across all namespaces. |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
rl <kind> delete <name>
Delete a resource by name, or every resource declared in a manifest with -f.
rl workstation delete my-box
rl delete -f manifest.yaml # delete everything the file declaresdelete -f is available without naming a kind, since the manifest already declares
each object’s kind.
Deleting a Workstation or a Secret asks you to confirm first, with a short summary of what is about to be destroyed — for a workstation, its provider, its current status, and whether it is a billed cloud VM:
About to delete workstation dev/my-box
provider: gcp (billed cloud VM)
status: Ready
Deletion is final: the workstation, its disk, and any state on it are gone,
and anything not pushed elsewhere cannot be recovered.
Delete workstation/my-box? [y/N]Only y or yes proceeds; anything else — including a bare Enter — cancels. Pass
-y to skip the question. The prompt appears only at a real terminal: when
either input or output is redirected, the delete proceeds exactly as if -y had been
given, so scripts, pipelines, and cleanup traps behave as they always have.
| Flag | Description |
|---|---|
-f, --filename <file> | Manifest file, http(s):// URL, or - for stdin: delete every resource it declares. |
-y, --yes | Skip the confirmation prompt when deleting a Workstation or Secret. |
-n, --namespace <ns> | Namespace. |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
rl <kind> edit <name>
Fetch the object as YAML, open it in $VISUAL/$EDITOR, and re-apply on save.
rl workstation edit my-box
rl secret edit db --reveal # edit real Secret values in place| Flag | Description |
|---|---|
-n, --namespace <ns> | Namespace. |
--reveal | Edit a Secret’s real values (requires the access right). |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
rl <kind> copy <name> <new-name>
Copy a resource under a new name. The copy is a fresh, un-owned object (owner
references are dropped) routed like apply, preserving the source’s origin.
rl workstationconfig copy ide ide-experimental| Flag | Description |
|---|---|
-n, --namespace <ns> | Namespace. |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |
rl <kind> wait [name...]
Block until resources reach a desired state, like kubectl wait.
rl workstation wait my-box --for Ready --timeout 10m
rl workstation wait my-box --for Deleted
rl ws wait --all --for condition=Configured=True
rl ws wait my-box --for=jsonpath='{.status.phase}'=Running
rl ws wait my-box --for=jsonpath='{.status.conditions[?(@.type=="Ready")].status}'=FalseSupported: Ready, Deleted, a phase name (e.g. Running, Stopped),
condition=<Type>[=<Status>], and jsonpath='{<path>}'=<value> — the same
JSONPath grammar get -o jsonpath= evaluates. wait prints
condition met and exits 0 when satisfied, 1 on timeout.
Note
--for=jsonpath=, an expression that cannot evaluate reads as “not
satisfied yet”, so a permanently broken one (the integer-comparison trap above,
say) waits out the full timeout instead of failing fast. Try the same expression
under get -o jsonpath= first, where it errors immediately.| Flag | Description |
|---|---|
--for <condition> | Condition to wait for (default Ready). |
--timeout <duration> | How long to wait (default 5m; 0 = no timeout). |
--all | Wait on all resources of the kind in the namespace. |
-A, --all-namespaces | Wait across all namespaces. |
-n, --namespace <ns> | Namespace. |
--as <subject> | Impersonate a subject. |
--home <dir> | Data directory. |