Synchronize CLI documentation with management command parity
This commit is contained in:
+50
-55
@@ -1,12 +1,12 @@
|
|||||||
# CLI
|
# CLI
|
||||||
|
|
||||||
The CLI is a standalone command-line tool that provides scripting and automation access to all ScadaBridge administrative operations. It connects to the central cluster's HTTP Management API and dispatches commands to the `ManagementActor` via `POST /management`. Authentication and role resolution happen server-side; the CLI sends LDAP credentials as HTTP Basic Auth on every request.
|
The CLI is a standalone command-line tool that provides scripting and automation access to all HTTP-eligible ManagementActor operations. It connects to the central cluster's HTTP Management API and dispatches commands to the `ManagementActor` via `POST /management`. Authentication and role resolution happen server-side; the CLI sends LDAP credentials as HTTP Basic Auth on every request.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The CLI component lives in `src/ZB.MOM.WW.ScadaBridge.CLI/` and builds to a self-contained `scadabridge` executable (or `scadabridge.exe` on Windows). It is not part of the Host binary — it deploys on any machine with HTTP access to a central node.
|
The CLI component lives in `src/ZB.MOM.WW.ScadaBridge.CLI/` and builds to a .NET 10 `scadabridge` executable (or `scadabridge.exe` on Windows); self-contained distribution requires an explicit publish configuration. It is not part of the Host binary — it deploys on any machine with HTTP access to a central node.
|
||||||
|
|
||||||
The tool is built on `System.CommandLine` and exposes a hierarchical command tree organized by management domain (`template`, `instance`, `site`, `data-connection`, and so on). Every command follows the same pattern: resolve the management URL and credentials, construct a command object whose type maps to a `ManagementCommandRegistry` entry, serialize it into the `{ command, payload }` JSON envelope, and `POST` it to `<managementUrl>/management`. The server response — JSON success body or `{ error, code }` error envelope — is printed to stdout or stderr respectively.
|
The tool is built on `System.CommandLine` and exposes a hierarchical command tree organized by management domain (`template`, `instance`, `site`, `data-connection`, and so on). Management commands follow the same pattern: resolve the management URL and credentials, construct a command object whose type maps to a `ManagementCommandRegistry` entry, serialize it into the `{ command, payload }` JSON envelope, and `POST` it to `<managementUrl>/management`. The server response — JSON success body or `{ error, code }` error envelope — is printed to stdout or stderr respectively.
|
||||||
|
|
||||||
The CLI is the preferred automation interface. When setting up system state (sites, templates, data connections, deployments, security) without the Central UI, use `scadabridge` commands rather than direct database manipulation.
|
The CLI is the preferred automation interface. When setting up system state (sites, templates, data connections, deployments, security) without the Central UI, use `scadabridge` commands rather than direct database manipulation.
|
||||||
|
|
||||||
@@ -20,7 +20,11 @@ Direct-to-node access (`http://localhost:9001` for `central-a`, `http://localhos
|
|||||||
|
|
||||||
### Command dispatch
|
### Command dispatch
|
||||||
|
|
||||||
Most commands serialize to a named management command and reach the `ManagementActor` through the `POST /management` endpoint. The `audit query` and `audit export` commands are the exception — they call plain REST endpoints (`GET /api/audit/query`, `GET /api/audit/export`) introduced by Audit Log (#23) and therefore use `SendGetAsync`/`SendGetStreamAsync` on `ManagementHttpClient` rather than `SendCommandAsync`. The streaming `audit export` path uses `HttpCompletionOption.ResponseHeadersRead` to avoid buffering large payloads in memory.
|
Management commands use `POST /management`. Audit query, export, and tree use
|
||||||
|
`GET /api/audit/query`, `/api/audit/export`, and `/api/audit/tree`; source-node
|
||||||
|
backfill uses `POST /api/audit/backfill-source-node`. `audit verify-chain` is a
|
||||||
|
local placeholder that performs no verification. `debug stream` uses a persistent
|
||||||
|
SignalR connection. Audit export streams the response without buffering it all.
|
||||||
|
|
||||||
### Authentication and roles
|
### Authentication and roles
|
||||||
|
|
||||||
@@ -30,46 +34,37 @@ Role enforcement is applied by the `ManagementActor`. Operations require the app
|
|||||||
|
|
||||||
| Role | Covers |
|
| Role | Covers |
|
||||||
|------|--------|
|
|------|--------|
|
||||||
| `Admin` | Security settings, site management, SMTP config, audit-config queries |
|
| `Administrator` | Security settings, site mutations, SMTP/SMS provider changes, bundle preview/import, certificate management, audit-config queries |
|
||||||
| `Design` | Templates, shared scripts, external systems, DB connections, API methods, notification lists |
|
| `Designer` | Template/schema/script authoring, external systems, DB connections, API methods, notification lists, connection design, bundle export |
|
||||||
| `Deployment` | Instance lifecycle (deploy, enable, disable, delete), data connection bindings, bundle import/export |
|
| `Deployer` | Instance lifecycle and overrides, bindings, deployment, runtime debugging, parked-call decisions |
|
||||||
| `OperationalAudit` | Reading the Audit Log (`audit query`) |
|
| `Operator` | Submit secured writes |
|
||||||
| `AuditExport` | Exporting the Audit Log (`audit export`) |
|
| `Verifier` | Approve/reject secured writes from a different principal |
|
||||||
|
| Operational audit roles | Audit query/tree and Flow Debugger replay |
|
||||||
|
| Audit export roles | Export the audit log |
|
||||||
|
|
||||||
|
These are operation-level gates, not blanket restrictions on every read in a
|
||||||
|
group. Many management reads require authentication only; site scope and
|
||||||
|
operation-specific checks still apply. Secured-write listing accepts Operator,
|
||||||
|
Verifier, or Administrator. See the [server authorization matrix](../../src/ZB.MOM.WW.ScadaBridge.ManagementService/ManagementActor.cs).
|
||||||
|
|
||||||
A request lacking the required role exits with code `2` (authorization failure). A bad-credential response (HTTP 401) exits with code `1`.
|
A request lacking the required role exits with code `2` (authorization failure). A bad-credential response (HTTP 401) exits with code `1`.
|
||||||
|
|
||||||
### Output formats
|
### Output formats
|
||||||
|
|
||||||
Every command accepts `--format json` (default) or `--format table`. For JSON output, successful server responses are written to stdout verbatim — the server controls the JSON shape and no re-serialization is applied. Locally-constructed output (errors, `debug stream` events) is serialized by `OutputFormatter` with indentation and camelCase. Table output renders a padded plain-text table derived from the response JSON — arrays become rows, single objects become a two-column `Property / Value` table. Errors are always written as `{ "error": "...", "code": "..." }` to stderr, regardless of format.
|
Management commands accept `--format json` (default) or `--format table`. Audit export accepts its own export formats. For JSON output, successful server responses are written to stdout verbatim — the server controls the JSON shape and no re-serialization is applied. Locally-constructed output (errors, `debug stream` events) is serialized by `OutputFormatter` with indentation and camelCase. Table output renders a padded plain-text table derived from the response JSON — arrays become rows, single objects become a two-column `Property / Value` table. Errors are always written as `{ "error": "...", "code": "..." }` to stderr, regardless of format.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
`Program.cs` invokes `CliApplication.BuildRoot()`, which builds the `RootCommand` tree, attaches the four global options (`--url`, `--username`, `--password`, `--format`), and registers each command group:
|
[`Program.cs`](../../src/ZB.MOM.WW.ScadaBridge.CLI/Program.cs) invokes
|
||||||
|
[`CliApplication.BuildRoot()`](../../src/ZB.MOM.WW.ScadaBridge.CLI/CliApplication.cs),
|
||||||
|
which registers the global options and all 20 command groups, including the
|
||||||
|
connection browse/certificate extensions. The executable and parity tests use
|
||||||
|
this same tree.
|
||||||
|
|
||||||
```csharp
|
Management actions use `CommandHelpers.ExecuteCommandAsync` for URL/credential
|
||||||
rootCommand.Add(TemplateCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
resolution, HTTP dispatch, formatting, and exit codes. Deployment aliases defer
|
||||||
rootCommand.Add(InstanceCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
the overall deadline to the server; bundles and Flow Debugger have explicit
|
||||||
rootCommand.Add(SiteCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
policies. Audit REST and SignalR actions use their respective transports.
|
||||||
rootCommand.Add(DeployCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(DataConnectionCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(ExternalSystemCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(NotificationCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(SecurityCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(AuditLogCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(AuditCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(HealthCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(DebugCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(SharedSchemaCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(SharedScriptCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(DbConnectionCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(ApiMethodCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(BundleCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(CachedCallCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(SecuredWriteCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
rootCommand.Add(FlowDebugCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
|
||||||
```
|
|
||||||
|
|
||||||
Every command's action delegates to `CommandHelpers.ExecuteCommandAsync`, which owns the URL/credential resolution, `ManagementHttpClient` lifetime, and exit-code mapping. This consolidation means the authorization-failure exit code (`2`) is enforced uniformly — including for the `bundle` group, which uses `ExecuteCommandAsync` with a longer 5-minute timeout and a per-command `onSuccess` handler rather than a separate HTTP path.
|
|
||||||
|
|
||||||
`CliConfig.Load()` is called at the start of every invocation. It merges `~/.scadabridge/config.json`, environment variables, and any defaults. A malformed or unreadable config file emits a warning to stderr and falls through to the environment variable / command-line precedence chain without crashing.
|
`CliConfig.Load()` is called at the start of every invocation. It merges `~/.scadabridge/config.json`, environment variables, and any defaults. A malformed or unreadable config file emits a warning to stderr and falls through to the environment variable / command-line precedence chain without crashing.
|
||||||
|
|
||||||
@@ -100,7 +95,7 @@ export SCADABRIDGE_USERNAME=multi-role
|
|||||||
export SCADABRIDGE_PASSWORD=password
|
export SCADABRIDGE_PASSWORD=password
|
||||||
|
|
||||||
scadabridge template create --name "PumpStation" --description "Standard pump station"
|
scadabridge template create --name "PumpStation" --description "Standard pump station"
|
||||||
scadabridge template attribute add --template-id 3 --name Speed --data-type Float --default-value 0
|
scadabridge template attribute add --template-id 3 --name Speed --data-type Float --value 0
|
||||||
scadabridge instance create --name "PS-01" --template-id 3 --site-id 1
|
scadabridge instance create --name "PS-01" --template-id 3 --site-id 1
|
||||||
scadabridge instance deploy --id 7
|
scadabridge instance deploy --id 7
|
||||||
```
|
```
|
||||||
@@ -109,26 +104,26 @@ scadabridge instance deploy --id 7
|
|||||||
|
|
||||||
| Group | Subcommands | Role required |
|
| Group | Subcommands | Role required |
|
||||||
|-------|-------------|---------------|
|
|-------|-------------|---------------|
|
||||||
| `template` | `list`, `get`, `create`, `update`, `delete`, `validate`, `resolved-members`, `move`; `attribute add/update/delete`; `alarm add/update/delete`; `script add/update/delete`; `composition add/delete`; `native-alarm-source add/list/update/remove` | `Design` |
|
| `template` | `list`, `get`, `create`, `update`, `delete`, `validate`, `resolved-members`, `move`; `attribute list/add/update/delete`; `alarm add/update/delete`; `script add/update/delete`; `composition add/delete`; `native-alarm-source add/list/update/remove`; `folder list/create/rename/move/reorder/delete`; `resync-members` | Designer mutations; reads generally authenticated |
|
||||||
| `instance` | `list`, `get`, `create`, `set-bindings`, `set-overrides`, `alarm-override set/delete/list`, `native-alarm-source set/clear/list/import`, `attribute-override delete`, `set-area`, `diff`, `deploy`, `enable`, `disable`, `delete` | `Deployment` |
|
| `instance` | `list`, `get`, `create`, `set-bindings`, `bind-module`, `set-overrides`, `import-overrides`, `alarm-override set/delete/list`, `native-alarm-source set/clear/list/import`, `attribute-override delete`, `set-area`, `diff`, `deploy`, `enable`, `disable`, `delete` | `Deployer` |
|
||||||
| `site` | `list`, `get`, `create`, `delete`, `deploy-artifacts`; `area list/create/update/delete` | `Admin` |
|
| `site` | `list`, `get`, `create`, `update`, `delete`, `deploy-artifacts`; `area list/create/update/move/delete` | Administrator site mutations; Designer/Deployer area mutations; Deployer artifact deployment |
|
||||||
| `deploy` | `instance`, `artifacts`, `status` | `Deployment` |
|
| `deploy` | `instance`, `site`, `artifacts`, `status` | `Deployer` |
|
||||||
| `data-connection` | `list`, `get`, `create`, `update`, `delete`, `move`; `browse`, `search`, `verify-endpoint`, `certs` | `Design` / `Deployment` |
|
| `data-connection` | `list`, `get`, `create`, `update`, `delete`, `move`; `browse`, `search`, `verify-endpoint`, `certs` | `Designer` / `Deployer` |
|
||||||
| `external-system` | `list`, `get`, `create`, `update`, `delete` | `Design` |
|
| `external-system` | `list`, `get`, `create`, `update`, `delete`; `method list/get/create/update/delete` | `Designer` |
|
||||||
| `notification` | `list`, `get`, `create`, `update`, `delete`, `rename`; `recipient add/delete`; `smtp list/create/update`; `sms list/create/update` | `Design` / `Admin` |
|
| `notification` | `list`, `get`, `create`, `update`, `delete`, `rename`; `recipient add/delete`; `smtp list/create/update`; `sms list/create/update` | `Designer` / `Administrator` |
|
||||||
| `security` | `api-key list/create/update/delete/set-methods`; `role-mapping list/create/update/delete`; `scope-rule list/add/delete` | `Admin` |
|
| `security` | `api-key list/create/update/delete/set-methods`; `role-mapping list/create/update/delete`; `scope-rule list/add/delete` | `Admin` |
|
||||||
| `shared-schema` | `list`, `get`, `create`, `update`, `delete` | Read: authenticated; mutations: Designer |
|
| `shared-schema` | `list`, `get`, `create`, `update`, `delete` | Read: authenticated; mutations: Designer |
|
||||||
| `secured-write` | `list`, `submit`, `approve`, `reject` | Operator / Verifier / Administrator, enforced per operation |
|
| `secured-write` | `list`, `submit`, `approve`, `reject` | Operator / Verifier / Administrator, enforced per operation |
|
||||||
| `cached-call` | `list`, `retry`, `discard` | Server-enforced operational roles and site scope |
|
| `cached-call` | `retry`, `discard` (list parked messages with `health parked-messages`) | Server-enforced operational roles and site scope |
|
||||||
| `flow-debug` | `run-method`, `run-script`, `run-value-change`, `replay` | Deployer; replay also accepts OperationalAudit roles |
|
| `flow-debug` | `run-method`, `run-script`, `run-value-change`, `replay` | Deployer; replay also accepts OperationalAudit roles |
|
||||||
| `shared-script` | `list`, `get`, `create`, `update`, `delete` | `Design` |
|
| `shared-script` | `list`, `get`, `create`, `update`, `delete` | `Designer` |
|
||||||
| `db-connection` | `list`, `get`, `create`, `update`, `delete` | `Design` |
|
| `db-connection` | `list`, `get`, `create`, `update`, `delete` | `Designer` |
|
||||||
| `api-method` | `list`, `get`, `create`, `update`, `delete` | `Design` |
|
| `api-method` | `list`, `get`, `create`, `update`, `delete` | `Designer` |
|
||||||
| `bundle` | `export`, `preview`, `import` | `Deployment` |
|
| `bundle` | `export`, `preview`, `import` | Designer export; Administrator preview/import |
|
||||||
| `audit` | `query`, `export`, `verify-chain` | `OperationalAudit` / `AuditExport` |
|
| `audit` | `query`, `export`, `tree`, `backfill-source-node`, `verify-chain` | Audit roles; Administrator backfill; verify-chain is a local placeholder |
|
||||||
| `audit-config` | `query` (config-change audit trail; was `audit-log` pre-M8) | `Admin` |
|
| `audit-config` | `query` (config-change audit trail; was `audit-log` pre-M8) | `Admin` |
|
||||||
| `health` | `summary`, `site`, `event-log`, `parked-messages` | `Deployment` |
|
| `health` | `summary`, `site`, `event-log`, `parked-messages` | Authenticated reads with server-side scope checks |
|
||||||
| `debug` | `snapshot`, `stream` | `Deployment` |
|
| `debug` | `snapshot`, `stream` | `Deployer` |
|
||||||
|
|
||||||
### Selected examples
|
### Selected examples
|
||||||
|
|
||||||
@@ -198,10 +193,10 @@ The management URL is resolved in this order: `--url` flag → `SCADABRIDGE_MANA
|
|||||||
|
|
||||||
## Dependencies & Interactions
|
## Dependencies & Interactions
|
||||||
|
|
||||||
- [Management Service (#18)](./ManagementService.md) — the server-side counterpart. Every CLI command (except `audit query`/`audit export`) translates to a named management command dispatched through `POST /management` to the `ManagementActor`. Role enforcement and LDAP authentication are applied there. The `ManagementCommandRegistry` in Commons maps command types to their names; both sides must stay in sync.
|
- [Management Service (#18)](./ManagementService.md) — the server-side counterpart. Every CLI command (except the audit REST/placeholder and SignalR stream operations) translates to a named management command dispatched through `POST /management` to the `ManagementActor`. Role enforcement and LDAP authentication are applied there. The `ManagementCommandRegistry` in Commons maps command types to their names; both sides must stay in sync.
|
||||||
- [Traefik Proxy (#20)](./TraefikProxy.md) — the recommended connection target. Pointing `--url` at the Traefik address ensures requests reach the active central node without per-command failover logic in the CLI. The `debug stream` command's WebSocket connection (SignalR `/hubs/debug-stream`) also traverses Traefik, which proxies the WebSocket upgrade natively.
|
- [Traefik Proxy (#20)](./TraefikProxy.md) — the recommended connection target. Pointing `--url` at the Traefik address ensures requests reach the active central node without per-command failover logic in the CLI. The `debug stream` command's WebSocket connection (SignalR `/hubs/debug-stream`) also traverses Traefik, which proxies the WebSocket upgrade natively.
|
||||||
- [Audit Log (#23)](./AuditLog.md) — the `audit` command group targets the `GET /api/audit/query` and `GET /api/audit/export` REST endpoints exposed by the Audit Log component, bypassing the management command envelope. The `audit-config` group (formerly `audit-log`) targets the configuration-change audit trail (`IAuditService`) via the standard management envelope.
|
- [Audit Log (#23)](./AuditLog.md) — the `audit` command group targets the query, export, tree, and backfill REST endpoints exposed by the Audit Log component, bypassing the management command envelope; `verify-chain` is a local placeholder. The `audit-config` group (formerly `audit-log`) targets the configuration-change audit trail (`IAuditService`) via the standard management envelope.
|
||||||
- [Security & Auth (#10)](./Security.md) — the server resolves LDAP credentials and maps group memberships to ScadaBridge roles (`Admin`, `Design`, `Deployment`, `OperationalAudit`, `AuditExport`). The CLI does not interact with LDAP directly.
|
- [Security & Auth (#10)](./Security.md) — the server resolves LDAP credentials and maps group memberships to ScadaBridge roles (including `Administrator`, `Designer`, `Deployer`, `Operator`, `Verifier`, and audit roles). The CLI does not interact with LDAP directly.
|
||||||
- [Commons (#16)](./Commons.md) — owns the management command record types and the `ManagementCommandRegistry` that maps each type to its wire name. The CLI project references Commons for these contracts.
|
- [Commons (#16)](./Commons.md) — owns the management command record types and the `ManagementCommandRegistry` that maps each type to its wire name. The CLI project references Commons for these contracts.
|
||||||
- [Transport (#24)](./Transport.md) — the `bundle` command group drives the Transport feature: `bundle export` requests a base64-encoded bundle from the server and streams it to a local `.scadabundle` file; `bundle preview` uploads a file and returns the diff manifest; `bundle import` uploads a file and applies it with a configurable conflict policy.
|
- [Transport (#24)](./Transport.md) — the `bundle` command group drives the Transport feature: `bundle export` requests a base64-encoded bundle from the server and streams it to a local `.scadabundle` file; `bundle preview` uploads a file and returns the diff manifest; `bundle import` uploads a file and applies it with a configurable conflict policy.
|
||||||
- Design spec: [Component-CLI.md](../requirements/Component-CLI.md).
|
- Design spec: [Component-CLI.md](../requirements/Component-CLI.md).
|
||||||
@@ -226,7 +221,7 @@ The central node or Traefik is not reachable at the configured URL. Verify the c
|
|||||||
|
|
||||||
### Authorization failure (exit 2)
|
### Authorization failure (exit 2)
|
||||||
|
|
||||||
The server returned HTTP 403 or an error code of `FORBIDDEN`/`UNAUTHORIZED`. The authenticated user's LDAP groups do not map to a role with permission for the requested operation. Use `security role-mapping list` (requires `Admin`) to inspect role mappings. The `multi-role` test user (`password`) holds `Admin`, `Design`, and `Deployment` roles.
|
The server returned HTTP 403 or an error code of `FORBIDDEN`/`UNAUTHORIZED`. The authenticated user's LDAP groups do not map to a role with permission for the requested operation. Use `security role-mapping list` (requires `Administrator`) to inspect role mappings. The `multi-role` test user (`password`) holds `Administrator`, `Designer`, and `Deployer` roles.
|
||||||
|
|
||||||
### Malformed config file warning
|
### Malformed config file warning
|
||||||
|
|
||||||
|
|||||||
@@ -278,3 +278,10 @@ No device writes or live configuration mutations were used for smoke verificatio
|
|||||||
Final local logs: `/tmp/parity-final-build.log`, `/tmp/parity-main-cli.log`,
|
Final local logs: `/tmp/parity-final-build.log`, `/tmp/parity-main-cli.log`,
|
||||||
`/tmp/parity-deploy.log`, `/tmp/parity-rig-before.json`,
|
`/tmp/parity-deploy.log`, `/tmp/parity-rig-before.json`,
|
||||||
`/tmp/parity-rig-after.json`, and `/tmp/parity-live-smoke.json`.
|
`/tmp/parity-rig-after.json`, and `/tmp/parity-live-smoke.json`.
|
||||||
|
|
||||||
|
### CLI documentation follow-up — complete (2026-09-10)
|
||||||
|
|
||||||
|
- [x] Integrated all new operations into the main CLI command reference and synchronized the component overview and requirements.
|
||||||
|
- [x] Documented the five existing external-system method commands that were missing from the README.
|
||||||
|
- [x] Corrected stale examples, SMS options, secured-write paging, authorization descriptions, and deployment deadline guidance.
|
||||||
|
- [x] Checked the executable help tree: all **159 leaf commands** appear in the reference. Documented example option names and local Markdown links/anchors passed validation. Documentation-only follow-up; runtime verification above remains applicable.
|
||||||
|
|||||||
@@ -48,9 +48,10 @@ The CLI uses a hierarchical subcommand structure mirroring the Management Servic
|
|||||||
scadabridge <group> <action> [options]
|
scadabridge <group> <action> [options]
|
||||||
```
|
```
|
||||||
|
|
||||||
All entities are identified by their integer **ID** (via `--id`, `--template-id`,
|
Commands identify targets by integer ID, site identifier, connection name, or
|
||||||
`--site-id`, etc.), not by name. Create/update commands take individual flags — there
|
canonical member name as shown in their help. Most edits use individual flags;
|
||||||
is no `--file` option. The authoritative, always-current reference is the in-repo
|
CSV imports use `--file`, shared schemas use `--schema-file`, and bundles use
|
||||||
|
`--input`/`--output`. 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
|
`src/ZB.MOM.WW.ScadaBridge.CLI/README.md`; the command lists below mirror the implemented command
|
||||||
tree at the time of writing.
|
tree at the time of writing.
|
||||||
|
|
||||||
@@ -58,6 +59,8 @@ tree at the time of writing.
|
|||||||
```
|
```
|
||||||
scadabridge template list [--detail] [--skip <n>] [--take <n>]
|
scadabridge template list [--detail] [--skip <n>] [--take <n>]
|
||||||
scadabridge template get --id <id>
|
scadabridge template get --id <id>
|
||||||
|
scadabridge template resolved-members --id <id>
|
||||||
|
scadabridge template move --id <id> [--folder-id <id>]
|
||||||
scadabridge template create --name <name> [--description <desc>] [--parent-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 update --id <id> [--name <name>] [--description <desc>] [--parent-id <id>]
|
||||||
scadabridge template validate --id <id>
|
scadabridge template validate --id <id>
|
||||||
@@ -75,6 +78,7 @@ scadabridge template composition add --template-id <id> --instance-name <name> -
|
|||||||
scadabridge template composition delete --id <id>
|
scadabridge template composition delete --id <id>
|
||||||
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 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 list --template-id <id>
|
||||||
|
scadabridge template native-alarm-source update --id <id> --name <name> --connection <name> --source-ref <ref> [--filter <expr>] [--description <desc>] [--locked]
|
||||||
scadabridge template native-alarm-source remove --id <id>
|
scadabridge template native-alarm-source remove --id <id>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -86,6 +90,8 @@ scadabridge instance create --name <name> --template-id <id> --site-id <id> [--a
|
|||||||
scadabridge instance set-bindings --id <id> --bindings <json>
|
scadabridge instance set-bindings --id <id> --bindings <json>
|
||||||
scadabridge instance bind-module --id <id> --module <name> --connection <id> --object <path> [--dry-run]
|
scadabridge instance bind-module --id <id> --module <name> --connection <id> --object <path> [--dry-run]
|
||||||
scadabridge instance set-overrides --id <id> --overrides <json>
|
scadabridge instance set-overrides --id <id> --overrides <json>
|
||||||
|
scadabridge instance attribute-override delete --instance-id <id> --attribute <canonical-name>
|
||||||
|
scadabridge instance native-alarm-source list --instance-id <id>
|
||||||
scadabridge instance import-overrides --id <id> --file <path.csv>
|
scadabridge instance import-overrides --id <id> --file <path.csv>
|
||||||
scadabridge instance alarm-override set --instance-id <id> --alarm <name> [--trigger-config <json>] [--priority <n>]
|
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 delete --instance-id <id> --alarm <name>
|
||||||
@@ -172,6 +178,7 @@ scadabridge data-connection get --id <id>
|
|||||||
scadabridge data-connection create --site-id <id> --name <name> --protocol <protocol> [--primary-config <json>] [--backup-config <json>] [--failover-retry-count <n>]
|
scadabridge data-connection create --site-id <id> --name <name> --protocol <protocol> [--primary-config <json>] [--backup-config <json>] [--failover-retry-count <n>]
|
||||||
scadabridge data-connection update --id <id> --name <name> --protocol <protocol> [--primary-config <json>] [--backup-config <json>] [--failover-retry-count <n>]
|
scadabridge data-connection update --id <id> --name <name> --protocol <protocol> [--primary-config <json>] [--backup-config <json>] [--failover-retry-count <n>]
|
||||||
scadabridge data-connection delete --id <id>
|
scadabridge data-connection delete --id <id>
|
||||||
|
scadabridge data-connection move --id <id> --site-id <destination-site-id>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Site-routed OPC UA operations (browse / search / verify-endpoint / certs)
|
#### Site-routed OPC UA operations (browse / search / verify-endpoint / certs)
|
||||||
@@ -210,12 +217,33 @@ scadabridge external-system method delete --id <id>
|
|||||||
scadabridge notification list
|
scadabridge notification list
|
||||||
scadabridge notification get --id <id>
|
scadabridge notification get --id <id>
|
||||||
scadabridge notification create --name <name> --emails <comma-separated>
|
scadabridge notification create --name <name> --emails <comma-separated>
|
||||||
scadabridge notification update --id <id> [--name <name>] [--emails <comma-separated>]
|
scadabridge notification update --id <id> --name <name> --emails <comma-separated>
|
||||||
|
scadabridge notification create --name <name> --type sms --phones <comma-separated>
|
||||||
|
scadabridge notification update --id <id> --name <name> --type sms --phones <comma-separated>
|
||||||
|
scadabridge notification rename --id <id> --name <name>
|
||||||
|
scadabridge notification recipient add --list-id <id> --name <name> [--email <email>] [--phone <E.164-number>]
|
||||||
|
scadabridge notification recipient delete --id <recipient-id>
|
||||||
scadabridge notification delete --id <id>
|
scadabridge notification delete --id <id>
|
||||||
scadabridge notification smtp list
|
scadabridge notification smtp list
|
||||||
scadabridge notification smtp update --id <id> --server <host> --port <n> --auth-mode <mode> --from-address <email> [--tls-mode <mode>] [--credentials <value>]
|
scadabridge notification smtp update --id <id> --server <host> --port <n> --auth-mode <mode> --from-address <email> [--tls-mode <mode>] [--credentials <value>]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Notification rename and individual recipient edits preserve unrelated IDs and
|
||||||
|
names. Whole-list update replaces recipients. Recipient add requires at least
|
||||||
|
one contact; the server validates it against the owning list channel.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge notification smtp create --server <host> --port <n> --auth-mode <mode> --from-address <email> [--tls-mode <mode>] [--credentials <value>] [--oauth2-authority <url>] [--oauth2-scope <scope>] [--transport <Smtp|Ews>]
|
||||||
|
scadabridge notification sms list
|
||||||
|
scadabridge notification sms create --account-sid <sid> --auth-token <token> [--from-number <number>] [--messaging-service-sid <sid>] [--api-base-url <url>] [--connection-timeout-seconds <n>] [--max-retries <n>] [--retry-delay-seconds <n>]
|
||||||
|
scadabridge notification sms update --id <id> --account-sid <sid> [--from-number <number>] [--messaging-service-sid <sid>] [--api-base-url <url>] [--auth-token <token>] [--connection-timeout-seconds <n>] [--max-retries <n>] [--retry-delay-seconds <n>]
|
||||||
|
```
|
||||||
|
|
||||||
|
SMS requires at least one sender: a From number or a Messaging Service SID.
|
||||||
|
Omitted timeout/retry settings preserve update values; creation uses defaults.
|
||||||
|
Omitted Auth Token preserves the stored token on update; omitted Messaging
|
||||||
|
Service SID clears it. Provider create/update require Administrator access.
|
||||||
|
|
||||||
### Security Commands
|
### Security Commands
|
||||||
```
|
```
|
||||||
scadabridge security api-key list
|
scadabridge security api-key list
|
||||||
@@ -332,6 +360,20 @@ Key behaviors:
|
|||||||
|
|
||||||
Unlike `debug snapshot` (which uses the HTTP Management API), `debug stream` uses `Microsoft.AspNetCore.SignalR.Client` as a dependency for its WebSocket transport.
|
Unlike `debug snapshot` (which uses the HTTP Management API), `debug stream` uses `Microsoft.AspNetCore.SignalR.Client` as a dependency for its WebSocket transport.
|
||||||
|
|
||||||
|
### Shared Schema Commands
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge shared-schema list
|
||||||
|
scadabridge shared-schema get --id <id>
|
||||||
|
scadabridge shared-schema create --name <name> --schema-file <path.json> [--scope <scope>]
|
||||||
|
scadabridge shared-schema update --id <id> --name <name> --schema-file <path.json> [--scope <scope>]
|
||||||
|
scadabridge shared-schema delete --id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Create/update validate JSON syntax locally and schema structure on the server.
|
||||||
|
Update replaces all three fields; omitted scope clears it. Mutations require
|
||||||
|
Designer access; reads require authentication.
|
||||||
|
|
||||||
### Shared Script Commands
|
### Shared Script Commands
|
||||||
```
|
```
|
||||||
scadabridge shared-script list
|
scadabridge shared-script list
|
||||||
@@ -393,22 +435,31 @@ table).
|
|||||||
|
|
||||||
### Secured-Write Commands (two-person MxGateway writes)
|
### Secured-Write Commands (two-person MxGateway writes)
|
||||||
|
|
||||||
Query two-person (MxGateway) secured writes — the pending queue plus terminal history.
|
Manage two-person MxGateway writes. Listing is gated any-of **Operator**,
|
||||||
Read-only; gated any-of **Operator** / **Verifier** / **Administrator** (the history
|
**Verifier**, or **Administrator** because the history exposes process-sensitive
|
||||||
exposes process-sensitive tag values, so it is not open to every authenticated user).
|
tag values. Submission requires Operator; approval/rejection require Verifier,
|
||||||
|
with a different authenticated principal from the submitter.
|
||||||
|
|
||||||
```
|
```
|
||||||
scadabridge secured-write list [--status <status>] [--site <identifier>] [--skip <n>] [--take <n>]
|
scadabridge secured-write list [--status <status>] [--site <identifier>] [--skip <n>] [--take <n>]
|
||||||
|
scadabridge secured-write submit --site <identifier> --connection <name> --tag-path <path> --value-json <json> --value-type <type> [--comment <text>]
|
||||||
|
scadabridge secured-write approve --id <long> [--comment <text>]
|
||||||
|
scadabridge secured-write reject --id <long> [--comment <text>]
|
||||||
```
|
```
|
||||||
|
|
||||||
`--status` / `--site` filter the results (omit to match all). `--skip` / `--take` provide
|
`--status` / `--site` filter the results (omit to match all). `--skip` / `--take` provide
|
||||||
offset paging (arch-review P3); omitting them uses the historical unpaged default
|
offset paging (arch-review P3); omitting them uses the default page
|
||||||
(`--skip 0 --take 200`). `--take` is clamped to `1..500` server-side. The result carries a
|
(`--skip 0 --take 200`). `--take` is clamped to `1..500` server-side. The result carries a
|
||||||
`totalCount` (rows matching the filters, ignoring paging) alongside the returned page.
|
`totalCount` (rows matching the filters, ignoring paging) alongside the returned page.
|
||||||
Overdue `Pending` rows are swept to `Expired` opportunistically each time the list is
|
Overdue `Pending` rows are swept to `Expired` opportunistically each time the list is
|
||||||
queried (server-side TTL, arch-review S2).
|
queried (server-side TTL, arch-review S2).
|
||||||
|
|
||||||
The `--format json|table` option is recursive and accepted on every command above.
|
Approval can execute a real device write. Only a server response with status
|
||||||
|
`Executed` exits zero; `Failed` or an invalid approval outcome exits one, with
|
||||||
|
JSON/table details retained. Authorization denial exits two. The server enforces
|
||||||
|
site scope, expiry, separation of duties, and concurrent decisions.
|
||||||
|
|
||||||
|
The `--format json|table` option is recursive and accepted on every management command above; audit export has its own output formats.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ Every command requires a connection to a running Central node. The management UR
|
|||||||
scadabridge --url http://central-host:5000 <command>
|
scadabridge --url http://central-host:5000 <command>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Docker:** With the Docker setup, the Central UI (and management API) is available at `http://localhost:9001`:
|
**Docker:** Use Traefik at `http://localhost:9000` to reach the active central node:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadabridge --url http://localhost:9001 <command>
|
scadabridge --url http://localhost:9000 <command>
|
||||||
```
|
```
|
||||||
|
|
||||||
For HA failover, use a load balancer URL. The management API is stateless (Basic Auth per request), so any central node can handle any request without sticky sessions.
|
The management API is stateless (Basic Auth per request), so sticky sessions are unnecessary. Direct node URLs (`9001`/`9002`) are useful for diagnostics; use Traefik for active-node routing and failover.
|
||||||
|
|
||||||
## Global Options
|
## Global Options
|
||||||
|
|
||||||
@@ -107,6 +107,24 @@ scadabridge --url <url> --format json template list # unmodified JS
|
|||||||
| `--skip` | no | Offset paging: number of items to skip (default 0) |
|
| `--skip` | no | Offset paging: number of items to skip (default 0) |
|
||||||
| `--take` | no | Offset paging: max items to return (clamped 1..1000; omit for unlimited) |
|
| `--take` | no | Offset paging: max items to return (clamped 1..1000; omit for unlimited) |
|
||||||
|
|
||||||
|
#### `template resolved-members`
|
||||||
|
|
||||||
|
Inspect effective template members, including inheritance. This does not apply composition or instance overrides.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge template resolved-members --id <template-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `template move`
|
||||||
|
|
||||||
|
Move a template into a folder, or to the root when `--folder-id` is omitted.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge template move --id <template-id> [--folder-id <folder-id>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires Designer access; server-side folder and dependency checks apply.
|
||||||
|
|
||||||
#### `template get`
|
#### `template get`
|
||||||
|
|
||||||
Get a single template by ID. Unlike `template list`, this returns the **full** template
|
Get a single template by ID. Unlike `template list`, this returns the **full** template
|
||||||
@@ -333,6 +351,16 @@ List the native alarm source bindings on a template.
|
|||||||
scadabridge --url <url> template native-alarm-source list --template-id <int>
|
scadabridge --url <url> template native-alarm-source list --template-id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `template native-alarm-source update`
|
||||||
|
|
||||||
|
Replace the fields of an existing native alarm source binding.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge template native-alarm-source update --id <source-id> --name <name> --connection <name> --source-ref <reference> [--filter <expression>] [--description <text>] [--locked]
|
||||||
|
```
|
||||||
|
|
||||||
|
The ID, name, connection, and source reference are required. Omitted filter and description clear those values; omitted `--locked` means unlocked. Requires Designer access; the server enforces locking rules.
|
||||||
|
|
||||||
#### `template native-alarm-source remove`
|
#### `template native-alarm-source remove`
|
||||||
|
|
||||||
Remove a native alarm source binding from a template (by its ID).
|
Remove a native alarm source binding from a template (by its ID).
|
||||||
@@ -566,6 +594,24 @@ Create Instance page can bind the module groups at creation time (design
|
|||||||
2026-08-21); the CLI equivalent is a follow-up `instance set-bindings` /
|
2026-08-21); the CLI equivalent is a follow-up `instance set-bindings` /
|
||||||
`instance bind-module` against the new instance id.
|
`instance bind-module` against the new instance id.
|
||||||
|
|
||||||
|
#### `instance native-alarm-source list`
|
||||||
|
|
||||||
|
List the native alarm source overrides for an instance.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge instance native-alarm-source list --instance-id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `instance attribute-override delete`
|
||||||
|
|
||||||
|
Delete one attribute override while preserving other overrides.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge instance attribute-override delete --instance-id <id> --attribute <canonical-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a composed name such as `Module.Speed` where applicable. Requires Deployer access and passes through server-side site-scope checks.
|
||||||
|
|
||||||
#### `instance native-alarm-source set`
|
#### `instance native-alarm-source set`
|
||||||
|
|
||||||
Override an inherited native alarm source binding for a single instance (upsert). Blank
|
Override an inherited native alarm source binding for a single instance (upsert). Blank
|
||||||
@@ -1143,6 +1189,16 @@ scadabridge --url <url> data-connection update --id <int> --name <string> --prot
|
|||||||
| `--backup-config` | no | Backup protocol-specific configuration as a JSON string |
|
| `--backup-config` | no | Backup protocol-specific configuration as a JSON string |
|
||||||
| `--failover-retry-count` | no | Retries before failover to the backup configuration (default: `3`) |
|
| `--failover-retry-count` | no | Retries before failover to the backup configuration (default: `3`) |
|
||||||
|
|
||||||
|
#### `data-connection move`
|
||||||
|
|
||||||
|
Move a data connection to a destination site.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge data-connection move --id <connection-id> --site-id <destination-site-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Both IDs are required. Requires Designer access; the server validates the destination and any dependency restrictions.
|
||||||
|
|
||||||
#### `data-connection delete`
|
#### `data-connection delete`
|
||||||
|
|
||||||
Delete a data connection.
|
Delete a data connection.
|
||||||
@@ -1326,6 +1382,48 @@ scadabridge --url <url> external-system delete --id <int>
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### `external-system method list`
|
||||||
|
|
||||||
|
List the methods belonging to an external system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge external-system method list --external-system-id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `external-system method get`
|
||||||
|
|
||||||
|
Read one method definition.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge external-system method get --id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `external-system method create`
|
||||||
|
|
||||||
|
Create an external HTTP method. Requires Designer access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge external-system method create --external-system-id <id> --name <name> --http-method <verb> --path <path> [--params <json>] [--return <json>]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `external-system method update`
|
||||||
|
|
||||||
|
Update an external HTTP method. Requires Designer access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge external-system method update --id <id> [--name <name>] [--http-method <verb>] [--path <path>] [--params <json>] [--return <json>]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `external-system method delete`
|
||||||
|
|
||||||
|
Delete a method by ID. Requires Designer access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge external-system method delete --id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### `notification` — Manage notification lists
|
### `notification` — Manage notification lists
|
||||||
|
|
||||||
#### `notification get`
|
#### `notification get`
|
||||||
@@ -1392,6 +1490,36 @@ scadabridge --url <url> notification update --id <int> --name <string> --type sm
|
|||||||
| `--emails` | conditional | Comma-separated recipient email addresses. **Required for `--type email`; rejected for `--type sms`.** |
|
| `--emails` | conditional | Comma-separated recipient email addresses. **Required for `--type email`; rejected for `--type sms`.** |
|
||||||
| `--phones` | conditional | Comma-separated recipient phone numbers in E.164. **Required for `--type sms`; rejected for `--type email`.** |
|
| `--phones` | conditional | Comma-separated recipient phone numbers in E.164. **Required for `--type sms`; rejected for `--type email`.** |
|
||||||
|
|
||||||
|
#### `notification rename`
|
||||||
|
|
||||||
|
Rename a list without replacing its recipients.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge notification rename --id <list-id> --name <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires Designer access. Unrelated recipient IDs, names, and contacts are preserved.
|
||||||
|
|
||||||
|
#### `notification recipient add`
|
||||||
|
|
||||||
|
Add an individual named recipient to an existing list.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge notification recipient add --list-id <id> --name <name> [--email <email>] [--phone <E.164-number>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Provide at least one of `--email` or `--phone`. The server checks the contact against the owning list channel. Requires Designer access; other recipients remain unchanged.
|
||||||
|
|
||||||
|
#### `notification recipient delete`
|
||||||
|
|
||||||
|
Delete one recipient by recipient ID.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge notification recipient delete --id <recipient-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires Designer access. Only the specified recipient is removed.
|
||||||
|
|
||||||
#### `notification delete`
|
#### `notification delete`
|
||||||
|
|
||||||
Delete a notification list.
|
Delete a notification list.
|
||||||
@@ -1412,6 +1540,16 @@ Show the current SMTP configuration.
|
|||||||
scadabridge --url <url> notification smtp list
|
scadabridge --url <url> notification smtp list
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `notification smtp create`
|
||||||
|
|
||||||
|
Create a new SMTP configuration. Requires Administrator access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge notification smtp create --server <host> --port <int> --auth-mode <mode> --from-address <email> [--tls-mode <mode>] [--credentials <value>] [--oauth2-authority <url>] [--oauth2-scope <scope>] [--transport <Smtp|Ews>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Uses the same settings as `smtp update`, without `--id`. This sends the distinct create contract; update with ID zero is not a creation operation. The server validates transport/authentication compatibility.
|
||||||
|
|
||||||
#### `notification smtp update`
|
#### `notification smtp update`
|
||||||
|
|
||||||
Update the SMTP configuration.
|
Update the SMTP configuration.
|
||||||
@@ -1445,22 +1583,35 @@ Show the current SMS (Twilio) configuration. The Twilio **Auth Token is never re
|
|||||||
scadabridge --url <url> notification sms list
|
scadabridge --url <url> notification sms list
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `notification sms create`
|
||||||
|
|
||||||
|
Create a new Twilio configuration. Requires Administrator access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge notification sms create --account-sid <sid> --auth-token <token> [--from-number <E.164-number>] [--messaging-service-sid <sid>] [--api-base-url <url>] [--connection-timeout-seconds <n>] [--max-retries <n>] [--retry-delay-seconds <n>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Account SID and auth token are required. Supply at least one sender: `--from-number` or `--messaging-service-sid`. Timeout must be positive; retries/delay must be nonnegative. Omitted policy settings use creation defaults. Explicit zero retry settings retain the outbox fallback semantics; they do not disable retries.
|
||||||
|
|
||||||
#### `notification sms update`
|
#### `notification sms update`
|
||||||
|
|
||||||
Update the SMS (Twilio) configuration.
|
Update the SMS (Twilio) configuration.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadabridge --url <url> notification sms update --id <int> --account-sid <string> --from-number <string> [--messaging-service-sid <string>] [--api-base-url <string>] [--auth-token <string>]
|
scadabridge --url <url> notification sms update --id <int> --account-sid <string> [--from-number <string>] [--messaging-service-sid <string>] [--api-base-url <string>] [--auth-token <string>] [--connection-timeout-seconds <n>] [--max-retries <n>] [--retry-delay-seconds <n>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
|--------|----------|-------------|
|
|--------|----------|-------------|
|
||||||
| `--id` | yes | SMS config ID |
|
| `--id` | yes | SMS config ID |
|
||||||
| `--account-sid` | yes | Twilio Account SID |
|
| `--account-sid` | yes | Twilio Account SID |
|
||||||
| `--from-number` | yes | Sender phone number (E.164) |
|
| `--from-number` | conditional | Sender phone number (E.164); provide this and/or `--messaging-service-sid` |
|
||||||
| `--messaging-service-sid` | no | Twilio Messaging Service SID. **Omitting it CLEARS the stored value** (the update overwrites it). |
|
| `--messaging-service-sid` | no | Twilio Messaging Service SID. **Omitting it CLEARS the stored value** (the update overwrites it). |
|
||||||
| `--api-base-url` | no | API base URL override (preserves existing if omitted) |
|
| `--api-base-url` | no | API base URL override (preserves existing if omitted) |
|
||||||
| `--auth-token` | no | Twilio Auth Token. **Omitting it PRESERVES the stored token.** Never printed back. |
|
| `--auth-token` | no | Twilio Auth Token. **Omitting it PRESERVES the stored token.** Never printed back. |
|
||||||
|
| `--connection-timeout-seconds` | no | Positive timeout; omission preserves the existing value |
|
||||||
|
| `--max-retries` | no | Nonnegative retry setting; omission preserves it; zero retains outbox fallback semantics |
|
||||||
|
| `--retry-delay-seconds` | no | Nonnegative delay; omission preserves it; zero retains outbox fallback semantics |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1952,6 +2103,56 @@ single `Property`/`Value` cell (deferred-work register row 57).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### `shared-schema` — Manage the shared JSON schema library
|
||||||
|
|
||||||
|
Reads require authentication; create/update/delete require Designer access.
|
||||||
|
|
||||||
|
#### `shared-schema list`
|
||||||
|
|
||||||
|
List all shared schemas.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge shared-schema list
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `shared-schema get`
|
||||||
|
|
||||||
|
Get one shared schema by ID.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge shared-schema get --id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `shared-schema create`
|
||||||
|
|
||||||
|
Create a schema from a UTF-8 JSON file.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge shared-schema create --name <name> --schema-file <path.json> [--scope <scope>]
|
||||||
|
```
|
||||||
|
|
||||||
|
The CLI validates file readability and JSON syntax; the server validates schema structure and uniqueness.
|
||||||
|
|
||||||
|
#### `shared-schema update`
|
||||||
|
|
||||||
|
Replace a schema's name, scope, and document.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge shared-schema update --id <id> --name <name> --schema-file <path.json> [--scope <scope>]
|
||||||
|
```
|
||||||
|
|
||||||
|
The CLI validates file readability and JSON syntax; the server validates schema structure and uniqueness. Omit `--scope` to clear it on update.
|
||||||
|
|
||||||
|
#### `shared-schema delete`
|
||||||
|
|
||||||
|
Delete a shared schema by ID.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge shared-schema delete --id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### `shared-script` — Manage shared scripts
|
### `shared-script` — Manage shared scripts
|
||||||
|
|
||||||
#### `shared-script list`
|
#### `shared-script list`
|
||||||
@@ -2270,9 +2471,9 @@ scadabridge --url <url> cached-call discard --site-id <string> --tracked-operati
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `secured-write` — Query two-person write requests
|
### `secured-write` — Manage two-person write requests
|
||||||
|
|
||||||
`secured-write list` reads the MxGateway secured-write queue and terminal history. It requires any of Operator, Verifier or Administrator and applies the caller's site scope. This group also exposes `submit`, `approve`, and `reject` through the same server-side authorization and separation-of-duties checks as the UI; see the parity additions below.
|
`secured-write list` reads the MxGateway secured-write queue and terminal history. It requires any of Operator, Verifier or Administrator and applies the caller's site scope. This group also exposes `submit`, `approve`, and `reject` through the same server-side authorization and separation-of-duties checks as the UI.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadabridge secured-write list --status Pending --site site-a --skip 0 --take 50
|
scadabridge secured-write list --status Pending --site site-a --skip 0 --take 50
|
||||||
@@ -2280,6 +2481,38 @@ scadabridge secured-write list --status Pending --site site-a --skip 0 --take 50
|
|||||||
|
|
||||||
`--status` and `--site` are optional filters. `--skip` defaults to 0; `--take` defaults to 200 and is clamped server-side to 1–500. Global connection, authentication and output-format options apply.
|
`--status` and `--site` are optional filters. `--skip` defaults to 0; `--take` defaults to 200 and is clamped server-side to 1–500. Global connection, authentication and output-format options apply.
|
||||||
|
|
||||||
|
#### `secured-write submit`
|
||||||
|
|
||||||
|
Submit a pending MxGateway write as an Operator. A distinct Verifier must approve or reject it.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge secured-write submit --site <identifier> --connection <name> --tag-path <path> --value-json <json> --value-type <type> [--comment <text>]
|
||||||
|
```
|
||||||
|
|
||||||
|
The first five options are required. JSON strings must include JSON quotes, for example `--value-json '"Auto"' --value-type String`. The server validates the type/value pairing and site scope and derives identity from authentication.
|
||||||
|
|
||||||
|
#### `secured-write approve`
|
||||||
|
|
||||||
|
Approve and execute a pending write as a Verifier. This can write to the real device.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge secured-write approve --id <long> [--comment <text>]
|
||||||
|
```
|
||||||
|
|
||||||
|
The 64-bit request ID is required. The server enforces a different principal from the submitter, expiry, site scope, and concurrent decision handling. Exit 0 requires `status: Executed`; a failed execution or invalid response exits 1, preserving response details. Authorization denial exits 2.
|
||||||
|
|
||||||
|
#### `secured-write reject`
|
||||||
|
|
||||||
|
Reject a pending write as a Verifier.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scadabridge secured-write reject --id <long> [--comment <text>]
|
||||||
|
```
|
||||||
|
|
||||||
|
The 64-bit request ID is required. The server enforces separation of duties and site scope. A rejection does not execute the device write.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### `flow-debug` — Trace an inbound API, script, or value-change flow end to end
|
### `flow-debug` — Trace an inbound API, script, or value-change flow end to end
|
||||||
|
|
||||||
The CLI face of the **Flow Debugger** (design `docs/plans/2026-09-05-flow-debugger-design.md`,
|
The CLI face of the **Flow Debugger** (design `docs/plans/2026-09-05-flow-debugger-design.md`,
|
||||||
@@ -2411,6 +2644,15 @@ run's outbound API / DB / notification rows, which are always audited.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Deployment deadlines
|
||||||
|
|
||||||
|
`deploy instance`, `instance deploy`, `deploy artifacts`, `site deploy-artifacts`,
|
||||||
|
and `deploy site` defer the overall deadline to the Management API. They keep the
|
||||||
|
bounded socket-connect timeout but do not impose the ordinary 30-second client
|
||||||
|
request deadline on a deployment that may still be applying. The API's configured
|
||||||
|
long-running timeout remains authoritative. Ordinary commands retain a 30-second
|
||||||
|
client deadline; explicit bundle/Flow Debugger timeouts retain their own policies.
|
||||||
|
|
||||||
## Architecture Notes
|
## Architecture Notes
|
||||||
|
|
||||||
The CLI connects to the Central cluster over **HTTP** (`ManagementHttpClient`), posting to the `/management` endpoints at the configured `managementUrl` — normally the Traefik load balancer, which routes to the active central node. It carries HTTP **Basic** credentials from `--username`/`--password`. Central's endpoint handler asks the `ManagementActor` in-process via `ManagementActorHolder`.
|
The CLI connects to the Central cluster over **HTTP** (`ManagementHttpClient`), posting to the `/management` endpoints at the configured `managementUrl` — normally the Traefik load balancer, which routes to the active central node. It carries HTTP **Basic** credentials from `--username`/`--password`. Central's endpoint handler asks the `ManagementActor` in-process via `ManagementActorHolder`.
|
||||||
@@ -2419,109 +2661,21 @@ There is no Akka dependency in the CLI at all: it does not join the cluster, doe
|
|||||||
|
|
||||||
An `HttpClient` is created per command invocation and disposed when the command completes.
|
An `HttpClient` is created per command invocation and disposed when the command completes.
|
||||||
|
|
||||||
Role enforcement is applied by the ManagementActor on the server side. The CLI authenticates against LDAP using `--username` / `--password`, resolves LDAP group memberships, then maps groups to ScadaBridge roles (Admin, Design, Deployment) via role mappings configured in the security settings. Operations require the appropriate role — for example, creating templates requires `Design`, deploying requires `Deployment`. In the test environment, use the `multi-role` user (password: `password`) which has all three roles.
|
Role enforcement is applied by the ManagementActor on the server side. The CLI sends `--username` / `--password` as HTTP Basic credentials. The server performs LDAP authentication, resolves group memberships, and maps them to roles such as Administrator, Designer, and Deployer. Operations require the appropriate role — for example, creating templates requires `Designer`, deploying requires `Deployer`. In the test environment, use the `multi-role` user (password: `password`) which has all three roles.
|
||||||
|
|
||||||
## Issues & Missing Features
|
## Issues & Missing Features
|
||||||
|
|
||||||
If you encounter bugs, unexpected behavior, or missing features in the CLI, log them in [`cli_issues.md`](../../cli_issues.md) in the project root. Include a brief description, the command involved, and any relevant error output.
|
If you encounter bugs, unexpected behavior, or missing features in the CLI, log them in [`cli_issues.md`](../../cli_issues.md) in the project root. Include a brief description, the command involved, and any relevant error output.
|
||||||
|
|
||||||
|
|
||||||
## Management parity additions (2026-09-10)
|
## Management parity additions (2026-09-10)
|
||||||
|
|
||||||
The production CLI exposes all 149 HTTP-eligible management contracts across 20
|
All 149 HTTP-eligible management contracts are exposed across 20 command groups.
|
||||||
command groups. `CliApplication.BuildRoot()` is used both by the executable and
|
The new operations are documented in their main command sections above, including
|
||||||
by the HTTP-dispatch parity test. Site-only `ReadTagValues` and the retired
|
[shared schemas](#shared-schema--manage-the-shared-json-schema-library),
|
||||||
`ResolveRoles` flow are not accepted by `POST /management`; binding tests continue
|
[notifications](#notification--manage-notification-lists), and
|
||||||
using the site's existing transport.
|
[secured writes](#secured-write--manage-two-person-write-requests).
|
||||||
|
[Deployment deadlines](#deployment-deadlines) apply to all deployment aliases.
|
||||||
|
|
||||||
### Shared schema library
|
`CliApplication.BuildRoot()` constructs the executable command tree and the tree
|
||||||
|
used by the HTTP parity test. Site-only `ReadTagValues` and retired `ResolveRoles`
|
||||||
```sh
|
are excluded from `POST /management`; site binding tests retain their transport.
|
||||||
scadabridge shared-schema list
|
|
||||||
scadabridge shared-schema get --id 7
|
|
||||||
scadabridge shared-schema create --name PumpPayload --schema-file ./pump.schema.json --scope Pump
|
|
||||||
scadabridge shared-schema update --id 7 --name PumpPayload --schema-file ./pump.schema.json
|
|
||||||
scadabridge shared-schema delete --id 7
|
|
||||||
```
|
|
||||||
|
|
||||||
Create/update read UTF-8 JSON from `--schema-file`; the server validates schema
|
|
||||||
structure and uniqueness. Update replaces the name, schema, and scope; omitting
|
|
||||||
`--scope` clears it. Mutations require Designer access.
|
|
||||||
|
|
||||||
### Individual notification edits and provider creation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
scadabridge notification rename --id 4 --name "Shift engineers"
|
|
||||||
scadabridge notification recipient add --list-id 4 --name "On-call engineer" --email engineer@example.com
|
|
||||||
scadabridge notification recipient delete --id 18
|
|
||||||
scadabridge notification smtp create --server smtp.example.com --port 587 --auth-mode None --from-address alerts@example.com
|
|
||||||
scadabridge notification sms create --account-sid AC0123456789abcdef0123456789abcdef --from-number +15551234567 --auth-token "<token>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Rename and individual recipient edits preserve unrelated recipient IDs and names.
|
|
||||||
Recipient add accepts `--email` and/or `--phone`; the server checks them against
|
|
||||||
its owning list's channel. Existing `notification update` retains its whole-list
|
|
||||||
recipient replacement semantics.
|
|
||||||
|
|
||||||
SMTP/SMS create accept their update options except `--id` and send distinct create
|
|
||||||
contracts. SMS create requires an auth token and a sender (`--from-number` and/or
|
|
||||||
`--messaging-service-sid`). Provider creation/update requires Administrator access.
|
|
||||||
|
|
||||||
SMS create and update also accept:
|
|
||||||
|
|
||||||
| Option | Meaning |
|
|
||||||
|---|---|
|
|
||||||
| `--connection-timeout-seconds` | Positive connection timeout |
|
|
||||||
| `--max-retries` | Nonnegative retry setting |
|
|
||||||
| `--retry-delay-seconds` | Nonnegative retry delay |
|
|
||||||
|
|
||||||
Omission preserves existing update values or uses creation defaults. Explicit
|
|
||||||
zero retry settings retain the existing outbox fallback semantics; they do not
|
|
||||||
promise that delivery will never retry. Existing secret-preservation and
|
|
||||||
messaging-service-ID clearing behavior on update is unchanged.
|
|
||||||
|
|
||||||
### Template, instance, and connection edits
|
|
||||||
|
|
||||||
```sh
|
|
||||||
scadabridge template resolved-members --id 3
|
|
||||||
scadabridge template move --id 3 --folder-id 8
|
|
||||||
scadabridge template native-alarm-source update --id 5 --name Pressure --connection Plc --source-ref Pressure --locked
|
|
||||||
scadabridge instance attribute-override delete --instance-id 7 --attribute Module.Speed
|
|
||||||
scadabridge instance native-alarm-source list --instance-id 7
|
|
||||||
scadabridge data-connection move --id 9 --site-id 2
|
|
||||||
```
|
|
||||||
|
|
||||||
`resolved-members` inspects effective inherited template members, without applying
|
|
||||||
composition or instance overrides. Omit `--folder-id` to move a template to the
|
|
||||||
root. Native-alarm-source update replaces all contract fields: optional `--filter`
|
|
||||||
and `--description` clear when omitted, and omitted `--locked` means false.
|
|
||||||
Individual attribute-override deletion preserves other overrides. Server-side
|
|
||||||
role, site-scope, locking, and dependency checks remain authoritative.
|
|
||||||
|
|
||||||
### Secured-write lifecycle
|
|
||||||
|
|
||||||
```sh
|
|
||||||
scadabridge secured-write submit --site site-a --connection Plc --tag-path Pump.Setpoint --value-json 42 --value-type Int32 --comment "Approved operating procedure"
|
|
||||||
scadabridge secured-write approve --id 123 --comment "Verified setpoint"
|
|
||||||
scadabridge secured-write reject --id 124 --comment "Incorrect target"
|
|
||||||
```
|
|
||||||
|
|
||||||
Submit requires Operator access; approve/reject require Verifier access. The
|
|
||||||
server derives identity from authentication and enforces a different principal
|
|
||||||
for the verification decision. Approval can execute a real device write.
|
|
||||||
`--value-json` must be valid JSON; the server validates the value/type pairing.
|
|
||||||
IDs for approve/reject are 64-bit integers; `--comment` is optional.
|
|
||||||
|
|
||||||
Approval exits zero only for an `Executed` response. A server-reported execution
|
|
||||||
failure exits one while retaining the response details; malformed or unexpected
|
|
||||||
approval responses also fail. Authorization denial exits two. JSON/table output
|
|
||||||
continues to use the standard formatter.
|
|
||||||
|
|
||||||
### Deployment deadline policy
|
|
||||||
|
|
||||||
`deploy instance`, `instance deploy`, `deploy artifacts`, `site deploy-artifacts`,
|
|
||||||
and `deploy site` defer the overall deadline to the Management API. They keep the
|
|
||||||
bounded socket-connect timeout but do not impose the ordinary 30-second client
|
|
||||||
request deadline on a deployment that may still be applying. The API's configured
|
|
||||||
long-running timeout remains authoritative. Ordinary commands retain a 30-second
|
|
||||||
client deadline; explicit bundle/Flow Debugger timeouts retain their own policies.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user