package attesto // [P2.2] Typed compliance events — SDK-side conventions, no backend change. // Each ToPayload() returns a plain payload map with regulation_refs included, // validated against the committed-payload number policy. Recording these never // claims conformity: Attesto attests records. // Typed event type identifiers. const ( EventTypeModelDecision = "attesto.model_decision" EventTypeHumanOverride = "attesto.human_override" EventTypeIncidentReport = "attesto.incident_report" EventTypeDataAccess = "attesto.data_access" ) // Regulation references per typed event (conventions for reports/auditors). var ( RefsModelDecision = []string{"EU-AI-Act:Art.12", "EU-AI-Act:Art.14"} RefsHumanOverride = []string{"EU-AI-Act:Art.14"} RefsIncidentReport = []string{"NIS2:Art.23", "EU-AI-Act:Art.62"} RefsDataAccess = []string{"GDPR:Art.30", "GDPR:Art.6"} ) func finishPayload(payload M, refs []string, extra M) (M, error) { for key, value := range extra { payload[key] = value } out := M{} for key, value := range payload { if value != nil && value != "" { out[key] = value } } out["regulation_refs"] = refs if err := AssertCommitmentSafeNumbers(map[string]any(out), "$"); err != nil { return nil, err } return out, nil } // ModelDecision is one model-driven decision (commitments only; raw inputs and // outputs never leave your process). type ModelDecision struct { Model string InputCommitment map[string]string OutputCommitment map[string]string Decision string ConfidenceBp int // basis points keep integers commitment-safe HumanInLoop bool OperatorRef string Extra M } func (e ModelDecision) EventType() string { return EventTypeModelDecision } func (e ModelDecision) ToPayload() (M, error) { return finishPayload(M{ "model": e.Model, "input_commitment": orNil(e.InputCommitment), "output_commitment": orNil(e.OutputCommitment), "decision": e.Decision, "confidence_bp": e.ConfidenceBp, "human_in_loop": e.HumanInLoop, "operator_ref": e.OperatorRef, }, RefsModelDecision, e.Extra) } // HumanOverride records a human overriding a model decision (Art. 14). type HumanOverride struct { OriginalEventRef string OperatorRef string JustificationCommitment map[string]string NewDecision string Extra M } func (e HumanOverride) EventType() string { return EventTypeHumanOverride } func (e HumanOverride) ToPayload() (M, error) { return finishPayload(M{ "original_event_ref": e.OriginalEventRef, "operator_ref": e.OperatorRef, "justification_commitment": orNil(e.JustificationCommitment), "new_decision": e.NewDecision, }, RefsHumanOverride, e.Extra) } // IncidentReport is a reportable incident with NIS2-style field names. type IncidentReport struct { Severity string Category string DetectedAt string SummaryCommitment map[string]string AffectedService string Extra M } func (e IncidentReport) EventType() string { return EventTypeIncidentReport } func (e IncidentReport) ToPayload() (M, error) { return finishPayload(M{ "severity": e.Severity, "category": e.Category, "detected_at": e.DetectedAt, "summary_commitment": orNil(e.SummaryCommitment), "affected_service": e.AffectedService, }, RefsIncidentReport, e.Extra) } // DataAccess records an access to personal or regulated data. type DataAccess struct { SubjectRefCommitment map[string]string Purpose string LegalBasis string AccessorRef string Extra M } func (e DataAccess) EventType() string { return EventTypeDataAccess } func (e DataAccess) ToPayload() (M, error) { return finishPayload(M{ "subject_ref_commitment": orNil(e.SubjectRefCommitment), "purpose": e.Purpose, "legal_basis": e.LegalBasis, "accessor_ref": e.AccessorRef, }, RefsDataAccess, e.Extra) } func orNil(m map[string]string) any { if len(m) == 0 { return nil } return m }