Story: Developer trusts the dev CA locally
Story ID: STORY-003 Status: Revision 1 Created: 2026-10-13
Statement
As a developer, I want Xianyun to generate a name-constrained dev CA and install it in my trust stores, so that browsers and tools accept the local cluster’s certificates without warnings and the CA can’t vouch for domains I don’t use.
Trigger
The developer runs xianyun ca trust, or runs xianyun up with the builtin:dev-ca deployer.
Acceptance Criteria
Scenario: First trust generates and installs the CA
Given no CA exists for profile "dev" and the config hostname is "gitlab.dev"
When the developer runs "xianyun ca trust" and approves the sudo prompt
Then a CA certificate MUST exist under the profile's pki "current" directory
And its permitted DNS subtrees MUST be ".local", ".test", ".svc", and "gitlab.dev"
And the system trust store MUST contain the certificate
Scenario: Untrust removes current and legacy entries
Given the CA is trusted under both the Xianyun name and the legacy Caproni name
When the developer runs "xianyun ca untrust"
Then neither entry MUST remain in the system trust store
And neither entry MUST remain in any NSS database where certutil is available
Scenario: The CA key isn’t exposed during a build
Given a profile with a generated CA and the "builtin:dev-ca" deployer
When "kustomize build" runs against the staged overlay
Then every staged file containing the CA key MUST have mode 0600
And the staging directory MUST have mode 0700
Scenario: Missing NSS tooling doesn’t fail the trust
Given certutil isn't on PATH
When the developer runs "xianyun ca trust"
Then the exit code MUST be 0
And the output MUST note that NSS databases weren't updated
EARS Constraints
- The system shall create
ca.keywith mode 0600 and shall never overwrite an existing key file. - If the CA’s constraints change, then the system shall re-sign with the same key.
- If the CA has expired, then the system shall generate a new one.
- When loading a CA, the system shall verify the certificate matches the private key.
- If
sudois unavailable and the user isn’t root, then the system shall report that the trust store wasn’t changed. (Not met:apply_sudologs a warning and runs the command without privileges, so the user sees the tool’s own permission error.)
Autonomy Limits
Not applicable for a human actor. Through MCP, ca trust and ca untrust are gated tools; ca inspect isn’t, and can generate a CA on first run.
Security Mapping
| Concern | Addressed By |
|---|---|
| Information disclosure (key in staging) | Third scenario; SR-010 (not yet met; measured 755 directory, 644 key) |
| Elevation of privilege (CA outside intended domains) | First scenario subtrees; SR-011 (partial) |
| Tampering (swapped key or certificate) | EARS constraint on loading; FR-008.6 (not yet met) |
| Spoofing (hostile cluster-reported zone) | SR-011 (not yet met, unproven exploit) |
| Repudiation (who installed or removed a trust anchor, and when) | No durable record today; SR-014 (not yet met) |
| Denial of service | Not applicable beyond the single-user local case; the 10-year lifetime and the expiry regeneration are the relevant limits |
Standards Mapping
- SLSA Level: N/A
- SSDF Practices: N/A
- SCVS Controls: N/A
Dependencies
- Depends on: none
- Blocks: none
- Related ADR: ADR-0006, ADR-0003
- Related spec requirement: FR-008, SR-010, SR-011
Notes
- The Keychain path on macOS was read, not run, in this review. A macOS variant of the first two scenarios needs the macOS build host.
- The third scenario fails today (see the threat model, TM-PKI-001).
- Whether every client enforces Name Constraints (TM-PKI-006) is unproven; the first scenario checks the certificate’s content, not client behaviour.