compliance

package
v1.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package compliance generates audit-based compliance reports for Chronicle.

Engine

Engine is the central type. Construct it with NewEngine, which accepts three separate store interfaces drawn from the same composite backend, plus a *slog.Logger:

engine := compliance.NewEngine(
    s,       // audit.Store
    s,       // verify.Store
    s,       // ReportStore
    logger,
)

When using a standard Chronicle backend (Postgres, Bun, SQLite, Memory) the same [store.Store] value satisfies all three interfaces.

Reports verify the hash chain behind their scope when the engine is given the deployment's chain with WithChain, and optionally its checkpoint store and signer with WithCheckpoints. Each report records the result in Report.Verification and what it covered in Report.VerificationScope. See WithVerifyWindow for how much of a long chain one report checks.

Report Types

Four report types are supported, each with its own input struct:

  • Engine.SOC2 — SOC2 Type II: access events, authentication, authorisation denials, critical errors; maps to SOC2 Trust Service Criteria CC6/CC7
  • Engine.HIPAA — HIPAA: PHI access, user activity, audit controls
  • Engine.EUAIAct — EU AI Act: AI system interactions, decisions, outcomes
  • Engine.Custom — arbitrary filters, sections, and metadata

All methods return a *Report and persist it to ReportStore automatically.

Report Structure

Report embeds chronicle.Entity (CreatedAt, UpdatedAt) and contains:

  • ID — TypeID with "report_" prefix
  • Title — human-readable report name
  • Type — "soc2", "hipaa", "euaiact", or "custom"
  • Period — DateRange (From/To time.Time)
  • AppID — application scope
  • TenantID — tenant scope (empty for single-tenant)
  • Sections — []Section, each with a Title, Events, Stats, and Notes
  • Stats — *Stats aggregate (TotalEvents, CriticalEvents, FailedEvents, DeniedEvents)
  • Verification — the *verify.Report from generation time, nil when none ran
  • VerificationScope — what that verification covered, and why it did not run when it did not
  • GeneratedBy — identity of the requestor
  • Format — the Format the report was exported in
  • Data — raw exported bytes (populated by Engine.Export)

Export

Engine.Export serialises a *Report to an io.Writer in the requested Format:

ReportStore

ReportStore persists generated reports:

Package compliance defines compliance report entities and the report store interface.

Index

Constants

View Source
const DefaultVerifyWindow uint64 = 50_000

DefaultVerifyWindow is how many sequences a report verifies when the engine was not given a window of its own.

VerifyChain holds every event in its range in memory at once, so the window is a memory bound, not a sampling rate. Fifty thousand events is tens of megabytes on a typical event, which a report generator can afford on every run.

View Source
const MaxSectionEvents = 1000

MaxSectionEvents bounds the evidence listing embedded in one report section.

A report covering a busy period can match far more events than belong in a single document, so the listing is a sample. Section.MatchedEvents and Section.EventsTruncated say so explicitly; the report's summary Stats are computed separately and are exact.

Variables

This section is empty.

Functions

func DecodeReportBody

func DecodeReportBody(data []byte, r *Report) error

DecodeReportBody fills r's Sections, Stats, Verification and VerificationScope from data written by EncodeReportBody.

It also reads the older form, a bare JSON array of sections. A report stored that way never kept its stats or verification, so those stay nil and the report reads as unverified, which is what it is.

func EncodeReportBody

func EncodeReportBody(r *Report) ([]byte, error)

EncodeReportBody serialises the parts of r a backend keeps in its data column. DecodeReportBody reverses it.

Types

type CustomInput

type CustomInput struct {
	Title       string          `json:"title"`
	Period      DateRange       `json:"period"`
	AppID       string          `json:"app_id" optional:"true"`
	TenantID    string          `json:"tenant_id,omitempty" optional:"true"`
	GeneratedBy string          `json:"generated_by" optional:"true"`
	Sections    []CustomSection `json:"sections"`
}

CustomInput defines inputs for a custom compliance report.

type CustomSection

type CustomSection struct {
	Title      string   `json:"title"`
	Categories []string `json:"categories,omitempty"`
	Actions    []string `json:"actions,omitempty"`
	Severity   []string `json:"severity,omitempty"`
	Notes      string   `json:"notes,omitempty"`
}

CustomSection defines a section in a custom compliance report.

type DateRange

type DateRange struct {
	From time.Time `json:"from"`
	To   time.Time `json:"to"`
}

DateRange defines a period for compliance reports.

type EUAIActInput

type EUAIActInput struct {
	Period      DateRange `json:"period"`
	AppID       string    `json:"app_id" optional:"true"`
	TenantID    string    `json:"tenant_id,omitempty" optional:"true"`
	GeneratedBy string    `json:"generated_by" optional:"true"`
}

EUAIActInput defines inputs for EU AI Act transparency report generation.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine generates compliance reports from audit data.

func NewEngine

func NewEngine(
	auditStore audit.Store, verifyStore verify.Store, reportStore ReportStore, logger log.Logger, opts ...EngineOption,
) *Engine

NewEngine creates a compliance engine.

Reports verify their scope's hash chain only when opts include WithChain. Pass the deployment's own chain there, and WithCheckpoints when it signs checkpoints.

func (*Engine) Custom

func (e *Engine) Custom(ctx context.Context, input *CustomInput) (*Report, error)

Custom generates a custom compliance report.

func (*Engine) EUAIAct

func (e *Engine) EUAIAct(ctx context.Context, input *EUAIActInput) (*Report, error)

EUAIAct generates an EU AI Act transparency report.

func (*Engine) Export

func (e *Engine) Export(_ context.Context, r *Report, format Format, w io.Writer) error

Export exports a report to the given format and writer.

func (*Engine) HIPAA

func (e *Engine) HIPAA(ctx context.Context, input *HIPAAInput) (*Report, error)

HIPAA generates a HIPAA audit report.

func (*Engine) SOC2

func (e *Engine) SOC2(ctx context.Context, input *SOC2Input) (*Report, error)

SOC2 generates a SOC2 Type II compliance report.

type EngineOption

type EngineOption func(*Engine)

EngineOption configures an Engine.

func WithChain

func WithChain(streams StreamSource, chain *hash.Chain) EngineOption

WithChain lets the engine verify the hash chain behind every report it generates.

chain must be the deployment's own chain, carrying its key provider. A verifier built without it recomputes digests unkeyed, so on an HMAC deployment every event would read as tampered or downgraded. Without this option reports carry no verification and say so.

func WithCheckpoints

func WithCheckpoints(store checkpoint.Store, signer checkpoint.Signer) EngineOption

WithCheckpoints lets report verification check signed checkpoints. Both halves are needed: a checkpoint store without a signer holds rows nothing can authenticate. If either is nil the option does nothing, which matches how the admin API's verifier is built.

func WithVerifyWindow

func WithVerifyWindow(n uint64) EngineOption

WithVerifyWindow sets how many sequences a report verifies at most. Zero keeps DefaultVerifyWindow.

type Format

type Format string

Format defines the export format for compliance reports.

const (
	FormatJSON     Format = "json"
	FormatCSV      Format = "csv"
	FormatMarkdown Format = "markdown"
	FormatHTML     Format = "html"
	FormatPDF      Format = "pdf"
)

Supported export formats.

type HIPAAInput

type HIPAAInput struct {
	Period      DateRange `json:"period"`
	AppID       string    `json:"app_id" optional:"true"`
	TenantID    string    `json:"tenant_id,omitempty" optional:"true"`
	GeneratedBy string    `json:"generated_by" optional:"true"`
}

HIPAAInput defines inputs for HIPAA audit report generation.

type ListOpts

type ListOpts struct {
	AppID    string
	TenantID string

	Limit  int
	Offset int
}

ListOpts defines pagination and scope options for listing reports.

Scope is applied by the store, before LIMIT/OFFSET. Filtering after pagination returned an empty page whenever another tenant's reports filled the first page.

type Report

type Report struct {
	chronicle.Entity
	ID       id.ID     `json:"id"`
	Title    string    `json:"title"`
	Type     string    `json:"type"`
	Period   DateRange `json:"period"`
	AppID    string    `json:"app_id"`
	TenantID string    `json:"tenant_id"`
	Sections []Section `json:"sections"`
	Stats    *Stats    `json:"stats,omitempty"`

	// Verification is the chain verification run when the report was
	// generated. It is nil when none ran; VerificationScope says why, and
	// nil never means verification passed.
	Verification *verify.Report `json:"verification,omitempty"`

	// VerificationScope is what Verification covered: the stream, the exact
	// sequences, whether the window cut the chain short, and the caveats
	// needed to read the result.
	VerificationScope *VerificationScope `json:"verification_scope,omitempty"`

	GeneratedBy string `json:"generated_by"`
	Format      Format `json:"format"`
	Data        []byte `json:"data,omitempty"`
}

Report is a generated compliance report.

type ReportStore

type ReportStore interface {
	// SaveReport persists a generated compliance report.
	SaveReport(ctx context.Context, r *Report) error

	// GetReport returns a report by ID.
	GetReport(ctx context.Context, reportID id.ID) (*Report, error)

	// ListReports returns reports matching opts, scoped before pagination.
	ListReports(ctx context.Context, opts ListOpts) ([]*Report, error)

	// DeleteReport removes a report.
	DeleteReport(ctx context.Context, reportID id.ID) error
}

ReportStore manages generated compliance reports.

type SOC2Input

type SOC2Input struct {
	Period      DateRange `json:"period"`
	AppID       string    `json:"app_id" optional:"true"`
	TenantID    string    `json:"tenant_id,omitempty" optional:"true"`
	GeneratedBy string    `json:"generated_by" optional:"true"`
}

SOC2Input defines inputs for SOC2 Type II report generation.

type Section

type Section struct {
	Title string `json:"title"`

	// Events is the evidence listing. It is capped at MaxSectionEvents, so it
	// may be a sample rather than the full set — check EventsTruncated.
	Events []*audit.Event `json:"events,omitempty"`

	// MatchedEvents is how many events matched this section's filters,
	// regardless of how many are embedded above.
	MatchedEvents int64 `json:"matched_events"`

	// EventsTruncated reports that Events is a sample of MatchedEvents.
	// A report that silently truncated its evidence would read as complete
	// when it was not.
	EventsTruncated bool `json:"events_truncated"`

	Stats *audit.AggregateResult `json:"stats,omitempty"`
	Notes string                 `json:"notes,omitempty"`
}

Section is a part of a compliance report.

type Stats

type Stats struct {
	TotalEvents    int64 `json:"total_events"`
	CriticalEvents int64 `json:"critical_events"`
	FailedEvents   int64 `json:"failed_events"`
	DeniedEvents   int64 `json:"denied_events"`
}

Stats holds summary statistics for a report.

type StreamSource

type StreamSource interface {
	GetStreamByScope(ctx context.Context, appID, tenantID string) (*stream.Stream, error)
}

StreamSource resolves the hash chain stream for a report's scope. Every standard backend implements it.

type VerificationScope

type VerificationScope struct {
	Status VerificationStatus `json:"status"`

	// StreamID is the stream verified, the one GetStreamByScope returns for
	// the report's AppID and TenantID.
	StreamID string `json:"stream_id,omitempty"`

	// Scheme and SchemeSince are the stream's digest pin, taken from the
	// stream row.
	Scheme      string `json:"scheme,omitempty"`
	SchemeSince uint64 `json:"scheme_since,omitempty"`

	// HeadSeq is the stream's head when the report was generated.
	HeadSeq uint64 `json:"head_seq"`

	// FromSeq and ToSeq are the sequences verification was asked to cover.
	// Verification.FirstEvent and LastEvent are the events it actually found
	// there.
	FromSeq uint64 `json:"from_seq"`
	ToSeq   uint64 `json:"to_seq"`

	// Window is the most sequences one report verifies, and Capped is whether
	// it cut this chain short. A capped report verified the newest Window
	// sequences up to the head and nothing before them.
	Window uint64 `json:"window"`
	Capped bool   `json:"capped"`

	// CheckpointsConfigured is whether the engine had a checkpoint store and
	// signer to consult.
	CheckpointsConfigured bool `json:"checkpoints_configured"`

	// Notes are the caveats a reader needs to interpret the result. Every
	// export renders them.
	Notes []string `json:"notes,omitempty"`
}

VerificationScope records what a report's chain verification covered.

Report.Verification is the verifier's own result and says nothing about why a range was chosen. This does, so a report never implies more coverage than it checked: the exact sequences asked for, whether the window cut the chain short, and the caveats that apply to reading the result.

type VerificationStatus

type VerificationStatus string

VerificationStatus says whether a report's chain verification ran, and if not, why not.

const (
	// VerificationRan means the chain was verified and Report.Verification
	// holds the result, which may be valid or not.
	VerificationRan VerificationStatus = "verified"

	// VerificationNoChain means the report's scope has no hash chain stream,
	// so there was nothing to verify. It is not a failure: a scope that never
	// recorded an event has no chain.
	VerificationNoChain VerificationStatus = "no_chain"

	// VerificationNotConfigured means the engine was built without a hash
	// chain (see WithChain), so it could not verify anything. It never falls
	// back to an unkeyed chain, which would report every keyed event as
	// tampered.
	VerificationNotConfigured VerificationStatus = "not_configured"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL