Proposal: a secure-install control plane¶
Status: P0 implemented on July 25, 2026; follow-on manager and provenance work remains staged.
Problem it solves: ca9 has strong individual capabilities—lockfile inventory,
malware and package-age policy, registry mediation, install-script analysis, and
runtime secret shielding—but teams still have to assemble those capabilities
themselves. Two common workflows also fall through the prevention layer:
Python installs that use requirements.txt/constraints files, and temporary policy
waivers that need an owner and an expiry date.
Goal: make the safe path the easy path. A repository should be able to run one read-only posture command, understand whether its dependency-install workflows are actually protected, and then enforce supported npm and pip installs with the same policy. Necessary exceptions must be narrow, time-bounded, and visible in evidence.
User pain points¶
ca9 run -- pip install -r requirements.txtis currently rejected even though requirements files are the normal Python deployment interface.ca9 vet, runtime preflight, feed state, and workflow checks are separate commands. A team adopting ca9 has no single answer to “are installs in this repository protected?”- Policy is all-or-nothing. A team with one accepted package exception must weaken a whole rule or maintain an out-of-band waiver that ca9 cannot audit.
- The GitHub Action exposes only part of the local product surface, so adoption requires custom workflow scripting.
P0 scope¶
1. Lock-backed pip requirement installs¶
ca9 run accepts local -r / --requirement and -c / --constraint files.
- Includes resolve relative to the containing file and must stay inside the repository root.
- Nested includes are supported with cycle and depth protection.
- Exact pins and hashes are preserved as evidence.
- Constraints refine direct requirements; a constraint-only entry is not treated as an installed package.
- Remote requirements, editable/direct URL dependencies, extra indexes, find-links, and out-of-root files remain blocked.
- Dependency and file arguments are preserved. When the PyPI gateway is active, ca9 removes top-level upstream index options and appends the loopback index as the final option so an index inside a requirements file cannot outrank it.
Acceptance:
- A malware-listed package in a requirements file is blocked before pip starts.
- Direct and nested requirements, constraints, hashes, comments, and continuations have fixtures.
- Unsupported or escaping input fails closed with a useful error.
2. Scoped, expiring policy exceptions¶
Add top-level [[exceptions]] entries to ca9.toml:
[[exceptions]]
policy_id = "ca9.package_age"
ecosystem = "pypi"
package = "example-package"
version = "1.2.*"
action = "warn"
owner = "platform-security"
reason = "Validated emergency release"
expires = "2026-08-01"
Rules:
policy_id,owner,reason, andexpiresare required.- Package, ecosystem, and version selectors are optional; omitted selectors broaden only that exception, never another policy.
- Expired entries do not match.
- Every applied exception is attached to the decision evidence.
- Known-malware decisions are never overridable.
- Later policy files replace the exception array under the existing merge model.
Acceptance:
- Validation rejects malformed, ownerless, reasonless, or invalid-date entries.
- Matching uses normalized package identities.
vetand runtime package decisions expose applied waiver evidence.- Expired and unrelated exceptions leave the original action unchanged.
3. ca9 protect¶
Add a local, non-mutating repository posture command:
ca9 protect --repo . --policy ca9.toml --format table
ca9 protect --repo . --format json --output ca9-protect.json
The v1 report combines:
- detected package-manager and lockfile workflows;
- whether ca9 runtime enforcement can parse each workflow;
- local feed readiness;
- current inventory, package-policy findings, and decisions;
- optional local GitHub Actions workflow checks;
- concrete remediation for unprotected or ambiguous workflows.
The stable JSON schema is ca9.protect.v1. Exit 1 means at least one blocking
posture or policy decision; warnings do not fail the command. The command performs no
network requests or repository mutations by default.
Acceptance:
- npm lockfile and pip requirements repositories report as supported.
- missing locks and detected-but-unsupported managers are explicit, actionable checks rather than silently ignored.
- JSON output is deterministic enough for CI fixtures.
- table and Markdown output lead with the overall posture and next actions.
4. First-class CI surface¶
The GitHub Action accepts command: protect, passes the repository and optional
policy, and can upload its SARIF output using the same guarded upload path as existing
commands.
Delivery sequence¶
- [x] Land this proposal and executable acceptance criteria.
- [x] Extend the pip preflight parser and focused runtime tests.
- [x] Add exception schema, matching, decision evidence, and tests.
- [x] Add the
protectreport/CLI and fixture repositories. - [x] Extend the Action, README, CLI docs, and navigation.
- [x] Run focused suites, lint/format checks, and the full test suite; distinguish any pre-existing environment failures from regressions.
Non-goals for P0¶
- Full PEP 751
pylock.toml,uv sync, Poetry, pnpm, or Yarn runtime mediation. - Automatic mutation of workflows, policy, lockfiles, or install-script allowlists.
- A hosted dashboard or telemetry service.
- Suppressing or downgrading known-malware decisions.
- Signing the hosted feed or evaluating registry provenance. Those remain high-value follow-ups once the adoption path is coherent.
Follow-on priorities¶
- Add
uv.lock/PEP 751 runtime drivers and pnpm install/build-script policy. - Verify npm and PyPI provenance attestations and expose them in inventory.
- Sign feed snapshots and publish populated release windows.
- Add dependency-to-code reachability for JavaScript/TypeScript.
- Offer a guarded
protect --fixonly for deterministic configuration changes, with backups and a dry-run diff.
Risks and mitigations¶
- Requirements syntax is broad. P0 supports the safe, common subset and rejects ambiguous network/source options rather than guessing.
- Exceptions can become permanent bypasses. Required ownership, reason, expiry, and evidence make drift visible; malware remains immutable.
- A combined command can hide nuance. The JSON report retains each component check, finding, decision, and warning rather than collapsing everything into a score.
- Manager detection can overpromise. Every detected workflow reports an explicit enforcement coverage state: supported, partial, or unsupported.