ssctl
ssctl is the command-line client: a thin, kubectl-shaped door onto the
REST surface. Everything in a substrate is an entity of a declared
type, addressed as /{group}/{plural}/{id}, and the CLI mirrors that: types,
get, apply, patch, delete, edit, link, unlink, watch. It lives in the same
repository as the substrate (cmd/ssctl) and builds with Go.
ssctl login --server https://substrate.example --tenant ada
ssctl types # every installed entity type
ssctl get tasks # list a collection
ssctl get tasks t9 -o yaml # one entity, apply-able envelope
ssctl apply -f task.yaml # put: merge, never prune
ssctl patch tasks t9 --state status=done
ssctl watch # resumable change stream
Login and configuration
ssctl login exchanges the tenant's rotating 6-digit code for a bearer token
(authentication) and stores it in the
config file, which holds one context per substrate. Config lives at
~/.config/ssctl/config.yaml (override with SSCTL_CONFIG); SUBSTRATE_SERVER,
SUBSTRATE_TOKEN, and SUBSTRATE_TENANT override the file, and flags override
both. The older TEILD_ and SS_ variables are still accepted as aliases,
and the canonical SUBSTRATE_* variable wins if more than one is set. --actor
picks which of the token's actors a write is attributed to.
Reading
ssctl get <plural> [id] reads a collection or one entity. The plural may be
fully qualified (tasks.tasks.teild.dev) or bare (tasks), which resolves
against the type registry; when two installed groups both declare the same
plural, that plural needs qualifying, or -g to name the group (every
extension installs a config, so configs always needs one). Lists take
--filter (the JSON filter grammar), -l label
selectors, --order-by, and --limit; --after resends the opaque keyset
cursor a page printed, and -w streams that one collection's changes instead
of listing it.
-o yaml writes each entity as an envelope
document (group, type, metadata, data, and the server-set status),
----separated, and -o json writes the same shape. REST returns the entity
flat; the envelope is the CLI's YAML form. status is ignored on input, so the
output applies back unchanged.
Writing
apply -f FILEapplies YAML manifests (----separated;-reads stdin). A document withmetadata.idis put at that id; without one it creates. Apply isput: it merges and never prunes; deletion is only ever the explicitdeleteverb. Schema documents apply too (schema): they ride the batch schema verb as one transaction.patch <plural> <id>edits in place:--state status=donefor transitions (apply cannot move a state),--propfor properties,--labelfor labels, and-pfor a raw JSON patch, where a null value deletes a key.edit <plural> <id>opens the manifest in$EDITORand applies what comes back.delete <plural> <id>tombstones; hard deletion waits on finalizers.link <plural> <id> <rel> <target>/unlink <plural> <id> <rel> <target>add and remove one outgoing edge (the REST edge verbs, ruling A8). The target is a bareidwhere the edge declaration pins one target type, or the qualifiedtype.group:idfor ato: anyedge;linktakes--prop key=valuefor edge properties.
Watching, triggers, and bundles
ssctl watch streams the changelog, one line per committed
change, resumable with --from and filterable by --types, --actors, and
--ops.
Delivery bookkeeping lives on triggers, not on
functions, so it is the trigger subcommands that drive it: status shows
each trigger's kind, callable, cursor, lag, last fire, and parked count;
parked lists the deliveries it gave up on and retry re-runs one; replay
resets an entity-sourced trigger's cursor; run synthesizes a single delivery;
and wake scans a trigger immediately. Trigger rows are ordinary entities, so
get / apply / delete edit them like anything else.
ssctl function call <name> --input <json> invokes one
function directly, applies its effects under the function's
actor, and prints the output. There is no build step: a function is inline
source on its manifest, so it installs with the ordinary apply.
ssctl bundle list / status report an extension's computed
state, and disable / enable / uninstall / purge move it through its
lifecycle; install and upgrade are ssctl apply of the closure, and connect
starts the host OAuth flow for an account
entity, printing the consent URL. token create / list / delete manage
bearer tokens: create prints the new
secret exactly once and the substrate keeps only its hash, so list and
delete are metadata alone.
Next: the web console, the same substrate in a browser.