# 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 <group> <action> [options]
```

### Template Commands
```
scadalink template list [--format json|table]
scadalink template get <name> [--format json|table]
scadalink template create --name <name> [--parent <parent>] --file <path>
scadalink template update <name> --file <path>
scadalink template delete <name>
scadalink template validate <name>
scadalink template diff <instance-code>
scadalink template attribute add --template-id <id> --name <name> --data-type <type> [--default-value <value>] [--tag-path <path>]
scadalink template attribute update --template-id <id> --name <name> [--data-type <type>] [--default-value <value>] [--tag-path <path>]
scadalink template attribute delete --template-id <id> --name <name>
scadalink template alarm add --template-id <id> --name <name> --trigger-attribute <attr> --condition <cond> --setpoint <value> [--severity <level>] [--notification-list <name>]
scadalink template alarm update --template-id <id> --name <name> [--condition <cond>] [--setpoint <value>] [--severity <level>] [--notification-list <name>]
scadalink template alarm delete --template-id <id> --name <name>
scadalink template script add --template-id <id> --name <name> --trigger-type <type> [--trigger-attribute <attr>] [--interval <ms>] --code <code>
scadalink template script update --template-id <id> --name <name> [--trigger-type <type>] [--trigger-attribute <attr>] [--interval <ms>] [--code <code>]
scadalink template script delete --template-id <id> --name <name>
scadalink template composition add --template-id <id> --module-template-id <id> --instance-name <name>
scadalink template composition delete --template-id <id> --instance-name <name>
```

### Instance Commands
```
scadalink instance list [--site <site>] [--area <area>] [--format json|table]
scadalink instance get <code> [--format json|table]
scadalink instance create --template <name> --site <site> --code <code> [--area <area>]
scadalink instance set-overrides <code> --file <path>
scadalink instance set-bindings <code> --bindings <json>
scadalink instance bind-connections <code> --file <path>
scadalink instance assign-area <code> --area <area>
scadalink instance enable <code>
scadalink instance disable <code>
scadalink instance delete <code>
```

### Site Commands
```
scadalink site list [--format json|table]
scadalink site get <site-id> [--format json|table]
scadalink site create --name <name> --id <site-id> [--node-a-address <addr>] [--node-b-address <addr>] [--grpc-node-a-address <addr>] [--grpc-node-b-address <addr>]
scadalink site update <site-id> --file <path>
scadalink site delete <site-id>
scadalink site area list <site-id>
scadalink site area create <site-id> --name <name> [--parent <parent-area>]
scadalink site area update <site-id> --name <name> [--new-name <name>] [--parent <parent-area>]
scadalink site area delete <site-id> --name <name>
```

### Deployment Commands
```
scadalink deploy instance <code>
scadalink deploy artifacts [--site <site>] [--type <artifact-type>]
scadalink deploy status [--format json|table]
```

### Data Connection Commands
```
scadalink data-connection list [--format json|table]
scadalink data-connection get <name> [--format json|table]
scadalink data-connection create --file <path>
scadalink data-connection update <name> --file <path>
scadalink data-connection delete <name>
scadalink data-connection assign <name> --site <site-id>
scadalink data-connection unassign <name> --site <site-id>
```

### External System Commands
```
scadalink external-system list [--format json|table]
scadalink external-system get <name> [--format json|table]
scadalink external-system create --file <path>
scadalink external-system update <name> --file <path>
scadalink external-system delete <name>
```

### Notification Commands
```
scadalink notification list [--format json|table]
scadalink notification get <name> [--format json|table]
scadalink notification create --file <path>
scadalink notification update <name> --file <path>
scadalink notification delete <name>
scadalink notification smtp list [--format json|table]
scadalink notification smtp update --file <path>
```

### Security Commands
```
scadalink security api-key list [--format json|table]
scadalink security api-key create --name <name>
scadalink security api-key update <name> [--name <new-name>] [--enabled <bool>]
scadalink security api-key enable <name>
scadalink security api-key disable <name>
scadalink security api-key delete <name>
scadalink security role-mapping list [--format json|table]
scadalink security role-mapping create --group <ldap-group> --role <role> [--site <site>]
scadalink security role-mapping update --id <id> [--group <ldap-group>] [--role <role>]
scadalink security role-mapping delete --group <ldap-group> --role <role>
scadalink security scope-rule list [--role-mapping-id <id>] [--format json|table]
scadalink security scope-rule add --role-mapping-id <id> --site-id <site-id>
scadalink security scope-rule delete --id <id>
```

### Audit Log Commands
```
scadalink audit-log query [--user <username>] [--entity-type <type>] [--from <date>] [--to <date>] [--format json|table]
```

### Health Commands
```
scadalink health summary [--format json|table]
scadalink health site <site-id> [--format json|table]
scadalink health event-log --site-identifier <site-id> [--from <date>] [--to <date>] [--search <term>] [--page <n>] [--page-size <n>] [--format json|table]
scadalink health parked-messages --site-identifier <site-id> [--page <n>] [--page-size <n>] [--format json|table]
```

### Debug Commands
```
scadalink debug snapshot --id <id> [--format json|table]
scadalink debug stream --id <instanceId> [--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 <id> [--format json|table]
scadalink shared-script create --name <name> --code <code>
scadalink shared-script update --id <id> [--name <name>] [--code <code>]
scadalink shared-script delete --id <id>
```

### Database Connection Commands
```
scadalink db-connection list [--format json|table]
scadalink db-connection get --id <id> [--format json|table]
scadalink db-connection create --name <name> --connection-string <string> [--provider <provider>]
scadalink db-connection update --id <id> [--name <name>] [--connection-string <string>] [--provider <provider>]
scadalink db-connection delete --id <id>
```

### Inbound API Method Commands
```
scadalink api-method list [--format json|table]
scadalink api-method get --id <id> [--format json|table]
scadalink api-method create --name <name> --code <code> [--description <desc>]
scadalink api-method update --id <id> [--name <name>] [--code <code>] [--description <desc>]
scadalink api-method delete --id <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.
