Security policy
Status
This repository is a local protocol demonstrator. It has not been independently audited and must not be connected to production systems or given real credentials.
The optional hardened lab improves the local trust boundaries with mTLS,
SPIFFE-format certificate identities, explicit RBAC, external OPA, PostgreSQL
authority, Temporal, OpenBao Transit, typed SIEM adapters, signed one-shot
executor grants, a dedicated executor database role, an out-of-process mTLS
executor, a signed ExecutionProof, signed request-bound AuthZEN decisions, a
fail-closed admission verifier, a separately keyed mTLS/read-only observer,
signed transparency receipts, S3 Object Lock COMPLIANCE and a durable emergency
stop. Its certificates are still issued by a local development CA for the
Windows application profile. The SOTA profile exercises TLS 1.3 between two
genuine SPIRE X.509-SVIDs and qualifies the Kubernetes NetworkPolicy adapter,
proof webhook, observer, outage/recovery, RBAC, admission and Cilium on one disposable local cluster. It has not
been independently reviewed or qualified across a cluster/CNI matrix. OpenBao
is not an HSM or separate administrative domain. The WORM lab shares the host
and the JSON transparency format is not a SCITT conformance claim. These controls do not change
the production prohibition.
All generated private keys, databases, downloaded lab binaries, and logs are
under ignored .aegis-lab/. Treat that directory as sensitive. Never place real
credentials or customer data in it. Use npm run lab:stop plus
npm run lab:postgres:stop after application tests, and npm run sota:pause
after Kubernetes tests.
Never set AEGIS_KUBERNETES_MODE=real against a cluster that is not disposable
and explicitly authorized. Real mode creates and deletes NetworkPolicies and
patches the reserved aegis.io/quarantine-id pod label.
Supported versions
Only the latest commit in this repository is maintained. There are no published production releases yet.
Reporting a vulnerability
Do not open a public issue containing exploit details, credentials, customer data, or a working bypass. Contact the repository owner privately and include:
- affected commit;
- security invariant violated;
- minimal reproduction without real secrets;
- expected and observed result;
- whether execution, audit, identity, policy, evidence, or restoration is affected.
Until a private reporting address and response SLA are published, this project must not claim a coordinated vulnerability-disclosure program.
Dependency policy
The dependency set is small on purpose and every addition is a decision, not a
convenience. Current production dependencies: pg, @kubernetes/client-node,
@temporalio/*, @aws-sdk/client-s3, @opentelemetry/api. The Rust verifier
under verifier/ adds seven, chosen so an auditor can read the whole set.
Two of the verifier's seven — ciborium and coset — were added for a reason
worth stating, because they look like the policy above being bent. AEGIS encodes
CBOR and COSE by hand (src/cbor.ts, src/cose.ts) in a deliberately small,
encode-only subset: no floats, no indefinite lengths, no decoder. A hand-written
encoder is only defensible if something that is not AEGIS reads what it
produces, so those two crates exist precisely to be the independent reader.
Putting them on the verifier side rather than the production side is the whole
point: the parser that faces untrusted bytes is a third party's, and the code
that faces the network never decodes CBOR at all.
- An addition to
dependenciesrequires a written reason in the commit message: what it does, why it is not written here instead, and what it pulls in transitively.@opentelemetry/apiwas accepted because it has no dependencies of its own and is inert without an SDK; an exporter was not, because the operator should choose their own backend. - Dependabot opens grouped updates weekly. An update touching
pg,@kubernetes/client-node,@temporalio/*or any crate inverifier/is a security-critical change under the policy below and needs the same review as a change to the signing path: those are the libraries standing between AEGIS and the authority store, the cluster and the signature. - The lockfile is authoritative. CI installs with
npm ci, and the Rust verifier builds from a committedCargo.lockon a pinned toolchain. - A dependency that cannot be replaced without a migration is preferable to one that can be replaced silently: prefer few, boring and widely reviewed.
Security-critical change policy
Changes to canonicalization, signatures, identities, policy decisions, capability consumption, evidence provenance, executor state transitions, audit, or restoration require:
- a new adversarial regression test;
- explicit review of fail-open/fail-closed behaviour;
- migration and replay analysis for stored artifacts;
- an update to the threat model when a trust boundary changes.
Production development additionally requires protected branches, two-person
review for policy/signing paths, signed releases, SBOM/provenance, dependency and
secret scanning, and an independent assessment before any write credential.
The assessor handoff is defined in docs/security-audit-package.md; only an
external signed report can close that gate.
Where that list stands, 2026-09-01
Written down rather than left implied: "production development requires X" reads as "X is planned", and an assessor needs to know which half is already true.
| Requirement | State |
|---|---|
| Signed commits | In place from 39d8152 onward, and verifiable by a reviewer rather than only on GitHub. Commits made locally carry the author's SSH key, whose public half is committed as .github/allowed_signers; merges performed through the GitHub interface carry GitHub's own web-flow key instead and are marked verified on GitHub's side. To check the first kind from a fresh clone: git config gpg.ssh.allowedSignersFile .github/allowed_signers && git log --show-signature. Without that config git reports No principal matched — it sees a signature but has nothing to match it against. Commits before 39d8152 are unsigned and predate signing being configured. |
| Signed releases and SBOM | In place. .github/workflows/release.yml publishes only after the gates that guard main pass on the tagged commit, and attaches a CycloneDX SBOM, the shared canonical-vector corpus, the independent verifier binary and SHA256SUMS. The tag itself is signed. |
| Build provenance | Described, not attested. GitHub refuses to persist attestations for a user-owned private repository — Feature not available for user-owned private repositories — so the release ships a provenance.json statement generated by the workflow and signed by nobody. The signed tag binds the release to a commit and SHA256SUMS binds that commit's build to the bytes; what is missing is a signature over the builder's identity. The attestation step is gated on the repository being public and starts working by itself if that changes. |
| Dependency and secret scanning | In place. supply-chain runs gitleaks over the working tree and the commit history, and Trivy over dependencies and manifests. |
CODEOWNERS on policy and signing paths |
Present, and advisory only — see the row below. |
| Protected branches | Not in place, and not currently obtainable. GitHub refuses both the branch-protection and the rulesets API on a private repository under a Free plan: Upgrade to GitHub Pro or make this repository public. Without it CODEOWNERS cannot be enforced and a direct push to main is possible. |
| Two-person review for policy and signing paths | Not in place. It presupposes protected branches, and a second person. |
| Independent assessment | Not in place. Unchanged: only an external signed report closes that gate. |
The two open items are one item twice — a single-author repository cannot enforce review on itself. Signed commits and a full CI run on every push make a bad push detectable, not impossible, and that distinction is the whole difference between a control and a habit.