Skip to content

CLI Quick Start

The csy command-line interface is a first-class, supported client — not a developer scaffold. It authenticates against the same schemes the browser uses, drives the same server-side UI session the browser drives, and runs declarative scenarios for CI. This page walks you from CLI access to rendering a view in your terminal and importing a tenant file.

For the full command reference, see the CLI client guide. For a first run-through in the free sandbox, see the Quick Start.

  1. Get the CLI and point it at an endpoint.
  2. Authenticate — interactively (OIDC) or with an API key for automation.
  3. Open csy ui and render a view in the terminal.
  4. Import a tenant configuration from YAML.
  • CLI access, arranged through a beta engagement (below).
  • A Consystence endpoint you can reach — a cloud cell or a site server.
  • Credentials for that endpoint: a member account (interactive login) or an API key (automation).

The CLI is a single .NET 10 binary named csy. CLI access is arranged through a beta engagement: contact us.

Running csy with no arguments prints a branded splash; csy --help lists the command groups, among them account, ai, api-key, auth, config, context, controller, display, electrical, instance, org, sequence, server, site, status, tenant, trend-import, and ui. Live operational reads — current tag values, historian trends, active alarms, the command audit — live under the instance and site groups; see the CLI reference.

The CLI works in contexts. A context is one endpoint — its server URL, its type (cell, site, edge, or account), an optional tenant slug, and its own stored credential. The auth-mode is derived from the type, so you never pick a login flow by hand.

Terminal window
# Define a context for a cloud cell
csy context add demo \
--server https://acme.consystence.com \
--type cell \
--tenant acme
# Make it current
csy context use demo
# Inspect what is defined
csy context list
csy context current

For a cell context, csy auth login runs OIDC against Entra External ID and stores a Bearer token. With a browser present it uses an authorization-code + PKCE loopback flow; for headless hosts add --device for the OIDC device-authorization grant (RFC 8628).

Terminal window
# Headless / SSH — enter the code at the displayed URL
csy auth login --device
Initiating device code flow...
Enter code: ABCD-EFGH
At: https://microsoft.com/devicelogin
Waiting for authentication...
Login successful (context 'demo').
Token expires at: 2026-06-29 11:42:00Z

The verification URL is taken verbatim from the identity provider’s device-authorization response — for the Consystence CIAM tenant (Entra External ID) that is Microsoft’s device-login page, not a consystence.com address.

The refresh token is stored in your OS keyring (Windows Credential Manager / macOS Keychain / libsecret), with a plaintext-file fallback only where no keyring exists. The short-lived access token is refreshed automatically against the identity provider — no re-login.

For a site context the same command takes the site sign-in path, which needs an email and PIN:

Terminal window
csy auth login --email [email protected] --pin <site-pin>

Check and decode your session at any time:

Terminal window
csy auth status # mechanism + credential source (no server call; never prints the credential)
csy auth whoami # decode the stored token locally (oid, tid, preferred_username)
csy auth logout # clear this context's credential, keep the context defined

For CI, scripts, and partner automation, use a tenant API key. A tenant owner or admin creates keys in the web console’s API keys view, with csy api-key create, or at POST /api/admin/api-keys; each key is shown in plaintext exactly once and prefixed csk_. A key is read-only by default: it reads tenant and site data, its role decides which reads it may make, and every write is refused. An operator key (--class operator) covers one person and one native site for at most 30 days, and can issue commands and acknowledge alarms on that site. See API keys.

The key is sent as the X-Api-Key header. Provide it three ways, in precedence order:

Terminal window
# 1. Explicit flag (wins over everything — an operator overriding CI)
csy --token csk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx auth status
# 2. Environment variable (typical for CI)
export CONSYSTENCE_TOKEN=csk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
csy auth status
# 3. Stored in the context config

An API key is the credential itself — there is no refresh, and it is never printed back by auth status.

csy ui is the CLI renderer: it consumes the server’s semantic UI tree over the wire format — a peer of the browser’s wire renderer, which consumes the same L1/L2 protocol on the same /hubs/ui-wire hub — so a view renders as text instead of DOM. It connects over SignalR to /hubs/ui-wire with the credential the CLI already resolved and drives the same server-side UI session the browser drives. For the architecture, see the multi-renderer pipeline.

flowchart LR
  CLI["csy ui<br/>(terminal)"] -->|"SignalR /hubs/ui-wire"| Hub["UI wire hub"]
  Hub --> Session["Server-side<br/>UI session"]
  Session -->|"wire format (L1 render, L2 patch)"| Hub
  Hub -->|"text patches"| CLI

Open the REPL, optionally with an initial route:

Terminal window
csy ui admin/api-keys

Inside the REPL these verbs are available (type help to list them):

CommandAction
goto <route>Open a view (e.g. goto admin/api-keys)
showRe-render the current view
inspectDump the semantic tree (ids, pointIds, types)
actionsList invocable actions on the current view
action <n|id>Invoke an action by index or actionId
chat <message>Message the assistant (needs a chat view, e.g. goto assistant/chat)
refreshPoll the server for changes
wait [seconds]Watch for changes (default 30 s)
wait until <p>Block until a predicate holds: rows <pointId> <n> | row-absent <id> | row-present <id> | view <id>
quit / exitLeave the REPL

For CI, run a view non-interactively from a declarative YAML scenario instead of the REPL:

Terminal window
csy ui --script scenario.csy.yaml --no-color

Script mode is single-shot and deterministic, and exits with a meaningful code: 0 = all steps passed, 1 = a step failed, 2 = a scenario or connect error. This is the surface that replaces browser click-throughs for integration testing.

A tenant configuration (org, sites, devices, tags) is portable as a single YAML file. Importing it targets an existing tenant; the tenant’s slug is part of the route (the --org flag) and you must be its owner or admin. To get a file to start from, export one from a site you can reach (csy tenant export, at the end of this section), edit it, and import it back.

First select the tenant (or pass --org on each call):

Terminal window
csy tenant select acme
csy tenant current

Check the file before importing it. --dry-run sends it to the server, which parses, validates and diffs it against the current model (plant-affecting changes first) and changes nothing. The plan it prints replaces any earlier plan for the tenant that was never applied. With no tenant selected, or against a site server or an older server, the CLI validates the file locally only and says so.

Terminal window
csy tenant import tenant.yaml --dry-run
Parsed: org=acme, 2 site(s), 14 device(s)
Server dry run: plan <plan-id> (import) for tenant acme, site(s) SITE-01, SITE-02. Nothing was changed.
<n> plant-affecting, <n> access, <n> policy, <n> model-only, <n> presentation, <n> declared but not applied.
…
To apply exactly this plan (before <expiry> UTC, and only while the model is unchanged):
csy tenant import-apply <plan-id> --org acme --confirm "<phrase>"
To discard it instead: csy tenant import-discard <plan-id> --org acme

A plan with blockers exits non-zero, so csy tenant import tenant.yaml --dry-run && csy tenant import tenant.yaml stops at a blocker.

Then import for real (use --force to overwrite existing entities, and --org to override the selected tenant):

Terminal window
csy tenant import tenant.yaml --org acme
Parsed: org=acme, 2 site(s), 14 device(s)
Import successful.

The server rejects a file whose org.slug does not match the route, so a mis-targeted import fails cleanly rather than writing to the wrong tenant. To produce a YAML file from a live site, use the inverse:

Terminal window
csy tenant export --site SITE-01 --org acme --output tenant.yaml