Auto-generated mirror. This page mirrors
docs/api-stability.md, the canonical source of truth. Do not edit this file directly; editdocs/api-stability.mdand re-runscripts/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.mdCandidate 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.mdCross-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>— explicitevidentia.yamloverride--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
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,_compatmodules - 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:
-
Announce: add a
DeprecationWarning(Python) or deprecation notice (REST response header) in minor release N. Document in CHANGELOG under "Deprecated". -
Maintain: the deprecated surface continues to work unchanged for at least 1 full minor-release cycle (release N through N+1).
-
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:
- Type checking (mypy strict): catches signature changes that would break callers
- Test suite (4,900+ tests): exercises public interfaces against expected behavior
- Import smoke test:
scripts/check_imports.py(reserved for v1.0) will validate that all documented entry points resolve - 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.