Synchronize CLI documentation with management command parity

This commit is contained in:
Joseph Doherty
2026-09-10 01:20:12 -04:00
parent 0cc9db9e94
commit 2b9a7c3ca0
4 changed files with 376 additions and 169 deletions
+50 -55
View File
@@ -1,12 +1,12 @@
# 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
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.
@@ -20,7 +20,11 @@ Direct-to-node access (`http://localhost:9001` for `central-a`, `http://localhos
### 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
@@ -30,46 +34,37 @@ Role enforcement is applied by the `ManagementActor`. Operations require the app
| Role | Covers |
|------|--------|
| `Admin` | Security settings, site management, SMTP config, audit-config queries |
| `Design` | Templates, shared scripts, external systems, DB connections, API methods, notification lists |
| `Deployment` | Instance lifecycle (deploy, enable, disable, delete), data connection bindings, bundle import/export |
| `OperationalAudit` | Reading the Audit Log (`audit query`) |
| `AuditExport` | Exporting the Audit Log (`audit export`) |
| `Administrator` | Security settings, site mutations, SMTP/SMS provider changes, bundle preview/import, certificate management, audit-config queries |
| `Designer` | Template/schema/script authoring, external systems, DB connections, API methods, notification lists, connection design, bundle export |
| `Deployer` | Instance lifecycle and overrides, bindings, deployment, runtime debugging, parked-call decisions |
| `Operator` | Submit secured writes |
| `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`.
### 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
`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
rootCommand.Add(TemplateCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(InstanceCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(SiteCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
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.
Management actions use `CommandHelpers.ExecuteCommandAsync` for URL/credential
resolution, HTTP dispatch, formatting, and exit codes. Deployment aliases defer
the overall deadline to the server; bundles and Flow Debugger have explicit
policies. Audit REST and SignalR actions use their respective transports.
`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
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 deploy --id 7
```
@@ -109,26 +104,26 @@ scadabridge instance deploy --id 7
| 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` |
| `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` |
| `site` | `list`, `get`, `create`, `delete`, `deploy-artifacts`; `area list/create/update/delete` | `Admin` |
| `deploy` | `instance`, `artifacts`, `status` | `Deployment` |
| `data-connection` | `list`, `get`, `create`, `update`, `delete`, `move`; `browse`, `search`, `verify-endpoint`, `certs` | `Design` / `Deployment` |
| `external-system` | `list`, `get`, `create`, `update`, `delete` | `Design` |
| `notification` | `list`, `get`, `create`, `update`, `delete`, `rename`; `recipient add/delete`; `smtp list/create/update`; `sms list/create/update` | `Design` / `Admin` |
| `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`, `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`, `update`, `delete`, `deploy-artifacts`; `area list/create/update/move/delete` | Administrator site mutations; Designer/Deployer area mutations; Deployer artifact deployment |
| `deploy` | `instance`, `site`, `artifacts`, `status` | `Deployer` |
| `data-connection` | `list`, `get`, `create`, `update`, `delete`, `move`; `browse`, `search`, `verify-endpoint`, `certs` | `Designer` / `Deployer` |
| `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` | `Designer` / `Administrator` |
| `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 |
| `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 |
| `shared-script` | `list`, `get`, `create`, `update`, `delete` | `Design` |
| `db-connection` | `list`, `get`, `create`, `update`, `delete` | `Design` |
| `api-method` | `list`, `get`, `create`, `update`, `delete` | `Design` |
| `bundle` | `export`, `preview`, `import` | `Deployment` |
| `audit` | `query`, `export`, `verify-chain` | `OperationalAudit` / `AuditExport` |
| `shared-script` | `list`, `get`, `create`, `update`, `delete` | `Designer` |
| `db-connection` | `list`, `get`, `create`, `update`, `delete` | `Designer` |
| `api-method` | `list`, `get`, `create`, `update`, `delete` | `Designer` |
| `bundle` | `export`, `preview`, `import` | Designer export; Administrator preview/import |
| `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` |
| `health` | `summary`, `site`, `event-log`, `parked-messages` | `Deployment` |
| `debug` | `snapshot`, `stream` | `Deployment` |
| `health` | `summary`, `site`, `event-log`, `parked-messages` | Authenticated reads with server-side scope checks |
| `debug` | `snapshot`, `stream` | `Deployer` |
### Selected examples
@@ -198,10 +193,10 @@ The management URL is resolved in this order: `--url` flag → `SCADABRIDGE_MANA
## 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.
- [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.
- [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.
- [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 (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.
- [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).
@@ -226,7 +221,7 @@ The central node or Traefik is not reachable at the configured URL. Verify the c
### 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
@@ -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`,
`/tmp/parity-deploy.log`, `/tmp/parity-rig-before.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.
+60 -9
View File
@@ -48,9 +48,10 @@ The CLI uses a hierarchical subcommand structure mirroring the Management Servic
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
Commands identify targets by integer ID, site identifier, connection name, or
canonical member name as shown in their help. Most edits use individual flags;
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
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 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 update --id <id> [--name <name>] [--description <desc>] [--parent-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 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 update --id <id> --name <name> --connection <name> --source-ref <ref> [--filter <expr>] [--description <desc>] [--locked]
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 bind-module --id <id> --module <name> --connection <id> --object <path> [--dry-run]
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 alarm-override set --instance-id <id> --alarm <name> [--trigger-config <json>] [--priority <n>]
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 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 move --id <id> --site-id <destination-site-id>
```
#### 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 get --id <id>
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 smtp list
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
```
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.
### 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
```
scadabridge shared-script list
@@ -393,22 +435,31 @@ table).
### Secured-Write Commands (two-person MxGateway writes)
Query two-person (MxGateway) secured writes — the pending queue plus terminal history.
Read-only; gated any-of **Operator** / **Verifier** / **Administrator** (the history
exposes process-sensitive tag values, so it is not open to every authenticated user).
Manage two-person MxGateway writes. Listing is gated any-of **Operator**,
**Verifier**, or **Administrator** because the history exposes process-sensitive
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 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
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
`totalCount` (rows matching the filters, ignoring paging) alongside the returned page.
Overdue `Pending` rows are swept to `Expired` opportunistically each time the list is
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