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
FlagDescription
-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 it

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

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

CodeMeaning
0No differences.
1Differences found — not an error.
2Usage error.
3The 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}'
FlagDescription
-n, --namespace <ns>Namespace.
-A, --all-namespacesList across all namespaces.
-o, --output <format>table (default), yaml, json, jsonpath='{.path}'.
-w, --watchStream changes as they happen.
--revealReveal 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        1m4s

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

FormExample
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
FlagDescription
-n, --namespace <ns>Namespace.
-A, --all-namespacesDescribe 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 declares

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

FlagDescription
-f, --filename <file>Manifest file, http(s):// URL, or - for stdin: delete every resource it declares.
-y, --yesSkip 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
FlagDescription
-n, --namespace <ns>Namespace.
--revealEdit 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
FlagDescription
-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}'=False

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

Under --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.
FlagDescription
--for <condition>Condition to wait for (default Ready).
--timeout <duration>How long to wait (default 5m; 0 = no timeout).
--allWait on all resources of the kind in the namespace.
-A, --all-namespacesWait across all namespaces.
-n, --namespace <ns>Namespace.
--as <subject>Impersonate a subject.
--home <dir>Data directory.