# Component: CLI ## Purpose The CLI is a standalone command-line tool for scripting and automating administrative operations against the ScadaLink central cluster. It connects to the Central Host's HTTP Management API (`POST /management`), which dispatches commands to the ManagementActor. Authentication and role resolution are handled server-side — the CLI sends credentials via HTTP Basic Auth. The CLI provides the same administrative capabilities as the Central UI, enabling automation, batch operations, and integration with CI/CD pipelines. ## Location Standalone executable, not part of the Host binary. Deployed on any machine with HTTP access to a central node. `src/ScadaLink.CLI/` ## Responsibilities - Parse command-line arguments and dispatch to the appropriate management operation. - Send HTTP requests to the Central Host's Management API endpoint with Basic Auth credentials. - Display structured responses from the Management API. - Support both JSON and human-readable table output formats. ## Technology - **Argument parsing**: `System.CommandLine` library for command/subcommand/option parsing with built-in help generation. - **Transport**: HTTP client connecting to the Central Host's `POST /management` endpoint. Authentication is via HTTP Basic Auth — the server performs LDAP bind and role resolution. - **Serialization**: Commands serialized as JSON with a type discriminator (`command` field). Message contracts from Commons define the command types. ## Authentication The CLI sends user credentials to the Management API via HTTP Basic Auth: 1. The user provides credentials via `--username` / `--password` options. 2. On each request, the CLI encodes credentials as a Basic Auth header and sends them with the command. 3. The server performs LDAP authentication, group lookup, and role resolution — the CLI does not communicate with LDAP directly. 4. Credentials are not stored or cached between invocations. Each CLI invocation requires fresh credentials. ## Connection The CLI connects to the Central Host via HTTP: - **Management URL**: The URL of a central node's web server (e.g., `http://localhost:9001`). The management API is served at `POST /management` on the same host as the Central UI. - **Failover**: For HA, use a load balancer URL in front of both central nodes. The management API is stateless (Basic Auth per request), so any central node can handle any request without sticky sessions. - **No Akka.NET dependency**: The CLI is a pure HTTP client with no Akka.NET runtime. ## Command Structure The CLI uses a hierarchical subcommand structure mirroring the Management Service message groups: ``` scadalink [options] ``` ### Template Commands ``` scadalink template list [--format json|table] scadalink template get [--format json|table] scadalink template create --name [--parent ] --file scadalink template update --file scadalink template delete scadalink template validate scadalink template diff scadalink template attribute add --template-id --name --data-type [--default-value ] [--tag-path ] scadalink template attribute update --template-id --name [--data-type ] [--default-value ] [--tag-path ] scadalink template attribute delete --template-id --name scadalink template alarm add --template-id --name --trigger-attribute --condition --setpoint [--severity ] [--notification-list ] scadalink template alarm update --template-id --name [--condition ] [--setpoint ] [--severity ] [--notification-list ] scadalink template alarm delete --template-id --name scadalink template script add --template-id --name --trigger-type [--trigger-attribute ] [--interval ] --code scadalink template script update --template-id --name [--trigger-type ] [--trigger-attribute ] [--interval ] [--code ] scadalink template script delete --template-id --name scadalink template composition add --template-id --module-template-id --instance-name scadalink template composition delete --template-id --instance-name ``` ### Instance Commands ``` scadalink instance list [--site ] [--area ] [--format json|table] scadalink instance get [--format json|table] scadalink instance create --template --site --code [--area ] scadalink instance set-overrides --file scadalink instance set-bindings --bindings scadalink instance bind-connections --file scadalink instance assign-area --area scadalink instance enable scadalink instance disable scadalink instance delete ``` ### Site Commands ``` scadalink site list [--format json|table] scadalink site get [--format json|table] scadalink site create --name --id scadalink site update --file scadalink site delete scadalink site area list scadalink site area create --name [--parent ] scadalink site area update --name [--new-name ] [--parent ] scadalink site area delete --name ``` ### Deployment Commands ``` scadalink deploy instance scadalink deploy artifacts [--site ] [--type ] scadalink deploy status [--format json|table] ``` ### Data Connection Commands ``` scadalink data-connection list [--format json|table] scadalink data-connection get [--format json|table] scadalink data-connection create --file scadalink data-connection update --file scadalink data-connection delete scadalink data-connection assign --site scadalink data-connection unassign --site ``` ### External System Commands ``` scadalink external-system list [--format json|table] scadalink external-system get [--format json|table] scadalink external-system create --file scadalink external-system update --file scadalink external-system delete ``` ### Notification Commands ``` scadalink notification list [--format json|table] scadalink notification get [--format json|table] scadalink notification create --file scadalink notification update --file scadalink notification delete scadalink notification smtp list [--format json|table] scadalink notification smtp update --file ``` ### Security Commands ``` scadalink security api-key list [--format json|table] scadalink security api-key create --name scadalink security api-key update [--name ] [--enabled ] scadalink security api-key enable scadalink security api-key disable scadalink security api-key delete scadalink security role-mapping list [--format json|table] scadalink security role-mapping create --group --role [--site ] scadalink security role-mapping update --id [--group ] [--role ] scadalink security role-mapping delete --group --role scadalink security scope-rule list [--role-mapping-id ] [--format json|table] scadalink security scope-rule add --role-mapping-id --site-id scadalink security scope-rule delete --id ``` ### Audit Log Commands ``` scadalink audit-log query [--user ] [--entity-type ] [--from ] [--to ] [--format json|table] ``` ### Health Commands ``` scadalink health summary [--format json|table] scadalink health site [--format json|table] scadalink health event-log --site-identifier [--from ] [--to ] [--search ] [--page ] [--page-size ] [--format json|table] scadalink health parked-messages --site-identifier [--page ] [--page-size ] [--format json|table] ``` ### Debug Commands ``` scadalink debug snapshot --id [--format json|table] scadalink debug stream --id [--url ...] [--username ...] [--password ...] ``` The `debug snapshot` command retrieves a point-in-time snapshot via the HTTP Management API. The `debug stream` command streams live attribute values and alarm state changes in real-time using a SignalR WebSocket connection. The CLI connects to the `/hubs/debug-stream` SignalR hub on the central server, authenticates with Basic Auth, and subscribes to the specified instance. Events are printed as they arrive — JSON format (default) outputs one NDJSON object per event; table format shows streaming rows. Press Ctrl+C to disconnect. Key behaviors: - **Automatic reconnection**: Uses SignalR's `.WithAutomaticReconnect()` to re-establish the connection on loss. - **Re-subscription**: Automatically re-subscribes to the instance after reconnection. - **Traefik compatible**: Works through the Traefik reverse proxy — WebSocket upgrade is proxied natively. - **Required role**: `Deployment`. Unlike `debug snapshot` (which uses the HTTP Management API), `debug stream` uses `Microsoft.AspNetCore.SignalR.Client` as a dependency for its WebSocket transport. ### Shared Script Commands ``` scadalink shared-script list [--format json|table] scadalink shared-script get --id [--format json|table] scadalink shared-script create --name --code scadalink shared-script update --id [--name ] [--code ] scadalink shared-script delete --id ``` ### Database Connection Commands ``` scadalink db-connection list [--format json|table] scadalink db-connection get --id [--format json|table] scadalink db-connection create --name --connection-string [--provider ] scadalink db-connection update --id [--name ] [--connection-string ] [--provider ] scadalink db-connection delete --id ``` ### Inbound API Method Commands ``` scadalink api-method list [--format json|table] scadalink api-method get --id [--format json|table] scadalink api-method create --name --code [--description ] scadalink api-method update --id [--name ] [--code ] [--description ] scadalink api-method delete --id ``` ## Configuration Configuration is resolved in the following priority order (highest wins): 1. **Command-line options**: `--url`, `--username`, `--password`, `--format`. 2. **Environment variables**: - `SCADALINK_MANAGEMENT_URL` — Management API URL (e.g., `http://central-host:5000`). - `SCADALINK_FORMAT` — Default output format (`json` or `table`). 3. **Configuration file**: `~/.scadalink/config.json` — Persistent defaults for management URL and output format. ### Configuration File Format ```json { "managementUrl": "http://central-host:5000" } ``` ## Output Formats - **JSON** (default): Machine-readable JSON output to stdout. Suitable for piping to `jq` or processing in scripts. Errors are written to stderr as JSON objects with `error` and `code` fields. - **Table** (`--format table` or `--table`): Human-readable tabular output with aligned columns. Suitable for interactive use. ## Exit Codes | Code | Meaning | |------|---------| | 0 | Success | | 1 | General error (command failed, connection failure, or authentication failure) | | 2 | Authorization failure (insufficient role) | ## Error Handling - **Connection failure**: If the CLI cannot connect to the management URL (e.g., DNS failure, connection refused), it exits with code 1 and a descriptive error message. - **Command timeout**: If the server does not respond within 30 seconds, the command fails with a timeout error (HTTP 504). - **Authentication failure**: If the server returns HTTP 401 (LDAP bind failed), the CLI exits with code 1. - **Authorization failure**: If the server returns HTTP 403, the CLI exits with code 2. ## Dependencies - **Commons**: Message contracts (`Messages/Management/`) for command type definitions and registry. - **System.CommandLine**: Command-line argument parsing. - **Microsoft.AspNetCore.SignalR.Client**: SignalR client for the `debug stream` command's WebSocket connection. ## Interactions - **Management Service (via HTTP)**: The primary runtime dependency. All operations except `debug stream` are sent as HTTP POST requests to the Management API endpoint on a central node, which dispatches to the ManagementActor. - **Central Host**: Serves the Management API at `POST /management` and the debug stream SignalR hub at `/hubs/debug-stream`. Handles LDAP authentication, role resolution, and ManagementActor dispatch. - **Debug Stream Hub (via SignalR WebSocket)**: The `debug stream` command connects to the `/hubs/debug-stream` hub on the central server for real-time event streaming. This is the only CLI command that uses a persistent connection rather than request/response.