mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:11:29 +00:00
web, docs: IssuerHierarchyPage + sysadmin runbook + connectors row (Rank 8 commit 5)
Final commit of the 5-commit Rank 8 chain. Operator-facing surface
on top of the service + handler layers shipped in commits 1-4.
Frontend (web/src):
- api/client.ts: 3 new functions + IntermediateCA interface
(listIntermediateCAs, getIntermediateCA, retireIntermediateCA).
- pages/IssuerHierarchyPage.tsx: recursive nested <ul> render of
the hierarchy tree at /issuers/:id/hierarchy. buildHierarchyTree
is a pure helper that walks the flat list and groups children
on parent_ca_id; the dendrogram view is parking-lot work tracked
in WORKSPACE-ROADMAP. Two-phase retire UX surfaces 'Retire…'
then 'Confirm retire (terminal)' when the row is in retiring
state. Admin gate is enforced at the API; the page renders the
backend's 403 as ErrorState for non-admin callers.
- main.tsx: register the new /issuers/:id/hierarchy route.
CI guard update:
- scripts/ci-guards/T-1-frontend-page-coverage.sh: add
IssuerHierarchyPage to the deferred-test allowlist with the
standard 'why deferred' comment. Admin-gate + recursive build
semantics are already pinned at the backend layer
(intermediate_ca_test.go service tests + intermediate_ca_test.go
handler triplet). Vitest test deferred until next feature
change touches the page.
Docs:
- docs/intermediate-ca-hierarchy.md: new operator runbook
covering:
Concepts (HierarchyMode 'single' vs 'tree', defense-in-depth
on key bytes never persisting on rows).
Lifecycle states + drain-first semantics
(active → retiring → retired with active-children gate).
Three deployment patterns: 4-level FedRAMP boundary CA,
3-level financial-services policy CA, 2-level internal
PKI.
RFC 5280 enforcement (§3.2 self-signed, §4.2.1.9 path-length
tightening, §4.2.1.10 NameConstraints subset).
Migration from single → tree using the load-bearing
TestLocal_HierarchyMode_SingleVsTree_ByteIdentical pin as
the canary.
API reference + observability (IntermediateCAMetrics
Prometheus exposure).
Known limitations + Rank-8 follow-on roadmap.
- docs/connectors.md: extend the Built-in Local CA section with
a 'Tree mode (Rank 8)' paragraph describing the new chain
assembly path + cross-link to docs/intermediate-ca-hierarchy.md.
Roadmap:
- WORKSPACE-ROADMAP.md: 5 follow-on items under a new
'Intermediate CA hierarchy extensions (Rank 8 V2 follow-ons)'
bullet block:
HSM-backed roots (PKCS#11 / cloud KMS drivers via existing
signer.Driver interface — no service-layer change needed).
Automated CA rotation (parallel-validity windows ahead of
expiry).
Intra-hierarchy CRL chaining (per-CA CRL endpoints stitched
at issue time).
NameConstraints policy templates (FedRAMP / financial /
internal PKI declarative templates instead of hand-rolled
JSON).
D3 dendrogram visualization (separate page so the existing
list view stays the default + the dep stays opt-in).
Verified locally:
gofmt: clean.
go vet ./...: exit 0.
tsc --noEmit (web/): exit 0 (no TypeScript errors).
go test -short -count=1 ./internal/api/handler/... + service +
local: ok across all three packages, 4-5s each.
All 24 CI guards: clean
(T-1 frontend-page-coverage with the new
IssuerHierarchyPage allowlist entry; openapi-handler-parity,
M-008 admin-gate, every other guard untouched).
Rank 8 chain complete:
66d2af3 domain, migrations: IntermediateCA type + intermediate_cas
+ Issuer.HierarchyMode (commit 1)
fb54ebc service: IntermediateCAService + IntermediateCAMetrics
+ RFC 5280 enforcement (commit 2)
62523fb service: 10 IntermediateCAService tests + in-memory fake
repo (commit 2.5)
ae597f7 local: tree-mode chain assembly + byte-equivalence pin
(commit 3 — load-bearing backwards-compat refuse-to-ship
pin in TestLocal_HierarchyMode_SingleVsTree_ByteIdentical)
34adcfb api, handler: 4 admin-gated CA hierarchy endpoints +
OpenAPI (commit 4)
HEAD web, docs: IssuerHierarchyPage + sysadmin runbook +
connectors row (this commit)
Reference: cowork/rank-8-intermediate-ca-hierarchy-prompt.md, commit 5.
This commit is contained in:
@@ -813,3 +813,39 @@ export const acknowledgeHealthCheck = (id: string) =>
|
||||
|
||||
export const getHealthCheckSummary = () =>
|
||||
fetchJSON<HealthCheckSummary>(`${BASE}/health-checks/summary`);
|
||||
|
||||
// IntermediateCA hierarchy (Rank 8 of the 2026-05-03 deep-research
|
||||
// deliverable). Admin-gated at the handler layer; non-admin Bearer
|
||||
// callers get 403. Operators drive the hierarchy from
|
||||
// IssuerHierarchyPage; the recursive tree render is built from the
|
||||
// flat list returned here by walking each row's parent_ca_id.
|
||||
export interface IntermediateCA {
|
||||
id: string;
|
||||
owning_issuer_id: string;
|
||||
parent_ca_id?: string | null;
|
||||
name: string;
|
||||
subject: string;
|
||||
state: 'active' | 'retiring' | 'retired';
|
||||
cert_pem: string;
|
||||
key_driver_id: string;
|
||||
not_before: string;
|
||||
not_after: string;
|
||||
path_len_constraint?: number | null;
|
||||
name_constraints?: { permitted?: string[]; excluded?: string[] }[];
|
||||
ocsp_responder_url?: string;
|
||||
metadata?: Record<string, string>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export const listIntermediateCAs = (issuerID: string) =>
|
||||
fetchJSON<{ data: IntermediateCA[] }>(`${BASE}/issuers/${issuerID}/intermediates`);
|
||||
|
||||
export const getIntermediateCA = (id: string) =>
|
||||
fetchJSON<IntermediateCA>(`${BASE}/intermediates/${id}`);
|
||||
|
||||
export const retireIntermediateCA = (id: string, note: string, confirm: boolean) =>
|
||||
fetchJSON<{ id: string; decided_by: string; confirmed: boolean }>(
|
||||
`${BASE}/intermediates/${id}/retire`,
|
||||
{ method: 'POST', body: JSON.stringify({ note, confirm }) },
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user