M-26-14 Maturity
Overview
Section titled “Overview”M-26-14 Maturity makes Logrite the tool that gets a federal agency to OMB M-26-14 logging maturity and proves it. Three roles:
- Instrument — the analyzer generates the baseline a–e event logs (identity, network, object-access, privilege, infra) into the agency’s apps.
- Operate — Logrite is the agency’s CEM/THIRF stack over its ingested logs (centralization, anomaly detection, alerting, AI — present today; plus retention tiering, Merkle integrity, and access governance — the Phase 2 backend is now shipped, see below).
- Prove — an Appendix-C maturity dashboard + self-assessment export scoring the agency’s posture (lowest-watermark, % of the agency’s systems at each level).
Phasing
Section titled “Phasing”| Phase | Scope | Status |
|---|---|---|
| Phase 1 | Prove (scoring + dashboard) + Instrument (a–e code-gen) | Shipped |
| Phase 2 (backend) | Operate — @EnableScheduling, row-hash-chained governance audit, JIT access grants + scheduled expiry, two-gate retirement, scheduled Merkle integrity manifests + verification, retention ISM contracts; the scorer reads all of these | Shipped + live |
| Phase 2 (infra) | The privileged OpenSearch admin user + LOGRITE_PROVISIONER_ENABLED=true (flips OS writes from fail-soft to real), a snapshot repo, at-rest encryption, NTP attestation | Needs infra |
| Phase 3 | Active Collector-Agent coverage → the scorer + the CDM/HWAM/SWAM asset-feed connector (/api/v1/inventory/*) → Inventory Visibility, both shipped; only third-party CDM data-format adapters deferred | Shipped |
| Memo completeness | The compliance clock (L1/120d · L2/180d · L3/320d from the LRA), the Log Access (CISA/FBI) release, and HVA / High-Impact risk banding — three memo requirements the dashboard previously scored around rather than implemented | Shipped |
Scoring model
Section titled “Scoring model”MaturityScoringService computes each of the five Appendix-C elements to a level 0–4, and overall = MIN(element levels) — the lowest watermark. It records the capping element and the % of the agency’s systems at each level.
| Element | Source |
|---|---|
| Inventory Visibility | live — % of inventoried assets shipping logs from a CDM/HWAM/SWAM feed (/api/v1/inventory/*) + active Collector Agents; else attested |
| Collection Coverage | live — % of the agency’s systems bound to a log index / serviceName, plus active heartbeating Collector Agents |
| Collection Operations | live — % of systems with analyzer rules configured |
| Data Retention | live — an active retention contract’s searchable/retrievable window (Phase 2); falls back to attested |
| Log Management | live — a verified Merkle integrity manifest (→ L3) + an active JIT access grant (→ L4) (Phase 2); else attested |
Each element also carries a measurementSources entry ("live" | "attested") recording whether its final level came from a live signal or a self-attestation. Attested values are stamped selfAttested:true and shown separately from live-measured signals — both on the dashboard (a per-element live/attested badge + a Honesty preserved panel) and in the export. As Phase 2 / Phase 3 signals come online for a tenant they replace the attestations automatically.
API (/api/v1/maturity, JWT-gated, tenant-scoped)
Section titled “API (/api/v1/maturity, JWT-gated, tenant-scoped)”| Method | Path | Purpose |
|---|---|---|
| GET | /assessment | Latest assessment: 5 element levels, overall = lowest, % per level, baseline a–k, attestations, measurementSources (live vs attested, per element) |
| POST | /recompute | Recompute from live signals + attestations |
| PUT | /attestation | Submit a self-attested element level (stored flagged selfAttested) |
| GET | /export?format=json|pdf | Agency Logging Plan self-assessment |
| GET | /ntp-status | Live NTP sync status (chronyc/timedatectl, polled every 5 min by NtpAttestationService) |
| GET | /milestones | The M-26-14 compliance clock — L1/120d, L2/180d, L3/320d from the LRA publication date |
| GET | /log-access-export?grantId=&from=&to= | Log Access (CISA/FBI) release — grant-scoped, time-boxed NDJSON with a SHA-256 chain of custody |
Live alert coverage. k_automated_alerts in baselineCoverage is now live-measured via AlertCoverageService — queries /_plugins/_alerting/monitors?size=100 and counts enabled monitors. coverage_ok=true when ≥ 2 monitors are enabled. The two agent monitors shipped in Sprint 3 (“Collector Agent - Compliance Flag Raised” and “Collector Agent - Ingest Silence”) are what drive this live signal.
M-26-14 is a selectable compliance framework via the existing POST /api/v1/monitoring/enable (no new endpoint) — MonitoringService resolves the m2614_compliance log type and auto-seeds per-project rows. The analyzer dispatcher emits the a–e event logs (delegating to the matching SOC2 generators) and runs them through the LogLeakDetector gate.
The compliance clock (/milestones)
Section titled “The compliance clock (/milestones)”M-26-14 dates the mandate as well as defining it: Level 1 within 120 days of CISA publishing the Logging Reference Architecture, Level 2 within 180, Level 3 (Advanced) within 320. LraMilestoneService computes the three due dates and each milestone’s state — met / pending / at-risk / overdue — against the agency’s current overall level. A level the agency has already reached reports met permanently; it cannot go overdue.
The clock starts at a date CISA sets, which the application cannot discover, so it is configured:
| Key | Env | Default | Meaning |
|---|---|---|---|
m2614.lra.published-date | M2614_LRA_PUBLISHED_DATE | (blank) | ISO-8601 LRA publication date |
m2614.lra.at-risk-days | M2614_LRA_AT_RISK_DAYS | 30 | Warning band before an unmet deadline |
Log Access (CISA/FBI) release (/log-access-export)
Section titled “Log Access (CISA/FBI) release (/log-access-export)”The memo requires an agency to provide logs to CISA/FBI on request during a compromise. LogAccessExportService implements that as a scoped release, not a second way into the store:
- It runs against an approved, unexpired
AccessGrant(FR-15) and can only read that grant’s index pattern. Arequested,expiredor expiry-less grant is refused (403); another tenant’s grant is invisible (404). - The window is mandatory and capped (
400if missing, inverted, or over the maximum), as is the document count. - The payload’s first NDJSON line is a
_custodyrecord — grant, requester, approver, index pattern, window, document count, and the SHA-256 of the documents — and the same digest is written into the hash-chained governance audit as acisa_exportaction. The recipient can recompute it; the agency can prove later exactly what it released. - A failed query raises rather than returning an empty “here are your logs” file, and nothing is recorded as released unless it was.
| Key | Env | Default |
|---|---|---|
m2614.log-access.max-docs | M2614_LOG_ACCESS_MAX_DOCS | 10000 |
m2614.log-access.max-window-days | M2614_LOG_ACCESS_MAX_WINDOW_DAYS | 31 |
m2614.log-access.time-field | M2614_LOG_ACCESS_TIME_FIELD | @timestamp |
Risk banding — HVA / High-Impact prioritization
Section titled “Risk banding — HVA / High-Impact prioritization”M-26-14 is explicitly risk-based: agencies are to prioritize High Value Assets and High Impact Systems. asset_inventory.criticality normalizes whatever the feed calls it (criticality, hva, isHva, fismaImpact, impactLevel) into hva / high_impact / standard, and /api/v1/inventory/summary returns byCriticality with per-band coverage.
An absent or unrecognized value leaves the existing band alone rather than defaulting to standard, so a feed refresh cannot quietly demote a known HVA.
Phase 2 Operate API (JWT-gated, tenant-scoped)
Section titled “Phase 2 Operate API (JWT-gated, tenant-scoped)”These back the Operate signals the scorer reads. All OpenSearch-side writes are fail-soft until LOGRITE_PROVISIONER_ENABLED=true + a privileged admin user are configured, so they never break the request path.
| Method | Path | Purpose |
|---|---|---|
| GET/POST | /api/v1/governance/audit /verify | Row-hash-chained governance audit trail + tamper verification |
| GET/POST | /api/v1/access/grants | Just-in-time OpenSearch-role access grants; a @Scheduled sweep expires them |
| GET/POST | /api/v1/retirement/requests | Two-gate log/role retirement (requester ≠ gate-1 ≠ gate-2; single-approver → 400) |
| GET/POST | /api/v1/integrity/manifests /verify /compute | @Scheduled Merkle integrity manifests over ingested windows + re-verification |
| GET/POST | /api/v1/retention/policy /status | Retention-tier (ISM) policy + contracts defining searchable/retrievable windows |
| POST/GET | /api/v1/inventory/assets /summary | CDM/HWAM/SWAM asset-feed ingest (upsert by asset id, governance-audited) → Inventory Visibility |
A nightly/periodic @Scheduled job computes new Merkle manifests and re-verifies recent ones; a tampered window flips verify_status:mismatch, which the scorer treats as Log Management L3 unmet. The Phase-2 dashboard cards are shipped — the /m26-14 page has an Operate & Inventory signals row driving Log Integrity (manifests + verify), Access Governance (JIT grants), Data Retention (ISM contract), and the CDM/HWAM/SWAM Inventory Feed coverage.
Frontend
Section titled “Frontend”The /m26-14 page (sidebar, after AI Warden) is organized into three pill tabs (AI-Warden style): Maturity · Operate · Plan. The page header (title + Recompute + Export plan) stays above the tabs. (The earlier 6-stage lifecycle strip was replaced by the tabs.)
- Maturity — the Enable M-26-14 as a framework card (shows enabled-status derived from baseline a–e coverage + a CTA into the Compliance flow with M-26-14 pre-selected); the lowest-watermark overall badge + L0→L4 stepper; five element level cards each with a live-measured / self-attested source badge (
measurementSources) + %-of-systems and, for the GAP elements, an attest level picker; a Honesty preserved panel separating live-measured from self-attested elements (FR-20); the Instrument baseline a–k coverage as a read-only tri-state tile grid (✓ a–e code-gen · ◑ f–k SOC-side · ○ gap); and a sortable Appendix-C thresholds table. - Operate — the CEM/THIRF capability map (PRESENT vs BUILD, FR-7 … FR-16); the Operate & Inventory signals row (Log Integrity manifests + verify, Access Governance JIT grants, Data Retention ISM contract, CDM/HWAM/SWAM Inventory Feed coverage); and the OperationsPanel (JIT / two-gate / asset ingest / governance audit).
- Plan — the Remediate capping-element runbook cards (the “go to controls” button switches to the Operate tab); an Export — Agency Logging Plan card (PDF/JSON, honesty preserved); and the collapsible FR-1…FR-25 traceability grid.
Timestamp Sync has three states, not two. source:"none" means the ingest host exposes neither chrony nor timedatectl, so sync could not be measured — which the card says explicitly, because it is not the same claim as “measured, and out of sync”.
The Log Access release form lists only grants that are approved and still inside their window (the backend refuses the rest, so offering them would only produce a 403 the user cannot act on), takes a window, downloads the NDJSON, and shows the SHA-256 chain-of-custody digest back on screen.
Attest level — downgrades are allowed. The GAP-element level picker now offers all levels L0–L4 (the previous “attest upward only / lower levels disabled” restriction was removed). Selecting a level below the current one is permitted but pops a confirmation modal warning that, since overall is the lowest watermark, the downgrade may reduce the overall grade. Raising a level applies immediately. Both still PUT /api/v1/maturity/attestation.
Export — what the Agency Logging Plan carries
Section titled “Export — what the Agency Logging Plan carries”GET /export (JSON and PDF) now carries the surrounding controls, not just the score: the deadlines, an operating posture block (NTP attestation, retention windows, integrity manifest counts, access-grant governance + the audit-chain verification, inventory risk bands), and the cross-cutting LRA topics (Zero Trust · AI · IoT/OT · HVA). Every part of the posture block is best-effort — a control that cannot be read reports its error rather than dropping out of the filed document silently.
The PDF renderer wraps and paginates. It previously emitted a single page and stepped the text cursor per line regardless of where it landed, so content past ~46 lines was drawn below the page and past ~92 characters off the right edge — invisible in the byte stream, visible only in the document an agency files.
Governance audit chain fix (2026-10-14)
Section titled “Governance audit chain fix (2026-10-14)”POST /api/v1/governance/verify failed from the first row in every workspace measured on dev
(“0 of 580 verified”). Not tampering: rows were hashed with a nanosecond timestamp and stored
in timestamp(6), which rounds to microseconds, so the verifier rehashed a different value. A null
actor (hashed as "", stored as “system”) and nested details (jsonb reorders keys) had the same shape.
- New rows are hashed canonically (
v2|…): UTC instant truncated to microseconds, the actor as stored, details sorted at every depth. - Existing rows are verified by recovering the rounded-away precision: the original instant is
one of the 1,000 values within ±500 ns of the stored microsecond, and only an exact hash match
counts (
legacyRecovered). A legacy row with nested details cannot be re-derived and is reported asunverifiableLegacy(with ids) — never counted as verified. - Forks — two rows chained to the same head, found in both workspaces — came from unserialised
appends. Appends are now serialised per workspace with
pg_advisory_xact_lock(PostgreSQL only; the integration tests run on SQLite, which has no such function). Existing forks are accepted only when the row’s own hash verifies, and listed (forks,forkIds); the row a fork branched past has no successor committing to it, which is the one gap and is stated. - Live on dev (backend
533199a4): workspace 3103 VERIFIED 600 of 600; 4102 265 of 266 (1 unverifiable legacy row); a row written afterwards verifies under the new form.