Compliance & Reporting
Overview
Compliance & Reporting transforms the Release Orchestrator’s audit capabilities into a comprehensive compliance management system. This enhancement provides pre-built compliance report templates, evidence chain visualization, audit query interface, regulatory framework alignment, and automated compliance checking.
This is a best-in-class implementation designed to meet the needs of enterprises operating under strict regulatory requirements (SOC2, ISO 27001, PCI-DSS, HIPAA, FedRAMP, GDPR).
Design Principles
- Continuous Compliance: Real-time compliance status, not periodic audits
- Evidence-First: All compliance claims backed by cryptographic evidence
- Framework-Agnostic: Adaptable to any regulatory framework
- Auditor-Friendly: Reports designed for external auditor consumption
- Immutable Records: Tamper-proof audit trail
- Automated Where Possible: Reduce manual compliance burden
Runtime truthfulness rule: automated compliance must fail closed when a control has no evidence-backed validator. Unsupported automated controls are failed controls with remediation guidance, not not applicable; manual review controls may remain partial until a reviewer disposition is recorded.
Architecture
Component Overview
┌────────────────────────────────────────────────────────────────────────┐
│ Compliance & Reporting System │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │
│ │ ComplianceEngine │───▶│ ReportGenerator │───▶│ EvidenceChain │ │
│ │ │ │ │ │ Visualizer │ │
│ └──────────────────┘ └───────────────────┘ └─────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │
│ │ FrameworkMapper │ │ AuditQueryEngine │ │ ControlValidator│ │
│ │ │ │ │ │ │ │
│ └──────────────────┘ └───────────────────┘ └─────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │
│ │ ExportService │ │ ScheduledReports │ │ AlertManager │ │
│ │ │ │ │ │ │ │
│ └──────────────────┘ └───────────────────┘ └─────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────┘
Key Components
1. ComplianceEngine
Core compliance evaluation engine:
public sealed class ComplianceEngine
{
private readonly ImmutableArray<IComplianceFramework> _frameworks;
private readonly IControlValidator _validator;
private readonly IEvidenceStore _evidenceStore;
public async Task<ComplianceStatus> EvaluateAsync(
ComplianceEvaluationRequest request,
CancellationToken ct)
{
var status = new ComplianceStatus
{
TenantId = request.TenantId,
EvaluatedAt = _timeProvider.GetUtcNow(),
Frameworks = new List<FrameworkStatus>()
};
foreach (var frameworkId in request.Frameworks)
{
var framework = _frameworks.First(f => f.Id == frameworkId);
var frameworkStatus = await EvaluateFrameworkAsync(framework, request, ct);
status.Frameworks.Add(frameworkStatus);
}
// Calculate overall compliance score
status.OverallScore = CalculateOverallScore(status.Frameworks);
status.ComplianceLevel = DetermineComplianceLevel(status.OverallScore);
return status;
}
private async Task<FrameworkStatus> EvaluateFrameworkAsync(
IComplianceFramework framework,
ComplianceEvaluationRequest request,
CancellationToken ct)
{
var frameworkStatus = new FrameworkStatus
{
FrameworkId = framework.Id,
FrameworkName = framework.Name,
Version = framework.Version,
Controls = new List<ControlStatus>()
};
foreach (var control in framework.Controls)
{
var controlStatus = await EvaluateControlAsync(control, request, ct);
frameworkStatus.Controls.Add(controlStatus);
}
// Calculate framework compliance
frameworkStatus.TotalControls = framework.Controls.Count;
frameworkStatus.PassedControls = frameworkStatus.Controls.Count(c => c.Status == ControlEvaluationStatus.Passed);
frameworkStatus.FailedControls = frameworkStatus.Controls.Count(c => c.Status == ControlEvaluationStatus.Failed);
frameworkStatus.NotApplicableControls = frameworkStatus.Controls.Count(c => c.Status == ControlEvaluationStatus.NotApplicable);
frameworkStatus.Score = (double)frameworkStatus.PassedControls /
(frameworkStatus.TotalControls - frameworkStatus.NotApplicableControls) * 100;
return frameworkStatus;
}
private async Task<ControlStatus> EvaluateControlAsync(
ComplianceControl control,
ComplianceEvaluationRequest request,
CancellationToken ct)
{
var controlStatus = new ControlStatus
{
ControlId = control.Id,
ControlName = control.Name,
Category = control.Category,
Description = control.Description,
Evidence = new List<EvidenceReference>()
};
// Validate control
var validationResult = await _validator.ValidateAsync(control, request, ct);
controlStatus.Status = validationResult.Status;
controlStatus.Findings = validationResult.Findings;
// Collect evidence
var evidence = await _evidenceStore.GetEvidenceForControlAsync(
request.TenantId, control.Id, request.DateRange, ct);
controlStatus.Evidence = evidence.Select(e => new EvidenceReference
{
EvidenceId = e.Id,
Type = e.Type,
CollectedAt = e.CollectedAt,
Summary = e.Summary
}).ToList();
return controlStatus;
}
}
public sealed record ComplianceStatus
{
public Guid TenantId { get; init; }
public DateTimeOffset EvaluatedAt { get; init; }
public double OverallScore { get; init; }
public ComplianceLevel ComplianceLevel { get; init; }
public List<FrameworkStatus> Frameworks { get; init; }
}
public enum ComplianceLevel
{
FullyCompliant, // 100%
SubstantiallyCompliant, // 90-99%
PartiallyCompliant, // 70-89%
NonCompliant // <70%
}
2. FrameworkMapper
Maps organizational controls to compliance frameworks:
public sealed class FrameworkMapper
{
private readonly ImmutableDictionary<string, IComplianceFramework> _frameworks;
public FrameworkMapper()
{
_frameworks = LoadFrameworks().ToImmutableDictionary(f => f.Id);
}
private IEnumerable<IComplianceFramework> LoadFrameworks()
{
yield return new Soc2Framework();
yield return new Iso27001Framework();
yield return new PciDssFramework();
yield return new HipaaFramework();
yield return new FedRampFramework();
yield return new GdprFramework();
yield return new NistCsfFramework();
}
public IReadOnlyList<ControlMapping> MapToFramework(
string frameworkId,
IReadOnlyList<OrganizationalControl> orgControls)
{
var framework = _frameworks[frameworkId];
var mappings = new List<ControlMapping>();
foreach (var frameworkControl in framework.Controls)
{
var mapping = new ControlMapping
{
FrameworkControl = frameworkControl,
MappedOrgControls = new List<OrganizationalControl>()
};
// Find matching organizational controls
foreach (var orgControl in orgControls)
{
if (IsMatch(frameworkControl, orgControl))
{
mapping.MappedOrgControls.Add(orgControl);
}
}
mapping.CoverageStatus = mapping.MappedOrgControls.Any()
? CoverageStatus.Covered
: CoverageStatus.Gap;
mappings.Add(mapping);
}
return mappings;
}
private bool IsMatch(ComplianceControl frameworkControl, OrganizationalControl orgControl)
{
// Check explicit mappings
if (orgControl.FrameworkMappings?.Contains(frameworkControl.Id) == true)
return true;
// Check keyword matching
var keywords = frameworkControl.Keywords ?? ImmutableArray<string>.Empty;
return keywords.Any(k => orgControl.Description?.Contains(k, StringComparison.OrdinalIgnoreCase) == true);
}
}
// SOC 2 Framework Implementation
public sealed class Soc2Framework : IComplianceFramework
{
public string Id => "soc2-type2";
public string Name => "SOC 2 Type II";
public string Version => "2017";
public ImmutableArray<ComplianceControl> Controls => new[]
{
// Security (Common Criteria)
new ComplianceControl
{
Id = "CC1.1",
Name = "COSO Principle 1",
Category = "Control Environment",
Description = "The entity demonstrates a commitment to integrity and ethical values.",
Keywords = new[] { "integrity", "ethics", "code of conduct" }.ToImmutableArray()
},
new ComplianceControl
{
Id = "CC6.1",
Name = "Logical and Physical Access Controls",
Category = "Logical and Physical Access",
Description = "The entity implements logical access security software, infrastructure, and architectures.",
Keywords = new[] { "access control", "authentication", "authorization", "mTLS" }.ToImmutableArray(),
AutomatedChecks = new[]
{
new AutomatedCheck
{
Id = "cc6.1.1",
Description = "All agent connections use mTLS",
CheckType = CheckType.AgentSecurity
},
new AutomatedCheck
{
Id = "cc6.1.2",
Description = "User authentication via SSO/OIDC",
CheckType = CheckType.AuthenticationMethod
}
}.ToImmutableArray()
},
new ComplianceControl
{
Id = "CC7.2",
Name = "System Operations",
Category = "System Operations",
Description = "The entity monitors system components and the operation of those components for anomalies.",
Keywords = new[] { "monitoring", "alerting", "anomaly detection" }.ToImmutableArray()
},
new ComplianceControl
{
Id = "CC8.1",
Name = "Change Management",
Category = "Change Management",
Description = "The entity authorizes, designs, develops, configures, documents, tests, approves, and implements changes.",
Keywords = new[] { "change management", "approval", "deployment", "release" }.ToImmutableArray(),
AutomatedChecks = new[]
{
new AutomatedCheck
{
Id = "cc8.1.1",
Description = "All production deployments require approval",
CheckType = CheckType.ApprovalRequired
},
new AutomatedCheck
{
Id = "cc8.1.2",
Description = "All changes produce evidence packets",
CheckType = CheckType.EvidenceGenerated
}
}.ToImmutableArray()
}
// ... more controls
}.ToImmutableArray();
}
3. ReportGenerator
Generates compliance reports:
public sealed class ReportGenerator
{
public async Task<ComplianceReport> GenerateAsync(
ReportRequest request,
CancellationToken ct)
{
var report = new ComplianceReport
{
Id = Guid.NewGuid(),
Type = request.ReportType,
GeneratedAt = _timeProvider.GetUtcNow(),
GeneratedBy = request.RequestedBy,
DateRange = request.DateRange
};
// Get compliance status
var status = await _complianceEngine.EvaluateAsync(new ComplianceEvaluationRequest
{
TenantId = request.TenantId,
Frameworks = request.Frameworks,
DateRange = request.DateRange
}, ct);
report.ComplianceStatus = status;
// Generate sections based on report type
switch (request.ReportType)
{
case ReportType.ExecutiveSummary:
report.Sections = await GenerateExecutiveSummaryAsync(status, ct);
break;
case ReportType.DetailedCompliance:
report.Sections = await GenerateDetailedReportAsync(status, request, ct);
break;
case ReportType.GapAnalysis:
report.Sections = await GenerateGapAnalysisAsync(status, ct);
break;
case ReportType.AuditReadiness:
report.Sections = await GenerateAuditReadinessAsync(status, request, ct);
break;
case ReportType.EvidencePackage:
report.Sections = await GenerateEvidencePackageAsync(status, request, ct);
break;
}
// Add standard sections
report.Sections.Add(GenerateMethodologySection());
report.Sections.Add(GenerateDisclaimerSection());
return report;
}
private async Task<List<ReportSection>> GenerateDetailedReportAsync(
ComplianceStatus status,
ReportRequest request,
CancellationToken ct)
{
var sections = new List<ReportSection>();
// Overview section
sections.Add(new ReportSection
{
Title = "Compliance Overview",
Content = new OverviewContent
{
EvaluationDate = status.EvaluatedAt,
OverallScore = status.OverallScore,
ComplianceLevel = status.ComplianceLevel,
FrameworkSummaries = status.Frameworks.Select(f => new FrameworkSummary
{
Name = f.FrameworkName,
Score = f.Score,
PassedControls = f.PassedControls,
TotalControls = f.TotalControls
}).ToList()
}
});
// Per-framework sections
foreach (var framework in status.Frameworks)
{
var frameworkSection = new ReportSection
{
Title = $"{framework.FrameworkName} Compliance",
Subsections = new List<ReportSection>()
};
// Group controls by category
var byCategory = framework.Controls.GroupBy(c => c.Category);
foreach (var category in byCategory)
{
var categorySection = new ReportSection
{
Title = category.Key,
Content = new ControlCategoryContent
{
Controls = category.Select(c => new ControlDetail
{
Id = c.ControlId,
Name = c.ControlName,
Status = c.Status,
Findings = c.Findings,
EvidenceCount = c.Evidence.Count,
EvidenceReferences = c.Evidence
}).ToList()
}
};
frameworkSection.Subsections.Add(categorySection);
}
sections.Add(frameworkSection);
}
// Findings summary
var allFindings = status.Frameworks
.SelectMany(f => f.Controls)
.SelectMany(c => c.Findings ?? Enumerable.Empty<Finding>())
.ToList();
sections.Add(new ReportSection
{
Title = "Findings Summary",
Content = new FindingsSummaryContent
{
TotalFindings = allFindings.Count,
CriticalFindings = allFindings.Count(f => f.Severity == FindingSeverity.Critical),
HighFindings = allFindings.Count(f => f.Severity == FindingSeverity.High),
MediumFindings = allFindings.Count(f => f.Severity == FindingSeverity.Medium),
LowFindings = allFindings.Count(f => f.Severity == FindingSeverity.Low),
Findings = allFindings.OrderByDescending(f => f.Severity).ToList()
}
});
// Recommendations
sections.Add(await GenerateRecommendationsAsync(status, ct));
return sections;
}
}
4. EvidenceChainVisualizer
Visualizes evidence chains:
public sealed class EvidenceChainVisualizer
{
public async Task<EvidenceChainVisualization> VisualizeAsync(
Guid rootEvidenceId,
CancellationToken ct)
{
var root = await _evidenceStore.GetAsync(rootEvidenceId, ct);
var visualization = new EvidenceChainVisualization
{
RootEvidenceId = rootEvidenceId,
GeneratedAt = _timeProvider.GetUtcNow()
};
// Build the chain
var chain = await BuildChainAsync(root, ct);
visualization.Chain = chain;
// Create graph representation
visualization.Graph = CreateGraph(chain);
// Verify chain integrity
visualization.IntegrityVerification = await VerifyChainIntegrityAsync(chain, ct);
// Generate narrative
visualization.Narrative = GenerateNarrative(chain);
return visualization;
}
private async Task<EvidenceChain> BuildChainAsync(
EvidencePacket root,
CancellationToken ct)
{
var chain = new EvidenceChain
{
Nodes = new List<EvidenceNode>(),
Edges = new List<EvidenceEdge>()
};
var visited = new HashSet<Guid>();
var queue = new Queue<EvidencePacket>();
queue.Enqueue(root);
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (visited.Contains(current.Id))
continue;
visited.Add(current.Id);
// Add node
chain.Nodes.Add(new EvidenceNode
{
Id = current.Id,
Type = current.SubjectType,
Subject = current.SubjectId,
CollectedAt = current.CollectedAt,
Summary = GenerateSummary(current),
Signature = current.Signature,
SignatureValid = await VerifySignatureAsync(current, ct)
});
// Add edges for dependencies
foreach (var depId in current.DependsOn)
{
chain.Edges.Add(new EvidenceEdge
{
FromId = depId,
ToId = current.Id,
Relationship = "depends_on"
});
// Load dependent evidence
var dep = await _evidenceStore.GetAsync(depId, ct);
if (dep != null && !visited.Contains(dep.Id))
{
queue.Enqueue(dep);
}
}
}
return chain;
}
private EvidenceGraph CreateGraph(EvidenceChain chain)
{
var graph = new EvidenceGraph();
// Calculate layout (topological sort + horizontal levels)
var levels = CalculateLevels(chain);
foreach (var (level, nodes) in levels)
{
var y = level * 100;
var x = 0;
foreach (var node in nodes)
{
graph.Nodes.Add(new GraphNode
{
Id = node.Id.ToString(),
Label = $"{node.Type}\n{node.CollectedAt:g}",
X = x,
Y = y,
Color = GetNodeColor(node)
});
x += 150;
}
}
foreach (var edge in chain.Edges)
{
graph.Edges.Add(new GraphEdge
{
From = edge.FromId.ToString(),
To = edge.ToId.ToString(),
Label = edge.Relationship
});
}
return graph;
}
private string GenerateNarrative(EvidenceChain chain)
{
var sb = new StringBuilder();
var ordered = chain.Nodes.OrderBy(n => n.CollectedAt).ToList();
sb.AppendLine("## Evidence Chain Narrative");
sb.AppendLine();
foreach (var node in ordered)
{
sb.AppendLine($"### {node.CollectedAt:yyyy-MM-dd HH:mm:ss} UTC");
sb.AppendLine();
sb.AppendLine($"**{node.Type}** (ID: `{node.Id}`)");
sb.AppendLine();
sb.AppendLine(node.Summary);
sb.AppendLine();
if (node.SignatureValid)
{
sb.AppendLine($"✓ Signature verified");
}
else
{
sb.AppendLine($"⚠ Signature verification failed");
}
sb.AppendLine();
}
return sb.ToString();
}
}
5. AuditQueryEngine
Powerful query interface for audit data:
public sealed class AuditQueryEngine
{
public async Task<AuditQueryResult> QueryAsync(
AuditQuery query,
CancellationToken ct)
{
var result = new AuditQueryResult
{
QueryId = Guid.NewGuid(),
ExecutedAt = _timeProvider.GetUtcNow(),
Query = query
};
// Build SQL from query
var sql = BuildQuery(query);
// Execute
var connection = await _connectionPool.GetReadReplicaAsync(ct);
var records = await connection.QueryAsync<AuditRecord>(sql.ToString(), query.Parameters, ct);
result.Records = records.ToImmutableArray();
result.TotalCount = records.Count();
// Apply aggregations if requested
if (query.Aggregations != null)
{
result.Aggregations = ApplyAggregations(records, query.Aggregations);
}
return result;
}
private string BuildQuery(AuditQuery query)
{
var sql = new StringBuilder();
// Base query
sql.AppendLine(@"
SELECT
e.id,
e.subject_type,
e.subject_id,
e.collected_at,
e.content,
e.signature,
u.email as actor_email,
u.name as actor_name
FROM evidence_packets e
LEFT JOIN users u ON e.actor_id = u.id
WHERE e.tenant_id = @TenantId");
// Date range
if (query.DateRange != null)
{
sql.AppendLine("AND e.collected_at >= @StartDate");
sql.AppendLine("AND e.collected_at <= @EndDate");
}
// Subject type filter
if (query.SubjectTypes?.Any() == true)
{
sql.AppendLine("AND e.subject_type = ANY(@SubjectTypes)");
}
// Actor filter
if (query.ActorId.HasValue)
{
sql.AppendLine("AND e.actor_id = @ActorId");
}
// Text search
if (!string.IsNullOrEmpty(query.SearchText))
{
sql.AppendLine("AND e.content_tsv @@ plainto_tsquery(@SearchText)");
}
// Custom filters
foreach (var filter in query.Filters ?? Enumerable.Empty<QueryFilter>())
{
sql.AppendLine($"AND {BuildFilterClause(filter)}");
}
// Ordering
sql.AppendLine("ORDER BY e.collected_at DESC");
// Pagination
if (query.Limit.HasValue)
{
sql.AppendLine($"LIMIT {query.Limit}");
}
if (query.Offset.HasValue)
{
sql.AppendLine($"OFFSET {query.Offset}");
}
return sql.ToString();
}
}
public sealed record AuditQuery
{
public Guid TenantId { get; init; }
public DateRange? DateRange { get; init; }
public ImmutableArray<string>? SubjectTypes { get; init; }
public Guid? ActorId { get; init; }
public string? SearchText { get; init; }
public ImmutableArray<QueryFilter>? Filters { get; init; }
public ImmutableArray<string>? Aggregations { get; init; }
public int? Limit { get; init; }
public int? Offset { get; init; }
}
6. ControlValidator
Automated control validation:
public sealed class ControlValidator : IControlValidator
{
private readonly ImmutableDictionary<CheckType, IAutomatedCheck> _checks;
public async Task<ControlValidationResult> ValidateAsync(
ComplianceControl control,
ComplianceEvaluationRequest request,
CancellationToken ct)
{
var result = new ControlValidationResult
{
ControlId = control.Id,
Findings = new List<Finding>()
};
// Run automated checks
if (control.AutomatedChecks?.Any() == true)
{
foreach (var check in control.AutomatedChecks)
{
var checkImpl = _checks.GetValueOrDefault(check.CheckType);
if (checkImpl == null)
{
result.Findings.Add(new Finding
{
Severity = FindingSeverity.Low,
Message = $"Automated check {check.Id} not implemented",
CheckId = check.Id
});
continue;
}
var checkResult = await checkImpl.ExecuteAsync(request, ct);
if (!checkResult.Passed)
{
result.Findings.Add(new Finding
{
Severity = checkResult.Severity,
Message = checkResult.Message,
CheckId = check.Id,
Details = checkResult.Details
});
}
}
}
// Determine overall status
if (result.Findings.Any(f => f.Severity >= FindingSeverity.High))
{
result.Status = ControlEvaluationStatus.Failed;
}
else if (result.Findings.Any())
{
result.Status = ControlEvaluationStatus.PartiallyMet;
}
else
{
result.Status = ControlEvaluationStatus.Passed;
}
return result;
}
}
// Example automated check implementations
public sealed class ApprovalRequiredCheck : IAutomatedCheck
{
public CheckType Type => CheckType.ApprovalRequired;
public async Task<CheckResult> ExecuteAsync(
ComplianceEvaluationRequest request,
CancellationToken ct)
{
// Check that all production deployments required approval
var deployments = await _deploymentStore.GetByDateRangeAsync(
request.TenantId, request.DateRange, ct);
var productionDeployments = deployments
.Where(d => d.Environment.Name.Equals("production", StringComparison.OrdinalIgnoreCase));
var withoutApproval = productionDeployments
.Where(d => d.ApprovalRecords?.Any() != true)
.ToList();
if (withoutApproval.Any())
{
return new CheckResult
{
Passed = false,
Severity = FindingSeverity.Critical,
Message = $"{withoutApproval.Count} production deployments without approval",
Details = withoutApproval.Select(d => new
{
d.Id,
d.ReleaseId,
d.DeployedAt
}).ToList()
};
}
return CheckResult.Pass();
}
}
public sealed class EvidenceGeneratedCheck : IAutomatedCheck
{
public CheckType Type => CheckType.EvidenceGenerated;
public async Task<CheckResult> ExecuteAsync(
ComplianceEvaluationRequest request,
CancellationToken ct)
{
// Check that all deployments generated evidence
var deployments = await _deploymentStore.GetByDateRangeAsync(
request.TenantId, request.DateRange, ct);
var withoutEvidence = new List<Deployment>();
foreach (var deployment in deployments)
{
var evidence = await _evidenceStore.GetBySubjectAsync(
"deployment", deployment.Id, ct);
if (evidence == null)
{
withoutEvidence.Add(deployment);
}
}
if (withoutEvidence.Any())
{
return new CheckResult
{
Passed = false,
Severity = FindingSeverity.High,
Message = $"{withoutEvidence.Count} deployments without evidence packets",
Details = withoutEvidence.Select(d => d.Id).ToList()
};
}
return CheckResult.Pass();
}
}
Report Templates
Executive Summary Template
# Compliance Executive Summary
**Organization:** {{organization.name}}
**Report Period:** {{date_range.start}} to {{date_range.end}}
**Generated:** {{generated_at}}
## Overall Compliance Status
| Framework | Score | Status |
|-----------|-------|--------|
{{#each frameworks}}
| {{name}} | {{score}}% | {{status}} |
{{/each}}
**Overall Compliance Level:** {{compliance_level}}
## Key Findings
{{#if critical_findings}}
### Critical Issues ({{critical_findings.count}})
{{#each critical_findings}}
- **{{control_id}}**: {{message}}
{{/each}}
{{/if}}
{{#if high_findings}}
### High Priority Issues ({{high_findings.count}})
{{#each high_findings}}
- **{{control_id}}**: {{message}}
{{/each}}
{{/if}}
## Recommendations
{{#each recommendations}}
1. **{{title}}** (Priority: {{priority}})
{{description}}
{{/each}}
## Next Steps
1. Address critical findings within {{sla.critical}} days
2. Review and remediate high-priority findings
3. Schedule follow-up assessment for {{next_assessment_date}}
Audit Readiness Report
# Audit Readiness Report
**Framework:** {{framework.name}} {{framework.version}}
**Assessment Date:** {{generated_at}}
## Readiness Summary
**Ready for Audit:** {{#if ready}}Yes{{else}}No{{/if}}
**Controls Passing:** {{passing_controls}} / {{total_controls}}
**Evidence Coverage:** {{evidence_coverage}}%
## Control-by-Control Assessment
{{#each control_categories}}
### {{category_name}}
{{#each controls}}
#### {{control_id}} - {{control_name}}
**Status:** {{status}}
**Evidence Available:** {{evidence_count}} items
{{#if findings}}
**Findings:**
{{#each findings}}
- [{{severity}}] {{message}}
{{/each}}
{{/if}}
{{#if evidence}}
**Evidence Summary:**
{{#each evidence}}
- {{type}} ({{collected_at}}): {{summary}}
{{/each}}
{{/if}}
---
{{/each}}
{{/each}}
## Gap Analysis
{{#each gaps}}
| Control | Gap Description | Remediation Recommendation |
|---------|-----------------|---------------------------|
{{#each items}}
| {{control_id}} | {{gap}} | {{recommendation}} |
{{/each}}
{{/each}}
## Evidence Package Checklist
{{#each evidence_checklist}}
- [{{#if available}}x{{else}} {{/if}}] {{item}}
{{/each}}
API Design
REST Endpoints
# Compliance Status
GET /api/v1/compliance/status # Current compliance status
GET /api/v1/compliance/status/history # Historical compliance
# Reports
POST /api/v1/compliance/reports # Generate report
GET /api/v1/compliance/reports # List reports
GET /api/v1/compliance/reports/{id} # Get report
GET /api/v1/compliance/reports/{id}/download # Download report (PDF/HTML)
# Evidence
GET /api/v1/compliance/evidence # List evidence
GET /api/v1/compliance/evidence/{id} # Get evidence
GET /api/v1/compliance/evidence/{id}/chain # Get evidence chain
GET /api/v1/compliance/evidence/{id}/verify # Verify evidence integrity
# Audit Query
POST /api/v1/compliance/audit/query # Execute audit query
GET /api/v1/compliance/audit/saved-queries # List saved queries
POST /api/v1/compliance/audit/saved-queries # Save query
# Frameworks
GET /api/v1/compliance/frameworks # List frameworks
GET /api/v1/compliance/frameworks/{id} # Get framework details
GET /api/v1/compliance/frameworks/{id}/controls # Get controls
# Control Mappings
GET /api/v1/compliance/mappings # Get control mappings
PUT /api/v1/compliance/mappings # Update mappings
# Scheduled Reports
POST /api/v1/compliance/reports/schedules # Create schedule
GET /api/v1/compliance/reports/schedules # List schedules
DELETE /api/v1/compliance/reports/schedules/{id} # Delete schedule
Metrics & Observability
Prometheus Metrics
# Compliance Scores
stella_compliance_score{framework, tenant_id}
stella_compliance_controls_passed{framework, tenant_id}
stella_compliance_controls_failed{framework, tenant_id}
# Findings
stella_compliance_findings_total{severity, framework}
stella_compliance_findings_open{severity, framework}
stella_compliance_findings_remediated{severity, framework}
# Evidence
stella_evidence_collected_total{type}
stella_evidence_verification_total{status}
stella_evidence_chain_depth{type}
# Reports
stella_reports_generated_total{type, framework}
stella_report_generation_duration_seconds{type}
# Audit Queries
stella_audit_queries_total{status}
stella_audit_query_duration_seconds
Configuration
compliance:
frameworks:
- id: soc2-type2
enabled: true
controls_file: "./frameworks/soc2.yaml"
- id: iso27001
enabled: true
controls_file: "./frameworks/iso27001.yaml"
automated_checks:
enabled: true
schedule: "0 0 * * *" # Daily at midnight
reports:
scheduled:
- name: "Weekly Executive Summary"
type: executive_summary
schedule: "0 8 * * 1" # Monday 8am
recipients:
- compliance@example.com
- ciso@example.com
format: pdf
- name: "Monthly Detailed Report"
type: detailed_compliance
schedule: "0 8 1 * *" # 1st of month
recipients:
- compliance@example.com
format: html
evidence:
retention_days: 2555 # 7 years
verification_schedule: "0 */6 * * *" # Every 6 hours
alerts:
compliance_drop_threshold: 90
critical_finding_channels:
- type: slack
channel: "#compliance-alerts"
- type: email
recipients:
- compliance@example.com
Test Strategy
Unit Tests
- Framework mapping logic
- Control validation
- Report generation
- Query building
Integration Tests
- Full compliance evaluation
- Evidence chain building
- Report export (PDF/HTML)
- Scheduled report execution
Compliance Tests
- Framework coverage validation
- Evidence completeness
- Signature verification
Migration Path
Phase 1: Framework Foundation (Week 1-2)
- Compliance engine
- Framework definitions
- Control models
Phase 2: Automated Checks (Week 3-4)
- Control validator
- Automated check implementations
- Check scheduling
Phase 3: Reporting (Week 5-6)
- Report generator
- Report templates
- Export formats
Phase 4: Evidence Chain (Week 7-8)
- Chain visualizer
- Integrity verification
- Narrative generation
Phase 5: Audit Query (Week 9-10)
- Query engine
- Query UI
- Saved queries
Phase 6: Polish (Week 11-12)
- Scheduled reports
- Alerts
- Documentation
