Files
qinglong/deploy/console/ql3-cluster-copilot/README.md
T

345 lines
17 KiB
Markdown

# Cluster read-only field Console
This Console is an operator-workstation process, not a resident QingLong
service. Native execution serves digest-bound assets on an ephemeral
`127.0.0.1` port and forwards only a fixed vocabulary of Run, Task, Workflow
and Copilot reads to existing Cluster APIs. It never accepts a browser-provided
URL or method. Do not deploy it as a Kubernetes workload, Ingress, shared LAN
listener, Edge component or legacy 2.x Web route.
Run cancellation availability is a second, explicit authority. It is disabled
by default. Supplying a separate Run management mTLS config and short-lived
User assertion adds only status, blocked-list and inspect reads to the same
loopback process; rearm, stop and retry remain absent.
Worker observation is a third, independent authority and is also disabled by
default. Supplying its generic Worker management mTLS config and short-lived
User assertion adds only a fixed 16-item list and point inspect to the same
loopback process. Credential mutation, drain, revoke, background polling and
automatic pagination remain absent.
Use `ql3-cluster-admin` from the same independently verified Admin release as
the Cluster deployment. D-328 also supports the image-carried
`docker-loopback.sh`: it uses an explicit container-only listener but publishes
the same port exclusively on host `127.0.0.1`. Arbitrary `0.0.0.0`, host
networking and LAN publication remain forbidden.
## Verify the distribution
The multi-architecture `qinglong3-cluster-admin@sha256:…` OCI image is the
distribution artifact. It already carries the exact launcher, examples and
this document under `/opt/qinglong/share/ql3-copilot-console/`; there is no
second Node archive or package dependency graph to trust.
From the exact reviewed source tag, run `verify-release.sh` with the immutable
image digest, repository, 40-hex source revision and full tag ref. The verifier
requires `cosign` and authenticated `gh`, then independently checks the keyless
release-workflow identity, SLSA provenance, CycloneDX SBOM and digest-bound OS
vulnerability evidence. It rejects tags and mutable image references.
```sh
deploy/console/ql3-cluster-copilot/verify-release.sh \
ghcr.io/replace-owner/qinglong3-cluster-admin@sha256:REPLACE_64_HEX \
replace-owner/qinglong \
REPLACE_40_HEX_SOURCE_REVISION \
refs/tags/v3.0.0-alpha.1
```
For a release acceptance ceremony, prefer the source-tag workstation runner.
Create a current-owner `0700` report directory and a canonical `0600` file
containing only a short-lived GitHub token. Pass canonical absolute executable
paths (resolve Homebrew symlinks first). The token is sent only to the four
`gh attestation verify` children, never in argv, the report or the `cosign` and
`docker` environments.
```sh
node scripts/ql3-cluster-admin-release-workstation-ceremony.cjs \
--image=ghcr.io/replace-owner/qinglong3-cluster-admin@sha256:REPLACE_64_HEX \
--repository=replace-owner/qinglong \
--source-revision=REPLACE_40_HEX_SOURCE_REVISION \
--source-ref=refs/tags/v3.0.0-alpha.1 \
--cosign=/canonical/absolute/cosign \
--gh=/canonical/absolute/gh \
--docker=/canonical/absolute/docker \
--github-token-file=/absolute/private/release/github-token \
--output=/absolute/private/release/admin-ceremony.json
```
In addition to the signature, provenance, CycloneDX, OS-vulnerability and
source-derived release-candidate contract
checks, the runner pulls the same immutable digest, confirms its local
`RepoDigests` binding, and runs the image-carried `evidence-verify` command on
a fixed non-sensitive vector with network disabled, a read-only root, no
capabilities, no-new-privileges, 128 MiB, 0.25 CPU and 32 PIDs. It writes one
new `0600` low-sensitive report and will not replace an existing file.
Audit that report independently against the expected public release identity:
```sh
node scripts/ql3-cluster-admin-release-workstation-ceremony-audit.cjs \
--report=/absolute/private/release/admin-ceremony.json \
--image=ghcr.io/replace-owner/qinglong3-cluster-admin@sha256:REPLACE_64_HEX \
--repository=replace-owner/qinglong \
--source-revision=REPLACE_40_HEX_SOURCE_REVISION \
--source-ref=refs/tags/v3.0.0-alpha.1
```
The offline audit verifies canonical encoding, exact structure, tool and
transcript digests, isolation claims and expected release binding. It does not
replay registry or transparency-log queries and cannot turn the unsigned local
report into an attestation or action authority. Keep the report private even
though it contains no credential or workstation identity.
After verification, pull that exact digest. The signature covers the embedded
host launcher and templates as part of the image filesystem. Operators may
either use the launcher from the matching reviewed tag or extract its exact
image-carried copy with `docker create` plus `docker cp` before execution.
## Prepare private authority
Create an absolute canonical directory with mode `0700`. For native execution
it is owned by the current operator; for the image-carried launcher it and all
files are owned by UID/GID `10001:10001`. Copy `client-config.example.json` to
`client.json`, install the reviewed
Cluster API CA as `ca.pem`, and install a separately issued `ql3c_` Project API
credential as `credential`. Give the credential only `run.read`, `task.read`
and `artifact.read`; `run.read` covers Run and Workflow observations, while
`task.read` covers Task list/detail and `artifact.read` covers an explicitly
requested Copilot output. The Console has no route for Run/Workflow start,
diagnosis creation or cancellation even if a wider credential is supplied.
To enable the optional cancellation drill-down, copy
`run-management-client-config.example.json` to `run-management-client.json`,
install its CA, client certificate and private key, and issue a short-lived
strong User assertion with only `run.read` into
`run-management-assertion.jwt`. These files are independent of the Project API
credential. Supplying only one of config/assertion is rejected before a
listener or Cluster request is created. The assertion is reread for every
explicit click so it can be rotated or removed while the Console is running.
To enable Worker observation, copy
`worker-management-client-config.example.json` to
`worker-management-client.json`, install its separate CA, client certificate
and private key, and issue a short-lived strong User assertion with only
`worker.manage` into `worker-management-assertion.jwt`. Its endpoint must be
the D-374 canonical `/api/v3/workers/management`; the Console cannot accept the
legacy credential-management path or a credential mutation command file.
To enable Plugin Package installation observation, copy
`package-management-client-config.example.json` to
`package-management-client.json`, install its CA, and issue a short-lived
strong User assertion with only `package.manage` into
`package-management-assertion.jwt`. Its endpoint is fixed to the canonical
`/api/v3/plugin-packages/management`. This authority is independent of the
Project, Run and Worker files; the Console exposes no Package command file or
lifecycle mutation.
Create an independent 256-bit browser session key without placing its value in
argv or an environment variable:
```sh
install -d -m 0700 /absolute/private/ql3-copilot-console
umask 077
node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))' > /absolute/private/ql3-copilot-console/session
chmod 0600 /absolute/private/ql3-copilot-console/client.json /absolute/private/ql3-copilot-console/ca.pem /absolute/private/ql3-copilot-console/credential /absolute/private/ql3-copilot-console/session
```
If optional Run management reads are enabled, apply the same owner-private,
canonical, non-symlink `0600` rule to `run-management-client.json`,
`run-management-ca.pem`, `run-management-client.crt`,
`run-management-client.key` and `run-management-assertion.jwt`.
Apply the same rule to `worker-management-client.json`,
`worker-management-ca.pem`, `worker-management-client.crt`,
`worker-management-client.key` and `worker-management-assertion.jwt` when
Worker observation is enabled.
Apply the same rule to `package-management-client.json`,
`package-management-ca.pem` and `package-management-assertion.jwt` when Package
observation is enabled.
Every file must be a current-owner, non-symlink, canonical regular file. The
session file contains exactly 43 base64url characters and no newline. It is a
browser-to-loopback secret only; it cannot authenticate to the Cluster API.
The `ql3c_` credential remains in the BFF process and is reread for every
upstream request so file rotation takes effect without browser disclosure.
## Check and start
Run the preflight first:
```sh
ql3-cluster-admin copilot-console --check \
--config /absolute/private/ql3-copilot-console/client.json \
--credential /absolute/private/ql3-copilot-console/credential \
--session /absolute/private/ql3-copilot-console/session
```
Append both flags to preflight and serve when the optional authority is
intended:
```sh
--run-management-config /absolute/private/ql3-copilot-console/run-management-client.json \
--run-management-assertion /absolute/private/ql3-copilot-console/run-management-assertion.jwt
```
Worker observation uses its own pair:
```sh
--worker-management-config /absolute/private/ql3-copilot-console/worker-management-client.json \
--worker-management-assertion /absolute/private/ql3-copilot-console/worker-management-assertion.jwt
```
Package observation also uses an independent pair:
```sh
--package-management-config /absolute/private/ql3-copilot-console/package-management-client.json \
--package-management-assertion /absolute/private/ql3-copilot-console/package-management-assertion.jwt
```
It validates every configured private authority and performs one unauthenticated
TLS 1.3 `GET /readyz`. It does not open the Console listener or reveal paths,
endpoint, credential, Project or Cluster identity.
Start a session with an ephemeral port:
```sh
ql3-cluster-admin copilot-console \
--config /absolute/private/ql3-copilot-console/client.json \
--credential /absolute/private/ql3-copilot-console/credential \
--session /absolute/private/ql3-copilot-console/session \
--port=0
```
Open only the exact `http://127.0.0.1:<port>` origin printed by the process,
then enter the session key from the private file. The browser keeps it only in
page memory; reloading locks the page. Stop the process with `SIGINT` or
`SIGTERM`, then remove or rotate the session file.
Unlock performs one authenticated same-origin
`POST /api/v1/session/capabilities` with one fixed schema-only body. This is a local configuration read with
`upstreamReads: 0`: it neither contacts the Cluster nor creates evidence. The
page hides optional operation groups that the process did not enable, while the
BFF independently checks the same immutable operation set before invoking any
executor. A disabled operation is masked as `404` even if a caller constructs
the fixed route manually. The thirteen base reads are mandatory; each optional
Run, Worker or Package authority group must be enabled completely or remains
disabled.
The BFF accepts at most two concurrent reads and sixteen connections, rejects
a third request without queueing, caps request bodies at 4 KiB and responses at
approximately 2 MiB, disables cache/cookies/frames/workers, and never polls.
Model text is rendered as plain text and remains untrusted advice. These limits
keep the workstation surface bounded, but this Cluster-only product is still
excluded from small router Edge/Standalone artifacts.
The default page exposes thirteen exact operations: Copilot `inspect|output`; Run
list/detail/events/steps; Task list/detail; and Workflow list plus Workflow Run
list/detail/events/steps. List responses use 32-row pages and offer an explicit
next-page read only when the upstream cursor says more data exists. There is no
automatic cascade from a list to details, steps or events, so each authority
read remains visible and intentional.
Explicit Run management authority raises the available vocabulary to sixteen:
one Project cancellation status, one fixed 16-item blocked snapshot page and
one low-sensitive Run cancellation inspection. An `attention_required` status
offers a user-clicked blocked-list step; each returned Run offers a user-clicked
inspect step. Pagination is also click-only. There is no polling, automatic
page traversal, bulk inspection or mutation route.
Explicit Worker management authority adds `worker_list|worker_inspect`. The
list is fixed at 16 items and exposes a next cursor only as a new user-clicked
read; each listed Worker can be inspected only by another explicit click. The
projection contains bounded lifecycle, compatibility, architecture, protocol
and capacity facts, but no credential, raw capability, label or Secret.
Explicit Package management authority adds `package_list|package_inspect`, for
a maximum vocabulary of twenty operations when every optional authority is
enabled. The list is fixed at 16 installations with click-only pagination and
click-only inspection. The product projection includes Package version,
installation state, availability and bounded recovery codes, but omits
installation IDs, locks, record digests, transport identity and every Package
mutation. Package authority remains disabled by default.
## Export a redacted evidence bundle
After at least one successful read, **Export redacted bundle** creates one
UTF-8 JSON file entirely in browser memory. Export performs zero BFF or Cluster
requests and includes only the evidence already visible in the current page.
The ledger retains at most the newest sixteen entries and 8 MiB of canonical
raw facts; reaching either limit removes the oldest visible and in-memory
entry. The generated file is capped at 512 KiB.
The fixed allowlist keeps operation, local observation time, reviewed status
enums, bounded numeric/boolean facts, pagination state and typed aliases.
Project, Run, Task, Workflow, Package, Step, Artifact, request and digest values
become per-bundle aliases without an exported mapping. Free text, names,
paths/URLs, commands, inputs/outputs, environment, errors/messages, credentials,
tokens, authorization, unknown fields and Copilot model text are omitted. A
canonical SHA-256 for each omitted raw fact allows a later local comparison
without embedding that fact.
The top-level SHA-256 detects changes to the redacted JSON, but it is not a
server signature, durable audit, origin attestation or action authority. Review
the file before sharing it. Generation uses no upload, clipboard/share API,
browser storage, worker, timer or service-side temporary file. **Clear page**
removes the current in-memory ledger without sending a request.
Verify the downloaded file independently with the same reviewed Admin release:
```sh
ql3-cluster-admin evidence-verify \
--bundle=/absolute/qinglong-cluster-evidence.json
```
The verifier performs one offline read-only file read, rejects noncanonical
JSON and structural/redaction/typed-alias drift, and independently recomputes
the bundle digest. Its output deliberately says that raw fact digests were not
recomputed because those sensitive facts are absent; it does not claim a
server signature, attestation, durable audit or action authority. The command
does not upload, mutate or write the bundle.
## Run the verified image
Create a dedicated Docker network whose egress is restricted by the host
firewall to DNS and the exact Cluster API destination. Copy
`host-environment.example.json` values into the launcher environment, replacing
the image with the verified digest and selecting one unused host port. The
launcher rejects `bridge|default|host|none`, mutable tags, noncanonical private
roots, ports outside `1024..65535` and unknown resource classes.
The image launcher keeps Run management disabled unless
`QL3_COPILOT_CONSOLE_RUN_MANAGEMENT=enabled` is set. Enabled mode reads
`run-management-client.json` and `run-management-assertion.jwt` from the same
read-only private mount; all certificate paths in the config must point into
that mount. `disabled` is the only default and unknown values fail closed.
Worker observation follows the independent
`QL3_COPILOT_CONSOLE_WORKER_MANAGEMENT=enabled` switch and reads only its
Worker config/assertion pair. Package observation follows
`QL3_COPILOT_CONSOLE_PACKAGE_MANAGEMENT=enabled` and reads only its Package
config/assertion pair. Enabling one management authority does not enable either
of the others.
| Resource class | Memory | CPU | PIDs | Console reads |
| --- | ---: | ---: | ---: | ---: |
| `compact` | 192 MiB | 0.25 | 32 | 2, no queue |
| `standard` | 512 MiB | 1 | 64 | 2, no queue |
Validate private authority and the upstream unauthenticated TLS 1.3 readiness
route without opening or publishing a listener:
```sh
deploy/console/ql3-cluster-copilot/docker-loopback.sh check
```
Then start the foreground session:
```sh
deploy/console/ql3-cluster-copilot/docker-loopback.sh serve
```
The launcher fixes non-root UID, read-only root, no capabilities,
no-new-privileges, bounded memory/CPU/PIDs, an 8 MiB noexec tmpfs, one read-only
private mount and `--pull never`. `serve` alone adds
`--publish 127.0.0.1:<port>:<port>/tcp`; `check` publishes nothing. The
container listener is reachable only through this reviewed publication and
continues to require the 256-bit browser session token plus exact Host/Origin.