Files
Joseph Doherty c899cb162c refactor: scrub residual ScadaLink refs → ScadaBridge (env vars, config keys, assembly name, SQL login)
Renames the 13 SCADALINK_* runtime env vars → SCADABRIDGE_*, the ScadaLink__
.NET config keys → ScadaBridge__, the stale ScadaLink.Host.exe assembly name
→ ZB.MOM.WW.ScadaBridge.Host.exe, the scadalink_app SQL login → scadabridge_app,
and residual identifiers/comments/docs. Migration records (prior rename
tooling/design, DB-rename helper, this scrub script) carved out.

Adds tools/scrub-scadalink-refs.sh.
2026-05-31 21:50:38 -04:00

20 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.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:

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>
scadabridge template native-alarm-source add --template-id <id> --name <name> --connection <name> --source-ref <ref> [--filter <expr>] [--description <desc>] [--locked]
scadabridge template native-alarm-source list --template-id <id>
scadabridge template native-alarm-source remove --id <id>

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 native-alarm-source set --instance-id <id> --source <name> [--connection <name>] [--source-ref <ref>] [--filter <expr>]
scadabridge instance native-alarm-source clear --instance-id <id> --source <name>
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}.

Native Alarm Source Commands

The native-alarm-source subcommands manage the read-only native alarm mirror — alarms surfaced from an alarm-capable data connection rather than evaluated by the ScadaBridge alarm engine. Native alarm sources are declared on a template and may be overridden per instance. The subcommands map to management commands that resolve via ManagementCommandRegistry:

  • --connection names an alarm-capable data connection (OPC UA or MxGateway).
  • --source-ref is the connection-specific reference: an OPC UA SourceNode nodeId or an MxAccess object/area.
  • --filter is an optional connection-specific filter expression that narrows the mirrored alarm set.

Template-level (defines the inherited native alarm sources):

CLI command Management command Required role
template native-alarm-source add AddTemplateNativeAlarmSourceCommand Design
template native-alarm-source list ListTemplateNativeAlarmSourcesCommand
template native-alarm-source remove DeleteTemplateNativeAlarmSourceCommand Design

add takes --name, --connection, and --source-ref (required), plus optional --filter, --description, and --locked (a flag that prevents instance-level override). remove targets a single native alarm source by its own --id.

Instance-level (per-instance overrides of an inherited source; upsert semantics):

CLI command Management command Required role
instance native-alarm-source set SetInstanceNativeAlarmSourceOverrideCommand Deployment
instance native-alarm-source clear DeleteInstanceNativeAlarmSourceOverrideCommand Deployment

set is an upsert keyed by --instance-id and --source (the inherited source name): a blank/omitted --connection, --source-ref, or --filter keeps the inherited value, so only the supplied options are overridden. clear removes the override and reverts the instance to the inherited template value.

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 central AuditLog table, 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 central AuditLog to 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 as audit 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):

  1. Command-line options: --url, --username, --password, --format.
  2. Environment variables:
    • SCADABRIDGE_MANAGEMENT_URL — Management API URL (e.g., http://central-host:5000).
    • SCADABRIDGE_FORMAT — Default output format (json or table).
    • SCADABRIDGE_USERNAME / SCADABRIDGE_PASSWORD — LDAP credentials. Preferred over --password on the command line, which is visible in process listings and shell history. Credentials are never read from the config file.
  3. 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 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.
  • Management Service (#18): The CLI hits the central cluster via the existing HTTP Management API (POST /management), which dispatches to the ManagementActor. The scadabridge audit command group rides a parallel REST surface on the same Host (GET /api/audit/query and GET /api/audit/export), sharing HTTP Basic Auth with /management but bypassing the actor for read-only, keyset-paged / streaming workloads.
  • Audit Log (#23): The scadabridge audit query and audit export subcommands 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-chain rides POST /management until hash-chain verification ships. Permission checks (OperationalAudit, AuditExport) are enforced server-side by AuditEndpoints.

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.