Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
18 KiB
Component: CLI
Purpose
The CLI is a standalone command-line tool for scripting and automating administrative operations against the ScadaBridge 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/ZB.MOM.WW.ScadaBridge.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.CommandLinelibrary for command/subcommand/option parsing with built-in help generation. - Transport: HTTP client connecting to the Central Host's
POST /managementendpoint. Authentication is via HTTP Basic Auth — the server performs LDAP bind and role resolution. - Serialization: Commands serialized as JSON with a type discriminator (
commandfield). Message contracts from Commons define the command types.
Authentication
The CLI sends user credentials to the Management API via HTTP Basic Auth:
- The user provides credentials via
--username/--passwordoptions. - On each request, the CLI encodes credentials as a Basic Auth header and sends them with the command.
- The server performs LDAP authentication, group lookup, and role resolution — the CLI does not communicate with LDAP directly.
- 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 atPOST /managementon 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:
scadabridge <group> <action> [options]
All entities are identified by their integer ID (via --id, --template-id,
--site-id, etc.), not by name. Create/update commands take individual flags — there
is no --file option. The authoritative, always-current reference is the in-repo
src/ZB.MOM.WW.ScadaBridge.CLI/README.md; the command lists below mirror the implemented command
tree at the time of writing.
Template Commands
scadabridge template list
scadabridge template get --id <id>
scadabridge template create --name <name> [--description <desc>] [--parent-id <id>]
scadabridge template update --id <id> [--name <name>] [--description <desc>] [--parent-id <id>]
scadabridge template validate --id <id>
scadabridge template delete --id <id>
scadabridge template attribute add --template-id <id> --name <name> --data-type <type> [--value <value>] [--description <desc>] [--data-source <ref>] [--locked <bool>]
scadabridge template attribute update --id <id> [--name <name>] [--data-type <type>] [--value <value>] [--description <desc>] [--data-source <ref>] [--locked <bool>]
scadabridge template attribute delete --id <id>
scadabridge template alarm add --template-id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked <bool>]
scadabridge template alarm update --id <id> [--name <name>] [--trigger-type <type>] [--priority <n>] [--description <desc>] [--trigger-config <json>] [--locked <bool>]
scadabridge template alarm delete --id <id>
scadabridge template script add --template-id <id> --name <name> --code <code> --trigger-type <type> [--trigger-config <json>] [--locked <bool>] [--parameters <json>] [--return-def <json>]
scadabridge template script update --id <id> [--name <name>] [--code <code>] [--trigger-type <type>] [--trigger-config <json>] [--locked <bool>] [--parameters <json>] [--return-def <json>]
scadabridge template script delete --id <id>
scadabridge template composition add --template-id <id> --instance-name <name> --composed-template-id <id>
scadabridge template composition delete --template-id <id> --instance-name <name>
Instance Commands
scadabridge instance list [--site-id <id>] [--template-id <id>] [--search <term>]
scadabridge instance get --id <id>
scadabridge instance create --name <name> --template-id <id> --site-id <id> [--area-id <id>]
scadabridge instance set-bindings --id <id> --bindings <json>
scadabridge instance set-overrides --id <id> --overrides <json>
scadabridge instance alarm-override set --instance-id <id> --alarm <name> [--trigger-config <json>] [--priority <n>]
scadabridge instance alarm-override delete --instance-id <id> --alarm <name>
scadabridge instance alarm-override list --instance-id <id>
scadabridge instance set-area --id <id> [--area-id <id>]
scadabridge instance diff --id <id>
scadabridge instance deploy --id <id>
scadabridge instance enable --id <id>
scadabridge instance disable --id <id>
scadabridge instance delete --id <id>
--bindings is a JSON array of [attributeName, dataConnectionId] pairs, e.g.
[["Speed", 5], ["Mode", 7]]. --overrides is a JSON object of attribute name to
value, e.g. {"Speed": "100", "Mode": null}.
Site Commands
scadabridge site list
scadabridge site get --id <id>
scadabridge site create --identifier <id> --name <name> [--description <desc>] [--node-a-address <addr>] [--node-b-address <addr>] [--grpc-node-a-address <addr>] [--grpc-node-b-address <addr>]
scadabridge site update --id <id> [--name <name>] [--description <desc>] [--node-a-address <addr>] [--node-b-address <addr>] [--grpc-node-a-address <addr>] [--grpc-node-b-address <addr>]
scadabridge site delete --id <id>
scadabridge site area list --site-id <id>
scadabridge site area create --site-id <id> --name <name> [--parent-id <id>]
scadabridge site area update --id <id> --name <name>
scadabridge site area delete --id <id>
scadabridge site deploy-artifacts [--site-id <id>]
Deployment Commands
scadabridge deploy instance --id <id>
scadabridge deploy artifacts [--site-id <id>]
scadabridge deploy status [--instance-id <id>] [--status <status>] [--page <n>] [--page-size <n>]
Data Connection Commands
scadabridge data-connection list [--site-id <id>]
scadabridge data-connection get --id <id>
scadabridge data-connection create --site-id <id> --name <name> --protocol <protocol> [--backup-config <json>] [--failover-retry-count <n>]
scadabridge data-connection update --id <id> [--name <name>] [--protocol <protocol>] [--backup-config <json>] [--failover-retry-count <n>]
scadabridge data-connection delete --id <id>
External System Commands
scadabridge external-system list
scadabridge external-system get --id <id>
scadabridge external-system create --name <name> --endpoint-url <url> --auth-type <type> [--auth-config <json>]
scadabridge external-system update --id <id> [--name <name>] [--endpoint-url <url>] [--auth-type <type>] [--auth-config <json>]
scadabridge external-system delete --id <id>
scadabridge external-system method list --external-system-id <id>
scadabridge external-system method get --id <id>
scadabridge external-system method create --external-system-id <id> --name <name> --http-method <verb> --path <path> [--params <json>] [--return <json>]
scadabridge external-system method update --id <id> [--name <name>] [--http-method <verb>] [--path <path>] [--params <json>] [--return <json>]
scadabridge external-system method delete --id <id>
Notification Commands
scadabridge notification list
scadabridge notification get --id <id>
scadabridge notification create --name <name> --emails <comma-separated>
scadabridge notification update --id <id> [--name <name>] [--emails <comma-separated>]
scadabridge notification delete --id <id>
scadabridge notification smtp list
scadabridge notification smtp update --id <id> --server <host> --port <n> --auth-mode <mode> --from-address <email>
Security Commands
scadabridge security api-key list
scadabridge security api-key create --name <name>
scadabridge security api-key update --id <id> --enabled <bool>
scadabridge security api-key delete --id <id>
scadabridge security role-mapping list
scadabridge security role-mapping create --ldap-group <group> --role <role>
scadabridge security role-mapping update --id <id> [--ldap-group <group>] [--role <role>]
scadabridge security role-mapping delete --id <id>
scadabridge security scope-rule list [--mapping-id <id>]
scadabridge security scope-rule add --mapping-id <id> --site-id <id>
scadabridge security scope-rule delete --id <id>
Audit Log Commands
scadabridge audit-log query [--user <username>] [--entity-type <type>] [--action <action>] [--from <date>] [--to <date>] [--page <n>] [--page-size <n>]
The legacy audit-log query above targets the original configuration-change audit
(IAuditService) surface. The new centralized Audit Log component (#23) is exposed via
the scadabridge audit group below.
Centralized Audit Commands
The scadabridge audit group targets the centralized Audit Log component (#23) and
exposes the UI-equivalent operational audit surface. Permissions follow the same
read-vs-export split the Central UI uses (see Component-AuditLog.md, Security &
Tamper-Evidence, and Security & Auth #10): audit query and audit verify-chain
require the OperationalAudit permission; audit export additionally requires
AuditExport. The server enforces permission checks and returns HTTP 403 (CLI
exit code 2) on denial.
scadabridge audit query --since <t> [--until <t>] [--channel <c>] [--kind <k>] [--status <s>] [--site <s>] [--instance <i>] [--target <t>] [--actor <a>] [--correlation-id <id>] [--errors-only] [--page <n>] [--page-size <n>]
scadabridge audit export --since <t> --until <t> --format csv|jsonl|parquet --output <path> [--channel <c>] [--kind <k>] [--status <s>] [--site <s>] [--target <t>] [--actor <a>]
scadabridge audit verify-chain --month <YYYY-MM>
audit query— filtered query against the centralAuditLogtable, matching the Central UI Audit Log page filter set (time range, channel, kind, status, site, instance/script, target, actor, correlation ID, errors-only). Results stream as JSON (default) or table.audit export— server-side streaming export of the centralAuditLogto the requested format (csv,jsonl,parquet) written to--output. The server streams rows rather than materializing them in memory; the CLI writes bytes through to disk. Supports the same scoping filters asaudit query.audit verify-chain— hash-chain verification for the named month. No-op in v1: the command is defined so the command tree is stable, but verification only becomes meaningful once the hash-chain ships (see Component-AuditLog.md, Security & Tamper-Evidence). Until then, the server responds with a "verification not yet available" status and the CLI exits 0.
Health Commands
scadabridge health summary
scadabridge health site --identifier <site-identifier>
scadabridge health event-log --site <site-identifier> [--event-type <type>] [--severity <level>] [--keyword <term>] [--from <date>] [--to <date>] [--page <n>] [--page-size <n>] [--instance-name <name>]
scadabridge health parked-messages --site <site-identifier> [--page <n>] [--page-size <n>]
Debug Commands
scadabridge debug snapshot --id <id>
scadabridge debug stream --id <id>
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
scadabridge shared-script list
scadabridge shared-script get --id <id>
scadabridge shared-script create --name <name> --code <code> [--parameters <json>] [--return-def <json>]
scadabridge shared-script update --id <id> [--name <name>] [--code <code>] [--parameters <json>] [--return-def <json>]
scadabridge shared-script delete --id <id>
Database Connection Commands
scadabridge db-connection list
scadabridge db-connection get --id <id>
scadabridge db-connection create --name <name> --connection-string <string>
scadabridge db-connection update --id <id> [--name <name>] [--connection-string <string>]
scadabridge db-connection delete --id <id>
Inbound API Method Commands
scadabridge api-method list
scadabridge api-method get --id <id>
scadabridge api-method create --name <name> --script <code> [--timeout <seconds>] [--parameters <json>] [--return-def <json>]
scadabridge api-method update --id <id> [--script <code>] [--timeout <seconds>] [--parameters <json>] [--return-def <json>]
scadabridge api-method delete --id <id>
The --format json|table option is recursive and accepted on every command above.
Configuration
Configuration is resolved in the following priority order (highest wins):
- Command-line options:
--url,--username,--password,--format. - Environment variables:
SCADALINK_MANAGEMENT_URL— Management API URL (e.g.,http://central-host:5000).SCADALINK_FORMAT— Default output format (jsonortable).SCADALINK_USERNAME/SCADALINK_PASSWORD— LDAP credentials. Preferred over--passwordon the command line, which is visible in process listings and shell history. Credentials are never read from the config file.
- Configuration file:
~/.scadabridge/config.json— Persistent defaults for management URL and output format only (never credentials).
Configuration File Format
{
"managementUrl": "http://central-host:5000"
}
Output Formats
- JSON (default): Machine-readable JSON output to stdout. Suitable for piping to
jqor processing in scripts. Errors are written to stderr as JSON objects witherrorandcodefields. - Table (
--format tableor--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 streamcommand's WebSocket connection. - Management Service (#18): The CLI hits the central cluster via the existing HTTP Management API (
POST /management), which dispatches to the ManagementActor. Thescadabridge auditcommand group rides a parallel REST surface on the same Host (GET /api/audit/queryandGET /api/audit/export), sharing HTTP Basic Auth with/managementbut bypassing the actor for read-only, keyset-paged / streaming workloads. - Audit Log (#23): The
scadabridge audit queryandaudit exportsubcommands target the centralized Audit Log component's REST endpoints (GET /api/audit/query,GET /api/audit/export) on the Host's Management API surface;audit verify-chainridesPOST /managementuntil hash-chain verification ships. Permission checks (OperationalAudit,AuditExport) are enforced server-side byAuditEndpoints.
Interactions
- Management Service (via HTTP): The primary runtime dependency. All operations except
debug streamare 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 /managementand 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 streamcommand connects to the/hubs/debug-streamhub on the central server for real-time event streaming. This is the only CLI command that uses a persistent connection rather than request/response.