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 a fresh install to rendering a view in your terminal and importing a demo tenant.

For the full command reference, see the CLI client guide. For the platform’s first run-through, see the Quick Start.

  1. Install 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 demo tenant from YAML.
  • 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. During the beta, build it from the monorepo (below). Tagged releases will publish single-file binaries for Windows and Linux (csy-<version>-linux-x64, csy-<version>-win-x64.exe) plus a cross-platform .NET global tool (dotnet tool install --global Consystence.Cli, also invoked as csy) that covers macOS — the first packaged release has not shipped yet. For OS-specific steps, follow the installation guide.

To build from the monorepo:

Terminal window
# From the repository root
dotnet build backend/Cli/Consystence.Cli -c Release
# Run a command directly from the project
dotnet run --project backend/Cli/Consystence.Cli -- --help

Running csy with no arguments prints a branded splash; csy --help lists the command groups: account, ai, auth, config, context, controller, electrical, instance, org, provision, 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-PIN path (the LocallyIssuedJwt scheme), which needs an email and PIN:

Terminal window
csy auth login --email [email protected] --pin 837192

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. Keys are minted at POST /api/admin/api-keys (owner/admin), shown in plaintext exactly once, and prefixed csk_. See API keys.

The key is sent as the X-Api-Key header; the server’s MultiAuth selector routes it to the ApiKey scheme. 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 actor (keyed by sessionId) 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["UI session actor<br/>(keyed by sessionId)"]
  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 organisation; the org is part of the route and you must be its owner or admin.

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

Terminal window
csy tenant select acme
csy tenant current

Validate the file locally before touching the server — --dry-run parses and reports the contents with no server call:

Terminal window
csy tenant import tenant.yaml --dry-run
Parsed: org=acme, 2 site(s), 14 device(s)
Dry run: validation passed, no changes made.

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

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