Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6333eb9
feat: add safe managed bundle refresh
danielewood Sep 20, 2026
ec168ce
docs: link bundle refresh changelog to PR 225
danielewood Sep 20, 2026
bbe4992
fix: preserve bundle defaults and address refresh review
danielewood Sep 20, 2026
6eee5b4
fix: keep managed CA and duplicate refreshes usable
danielewood Sep 20, 2026
3cb05ec
fix: allow refreshes of complete legacy bundles
danielewood Sep 20, 2026
4d41ab9
fix: keep optional skips from blocking managed refreshes
danielewood Sep 20, 2026
64574cc
fix: validate trust and reserve managed output names
danielewood Sep 20, 2026
f3ba5e6
fix: prevent case-insensitive bundle overwrites
danielewood Sep 20, 2026
3ac6d70
fix: identify managed scan trust-loading failures
danielewood Sep 20, 2026
f53cc3a
fix: preserve unselected bundle directory aliases
danielewood Sep 20, 2026
4e7c942
fix: isolate scan databases and unselected bundle names
danielewood Sep 20, 2026
97619c1
fix: recognize manifest filename case variants
danielewood Sep 20, 2026
73e4016
fix: reject platform-reserved bundle directories
danielewood Sep 20, 2026
d4a423c
fix: keep future certificates out of managed refreshes
danielewood Sep 20, 2026
ab134d3
fix: honor refresh lock filename case variants
danielewood Sep 21, 2026
767bc57
docs: clarify selected bundle artifact generation
danielewood Sep 21, 2026
2126bbb
fix: preserve bundle safety between planning and writes
danielewood Sep 21, 2026
182524e
fix: keep managed write outcomes and trust current
danielewood Sep 21, 2026
d5adf4a
fix: preserve scan exclusions and staged bundle integrity
danielewood Sep 21, 2026
a2ceeb9
fix: pin bundle refreshes to reviewed destinations
danielewood Sep 21, 2026
19d2bad
fix: protect refreshes from incomplete certificate metadata
danielewood Sep 21, 2026
e577951
fix: reject incomplete and unwritable bundle refreshes
danielewood Sep 22, 2026
dd84c2e
fix: anchor refresh staging and preserve blocked decisions
danielewood Sep 22, 2026
138394e
fix: block refreshes when destination or lock changes
danielewood Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .claude/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Certificate/key processing, in-memory storage, and persistence. Used by both CLI
- `process.go` — `ProcessData()`: format detection and parsing pipeline (PEM → DER → PKCS#7 → PKCS#8 → SEC1 → Ed25519 → JKS → PKCS#12). Calls `CertHandler` for each parsed item.
- `memstore.go` — `MemStore`: in-memory `CertHandler` implementation and primary runtime store. `CertRecord`/`KeyRecord` types. Stores multiple certs per SKI via composite key (serial + AKI). Provides `ScanSummary()`, `AllCertsFlat()`, `AllKeysFlat()`, `CertsByBundleName()`, `BundleNames()`, `DumpDebug()`.
- `summary.go` — `ScanSummary` struct for aggregate scan counts, including roots/intermediates/leaves/keys/matches plus expired, Mozilla-trusted, system-trusted, and untrusted certificate totals.
- `export_formats.go` — Supported artifact selection and minimal managed-export defaults; key-free artifacts do not require a private key.
- `export.go` — `GenerateBundleFiles()`: creates all output files for a bundle (PEM variants, key, P12, K8s YAML, JSON, YAML, CSR). All key output is normalized to PKCS#8 format. `BundleExportInput` and `ExportMatchedBundleInput` support an `EncryptKey` option for PKCS#8 v2 password-protecting exported `.key` files. `GenerateJSON`, `GenerateYAML`, `GenerateCSR` also exported individually. `BundleWriter` interface and `ExportMatchedBundles()` provide shared export orchestration for both CLI and WASM.
- `validate.go` — Certificate validation checks. `RunValidation()` orchestrates all checks for a certificate. `CheckExpiration()`, `CheckKeyStrength()`, `CheckSignature()`, `CheckTrustChain()` for individual validation steps. Types: `RunValidationInput`, `ValidationResult`, `ValidationCheck`, `CheckTrustChainInput`.
- `aia.go` — Store-aware AIA resolution. `ResolveAIA()` fetches missing intermediates via AIA URLs using an `AIAFetcher` callback. `HasUnresolvedIssuers()` checks if any certs need issuer resolution. Type: `ResolveAIAInput`.
Expand All @@ -45,7 +46,11 @@ Certificate/key processing, in-memory storage, and persistence. Used by both CLI
CLI business logic and file I/O. Delegates to `internal/certstore/` for processing, storage, and export. No SQLite dependency at this layer.

- `crypto.go` — File ingestion pipeline. `ProcessFile()` and `ProcessData()` delegate to `certstore.ProcessData()` with `MemStore` as the handler. Also handles CSR detection for CLI logging.
- `exporter.go` — Bundle export. `ExportBundles()` iterates `MemStore` bundle names, finds matching certs/keys, builds chains via `certstore.ExportMatchedBundles()`. `filesystemWriter` implements `certstore.BundleWriter` to write results to disk with appropriate permissions (0600 for sensitive files).
- `bundleplan.go` — `PlanBundleExports()` selects scoped candidates deterministically, records unselected alternatives, checks keys/validity/trust before replacement conflicts, enforces required bundles, generates selected artifacts in memory, and returns a manifest. It reserves every selected primary directory before candidate lookup, reserves manifest and refresh-lock names, and rejects directory collisions using Unicode normalization and case-insensitive comparison. Control characters, Windows device names, and components longer than 255 bytes are rejected in both directories and generated artifacts. Existing directory names, unselected configuration aliases, and manifest bundle identities protect scoped refreshes even with force enabled; directory names are rechecked under the write lock. Future-dated leaves are always skipped, including with force. Expired leaves require a separate opt-in and retain trust verification at their issuance time. Internal callers can supply `CustomRoots` with the `custom` trust store. `BundleExportPlan.Write()` rechecks each candidate's validity and captured trusted chain before any replacement and immediately before its own write, applies validated plans, and records created/replaced/blocked status. A committed replacement retains its status if backup or lock cleanup fails. Refresh locks use an anchored directory handle and check marker identity before commit and release. Destination changes, lost locks, and scope conflicts at write time block pending entries and invalidate the plan while preserving committed statuses. Failed existing-directory reinspection also blocks the affected entry and preserves both the validation sentinel and underlying inspection cause.
- `bundleplan_existing.go` — Inspects existing bundle leaves, recognizes managed manifests case-insensitively to retain bundle and selected CA identities, and fingerprints contents to reject stale plans and symlinked output bundles. Conflicting manifest identities are rejected. Malformed or incomplete JSON/YAML certificate metadata makes replacement ambiguous even alongside a valid leaf: certificate PEM fields must be nonempty, and manifests require bundle identity and selected leaf PEM. Artifact suffixes and the CSR JSON/Kubernetes YAML exclusions are case-insensitive. Malformed artifact diagnostics retain the filename even when the directory has zero or multiple identifiable leaves.
- `bundlepaths.go` — Rejects config, password, and database paths inside managed output before scan inputs are loaded. Resolves symlinks before parent traversal, including missing destinations and aliases in the working directory. Pins each plan's absolute resolved output and nearest existing directory identity, upgrading the guard when a new output root is created. Checks that identity before writes, under the lock, and before committing staged artifacts. Shares NFC-normalized case comparison with the planner and scan exclusions.
- `scanwalk.go` — Walks eligible scan inputs, excludes declared output/database/password paths using NFC-normalized case comparison of whole components, and checks symlink targets. Canonical exclusions resolve symlinked working directories and existing ancestors of future outputs. In-root symlink containment uses the same canonical absolute paths. Directory exclusions prune their descendants; similarly prefixed sibling directories remain eligible. Scan-root containment uses a separate strict path boundary.
- `exporter.go` — Bundle export. `ExportBundles()` iterates `MemStore` bundle names, finds matching certs/keys, builds chains via `certstore.ExportMatchedBundles()`. `filesystemWriter` implements `certstore.BundleWriter` to write results to disk with appropriate permissions (0600 for sensitive files), using staged replacement and rollback with short temporary/backup names independent of the bundle name. Staging, writes, renames, rollback, and cleanup use opened parent/staging directory handles, preserving cleanup after output-root relocation. Managed writes use a pre-commit callback to recheck cancellation, scope, validity/trust, and existing contents after staging, then write the final manifest. A final guard validates the opened parent against the pinned destination and repeats the safety checks after manifest staging, immediately before the first rename. A distinct wrapped error identifies committed replacements whose backup cleanup failed and includes the retained backup path.
- `bundleconfig.go` — YAML config parsing. Supports `defaultSubject` inheritance. Validates `bundleName` values as DNS-1123 labels at load time with line-number error reporting.
- `inspect.go` — Certificate/key/CSR inspection. `InspectFile()` and `InspectData()` produce `InspectResult` structs, including top-level certificate extension summaries for certificate records. `ResolveInspectAIA()` fetches missing intermediates for trust annotation. `AnnotateInspectTrust()` records `trust_anchors` / `trust_warnings` and the derived trusted/untrusted status. `FormatInspectResults()` renders text or JSON output.
- `verify.go` — Chain validation and diagnostics. `VerifyCert()` checks chains, key matches, and expiry, first assembling intermediates with a non-verifying AIA walk and then probing Mozilla/system/file trust sources explicitly so `trust_anchors` / `trust_warnings` survive even when one source is unavailable; verbose mode also includes top-level certificate extension summaries for the leaf and displayed chain entries. `DiagnoseChain()` analyzes chain failures. `FormatVerifyResult()` and `FormatDiagnoses()` for output. Types: `VerifyInput`, `VerifyResult`, `ChainCert`, `Diagnosis`, `DiagnoseChainInput`.
Expand All @@ -63,7 +68,8 @@ Thin CLI layer. Each file is one Cobra command. Flag variables are package-level

- `main.go` — Entry point. CLI version string, memory limit enforcement, exit code handling (0 success, 1 general error, 2 `ValidationError`).
- `root.go` — Root Cobra command with shared flags: `--log-level`, `--passwords`, `--password-file`, `--allow-expired`, `--verbose`. `--verbose` enables extended certificate details such as serials, key/signature metadata, and extension summaries on commands that support it. Registers all subcommands.
- `scan.go` — Main scanning command with `--dump-keys`, `--dump-certs`, `--max-file-size`, `--bundle-path` flags.
- `scan.go` — Main scanning command with `--dump-keys`, `--dump-certs`, `--max-file-size`, `--bundle-path` flags. Declared bundle/dump outputs, database paths, password files, and their symlink aliases are excluded from file ingestion; `--load-db` still explicitly imports the saved inventory. Export config failures are fatal. Bundle assignment runs after AIA resolution, including fetched CA certificates in configured exports.
- `scan_export.go` — Managed bundle CLI: scope, explicit `--write`, `--dry-run`, required bundles, output formats, separate input/output password files, and text/JSON plan rendering. P12 retains the default `changeit` password; PEM key encryption requires an explicit output password. Passes `--allow-expired` independently of `--force` and renders unselected candidate decisions.
- `bundle.go` — Build verified certificate chains from leaf certs; resolves intermediates via AIA; outputs PEM, chain, fullchain, PKCS#12, or JKS with `--key`, `--force`, `--trust-store` flags.
- `inspect.go` — Display detailed certificate, key, or CSR information with text or JSON output (`--format`); certificate output includes the top-level extension list, JSON certificate entries include `trust_anchors` and `trust_warnings`, and expired items are filtered unless `--allow-expired`.
- `verify.go` — Verify certificate chains, key matches, expiry windows, and optional OCSP/CRL status; returns exit code 2 on validation failures; always checks Mozilla + system trust and accepts `--roots` for additional file-backed trust anchors, including pinned or legacy non-CA anchors. Flags: `--key`, `--roots`, `--expiry`, `--diagnose`, `--ocsp`, `--crl`, `--format`. Verbose output includes extension summaries for the leaf and chain entries.
Expand Down
Loading
Loading