18 KiB
Requirements Document
Introduction
XCE operators need a tool to build and deploy cluster configuration. Currently, YAML config files (configs/{hosts,queues,resources,users}.yaml) exist with JSON schemas for validation, but no tool compiles them into the ClusterConfig Cap'n Proto binary the controller expects. The controller-side OP_upgradeConfig handler exists (config_repl.c — receives binary, verifies SHA-256, pushes to standbys, awaits majority ACK), but no CLI sends it.
xceadmin is a Python CLI that fills this gap: config build compiles YAML into cluster.bin, config push sends it to a controller over TLS+capnp.
Config source of truth is git-managed YAML. cluster.bin is a derived artifact — deterministic for the same inputs, never committed to git.
Boundary Context
-
In scope:
- YAML →
ClusterConfigcapnp binary compilation (config build) - TLS+capnp config deployment to controller (
config push) - Auto primary discovery via controller redirect reply
- JSON Schema validation of YAML inputs
- Reserved name defense-in-depth (
"lost_and_found") xcecommonshared Python package (capnp schema, wire protocol, config models)- Integration with xce-build and xce-deploy-ha skills
- Nix dev shell: Python package dependencies
config diff— human-readable diff of two built config binaries (nice-to-have)config push --dry-run— validate without committing (nice-to-have)
- YAML →
-
Out of scope:
- Controller-side config replication, snapshot, restart logic (owned by
config-upgrade-snapshot) - Entity-bearing snapshot payload (owned by
entity-bearing-snapshots) - Performance tuning / per-instance parameter extraction (separate work item) — specifically
port, thejobmgr/workermgr/qmgr/resmgrhash-table-size fields, andxstrings(xstring hash size). These are not cluster-shared: each controller instance is expected to set them locally, soxceadminnever reads, accepts, or writes them (see Requirement 1.8). candidates(peer/standby list) — changes independently and more frequently than the rest of cluster config (adding/removing a standby shouldn't require aconfig build/config pushcycle);xceadminnever reads, accepts, or writes it (see Requirement 1.10). Peer membership is owned elsewhere (mechanism TBD — see brief.md Open Design Question #3).configGenerationmanagement — fully controller-owned (see Adjacent Expectations below);xceadminnever reads, increments, or overrides it.- xcecli Python rewrite (future, separate spec)
- Controller config version history, rollback safety validation, config activation status (owned by
config-upgrade-snapshot, see Future Roadmap in brief.md)
- Controller-side config replication, snapshot, restart logic (owned by
-
Adjacent expectations:
- Controller
OP_upgradeConfighandler (config_repl.c) exists and is operational. On receivingUpgradeConfigRequest{sha256, bytes}:- SHA-256 transit verification
- Cap'n Proto decode + reserved name check (
"lost_and_found") - Auto-increment generation:
new_gen = current_gen + 1, re-encode binary — the primary is the sole authority forconfigGeneration; whatever valuexceadminships incluster.binis discarded and replaced - Save to disk, replicate to standbys, await majority ACK
- Commit
WAL_CONFIG_MARKER, signal controller restart - Return
UpgradeConfigReply{versionTag, ackedCount, error}
- Controller redirect: Standby returns
redirectHost/redirectPortin theRequestReplyunion — xceadmin follows the redirect to reach the primary. - Per-instance field preservation — RESOLVED structurally, not by controller behavior:
port,candidates, the hash-table-size fields, andxstringsare being removed from theClusterConfigcapnp struct entirely, owned by the newclusterconfig-schema-splitspec. Once that lands, there is no zero-default-clobber risk to preserve against — these fields simply won't exist incluster.bin. - ProbeConfigTag RPC (
config_repl.c:1774): Separate direct-capnp RPC (not inRequestunion) for reading current config tag. Used byconfig push --dry-runto validate before committing. controller.capnpschema is the single source of truth forClusterConfig,Request/Replyunions, and all OP codes.config-upgrade-snapshotspec will extend the controller-side pipeline with: entity-bearing snapshots after config change, rollback safety validation, version history tracking, config activation status.- Blocked controller-side gaps (NOT owned by admin-config-tool):
- Entity-bearing snapshot after config upgrade (blocked on
entity-bearing-snapshotsspec) - Config rollback safety (e.g., reject rollback while jobs reference deleted queues)
- Config version history query (
xceadmin config history)
- Entity-bearing snapshot after config upgrade (blocked on
- Dependency on
clusterconfig-schema-split— split by subcommand:config build: no dependency. Req 1.8/1.10 already exclude these fields from YAML/CLI regardless of schema state — safe to build in parallel, ship anytime.config push: soft-blocked in practice. Untilclusterconfig-schema-splitlands (fields actually removed from the schema) or someone independently verifiesconfig_repl.c'sdo_upgrade_config_with_bytespreserves per-instance fields on a zero-valued push, runningxceadmin config pushagainst a real cluster risks silently wipingcandidates(peer list — HA/Raft membership),port, and perf-tuning on every node it touches. That's not a hypothetical: nobody has checked the controller does the right thing here today. Recommendclusterconfig-schema-split(or equivalent verification) land beforeconfig pushis exercised against anything but a disposable test cluster;config buildand the rest of the CLI can proceed in parallel.
- Controller
Requirements
Requirement 1: Config Build
Objective: As an XCE operator, I want to compile YAML config files into a ClusterConfig capnp binary, so that I can produce a deployable cluster configuration from human-editable source files.
Acceptance Criteria
- When the operator runs
xceadmin config build <configs_dir>, the system shall scan the directory for YAML config files (hosts.yaml,queues.yaml,resources.yaml,users.yaml), validate each against its corresponding JSON schema from theschema/subdirectory, map the validated data to theClusterConfigcapnp struct, serialize it to binary, and write the result tocluster.binin the current working directory. - When the operator specifies
-o <path>, the system shall write the output to the given path instead of the defaultcluster.bin. - When the operator specifies
-o -, the system shall write the binary output to stdout. - After writing output, the system shall print the SHA-256 hash of the resulting binary to stderr.
- Where a YAML file is missing from the configs directory, the system shall treat the corresponding section as empty and continue building (missing files are not errors).
- Where the
schema/subdirectory is missing from the configs directory, the system shall report an error and exit non-zero. - Where the YAML content passes JSON Schema validation but maps to fields not present in the
ClusterConfigcapnp struct, the system shall warn the operator about unrecognized fields. - The system shall not source
port, thejobmgr/workermgr/qmgr/resmgrhash-table-size fields, orxstrings(xstring hash size) from YAML or CLI input under any circumstance. These are per-controller-instance performance-tuning parameters, not cluster-shared configuration, and the builtcluster.binshall leave them unset (capnp default). - The system shall not source, read, or write
configGeneration. The builtcluster.binshall leave it unset (capnp default) — the primary controller is the sole authority for assigning it (see Requirement 2 note). - The system shall not source
candidates(controller peer/standby list) from YAML or CLI input. Standby membership changes independently of, and more frequently than, the rest of cluster configuration, so it shall not require aconfig build/config pushcycle; the builtcluster.binshall leavecandidatesunset (capnp default). Peer membership sourcing mechanism is out of scope for this spec (open question — see brief.md Open Design Question #3). clusterNameand theheartbeatInterval/inactiveHeartbeatCountfields (controller→worker liveness — not the HA/Raft election heartbeat, which has noClusterConfigfield, see.kiro/steering/config-design.mdRule 0) SHALL be sourced from YAML and included incluster.bin— these must stay consistent across all controller instances (unlike Criteria 8–10, which are intentionally excluded). Source location (existing YAML file vs. newcluster.yaml) is a design-phase decision.
Criteria 8–11 apply the Configuration Inclusion Rules (brief.md): a field is excluded from cluster.bin if it can change independently of the rest of cluster config, OR if it's expected to change frequently; otherwise it's included. Any future ClusterConfig field proposed for xceadmin's YAML mapping must be classified against these two rules first.
Requirement 2: Config Push
Objective: As an XCE operator, I want to deploy a compiled cluster.bin to a controller cluster, so that the configuration takes effect on the running system.
Acceptance Criteria
- When the operator runs
xceadmin config push <cluster.bin>, the system shall connect to the controller via TLS, send the binary as a capnpRequestwithOP_upgradeConfigopcode,upgradeconfigrequestmsgbody (binary bytes + SHA-256 hash), framed with an 8-byte uint64 size prefix in host byte order, and display the result. - When the operator specifies
--controller <host:port>, the system shall connect to the given controller address instead of the defaultlocalhost:9991. - The system shall use the same TLS certificate bundle the controller cluster is deployed with (
xce.crt/xce.key, default pathha/data/) to establish trust — no separate client CA flag or cert-issuance step. This matches the trust modelxce_testanddeploy-ha.shalready use for controller-to-controller TLS. - When the operator specifies
--cert-dir <path>, the system shall loadxce.crt/xce.keyfrom the given directory instead of the defaultha/data/. - After receiving the controller's reply, the system shall display the result to the operator:
versionTag(the new generation number assigned by the controller),ackedCount(number of standbys that acknowledged), and anyerrormessage. - When the controller indicates the config was committed (positive
ackedCount), the system shall exit 0. - If the controller reports an error (non-empty
errorfield or zeroackedCount), the system shall display the error and exit non-zero.
Requirement 3: Auto Primary Discovery
Objective: As an XCE operator, I want xceadmin to automatically find the primary controller, so that I don't need to know which node is currently primary.
Acceptance Criteria
- When the operator connects to any controller node (primary or standby), the system shall inspect the reply for
redirectHostandredirectPortfields. If both are non-empty, the system shall reconnect to the redirect target and resend the request. - When the redirect target itself redirects, the system shall follow at most 3 redirect hops before reporting an error.
- When a standby controller redirects the operator to the primary, the system shall display the redirect target to inform the operator where the config was actually pushed.
- When connecting to a controller and no primary has been elected yet, the system shall time out after 30 seconds and report "no primary available" to the operator.
Requirement 4: Validation
Objective: As an XCE operator, I want validation errors caught early, so that invalid configuration never reaches a running cluster.
Acceptance Criteria
- When a YAML file fails JSON Schema validation, the system shall report the specific validation error with file name, line/field path, and the constraint violated, then exit non-zero.
- When a YAML file uses the reserved name
"lost_and_found"for a queue or host, the system shall reject it with a clear error message explaining the reservation, even if it passes schema validation. - When a YAML file contains duplicate entries (duplicate queue names, host names, resource names, group names), the system shall report the duplicates and exit non-zero.
- When a YAML file references an undefined entity (e.g., a queue references a host group that is not defined), the system shall warn the operator but allow the build to proceed (the controller performs final referential integrity checks at runtime).
Requirement 5: xcecommon Shared Package
Objective: As a future Python tool author, I want a shared xcecommon package providing capnp schema loading, wire protocol, and config model types, so that I don't duplicate protocol code across tools.
Acceptance Criteria
- The
xcecommonpackage shall provide a capnp schema loader that readscontroller.capnpand exposes the compiled Python types (ClusterConfig,Request,Reply,UpgradeConfigRequest,UpgradeConfigReply, and all nested types). - The
xcecommonpackage shall provide a wire protocol module that handles TLS connection, capnp message framing (8-byte uint64 size prefix in host byte order), send, and receive. - The
xcecommonpackage shall provide YAML→capnp mapping functions that convert validated YAML data toClusterConfigcapnp struct fields. - Where the
xcecommonpackage is installed for development, it shall be importable under the namexcecommonfrom any tool within the project.
Requirement 6: Config Diff (Nice-to-Have)
Objective: As an XCE operator, I want to see what changed between two config binaries, so that I can review changes before pushing.
Acceptance Criteria
- When the operator runs
xceadmin config diff <cluster-v1.bin> <cluster-v2.bin>, the system shall decode both binaries, compare them field-by-field, and display a human-readable summary of additions, removals, and modifications. - The diff output shall group changes by section (queues, resources, hosts, users, limits, scalers) and show the specific fields that differ.
- When the two binaries are identical, the system shall report "no differences" and exit 0.
Requirement 7: Dry-Run Push (Nice-to-Have)
Objective: As an XCE operator, I want to validate that the controller will accept a config before committing to the push.
Acceptance Criteria
- When the operator runs
xceadmin config push --dry-run <cluster.bin>, the system shall connect to the controller, probe the generation, validate SHA and version compatibility, and report whether the config would be accepted, without actually committing the config to the cluster. - When dry-run passes, the system shall report "OK: controller will accept this config (generation N→M)" and exit 0.
- When dry-run fails, the system shall report the specific rejection reason and exit non-zero.
Requirement 8: Error Handling & Resilience
Objective: As an XCE operator, I want clear, actionable error messages for all failure modes, so that I can diagnose and fix configuration issues without reading controller logs.
Acceptance Criteria
- If the TLS connection to the controller fails, the system shall report "cannot connect to host:port: " and exit non-zero.
- If the controller returns a
Replywith non-zerostatus, the system shall report the status code and any error text from the reply, then exit non-zero. - If the capnp binary is corrupted or cannot be decoded, the system shall report "invalid cluster.bin: " and exit non-zero.
- If the
controller.capnpschema file is not found at the expected path, the system shall report the missing path and suggest checking the installation. - If the operator interrupts the push (SIGINT), the system shall clean up the TLS connection and exit gracefully with a "cancelled" message.
- The system shall time out after 30 seconds on any network operation and report "timeout connecting to host:port".
Requirement 9: Skill & Harness Integration
Objective: As a developer, I want xceadmin integrated into the existing development toolchain, so that building, deploying, and testing XCE clusters uses consistent tooling.
Acceptance Criteria
- When the developer runs
/xce-build xceadmin, the system shall make thexceadminCLI available on PATH and run the full test suite, reporting pass/fail. - When the developer runs
/xce-build xceadmin release, the system shall installxceadminfor production use (pinned, non-editable). - When the developer runs
/xce-deploy-hawith the--build-configflag, the system shall invokexceadmin config build configs/ -o ha/data/cluster.binas a precondition before deploying the cluster. - When
cluster.binis missing fromha/data/during deploy,/xce-deploy-hashall runxceadmin config buildautomatically (same as--build-config). - When
cluster.binalready exists and--build-configis not passed,/xce-deploy-hashall use the existing binary as-is (backward compatible). - The nix dev shell (
nix develop ./src) shall include all Python dependencies required by xceadmin after the nix environment is updated (see Nix Environment section in brief.md).
Requirement 10: Deterministic Builds
Objective: As an XCE operator, I want config builds to be deterministic, so that I can reproduce any historical config binary from its YAML source.
Acceptance Criteria
- When
xceadmin config buildis run twice with the same YAML inputs and same xceadmin version, the system shall produce byte-identicalcluster.binoutput. - The system shall not embed build timestamps, hostnames, usernames, or any other non-deterministic data in the output binary.
- When different operators on different machines build the same YAML with the same xceadmin version, the output shall be byte-identical.
Requirements Summary
| # | Requirement | Priority | Phase |
|---|---|---|---|
| 1 | Config Build | Core | Implementation |
| 2 | Config Push | Core | Implementation |
| 3 | Auto Primary Discovery | Core | Implementation |
| 4 | Validation | Core | Implementation |
| 5 | xcecommon Package | Core | Implementation |
| 6 | Config Diff | Nice-to-Have | Follow-up |
| 7 | Dry-Run Push | Nice-to-Have | Follow-up |
| 8 | Error Handling | Core | Implementation |
| 9 | Skill & Harness | Core | Implementation |
| 10 | Deterministic Builds | Core | Implementation |