Skip to content

Auto-generated mirror. This page mirrors docs/api-stability.md, the canonical source of truth. Do not edit this file directly; edit docs/api-stability.md and re-run scripts/wiki/sync_mirrors.py.

API stability commitments — NORMATIVE

Status: NORMATIVE as of v0.9.7 (2026-05-19). Promoted from DRAFT during the v0.9.7 P2.1 v1.0-prep cycle per the docs/v1.0-transition.md Candidate B framing. Promoted ahead of v1.0 so the v0.9.x → v1.0.0 transition does not require any additional API-contract changes — the contract is set and tested over the remaining v0.9.x line.

Scope: defines which surfaces carry semantic-versioning guarantees, and which surfaces remain free to change without a major-version bump. Operators MAY rely on the frozen surfaces; integrators MAY build against them with confidence that Evidentia will not break them without a deprecation cycle.

Pre-v1.0 vs post-v1.0 semantics: this NORMATIVE document is already binding — Evidentia will not knowingly break a frozen surface listed below in any v0.9.x release. The v1.0.0 release itself does not add new constraints; it ratifies the contract already in force.

Canonical location: docs/api-stability.md Cross-references: v1.0-transition.md, enterprise-grade.md, release-checklist.md, deprecation-calendar.md


Versioning semantics (post-v1.0)

Evidentia follows Semantic Versioning 2.0.0 with the following interpretation:

Bump Meaning Example
Major (X.0.0) Breaking change to a frozen surface Remove a Pydantic field, rename a CLI flag
Minor (1.X.0) New functionality, additive-only changes to frozen surfaces New EventAction, new CLI command, new optional model field
Patch (1.0.X) Bug fixes, security patches, doc updates, catalog content refreshes CVE fix, threshold default adjustment, typo

Pre-v1.0 (v0.9.7 onward — this document NORMATIVE): the contract listed in this document is binding. Breaking changes to frozen surfaces require a deprecation cycle per the policy in § "Deprecation policy" below. The v1.0.0 release ratifies (does not expand) the contract already in force.

Earlier pre-v1.0 (v0.9.0 – v0.9.6): minor bumps may have contained breaking changes to any surface. The v0.9.x line was the "stabilization window" where the public contract was identified and documented in DRAFT form.


Frozen surfaces

These surfaces carry full semver guarantees at v1.0+. Breaking changes require a major-version bump with a deprecation cycle.

1. Pydantic model fields

Packages: every module of evidentia_core.models, plus the model modules of evidentia_core.ai_governance, evidentia_core.rbac and evidentia_core.retention listed below.

All exported model classes have stable field names and types. Adding optional fields (with defaults) is a minor-bump change. Renaming, removing, or changing the type of an existing field is a major-bump trigger.

The table below enumerates the frozen modules; scripts/check_public_surface.py verifies every row (the module file is present, every listed symbol is defined) and that every evidentia_core/models/*.py module has a row.

Module Key models
models/common.py EvidentiaModel, ControlMapping, Severity, OLIRRelationship, NAMESPACE_EVIDENTIA_FINDING. The callables deterministic_finding_id(), new_id(), utc_now() and current_version() are also frozen.
models/control.py ControlImplementation, ControlInventory, ControlStatus
models/evidence.py EvidenceArtifact, EvidenceBundle, EvidenceType, EvidenceSufficiency. v0.9.5+ adds EvidenceArtifact.version / lineage_id / predecessor_id optional fields plus the new_version() factory helper; these are now frozen.
models/finding.py (v0.10.0+) SecurityFinding, Finding (v0.10.1 alias: both names refer to the same class), FindingStatus, ComplianceStatus. Frozen following the v0.10.0 OCSF-alignment evolution. Field changes are additive-only. v0.10.0 added the compliance_status and remediation optional fields. v0.10.1 introduces Finding as the canonical name alongside SecurityFinding; the SecurityFinding alias is retained for at least 1 minor cycle per the deprecation policy. Target removal of the SecurityFinding alias: v1.0.0 (the earliest major bump). See deprecation-calendar.md. v0.10.5 Phase 10 adds the deterministic-id derivation contract: when a SecurityFinding is constructed with both source_system and source_finding_id present and no explicit id=, the id field derives as uuid5(NAMESPACE_EVIDENTIA_FINDING, f"{source_system}\x00{source_finding_id}") via deterministic_finding_id() in models/common.py. The NAMESPACE_EVIDENTIA_FINDING UUID (c81bcb44-9b41-5b18-9f10-72b3b9b4d3d6) is pinned forever. Two collect() calls against an unchanged source produce findings with byte-identical id values. Per-collector verdicts in collector-idempotency-audit.md.
models/gap.py GapStatus, GapSeverity, GapAnalysisReport, ControlGap, Milestone, POAMState, ImplementationEffort, EfficiencyOpportunity. v0.9.5 adds Milestone.owner / Milestone.reviewer optional fields; these are now frozen.
models/catalog.py CatalogControl, CatalogSourceRow, ControlCatalog, CatalogAuditContext, CatalogPublicationNotice, CatalogStatus, FrameworkMapping, CrosswalkDefinition, RelationshipType, TextDepth, StatementRow, has_statement(), derive_text_depth(). v0.10.6 added the CrosswalkDefinition.provenance / verification / verification_note optional fields (additive-only and backward-compatible: the eight pre-v0.10.6 crosswalks load unchanged). v0.13 adds the optional withdrawn field, set from the OSCAL status prop.
models/open_corpora.py (v0.13+) ValueRef, FieldRef, FieldSelection, SourceBinding, SourceDocument, SemanticSelection, Occurrence, ControlSourceRef, Diagnostic, ControlBinding
models/open_corpora.py (v0.13+) NativeData, NativeBundle, PackageRef, SourceChunk
models/open_corpora.py (v0.13+) SourceUpload, ExternalImportRequest, ImportResult, NativeReadRequest, NativeReadError, CatalogPublicationObservation, CatalogStorageErrorEnvelope
models/open_corpora.py (v0.13+) StoredFile, StoredChunk, StoredChunks, SourceIndexEntry, PackagedIndex, ExternalStoredFile, ExternalEntry, ExternalIndex
models/tprm.py Vendor, VendorType, CriticalityTier, RegulatoryClassification, FourthParty, EvidenceRef
models/fedramp_ksi.py KsiEvidenceItem, KsiPersistenceCycle, KsiIndicatorEntry, FrrRequirementEntry, KsiStatusDocument
models/gap_diff.py GapDiffEntry, GapDiffSummary, GapDiff
models/model_risk.py Methodology, Provenance, Tier, ValidationStatus, ValidationSeverity, ModelInput, ModelOutput, ValidationFinding, ModelInventory
models/obligation.py PrivacyObligation, PrivacyRegime, ObligationCatalog
models/risk.py RiskLevel, LikelihoodRating, ImpactRating, RiskTreatment, TraceClaim, ReasoningTrace, RiskStatement, RiskRegister
models/threat.py AttackTechnique, TechniqueCatalog, Vulnerability, VulnerabilityCatalog
models/traceability.py ControlThreatMapping, TraceabilityMatrix
ai_governance/classification.py (v0.9.3+) AISystemDescriptor, AISystemClassification, EUAIActTier, NISTAIRMFFunction, AnnexIIIDomain
ai_governance/registry.py (v0.9.3+; v0.9.6 federal expansion) AISystemRegistryEntry, DeploymentStatus, ATOReference (v0.9.6)
ai_governance/fips199.py (v0.9.6+) FIPS199Categorization, FIPS199Impact
ai_governance/omb_m_24_10.py (v0.9.6+; DEPRECATED v0.10.12: OMB M-24-10 was rescinded 2025-04-03 by M-25-21) OMBImpactCategory. Retained and still loads (no behavior change, no runtime warning) per the deprecation policy; superseded by omb_m_25_21.
ai_governance/omb_m_25_21.py (v0.10.12+) HighImpactDetermination, HighImpactBasis, OMBHighImpactAssessment, triggers_minimum_practices(), crosswalk_from_legacy(). Models OMB M-25-21's single "high-impact AI" category (replaces the legacy rights-/safety-impacting split).
ai_governance/scr.py (v0.9.6+) SCRForm, SCRCategory
rbac/policy.py (v0.9.5+) Role, RBACPolicy. check_permission() and load_policy_from_file() callables also frozen.
retention/metadata.py (v0.7.11+) RetentionMetadata, RetentionClassification, RetentionLifecycleStage

How this table is checked: a backticked CapWords or UPPER_SNAKE name (for example EvidentiaModel or NAMESPACE_EVIDENTIA_FINDING) is a class, enum, type alias or constant that must exist in the row's module. A backticked name followed by parentheses, such as check_permission(), is a callable that must exist in the module or on one of the classes the row lists. A backticked lowercase name with no parentheses is a field or a module reference, and is not checked.

Serialization guarantee: JSON-serialized output of any frozen model at version N must be deserializable by version N+1 within the same major. Field ordering in JSON output is not guaranteed.

2. EventAction enum

Package: evidentia_core.audit.events

The EventAction enum is an append-only contract. Existing values are never removed or renamed post-v1.0. New values may be added in any minor release.

Current namespaces (as of v0.10.12). Example values below are real members of evidentia_core.audit.events.EventAction — the enum module is the authoritative source:

Prefix Domain Example values
COLLECT_* Evidence collection COLLECT_STARTED, COLLECT_COMPLETED, COLLECT_FAILED
AUTH_* Authentication AUTH_CREDENTIAL_RESOLVED, AUTH_CREDENTIAL_FAILED
CONFIG_* Configuration CONFIG_LOADED, CONFIG_RESOLVED, CONFIG_INVALID
SIGN_* Cryptographic signing SIGN_GPG_SIGNED, SIGN_SIGSTORE_SIGNED, SIGN_FAILED
VERIFY_* Verification VERIFY_DIGEST_PASSED, VERIFY_SIGNATURE_PASSED, VERIFY_COMPLETED
MANIFEST_* Manifest operations MANIFEST_GENERATED, MANIFEST_EMPTY_SET_ATTESTED, MANIFEST_INCOMPLETE
AI_* AI/LLM operations AI_RISK_GENERATED, AI_EVAL_FAITHFULNESS_CHECKED
AI_SYSTEM_* (v0.9.3+) AI system inventory AI_SYSTEM_CLASSIFIED, AI_SYSTEM_REGISTERED, AI_SYSTEM_UPDATED, AI_SYSTEM_RETIRED, AI_SYSTEM_DELETED, AI_SYSTEM_FIPS_CATEGORIZED (v0.9.6), AI_SYSTEM_OMB_CLASSIFIED (v0.9.6; deprecated-but-retained v0.10.12 — legacy M-24-10 path, append-only so it is never removed), AI_SYSTEM_HIGH_IMPACT_CLASSIFIED (v0.10.12 — OMB M-25-21 high-impact determination), AI_SYSTEM_SCR_EMITTED (v0.9.6)
AI_MCP_* MCP server operations AI_MCP_TOOL_AUTHORIZED, AI_MCP_TOOL_DENIED
POAM_* POA&M lifecycle POAM_CREATED, POAM_MILESTONE_REACHED, POAM_VERIFIED
CONMON_* Continuous monitoring CONMON_DAEMON_STARTED, CONMON_ALERT_DISPATCHED, CONMON_CYCLE_DUE, CONMON_CYCLE_OVERDUE, CONMON_CYCLE_MARKED_COMPLETED
EVIDENCE_* (v0.9.6+) Evidence WORM lineage EVIDENCE_VERSION_PERSISTED, EVIDENCE_WORM_VIOLATION_BLOCKED, EVIDENCE_LINEAGE_QUERIED
RBAC_* (v0.9.8+) Multi-tenant RBAC RBAC_TENANT_BOUNDARY_CROSSED
RETENTION_* Data retention RETENTION_RECORD_PUT, RETENTION_RECORD_EXTENDED, RETENTION_LEGAL_HOLD_APPLIED, RETENTION_LEGAL_HOLD_RELEASED, RETENTION_LIFECYCLE_TRANSITIONED, RETENTION_RECORD_PURGED, RETENTION_GDPR_PURGE
GOVERNANCE_* (v0.10.12) Governance metrics / workflows GOVERNANCE_CHALLENGE_CREATED, GOVERNANCE_METRIC_CREATED, GOVERNANCE_METRIC_OBSERVED, GOVERNANCE_METRIC_DELETED, GOVERNANCE_WORKFLOW_RUN, GOVERNANCE_WORKFLOW_ADVANCED, GOVERNANCE_WORKFLOW_DELETED
CATALOG_* (v0.10.12) Catalog management CATALOG_IMPORTED, CATALOG_REMOVED
TRACEABILITY_* (v0.10.12) Control↔threat traceability TRACEABILITY_EMITTED
INTEGRATIONS_* (v0.10.12) Output integrations INTEGRATIONS_SERVICENOW_PUSH

Operators building alerting / SIEM integrations on top of the audit log can depend on these values being stable.

3. CLI flag names and semantics

Package: evidentia (the CLI entry point)

Top-level command groups (18+ as of v0.9.7):

evidentia gap          evidentia catalog      evidentia risk
evidentia explain      evidentia integrations evidentia collect
evidentia oscal        evidentia tprm         evidentia model-risk
evidentia governance   evidentia retention    evidentia poam
evidentia conmon       evidentia ai-gov       evidentia eval
evidentia mcp          evidentia serve        evidentia evidence (v0.9.6)

Global flags (frozen across all commands):

  • --verbose / -v — DEBUG-level logging
  • --quiet / -q — ERROR-level only
  • --config <path> — explicit evidentia.yaml override
  • --offline — air-gap guard (v0.4.0)
  • --json-logs — ECS 8.11 JSON output (v0.7.0)
  • --rbac-identity <id> — per-invocation RBAC identity override (v0.9.6)

Stability contract:

  • Command names are frozen (rename = major bump)
  • Flag names (--flag-name) are frozen within each command
  • Flag semantics (what a flag does) are frozen
  • Flag default values may change in minor releases (documented in CHANGELOG)
  • Adding new flags is non-breaking (minor bump)
  • Adding new commands or subcommands is non-breaking (minor bump)

Deprecation for CLI: a deprecated flag emits a DeprecationWarning for at least 1 minor-release cycle before removal. The warning includes the replacement flag name.

4. Plugin contracts

Package: evidentia_core.plugins.*

Five ABC/Protocol contracts that third-party code may implement:

Contract Location Stability
AuthProvider plugins.auth._base Method signatures frozen
StorageBackend[T] plugins.storage._base Generic ABC; method signatures frozen
MarketplaceProvider plugins.marketplace._base Method signatures frozen
BaseSaaSCollector plugins.collectors._base Method signatures frozen; _auth_header() hook stable
ContinuousEvidenceSource plugins.continuous Protocol; poll() + health_check() + attributes frozen

What "method signatures frozen" means:

  • Parameter names, types, and ordering are stable
  • Return types are stable
  • Adding optional parameters (with defaults) is non-breaking
  • Adding new abstract methods to an ABC is a major-bump trigger (breaks existing implementations)

Supporting dataclasses used in plugin signatures are also frozen: AuthResult, CatalogManifest, EvidenceRecord.

5. Library entry points

Public importable paths that operators and integrators use:

# Core analysis + data models
from evidentia_core.gap_analyzer import GapAnalyzer
from evidentia_core.models import ControlGap, GapAnalysisReport, GapSeverity, GapStatus
from evidentia_core.audit.events import EventAction
from evidentia_core.catalogs.registry import FrameworkRegistry
from evidentia_core.conmon import derive_status, BUNDLED_CADENCES
from evidentia_core.models.gap import POAMState, Milestone
from evidentia_core.poam_store import save_poam, load_poam_by_id
from evidentia_core.plugins import AuthProvider, StorageBackend, ...

# v0.9.5+ RBAC + collaboration primitives
from evidentia_core.rbac import (
    Role, RBACPolicy, check_permission, load_policy_from_file,
)

# v0.9.6+ evidence WORM store + cloud mirror
from evidentia_core.evidence_store import (
    save_evidence, list_lineage, load_evidence_version,
    list_lineages, get_evidence_store_dir,
    EvidenceWORMViolation, InvalidEvidenceIdError,
    EVIDENCE_STORE_ENV_VAR,
)
from evidentia_core.evidence_store_worm import (
    mirror_to_worm, fetch_from_worm,
)

# v0.9.6+ AI-gov federal expansion
from evidentia_core.ai_governance import (
    FIPS199Categorization, FIPS199Impact,
    OMBImpactCategory, triggers_minimum_practices,
    ATOReference, AISystemRegistryEntry, DeploymentStatus,
)

# v0.10.12+ OMB M-25-21 high-impact AI (M-24-10 rescinded 2025-04-03;
# the legacy OMBImpactCategory symbol above stays on the stable surface,
# DEPRECATED but importable)
from evidentia_core.ai_governance.omb_m_25_21 import (
    HighImpactDetermination, HighImpactBasis, OMBHighImpactAssessment,
    triggers_minimum_practices, crosswalk_from_legacy,
)
from evidentia_core.ai_governance.scr import (
    SCRForm, SCRCategory, emit_scr_form, classify_change,
)

# v0.9.6+ OSCAL schema version constant
from evidentia_core.oscal import OSCAL_SCHEMA_VERSION

# v0.10.0+ OCSF Compliance Finding interchange (needs the [ocsf] extra)
from evidentia_core.ocsf import (
    finding_to_ocsf, finding_from_ocsf, OCSFMappingError,
)

# v0.10.4+ Gap-report OCSF Compliance Finding array emit (needs [ocsf] extra)
from evidentia_core.gap_analyzer.ocsf import gap_report_to_ocsf_array

# v0.10.5+ Gap-report OCSF Detection Finding array emit (needs [ocsf] extra)
from evidentia_core.gap_analyzer.ocsf_detection import (
    gap_report_to_ocsf_detection_array,
)

# v0.10.5+ Gap-report CycloneDX 1.6 VEX emit (no extra required)
from evidentia_core.gap_analyzer.vex import gap_report_to_cyclonedx_vex

# v0.12.0+ Explicit-root gap-report store handle. PUBLIC, PROVISIONAL:
# documented so integrators may build on it, with the freeze decision
# deliberately deferred to the v1.0 candidate review — see
# docs/v1.0-freeze-candidates.md §4.
from evidentia_core.gap_store import (
    GapReportRepository, GapStoreRootChangedError,
)

# AI features (production runtime)
from evidentia_ai.risk_statements import RiskStatementGenerator

# v0.10.5+ DFAH determinism + faithfulness harness (dev-time;
# extracted from evidentia_ai.eval to a dedicated workspace
# package so air-gap installs of the production runtime no
# longer pull the dev-time eval stack). This is now the ONLY
# import path: the evidentia_ai.eval.* re-export shims were
# removed in v0.12.0 as announced, along with the unconditional
# evidentia-eval dependency on evidentia-ai. See
# docs/deprecation-calendar.md § Recently removed.
from evidentia_eval import DFAHarness, faithfulness_score

# Collectors + integrations
from evidentia_collectors.bitsight import BitSightCollector
from evidentia_collectors.securityscorecard import (
    SecurityScorecardCollector,
)
from evidentia_integrations.alerting import (
    SMTPAlertChannel, SMTPConfig, WebhookAlertChannel, WebhookConfig,
)

# API + MCP entry points
from evidentia_api.app import create_app
from evidentia_api.rbac_dependency import require_role
from evidentia_mcp.server import build_server  # v0.8.0+ name

Stability contract: these import paths are frozen. Moving a class to a different module internally is allowed as long as the original import path continues to work (via re-export).

Entra/M365 collection contract (v0.13)

The evidentia_collectors.entra_m365 exports EntraM365Collector, EntraM365CollectRequest, EntraM365CollectResult, EntraM365CapabilityResult, EntraM365Diagnostic and EntraM365InputError are public. Their names and documented request/result fields follow this stability policy. collect_v2(request) returns the full result; collect(request) is the findings-only convenience method. New integrations should use collect_v2 so they retain coverage and provenance.

The entra-m365-collection/v1 result contains exactly nine capability records in canonical order, including unrequested capabilities. status describes the requested subset; full_surface_complete separately describes all nine. Declared tenant labels and credential modes do not verify authenticated identity. Bounds, source field distinctions and exported DLP formats are documented in the collector design.

The collect entra-m365 CLI leaf exposes --tenant-label, repeatable --capability, --lookback-days, --max-items, --max-pages, --dlp-export, --dlp-format and --output/-o. A valid partial/unavailable result is written before exit 1; complete is 0, invalid input 2 and read-role denial 77. Operational and output failures are 1. Stdout remains JSON-only when no output file is used.

POST /api/collectors/entra-m365/collect accepts the authoritative request model with inline DLP text, never a credential or server path. Its cumulative 8 MiB body limit is independent of Content-Length. Completed attempts return the validated full result with HTTP 200, including partial/unavailable evidence. Input errors are sanitized 400, body overflow 413 and unsupported media 415. Existing authentication and read RBAC apply; a Graph request without an AuthProvider returns 403. A missing optional feature returns 503 and advertises no success schema. Internal failure without a safe result returns sanitized 500. GET /api/collectors/status includes entra-m365 installation and configuration booleans, with live validation and identity verification explicitly false.

The fixed credential references are ENTRA_M365_ACCESS_TOKEN, ENTRA_M365_RETENTION_ACCESS_TOKEN and ENTRA_M365_AUTH_MODE. The primary mode is application or delegated, defaulting to application; retention is separately delegated. There is no token refresh, environment-name override or retention fallback. These names and meanings form the documented collector configuration contract. DLP-only ingestion resolves neither token.

Storage retention collection contract (v0.13)

The evidentia_collectors.retention public exports are StorageRetentionCollector, StorageRetentionCollectRequest, StorageRetentionCollectResult, StorageRetentionResourceResult, StorageRetentionComponentResult, StorageRetentionDiagnostic and StorageRetentionInputError. collect_v2(request) returns full evidence; collect(request) returns findings only. The strict request selects one provider, a scope label and 1 through 20 validated targets in order.

The storage-retention-collection/v1 result retains all selected resources, component states, native JSON projections, diagnostics, findings and a manifest. Its observation scope is configuration and its coverage is the selected resources. Object enforcement, recordset completeness and authenticated cloud identity are explicitly unassessed. A complete read leaves compliance unknown. Source timestamp strings and native numeric units are preserved. The canonical projection hash binds the selected logical fields, not a full provider recording.

evidentia collect retention --request-file PATH [--output PATH] reads a regular JSON file of at most 65536 bytes. Read-role checks precede input/output access. Output uses a reserved sibling and atomic replacement after full-result validation and comparison with the original selection. Complete exits 0; partial/unavailable evidence or output failure exits 1, invalid input exits 2, and read-role denial exits 77. Without --output, stdout contains full JSON only.

POST /api/collectors/retention/collect authenticates the actor and applies read RBAC before streaming its cumulative 65536-byte body. It accepts JSON only. Complete, partial and unavailable results return HTTP 200 with the full schema; invalid input returns 400, oversized input 413, unsupported media 415, and actor denial 401/403. Exact absence of the selected optional package returns 503; broken transitive imports or invalid internal results return sanitized 500. Results and optional-error classification remain bound to the original request even if a faulty worker mutates its argument.

The fixed credential references are AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and optional AWS_SESSION_TOKEN, STORAGE_RETENTION_AZURE_ACCESS_TOKEN, and STORAGE_RETENTION_GCS_ACCESS_TOKEN. There is no discovery, refresh or request override. GET /api/collectors/status adds retention with installation and per-provider reference-presence booleans; live validation and identity verification remain false. The design defines field selection, routes, limits and evidence semantics.

Enterprise retention collection contract (v0.13)

The evidentia_collectors.enterprise_retention public exports are EnterpriseRetentionCollector, EnterpriseRetentionCollectRequest, EnterpriseRetentionCollectResult, EnterpriseRetentionResourceResult, EnterpriseRetentionReadResult, EnterpriseRetentionObservation, EnterpriseRetentionDiagnostic, EnterpriseRetentionManifest and EnterpriseRetentionInputError. Collector construction requires an authorized trusted profile. collect_v2(request) returns full evidence; collect(request) returns findings. Internal profile, transport and correspondence helpers remain non-frozen internals.

The strict request selects google-vault, splunk-enterprise or elastic-ilm, a profile_alias, a scope_label, and 1 through 20 ordered unique targets. It contains no credential, origin, header, trust policy or raised-limit field. The enterprise-retention-collection/v1 result retains all selected resources and unique reads, native observations, coverage, diagnostics, findings and a cross-checked manifest. Compliance, record completeness, effective enforcement and authenticated provider identity remain unassessed. Vault retention rules are explicitly unassessed. Native types and source timestamp text remain literal; logical projection digests do not attest to an unretained full response recording.

collect enterprise-retention --request-file PATH [--output PATH] reads at most 65536 bytes after the CLI read-role guard. Profile authorization requires allow_local_cli=true. Output is reserved before collection and replaced atomically only after full-result and original selection validation. Complete exits 0; partial/unavailable or operational failure exits 1; invalid input exits 2; read-role or profile denial exits 77. Stdout output contains full JSON.

POST /api/collectors/enterprise-retention/collect requires configured authentication and read RBAC before input streaming, then exact profile-principal authorization. Every valid full result uses HTTP 200, including partial and unavailable evidence. Invalid input returns 400, authentication or access denial 401/403, excess cumulative bytes 413, unsupported media 415, and optional-feature absence 503. Unexpected execution or invalid results return fixed 500 errors. Only exact absence of the collectors package or enterprise feature counts as optional absence; internal/transitive import failures remain installation failures.

EVIDENTIA_ENTERPRISE_RETENTION_PROFILES_FILE identifies the trusted startup registry. API-injected registries take precedence and are detached; malformed configuration fails startup. The absent-feature route does not consume a registry or body. Unknown, wrong-provider and unauthorized profiles share profile_unavailable. API grants and local CLI grants are separate. GET /api/collectors/status adds enterprise_retention with installation availability and false live-validation/identity-verification flags, without credential or profile enumeration. The design defines the exact field selection, limits and source semantics; the console preserves the full result and exact native text.

6. REST API URIs

Package: evidentia_api.routers.*

All REST endpoints follow the pattern /api/<resource> and are versioned implicitly (no /v1/ prefix until a breaking REST change necessitates it).

Frozen URI prefixes (23 routers):

Prefix Router module Purpose
/api/health health.py Liveness + readiness
/api/config config.py Configuration state
/api/doctor doctor.py Environment diagnostics
/api/explain explain.py Control explanations
/api/llm-status llm_status.py LLM provider health
/api/frameworks frameworks.py Framework catalog
/api/init init_wizard.py First-run wizard (preview + commit)
/api/risk risks.py Risk statements + FAIR risk quantification
/api/gap gaps.py Gap analysis
/api/integrations integrations.py Integration status (Jira / ServiceNow / BI)
/api/tprm tprm.py Third-party risk
/api/model-risk model_risk.py AI model risk
/api/collectors collectors.py Collector status
/api/metrics metrics.py Prometheus metrics
/api/poam poam.py POA&M management
/api/conmon conmon.py Continuous monitoring
/api/ai-gov ai_gov.py AI governance
/api/governance governance.py Governance challenges, metrics, workflows
/api/retention retention.py Records retention + legal hold
/api/evidence evidence.py WORM evidence store
/api/catalog catalog.py Catalog management — import / crosswalk / license
/api/oscal oscal.py OSCAL Verify (verify a signed Assessment Result; read-only)
/api/traceability traceability.py Control ↔ threat traceability matrix

Stability contract:

  • URI paths are frozen (rename = major bump)
  • Response JSON field names are frozen (additions only)
  • HTTP methods per endpoint are frozen
  • Query parameter names are frozen
  • Adding new endpoints is non-breaking (minor bump)
  • Adding new optional query parameters is non-breaking

Error responses (2026-07-06 error-shape convergence): every deliberate 4xx/5xx raised by a handler carries the structured machine-readable payload

{"detail": {"error": "<snake_case_key>", ..., "message": "<human text>"}}

generalizing the v0.9.5 rbac_denied 403. The error keys are a stable, additions-only vocabulary (registry: evidentia_api.errors); clients MAY dispatch on them. message texts and site-specific context fields are informative — not frozen. FastAPI/Pydantic request-validation 422s keep their array-shape detail (unchanged). Each operation's deliberately-raised 4xx/5xx statuses are documented in its OpenAPI responses.


Non-frozen surfaces

These surfaces may change in any release (minor or patch) without constituting a breaking change. Operators should not depend on their stability.

Internal helpers

Any function, class, or module prefixed with underscore (_) is private. This includes:

  • evidentia_core._internal.*
  • Any _helper, _utils, _compat modules
  • Private methods on public classes (def _compute_score(...))

Test fixtures and utilities

Everything under tests/ is non-frozen:

  • Test data files (tests/data/dfah-calibration/corpus*.jsonl)
  • Fixture factories (tests/conftest.py)
  • Test helper modules (tests/helpers/)

Bundled catalog content

The compliance catalogs shipped with Evidentia evolve as authoritative sources publish updates (NIST revisions, ISO amendments, EU regulation enforcement dates). Catalog content changes are patch-level — they don't constitute API breaks.

No catalog pin command exists or is scheduled. To hold an assessment to a fixed catalog version, import the copy you assessed against with evidentia catalog import: a user-imported catalog shadows the bundled one under the same framework id, and evidentia catalog where <framework> shows which copy resolves.

Threshold defaults

Default values for scoring thresholds (faithfulness, risk determinism, health scoring) may be tuned between releases based on empirical calibration results. These changes are documented in CHANGELOG but are non-breaking because operators can always override via CLI flags:

  • --faithfulness-threshold
  • --faithfulness-threshold-mode {framework-aware,fixed}
  • --fail-on-determinism-rate-below
  • --health-score-weights

Scripts

Everything under scripts/ is operational tooling, not public API. Scripts may be added, removed, or refactored freely.

Docker image internals

The container's internal layout (file paths, installed packages, base image) is non-frozen. Only the CLI interface exposed by the container is stable (same guarantees as CLI flags above).

MCP tool descriptions and metadata

While MCP tool names are frozen (they're part of the tool contract with AI clients), tool descriptions and parameter descriptions may be refined for clarity without constituting a breaking change.


Deprecation policy

When a frozen surface must change:

  1. Announce: add a DeprecationWarning (Python) or deprecation notice (REST response header) in minor release N. Document in CHANGELOG under "Deprecated".

  2. Maintain: the deprecated surface continues to work unchanged for at least 1 full minor-release cycle (release N through N+1).

  3. Remove: earliest removal is in release N+2. Document in CHANGELOG under "Removed". This constitutes a major- version bump.

Example timeline:

v1.2.0 — deprecate --old-flag (warning emitted; --new-flag added)
v1.3.0 — --old-flag still works (warning still emitted)
v2.0.0 — --old-flag removed; major bump

For REST endpoints, deprecation is signaled via: - Deprecation: true response header (RFC 8594) - Sunset: <date> header when removal date is known


Compatibility testing

The release pipeline validates API stability via:

  1. Type checking (mypy strict): catches signature changes that would break callers
  2. Test suite (4,900+ tests): exercises public interfaces against expected behavior
  3. Import smoke test: scripts/check_imports.py (reserved for v1.0) will validate that all documented entry points resolve
  4. Schema regression: Pydantic model .model_json_schema() output compared between releases (reserved for v1.0)

Scope of this document

This document covers the library, CLI, REST, and plugin surfaces of Evidentia. It does NOT cover:

  • The web UI (packages/evidentia-ui/): frontend component APIs are not part of the public contract
  • GitHub Actions workflows: CI/CD implementation details
  • Development tooling: ruff config, mypy config, test infrastructure
  • Documentation format: doc structure may reorganize freely

MCP tool contract

Package: evidentia_mcp.server

MCP tool names are frozen — they are part of the contract with AI clients (Claude Desktop, Claude Code, custom MCP clients). Renaming a tool is a major-bump trigger; adding new tools is non-breaking.

Frozen tool surface (as of v0.9.7):

Tool Since Purpose
list_frameworks v0.8.0 Enumerate bundled catalogs
get_control v0.8.0 Single-control lookup
gap_analyze v0.8.0 Run gap analysis
gap_diff v0.8.0 Compare two gap reports
conmon_list_cadences v0.9.6 List CONMON cadences
conmon_next_due v0.9.6 Compute next-due date
conmon_check_state v0.9.6 Read state file → attention buckets
conmon_health v0.9.6 Health report wrapper
gap_analyze_sarif v0.10.2 Gap analysis + SARIF 2.1.0 output (CI-gate use)
collect_ocsf v0.10.2 OCSF ingestion (file mode only — URL ingest deliberately omitted to harden out the F-V101-L1 SSRF surface)
tprm_vendor_list v0.10.2 List vendors from the local TPRM store (read-only)
poam_list v0.10.2 List POA&Ms from the local store (read-only)
verify_signed_artifact v0.10.4 Verify an Evidentia signed-artifact bundle (*.ar); wraps verify_ar_file with validate_within(--allow-root) path-gating and the standard SignedToolOutput envelope. Returns the OSCAL-bound verification report.
conmon_series v0.13 Cadence evidence series over the evidence store
get_catalog_native v0.13 Exact-generation native catalog source bundle, with a required explicit CIMD grant

Tool parameter names are frozen. Tool descriptions may be refined for clarity without constituting a breaking change.

CIMD scope-grant semantics (v0.8.5+) are NOT frozen: operators adding new tools through CIMD registry updates (via evidentia mcp cimd-migrate in v0.9.7+) is a deployment-time concern, not an API contract.

In v0.13, the server uses MCP Python SDK 2.2. The original fourteen tool names, descriptions and input/output schemas match the prior locked SDK 1.29.1 contract. Python SDK attributes use snake case; protocol fields retain their wire aliases. The three CLI transport selectors and bind defaults are unchanged.

With a CIMD registry, request metadata client_id takes precedence over the configured fallback. Missing, null or empty-string metadata uses the fallback; other non-string values are denied. Missing, unknown and out-of-scope identities return protocol error -32602 with no error data, before handler or signer execution. Ordinary handler failures, invalid arguments and unknown tools use tool-error results. Explicit MCP exceptions retain their protocol error code. Out-of-root paths remain tool errors, with the SDK's generic error text.

Opt-in signatures authenticate the envelope's payload: the delivered JSON object from structuredContent, or the established content-only result list with standard optional fields restored. They do not authenticate tool_name, signed_at, the outer error flag, unrelated metadata, or display content when the structured object is signed. Clients must check for a signature and verify it before trusting the payload. Signer invocation failures retain the existing signature=null / signing_error envelope. Invalid enabled signer factories produce an error instead of unsigned success. The signing wrapper leaves non-object structured results and SDK input-request control flow unsigned. The modern SDK path can carry these shapes; legacy schema validation rejects them. None of the original fourteen tools declares such an output.


Env-var public contract (v0.9.7 NEW)

Operators rely on certain env vars to configure Evidentia at runtime. The names + semantics of these vars are frozen with the same guarantees as CLI flags. Removal requires a deprecation cycle.

Env var Since Purpose
EVIDENTIA_POAM_STORE_DIR v0.9.0 POA&M JSON store directory
EVIDENTIA_AI_REGISTRY_DIR v0.9.3 AI-gov registry directory
EVIDENTIA_TRUST_PROXY_HEADERS v0.9.5 X-Forwarded-For ingest gate
EVIDENTIA_RBAC_POLICY_FILE v0.9.5 (FastAPI), v0.9.6 (CLI) RBAC policy YAML path
EVIDENTIA_RBAC_IDENTITY v0.9.6 CLI RBAC identity
EVIDENTIA_EVIDENCE_STORE_DIR v0.9.6 Evidence store root directory
EVIDENTIA_EVIDENCE_AUTO_MIRROR_WORM v0.9.7 Gate auto-mirror to cloud WORM
EVIDENTIA_EVIDENCE_WORM_BACKEND_FACTORY v0.9.7 Dotted-path factory for auto-mirror backend

Revision history

Version Date Change
DRAFT 2026-05-16 Initial authoring during v0.9.3 P5
DRAFT 2026-05-18 v0.9.4 – v0.9.5 surfaces inventoried (no doc edits yet)
NORMATIVE 2026-05-19 Promoted from DRAFT during v0.9.7 P2.1. v0.9.4 – v0.9.6 surfaces backfilled: evidence WORM store, AI-gov federal fields, FIPS 199 + OMB + SCR models, CONMON MCP tools, OSCAL_SCHEMA_VERSION constant, RBAC primitives, evidence_store env vars. MCP tool contract section + env-var public-contract section added. Pre-v1.0 binding semantics in force.
NORMATIVE 2026-05-22 v0.10.0: models/finding.py joins the frozen-models table (SecurityFinding, FindingStatus, ComplianceStatus) following its additive OCSF-alignment evolution. New evidentia_core.ocsf library entry point (finding_to_ocsf / finding_from_ocsf / OCSFMappingError) added to §5. SARIF is a new evidentia gap output format — non-breaking per §3. See ocsf-mapping.md.
NORMATIVE 2026-05-23 v0.10.1: finding_from_ocsf gains an additive trust_unmapped: bool = True keyword-only parameter (closes F-V100-L1 from the v0.10.0 review). Non-breaking under §1 — additive optional parameter with default. See ocsf-mapping.md §5.1.
NORMATIVE 2026-05-23 v0.10.2: 4 new MCP tools added (gap_analyze_sarif, collect_ocsf, tprm_vendor_list, poam_list) per the MCP-as-backend theme. Append-only per the §MCP tool contract — non-breaking for existing AI clients; the 8 prior tools stay frozen.
NORMATIVE 2026-05-24 v0.10.4: (a) OutputFormat Literal extended with "ocsf" (additive-optional per §3) — evidentia gap analyze --format ocsf emits OCSF Compliance Findings (class_uid 2003), the symmetric counterpart to v0.10.0 SARIF emit + v0.10.1 OCSF ingest; (b) 13th MCP tool verify_signed_artifact added (append-only per §MCP tool contract); (c) evidentia_core.gap_analyzer.ocsf.gap_report_to_ocsf_array new public helper. No removals, no renames, no breaking changes. Reviewed under /pre-release-review skill v5.1 (first ship under v5.1).
NORMATIVE 2026-05-25 v0.10.5 Phases 7+8: OutputFormat Literal further extended with "ocsf-detection" + "cyclonedx-vex" (additive-optional per §3). evidentia gap analyze --format ocsf-detection emits OCSF Detection Findings (class_uid 2004) — SIEM-target counterpart to v0.10.4 Compliance Finding emit (Splunk / Elastic / Sentinel / Datadog ingest 2004 natively). evidentia gap analyze --format cyclonedx-vex emits CycloneDX 1.6 VEX — the supply-chain VEX surface complementing the release-time CycloneDX SBOM emit. Two new library entry points: evidentia_core.gap_analyzer.ocsf_detection.gap_report_to_ocsf_detection_array + evidentia_core.gap_analyzer.vex.gap_report_to_cyclonedx_vex. No removals, no renames, no breaking changes.
NORMATIVE 2026-05-25 v0.10.5 Phase 9: DFAH harness extracted from evidentia_ai.eval.* to a dedicated evidentia-eval workspace package (8th package). Public API moves from evidentia_ai.eval to evidentia_eval (same symbols, same signatures). The evidentia_ai.eval.* path is preserved as a deprecation shim through v0.11.x with a DeprecationWarning at import time; removal scheduled for v0.12.0 (a documented breaking change with a 2-minor-version migration window per §1). The evidentia-ai[eval-faithfulness] extra now proxies to evidentia-eval[faithfulness-semantic] — same packages, new home. Rationale: air-gap installs of the production risk-statement runtime no longer transitively pull the dev-time eval stack.
NORMATIVE 2026-05-25 v0.10.5 Phase 10: Collector idempotency hardening. New evidentia_core.models.common.deterministic_finding_id(source_system, source_finding_id) helper computes a UUID v5 from natural keys under the pinned NAMESPACE_EVIDENTIA_FINDING namespace. A new @model_validator(mode="before") on SecurityFinding runs the derivation when no explicit id= is supplied AND both source_system + source_finding_id are present at construction. Effect: two collect() calls against an unchanged source produce findings with byte-identical id values across runs. Additive-only; explicit id= always wins, OCSF round-trip via unmapped["evidentia"] unchanged, pre-v0.10.5 OSCAL AR documents continue to load. See docs/collector-idempotency-audit.md.
NORMATIVE 2026-05-26 v0.10.6 Phase 1 (commit C1): catalogs. Three new bundled YAML catalogs (osps-baseline-m1.yaml, osps-baseline-m2.yaml, osps-baseline-m3.yaml) bundling the OpenSSF OSPS Baseline at upstream commit ac6bbec (Apache-2.0, Tier-A verbatim). Catalog entries are OSPS assessment-requirements (per-maturity-level applicability filter): M1=25, M2=42, M3=63 (counts verified against upstream tarball; the v0.10.6 plan §Phase 1 expected 21/38/58 — upstream actual is used). Companion OSCAL Catalog 1.2.1 serialization shipped at osps-baseline.oscal.json (8 groups / 41 top-level OSPS controls, mirroring upstream top-level granularity per OSCAL Catalog convention; validated against Trestle 4.0.3 pydantic models). Additive-only per frozen-surface contract — existing 89 catalogs unchanged. scripts/catalogs/regenerate_manifest.py extended with *.oscal.json / *.oscal.yaml skip-filter so OSCAL sidecar artifacts don't register as manifest entries. Manifest count: 89 → 92 frameworks.
NORMATIVE 2026-05-27 v0.10.6 Phase 5 (commit C5): CrosswalkDefinition extended with 3 optional fields — provenance: str \| None, verification: Literal["self-attested-via-upstream", "hand-checked"] \| None, verification_note: str \| None. Additive-only per frozen-surface contract; the existing 8 in-tree crosswalks load unchanged (new fields default to None). 5 new OSPS-Baseline crosswalks land alongside in catalogs/data/mappings/: osps-baseline_to_nist-ssdf-800-218.json, osps-baseline_to_nist-csf-2.0.json, osps-baseline_to_eu-cra.json, osps-baseline_to_pci-dss-4.0.json, osps-baseline_to_nist-800-161.json. Mappings auto-extracted from the OpenSSF OSPS Baseline guidelines[] array at upstream commit ac6bbec; carry provenance="upstream-osps-guidelines" + verification="self-attested-via-upstream". Consumers requiring independent verification should plan a hand-check pass. See docs/v0.10.6-plan.md §Phase 5 + §12.1 for the brainstorm-vs-codebase pre-correction rationale.
NORMATIVE 2026-05-27 v0.10.6 Phase 6 (commit C6): new evidentia_collectors.github.osps module with 16 populate_osps_*(client, owner, repo) -> SecurityFinding helpers covering OSPS-AC/BR/DO/GV/LE/QA/VM family controls via GitHub REST API. Each helper sets compliance_status (PASS/FAIL/WARNING/NOT_APPLICABLE/UNKNOWN) + an osps-baseline ControlMapping plus optional NIST SP 800-53 Rev 5 cross-walk mappings; source_finding_id follows the deterministic osps-<id>:<owner>/<repo>[:<scope>] pattern that feeds the v0.10.5 P10 idempotent-id derivation. Coverage registry exposed as evidentia_collectors.github.osps.OSPS_COVERAGE: dict[str, str]. Additive-only per frozen-surface contract — existing GitHubCollector + DependabotCollector entry points unchanged. GitHubClient extended additively with 4 small probe methods: list_releases, are_vulnerability_alerts_enabled, is_code_scanning_enabled, list_security_advisories. OSPS-VM-05.03 helper is intentionally complementary to the existing DependabotCollector (posture-finding vs per-CVE evidence chain); both emit findings, neither subsumes the other. The 16 OSPS ids covered: OSPS-AC-03.01/03.02, OSPS-BR-06.01, OSPS-DO-02.01, OSPS-GV-03.01, OSPS-LE-02.01/03.01, OSPS-QA-01.01/01.02/02.01/03.01, OSPS-VM-02.01/03.01/04.01/05.03/06.02.
NORMATIVE 2026-07-06 Error-shape convergence: deliberate 4xx/5xx REST error responses now carry the structured {"detail": {"error": <snake_case key>, ..., "message": <human text>}} payload across all 19 error-raising routers + the rate-limit 429, generalizing the v0.9.5 rbac_denied shape and retiring the bare-string majority + the evidence {detail, next_version} 409 (the next_version hint keeps its key beside error: worm_violation). error keys are a stable additions-only vocabulary (registry: evidentia_api.errors); message texts + context fields are informative, not frozen. Pydantic 422 arrays unchanged; the F-V08-DAST-3 status normalization (manual body errors → 400 in most routers, except the documented F-V1012-S4-1 manual-422 convention on model_risk/tprm/poam/governance) unchanged. Every operation now documents its deliberately-raised 4xx/5xx in OpenAPI responses — 422 responses that can occur both ways document the union of the manual ErrorEnvelope and FastAPI's auto HTTPValidationError shapes. Human-readable texts preserved verbatim in message.
NORMATIVE 2026-08-20 v0.12.0: FIRST SCHEDULED REMOVAL under this contract. The evidentia_ai.eval deprecation shim package (evidentia_ai.eval + claim_extraction, faithfulness, faithfulness_semantic, harness, metrics, seeds, signing) is deleted, executing the removal announced in the v0.10.5 Phase 9 row above. evidentia_eval — unchanged since v0.10.5 — is the sole supported import path; §5's frozen-import list already named it. The unconditional evidentia-eval base dependency on evidentia-ai is dropped with the shims, so a production evidentia-ai install no longer resolves the dev-time eval stack at all. The evidentia-ai[eval-faithfulness] install extra is deliberately RETAINED (packaging alias, not an import path). Breaking under §1 for any caller still on the shim, with a 3-minor-cycle migration window (v0.10.5 announce → v0.11.x maintain → v0.12.0 remove) exceeding the 1-cycle minimum. See deprecation-calendar.md § Recently removed.
NORMATIVE 2026-08-20 v0.12.0 freeze-prep: §5 CORRECTED against the code, and the new scripts/check_public_surface.py gate wired in so it cannot drift again. This document has been NORMATIVE since v0.9.7, but nothing had ever executed §5's frozen import list — and four of its entries had never resolved at all. Corrected: GapFinding → GapStatus (no GapFinding symbol has ever existed in the codebase; the same phantom is fixed in §1's gap.py row); from evidentia_core.poam import POAMState, Milestone → from evidentia_core.models.gap import … (the poam package exports state helpers, not the models); evidentia_collectors.vendor_risk → the real per-vendor modules evidentia_collectors.bitsight / evidentia_collectors.securityscorecard (no vendor_risk module has ever existed), dropping RiskReconCollector and UpGuardCollector, which were never built (they appear as v0.10.1-plan scope that did not land; the only other mentions in the docs are of RiskRecon/UpGuard as third-party vendors, which is accurate); SmtpChannel / WebhookChannel → the real SMTPAlertChannel / WebhookAlertChannel (plus their SMTPConfig / WebhookConfig). This is not a breaking change: every corrected path raised ImportError/ModuleNotFoundError on every release that shipped it, so no operator could have depended on one. It was a false promise in a NORMATIVE document, and the correction retires it. scripts/check_public_surface.py now (a) executes every §5 import, (b) compares the frozen MCP tool table against the live server's registrations, and (c) asserts every frozen env var still appears in packages/*/src; it runs in the consistency gate scope and the pre-push hook.
NORMATIVE 2026-08-20 v0.12.0: evidentia_core.gap_store.GapReportRepository + GapStoreRootChangedError join §5 as PUBLIC, PROVISIONAL. An explicit-root handle on the gap-report store (frozen-slots dataclass; mandatory root resolved once via get_gap_store_dir(root); save / load_latest / list / load_by_key delegate under that bound root), plus evidentia_api.routers.gaps.create_stored_report_read_router(repository), a narrow read-only router that create_app() does not mount — an embedding seam, so no route or OpenAPI surface changed. Additive under §1: three new public symbols, nothing renamed or removed. "Provisional" is deliberate and narrower than the rest of §5: these paths are documented so integrators may build on them, but the freeze decision is deferred to the v1.0 candidate review, where the optional root_revalidator hook is a removal candidate if it still has no in-tree caller. See v1.0-freeze-candidates.md §4.
NORMATIVE 2026-09-06 v0.13 batch 4 (V13-01): the cadence evidence series. New evidentia conmon series <slug> CLI leaf, POST /api/conmon/series, and 14th MCP tool conmon_series (append-only per §MCP tool contract) all wrap evidentia_core.conmon.series.assert_series: a dated series read from the append-only evidence store (artifacts link to a cadence via metadata.cadence_slug) instead of the operator-maintained state file conmon check/conmon health read. evidentia_mcp.cli.V0_13_NEW_TOOLS extends the cimd-migrate default grant set alongside V0_9_6_NEW_TOOLS. No removals, no renames, no breaking changes. See conmon-runbook.md § Cadence evidence series and designs/cadence-assertion-layer-design.md.**
NORMATIVE 2026-09-06 v0.13 batch 5: the evidence store behind conmon check and conmon health. Additive request fields use_evidence_store (default false) on POST /api/conmon/check and POST /api/conmon/health; CheckRequest.entries may be empty when the store is used (minItems 1 to 0); CheckCycleRow.series (nullable) carries the series verdict. The server reads its own configured store, never a client path. CLI: --evidence-store on both leaves, --state-file optional when it is given. Existing clients are unaffected.
NORMATIVE 2026-09-06 v0.13 batch 7 (V13-03): the Google Workspace collector and the Okta extension. New evidentia collect google-workspace CLI leaf and POST /api/collectors/google-workspace/collect mirror the Vanta route's shape (base_url and customer normalization, per-field integer range validation, a token_env-named credentials_missing 503); collectors_status() gains a google-workspace entry. The collector's GOOGLE_WORKSPACE_ACCESS_TOKEN credential env var follows the same non-frozen convention as every other collector's token env var (GITHUB_TOKEN, OKTA_API_TOKEN, VANTA_API_TOKEN, etc.), none of which sit in the EVIDENTIA_*-scoped env-var public contract above. The Okta collector's findings now carry authored control_mappings instead of the discarding control_ids shim, gain a bounded retry on 429 and the transient 5xx class, real per-resource-type coverage counts, and a full status breakdown on the inventory finding; no Okta CLI, API or console surface changed. No removals, no renames, no breaking changes. See designs/google-workspace-collector-design.md.
NORMATIVE 2026-09-07 v0.13 batch 8 (V13-14, V13-17). §"Bundled catalog content" corrected: no catalog pin command exists or is scheduled; the documented escape hatch is catalog import (a user-imported catalog shadows the bundled one under the same id, catalog where shows which copy resolves). SCRForm (§1, ai_governance/scr.py) gains two optional fields (certification_package_overview_uri, change_type_explanation) and a to_scn_document() writer for the FedRAMP SCN-CSO-INF shape, validated by the new library helper evidentia_core.fedramp.validate_scn_document beside validate_sdr_document. Additive under §1: nothing renamed or removed; to_oscal_scr_notification() is unchanged.
NORMATIVE 2026-10-18 v0.13.0 pre-tag review tightenings. Every mutating REST operation carries a role gate (decision D8; inert under the default policy). Body-named credential env vars must match ^[A-Z][A-Z0-9_]{0,63}$ and the integration's prefix (D9). GET/PUT /api/config return the file as written (D10). CadenceSeries gains excluded_observations (additive); CheckEntry.slug is non-blank and at most 128 characters, window_days is capped at 3660, MarkCompletedRequest.when may not be in the future, inventory_content is capped at 8 MB, and POST /api/oscal/verify refuses Sigstore pins without a bundle. The OpenAPI document declares bearerAuth. No removals, no renames.
NORMATIVE 2026-09-07 v0.13 batch 9 (V13-11, V13-13): section 1 corrected against the code and gated. Seven section 1 rows named module files that do not exist anywhere under packages/*/src (vendor.py, vendor_finding.py, vendor_manifest.py, assessment.py, claim.py, oscal_profile.py, governance.py), sixteen listed class names had no definition (FrameworkMetadata, Control, ControlFamily, CatalogEntry, TPRMAssessment, TPRMFinding, VendorProfile, VendorRiskTier, VendorFinding, VendorManifest, Assessment, AssessmentStatus, OSCALProfile, AISystem, AIRiskClassification, GovernanceRecord), and seven real modules of evidentia_core.models had no row (fedramp_ksi, gap_diff, model_risk, obligation, risk, threat, traceability). The table now lists the real modules with paths relative to evidentia_core/ and their public classes; callables are written with parentheses; scripts/check_public_surface.py imports every listed module, checks every listed symbol and callable, and requires a row for every evidentia_core/models/*.py module. Not a breaking change: no operator could have depended on a class that was never defined. Additive under section 1 in the same batch: CatalogControl.withdrawn (optional, default false, set from the OSCAL status prop), TextDepth, StatementRow, has_statement() and derive_text_depth() in models/catalog.py, the text_depth property on the four catalog models, and FrameworkManifestEntry.text_depth / crosswalk_family; CrosswalkEngine gains an optional families argument and consults a catalog's family on every lookup. Nothing renamed or removed. The bundled catalog content stays non-frozen; its truth is now gated by scripts/check_catalog_truth.py. CLI catalog import now validates direct JSON through the category-aware loader before registering it; malformed catalogs that previously passed import are rejected with exit 1. Validation completes before a --force replacement, preserving the existing catalog on rejection.
NORMATIVE 2026-09-08 v0.13 batch 9 import correction. evidentia_core.oscal.profile.resolve_profile() adds the optional keyword-only source_catalog_path parameter; existing positional arguments retain their meaning. CLI catalog import --profile ... --catalog ... now uses the explicit local source instead of the profile href. An explicit override requires exactly one import; ambiguous multi-import overrides raise ProfileResolutionError. Relative override paths resolve from the caller working directory; hrefs still resolve from the profile directory. Profile resolution now includes top-level controls and recursively nested groups. API catalog imports validate a staged file before replacement; rejected invalid force=true imports preserve the installed catalog and manifest. No flags, payload fields, status codes or symbols were removed.
NORMATIVE 2026-09-08 Catalog API identifier validation. Framework IDs must match the complete documented identifier pattern. Trailing newlines and Windows device basenames, including names followed by an extension, are rejected with 400 invalid_id on all platforms before filesystem use. All bundled framework IDs remain valid. Import destinations are resolved and checked against the user catalog directory; temporary cleanup failures do not hide an import result. Catalog and manifest publication remain separate filesystem writes.
NORMATIVE 2026-09-09 v0.13 catalog currency (V13-09, first batch). Additive optional CatalogControl.properties, ControlCatalog.status / notes / verified_on / superseded_by / audit_contexts / publication_notices, CatalogStatus, CatalogAuditContext and CatalogPublicationNotice. Manifest and registry discovery carry lifecycle metadata, and CLI/API imports preserve validated currency and licensing fields. Native loading also preserves the already declared family hierarchy, EU risk categories and legacy note. Publication notices never enter control indexes, text-depth calculations or assessment denominators. Existing metadata defaults remain unknown or empty; dates do not activate requirements. Existing endpoints and CLI leaves expose these fields without removals or renames. API YAML dates serialize as ISO strings; unsupported non-JSON values return the documented 400 invalid_body before replacement. Imported license/source strings render literally in the CLI. Catalog IDs and statement content remain the non-frozen data surface; the CMMC and CISA content migrations imply no automatic equivalence.
NORMATIVE 2026-09-09 Catalog source evidence and nested gap requirements. Additive CatalogSourceRow and optional CatalogControl.source_rows (default empty). Rows preserve source hash claims, sheet/row locators, parsed scalar values and whitespace, separate raw and interpreted identifiers, and explicit merge-anchor projections with provenance. Dates become ISO text; finite JSON scalars are required. Parsed mappings do not preserve duplicate input keys. API YAML imports reject non-string mapping keys before JSON conversion can coerce or collapse them. Rows and mappings detach input and resist ordinary mutation; deep copies work and invalid copied evidence is rejected before model serialization. Hash syntax is checked without authenticating the source. Native JSON/YAML imports, saved reloads and framework/control responses retain rows at every enhancement depth. Source rows do not add indexed controls, statements or gap requirements. Gap traversal now includes active descendants at every depth and excludes a withdrawn node's entire subtree, preserving upstream order and existing raw requirement keys. Inventory matching searches IDs and titles at every depth while retaining the existing shallow candidate order, exact-ID priority and fuzzy thresholds.
NORMATIVE 2026-09-09 CMS/CJIS source browsing. CLI catalog control details expose retained source rows as literal JSON values; control and family text render literally in both detail and list views. Console source evidence is read-only and keeps raw cells separate from reviewed merge-anchor projections. No CLI flags, endpoint paths or existing model fields were removed. The new CMS ARS 5.2 and CJIS 6.1 companion IDs are catalog data additions; their historical predecessors remain available without implied control equivalence or assessment migration.
NORMATIVE 2026-09-09 CI verification controls. Repository workflow policy and local admission tooling strengthen development checks without changing runtime models, CLI leaves, API routes, MCP tools or environment-variable contracts. The existing strict docs build is required before merge.
NORMATIVE 2026-09-09 MCP SDK 2.2 migration (V13-16). The fourteen tool names, descriptions and input/output schemas retain their SDK 1.29.1 baseline. Typed dispatch corrects the demonstrated protocol scope/signing bypass. Request metadata overrides fallback; malformed identities deny before execution. Scope denials use protocol -32602 with no error data, ordinary failures remain tool errors, and explicit MCP exceptions retain their protocol code. Signing binds the delivered JSON payload under the existing payload-only envelope; invalid enabled factories fail. Transport selectors and bind defaults are unchanged.
NORMATIVE 2026-09-10 MCP description comparison clarification (V13-16). Python 3.13 and later strip common docstring indentation during compilation. The SDK 1.29.1 fixture was captured on Python 3.12 and remains unchanged. Tests compare descriptions using inspect.cleandoc, preserving wording, internal line breaks and relative indentation while normalizing docstring margins and outer blank lines. Tool names and input/output schemas are compared exactly. This clarifies the preceding migration row; it does not change tool source descriptions or runtime dispatch.
NORMATIVE 2026-09-10 Entra/M365 evidence (V13-02). Add the six public collector exports, bounded request and full result contract, collect entra-m365, POST /api/collectors/entra-m365/collect and the entra-m365 status entry. Nine capability records preserve source coverage, declared identity and precise event windows. Partial/unavailable attempts retain the full response; CLI input/output refusal and read RBAC are explicit. The three fixed ENTRA_M365_* references are documented configuration contracts. Existing collector endpoints and result models retain their shapes.
NORMATIVE 2026-09-10 Storage retention evidence (V13-04, storage milestone). Add seven public exports, the provider-discriminated request and full result contract, collect retention, POST /api/collectors/retention/collect, and the retention status entry. Six fixed S3/Azure/GCS reads preserve native configuration fields and every requested resource/component state. CLI read RBAC precedes path metadata access; API authentication and read RBAC precede streamed input. Detached selection checks bind returned provider, scope and ordered targets. Exact browser JSON export preserves native numbers without rounding. Fixed credential references, bounded input/output, safe error classification and unknown compliance are part of the contract. M365/Vault/Splunk/Elastic retention remains subsequent work.
NORMATIVE 2026-09-11 Enterprise retention evidence (V13-04, enterprise milestone). Add nine public exports, the bounded profile-selected request and full result contract, collect enterprise-retention, POST /api/collectors/enterprise-retention/collect, and the enterprise_retention status entry. Google Vault, Splunk Enterprise and Elastic ILM reads preserve native selected fields and per-resource coverage. Authentication and read RBAC precede API streaming; exact API-principal grants and local CLI grants remain separate. The trusted startup registry is named by EVIDENTIA_ENTERPRISE_RETENTION_PROFILES_FILE. Partial and unavailable results retain full evidence. Selection validation, atomic CLI output and exact browser JSON export are enforced. Live tenant acceptance, provider identity, effective enforcement, record completeness and Vault retention rules remain unassessed.

Public registry collection contract (v0.13)

The public exports in evidentia_collectors.registries are RegistryCollector, RegistryLookupRequest, RegistryLookupResult, RegistryObservation, SourceRead, RegistryDiagnostic, RegistryManifest, RegistryContext and RegistryInputError. The collector class is imported lazily. Model and schema imports do not require the optional XML verifier. collect_v2(request) returns the authoritative complete result; collect(request) returns findings only. Underscore modules, source adapters and trusted test capabilities remain internal.

A request selects one of eleven finite registries, its exact typed target and an optional scope label. It has no credential, URL, path, trust override or activation field. Results preserve lookup outcome, traversal status and freshness separately, with literal native observations, complete finding copies, source reads and a cross-checked manifest. Source evidence carries no inferred compliance decision.

collect registry --registry SELECTOR --request-file PATH [--output PATH] applies read authorization before file access, limits input to 65536 bytes and reserves a safe output before collection. It emits complete JSON, bounded to 4 MiB. Exit 0 requires complete found/not_found evidence with current_observation or non-stale dated_snapshot freshness. Ambiguous, partial, unavailable, stale or unknown freshness exits 1, as do operational failures. Invalid input exits 2; read denial exits 77. Output identity is bound to the original detached request.

POST /api/collectors/registry uses operation ID collect_registry. Real API authentication and read RBAC precede actual-byte streaming and collection in one worker. Valid full results, including partial/unavailable results and explicit verifier dependency failures, return 200. Invalid input returns 422, auth denial 401/403, exact collector-package absence 503, and internal/result failures 500 with fixed messages. A missing XML root produces missing_extra before I/O; a broken dependency produces dependency_failure, never a false uninstalled claim.

The console uses the same finite request and full response contract. Authentication is revalidated before each actual API call. SSL Labs live selection makes no network call. Candidate pages show at most twenty observations, each visible field at most 16 KiB, and the full validated JSON download preserves the original UTF-8 bytes. Native integer/float tokens and source timestamp precision are rendered from their original JSON spans. The registry design defines source scope, dated snapshots, signature policy and the exact approved FedRAMP publisher-data exception.

Incident clock collection contract (v0.13)

The public exports in evidentia_collectors.incident_clock are IncidentClockCollector, IncidentClockRequest, IncidentClockResult and IncidentInputError. The collector requires an issued authorization for the selected request. collect_v2(request) returns the complete result; collect(request) returns its informational finding view. Underscore modules, provider adapters, profile bootstrap helpers and in-process authority objects remain internal.

One request selects a provider, profile alias, exact record ID and clock alias. Jira and PagerDuty permit explicit native occurrences; PagerDuty requires a nonempty, timezone-aware interval of at most 366 days. Requests cannot supply credentials, origins, headers, source queries, workflow definitions or trust overrides. Trusted profiles grant exact records and clocks to named API principals; allow_local_cli separately grants local command use.

Full results retain source state, clock state, native timestamp cells, exact decimal seconds, admitted events, read receipts, definition and profile-binding hashes, diagnostics, a summary finding and a manifest. Source completeness applies only to the selected visible scope. A missing or ambiguous event, invalid instant, reversed order or incomplete traversal cannot produce an elapsed value. Workflow labels do not establish human determinations or legal deadlines.

collect incident-clock --provider SELECTOR --request-file PATH [--profiles-file PATH] [--output PATH] applies read authorization before file metadata access. Input is at most 16 KiB; the trusted profile file is at most 64 KiB. Output is reserved before collection and published atomically with original input/output identity checks. Full JSON is at most 16 MiB. Complete sources exit 0, including unresolved or reversed clocks. Incomplete/unavailable sources publish the full result and exit 1. Invalid input exits 2; authorization/profile refusal exits 77; operational and broken-feature failures exit 1. Failed output never replaces an existing file with a partial result.

POST /api/collectors/incident-clock has operation ID collect_incident_clock. Real API authentication and read RBAC precede actual-byte streaming, then profile authorization, then credential resolution. EVIDENTIA_INCIDENT_CLOCK_PROFILES_FILE is loaded lazily per admitted request unless an immutable store was injected. Bad requests return 400, bodies above 16384 bytes 413, wrong media types 415, profile refusal 403 and internal/publication failure 500. Valid source results return 200 regardless of source or clock state. Messages do not expose profile, credential, source-body or internal exception details.

Only genuine collectors-root or incident-feature absence makes this operation unavailable with 503 before body/profile I/O. Failed existence probes, missing transitive dependencies, broken modules and missing/non-callable provider exports refuse API startup or the CLI before request/profile/provider work. Thus a missing enterprise-retention parser dependency can leave the registry router unavailable while the installed incident feature refuses full application startup. This preserves each feature's dependency contract.

The console refreshes read authentication immediately before POST, binds results to the original request, discards stale completions and validates native source and exact clock correspondence. It reads at most 16 MiB of actual UTF-8 response bytes, independently of Content-Length. Twenty events are shown per page, with 4 KiB field previews. Full downloads preserve the original validated JSON. Hash fields retain their evidence identity; the browser does not independently verify raw publisher bodies that are not included in its response.

All observed providers use owned verified transport with fixed templates and finite traversal/publication bounds. Tests and demo cases are synthetic. Live provider access, entitlement and workflow correctness remain unverified. See the incident clock design and operator guide.

SCAP import contract (v0.13)

The public exports in evidentia_collectors.scap are collect_scap_bytes, collect_scap_file, ScapCollectionResult, ScapCompletionAssertion, ScapSourceProfile and ScapError. Underscore modules, prepared import objects, clock seams and adapter helpers are internal. Existing collector APIs and response schemas retain their contracts.

An import requires an explicit source profile and zero-based assessment index. Supported profiles are xccdf-1.2-results, oval-5.8-core-results, oval-5.11.2-core-results and oval-5.12.3-core-results. The result carries exact source byte/hash binding, the complete decoded native graph, the selected assessment, a summary finding, manifest, completion provenance, nullable evidence artifact, availability reasons, cadence association and diagnostics. Native outcomes do not establish compliance. Source references and instructions remain inert. Source authenticity and full XSD/platform validation are outside this contract.

collect scap --file PATH --source-profile PROFILE --assessment-index INDEX returns the full result by default. --output-view artifact selects artifact JSON; an unavailable artifact exits 2 without opening its destination. --output PATH uses bounded, checked file publication; otherwise output uses stdout with fixed failures and no delivery retry. Read-role refusal precedes adapter access. Optional --completion-assertion PATH requires --asserted-by for caller-declared provenance. --cadence-slug associates an existing cadence definition without scheduling collection.

POST /api/collectors/scap/collect accepts raw application/xml with identity encoding after configured-provider authentication and read authorization. Query fields are the required source_profile and assessment_index, plus optional cadence_slug. The optional X-Evidentia-SCAP-Completion-Assertion header is strict ASCII JSON, at most 2,048 bytes. Its six fields bind schema version, source hash, profile, selection, completion timestamp and reference. The API supplies the actor from the authenticated provider and principal; the caller cannot choose it. OVAL assertions do not turn generator timestamps into assessment completion, and XCCDF rejects caller completion assertions.

The source limit is 8 MiB and the full result limit is 16 MiB. Independent structural and decoded-size bounds apply even below the raw limit. CLI preparation, optional sidecar I/O, source processing and publication share the original 60-second deadline and UTC anchor, with a 10-second publication reserve. The API starts that clock after media, query, assertion-header and actor-provenance admission, before consuming the body. Unsupported, malformed, unsafe or over-limit input cannot return a partial success. Genuine missing optional support is distinguished from broken installed support. Fixed error messages keep source values and internal exceptions out of error responses.

The console validates the complete response and cross-copy correspondence before replacing the last good result. Result downloads preserve the full JSON; artifact downloads preserve the validated artifact. Saving remains a separate authorized action through the evidence store. Stable artifact identity does not advance its version, and duplicate version-1 saves retain append-only refusal. See the SCAP operator guide for exact limits and error behavior.

Native catalog source contract (v0.13)

ControlCatalog.native_source and CatalogControl.native_source_ref are additive optional fields. A bundle retains ordered source documents, occurrences, fields, context, references and digest bindings. Explicit native null and absent values remain distinct. Projection references do not duplicate documents or add assessment controls. Loading verifies the native bundle and its projection; a syntactically valid hash alone does not establish publisher authenticity.

GET /api/frameworks/{framework_id}/native-source requires the exact bundle_sha256 of the selected generation. The admitted frameworks are au-ism, cisa-scuba and externally imported bsi-grundschutz-plus-plus. A changed generation returns 409; unavailable or invalid native source and processing deadline errors retain fixed codes. CLI catalog show --native-source prints the complete bundle; with --control, it prints the bound occurrence and its context. No existing CLI leaf is removed or renamed.

POST /api/catalog/import-native accepts only the pinned BSI profile and exactly three inline UTF-8 documents. It rejects extra fields, duplicate keys, invalid Unicode, unsupported media/encoding and nonmatching source bytes. Authentication and write authorization run before request-body consumption. Raw body admission is 16 MiB, with an 8 MiB aggregate source limit. CLI native import requires both --native-profile and --source-dir, and refuses legacy overrides. Neither surface downloads source files or accepts their license terms.

Both ordinary and native CLI/API writers use the same local manifest transaction. Imports publish immutable payload generations. Removal updates registration and retains prior files. CLI imports require write and removals require admin before source-path access, matching the HTTP roles. The permissive default policy is unchanged. Unsupported storage fails closed; the supported local filesystem boundaries and older-writer limits are documented in the catalog guide.

Write errors carry a bounded publication observation with an error code that matches the envelope. The observation distinguishes unchanged, committed and indeterminate outcomes. A failed response must not be treated as proof of rollback. Process interruption is not converted into an ordinary HTTP result.

The fifteenth MCP tool, get_catalog_native, has a closed framework/digest input and complete validated native output. It requires a configured CIMD registry and a literal grant for this tool, even where the older tools allow no-registry operation. Authorization precedes argument processing, loading, cache use and signing. Default migration adds no grant; paths, imports and remote fetching are not part of this read-only tool. The original fourteen schemas and the existing payload-signature envelope remain unchanged.