core

package
v0.62.13 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package core is L1 of the SigComply CLI: the frozen Go interfaces and structs that every higher layer depends on. Framework, Policy, EvidenceRecord, Envelope, Vault, Rule, SourcePlugin, CloudClient, SubmissionPayload, and their supporting value types live here.

Once published these types are stable. The aggregation boundary (SubmissionPayload) is structurally counts-only and must not gain fields that could carry resource identifiers across L6.

See docs/architecture/02-layers.md for the full layer contract.

Index

Constants

View Source
const CadenceOnPush = "on_push"

CadenceOnPush is the virtual cadence value representing the on_push axis. A policy's effective cadences are its declared Cadence value plus this constant if its OnPush field is true. The planner treats "fires on every push" and "fires on a schedule" as members of the same set, so a single set-intersection filter (Filter.Cadences) handles both axes uniformly.

View Source
const PolicyStateSchemaVersion = "policy-state.v1"

PolicyStateSchemaVersion is the schema stamp on the per-policy state shard. Bumped on any wire-format change.

View Source
const RunSummarySchemaVersion = "summary.v2"

RunSummarySchemaVersion is stamped on every vault-side framework run summary. Bumped on any wire-format change.

Variables

This section is empty.

Functions

func PolicyCadences added in v0.21.0

func PolicyCadences(p *Policy) []string

PolicyCadences returns the policy's effective cadence set: its declared Cadence value plus CadenceOnPush if OnPush is true. Used by the planner for set-intersection filtering against Filter.Cadences and by orchestrator.DueCadences for scheduled-mode dispatch. Returns at most two entries.

func PolicyContentHash added in v0.21.0

func PolicyContentHash(p *Policy, schemaDigests map[string]string) string

PolicyContentHash computes the SHA-256 of a canonicalized policy spec plus its referenced evidence-type schema digests. A change to the policy YAML (rule reference, severity, slot accepts list, parameters) or any referenced schema produces a different hash; the planner treats hash mismatch with PolicyState.LastPolicyHash as "due regardless of cadence" so a bundle update can never silently re-certify old evidence with new rules.

schemaDigests is a map keyed by evidence-type ID; values are the digests of the corresponding JSON Schema as registered. An empty map is acceptable (callers without schema-digest access pass nil) — the hash will still discriminate policy-spec changes, just not schema bumps.

func PrimaryControlID added in v0.27.0

func PrimaryControlID(refs []ControlRef) string

PrimaryControlID returns the first ControlRef's ControlID, or "" when there are none. Used for single-string display surfaces (text/CSV report rows, status output) that show one control per policy.

Types

type AggregatedPolicy

type AggregatedPolicy struct {
	PolicyID           string       `json:"policy_id"`
	Controls           []ControlRef `json:"controls"`
	Status             PolicyStatus `json:"status"`
	Severity           Severity     `json:"severity"`
	Category           string       `json:"category,omitempty"`
	ResourcesEvaluated int          `json:"resources_evaluated"`
	ResourcesFailed    int          `json:"resources_failed"`
	Message            string       `json:"message"`
	RuleVersion        string       `json:"rule_version,omitempty"`

	// ConfiguredCadence is the cadence string in effect at this run
	// (e.g., "daily", "every:6h"). Non-identifying.
	ConfiguredCadence string `json:"configured_cadence,omitempty"`

	// LastEvaluatedAt is the start time of the most recent run that
	// actually evaluated this policy. For freshly-evaluated rows it
	// equals StartedAt; for carry-forward rows it points to the
	// earlier run. Non-identifying. Pointer so a zero value is omitted
	// from the wire rather than serialized as "0001-01-01T00:00:00Z"
	// (encoding/json's omitempty does not omit a zero time.Time struct).
	LastEvaluatedAt *time.Time `json:"last_evaluated_at,omitempty"`

	// NextDueAt is the wall-clock time after which the policy is due
	// to be re-evaluated. Nil when the policy is always due (cadence
	// "continuous" / "every:0s") or when the most recent terminal
	// status was not pass (on_fail_retry → due on next run). Pointer
	// for the same omit-zero reason as LastEvaluatedAt.
	NextDueAt *time.Time `json:"next_due_at,omitempty"`

	// IsCarriedForward is true when this row references a prior
	// evaluation rather than a fresh one in this run.
	IsCarriedForward bool `json:"is_carried_forward,omitempty"`

	// PolicyContentHash is the SHA-256 hash of the policy spec +
	// referenced evidence-type schemas at this run. The cloud uses
	// it to detect a bundle bump that may invalidate prior
	// evaluations.
	PolicyContentHash string `json:"policy_content_hash,omitempty"`
}

AggregatedPolicy is the per-policy projection that crosses the boundary. Counts only — no violation lists, no resource IDs. The Message string is regenerated from counts by the aggregator, never copied from the rule's violation text.

Cadence-related fields (ConfiguredCadence, LastEvaluatedAt, NextDueAt, IsCarriedForward, PolicyContentHash) are non-identifying scalars: they describe schedule/staleness, not who or what. The dashboard uses them to render staleness badges and "next due in N" without recomputing locally. The reflection test in cloud_test.go ensures no identity-carrying field can be added accidentally.

type AppliedException added in v0.20.0

type AppliedException struct {
	PolicyID        string `json:"policy_id"`
	State           string `json:"state"`
	Reason          string `json:"reason,omitempty"`
	ApprovedBy      string `json:"approved_by,omitempty"`
	ApprovedAt      string `json:"approved_at,omitempty"`
	ExpiresAt       string `json:"expires_at,omitempty"`
	ResourceID      string `json:"resource_id,omitempty"`
	ResourcePattern string `json:"resource_pattern,omitempty"`
}

AppliedException is the snapshot of a planner-resolved exception that was in effect for the run. The shape mirrors a project-config exception (spec.PolicyException under policies.<id>.exceptions, or a control-level not_applicable cascade), plus the resolved state and policy ID so an auditor reading the manifest doesn't need the project config to reconstruct who waived what and why.

type CIEnvironment

type CIEnvironment struct {
	Provider    string `json:"provider"`
	Workflow    string `json:"workflow,omitempty"`
	RunURL      string `json:"run_url,omitempty"`
	WorkerImage string `json:"worker_image,omitempty"`
}

CIEnvironment captures CI-runner metadata. Workflow names and run URLs are CI-system artifacts, not customer evidence.

type CarryForwardRef added in v0.21.0

type CarryForwardRef struct {
	LastEvaluatedAt  time.Time
	LastEvaluatedRun string
	LastEnvelopeRef  string
	LastKnownStatus  PolicyStatus
	SkipReason       string
}

CarryForwardRef pins a carry-forward result to a specific prior evaluation. The pair (LastEvaluatedRun, LastEnvelopeRef) is the vault path to a signed envelope an auditor can re-verify independently; LastKnownStatus is the status that prior evaluation produced. SkipReason is the planner's human-readable explanation for why this run did not re-evaluate.

type CloudClient

type CloudClient interface {
	Submit(ctx context.Context, payload SubmissionPayload) error
}

CloudClient transmits SubmissionPayload to a configured dashboard endpoint (SigComply Cloud or a self-hosted backend). It is the only consumer of SubmissionPayload outside the aggregator.

type Control

type Control struct {
	ID               string
	Name             string
	Description      string
	Category         string
	BaselineSeverity Severity
}

Control is one item in a framework's control catalog.

type ControlRef added in v0.27.0

type ControlRef struct {
	Framework        string              `json:"framework,omitempty"`
	FrameworkVersion string              `json:"framework_version,omitempty"`
	ControlID        string              `json:"control_id"`
	Relationship     ControlRelationship `json:"relationship,omitempty"`
}

ControlRef is a versioned pointer from a policy (or a result) to one control in one framework. Framework + FrameworkVersion namespace the ControlID so that, e.g., iso27001:2022 A.8.9 and iso27001:2013 A.12.1.2 can coexist during a standard's transition window, and a crosswalk link can point at a specific versioned control node.

A policy carrying more than one ControlRef is how one check satisfies controls across multiple frameworks without being authored, evaluated, or stored more than once (the SOC 2 / ISO 27001 ~70% overlap case).

type ControlRelationship added in v0.27.0

type ControlRelationship string

ControlRelationship records how completely a check satisfies a control, using the NIST IR 8477 (Set Theory Relationship Mapping) / OSCAL control-mapping vocabulary. A single check can satisfy controls across multiple frameworks with different relationships — e.g. fully satisfy SOC 2 CC6.1 while only partially covering PCI DSS 8.3. The zero value is treated as RelationshipEqual.

const (
	// RelationshipEqual means the check fully satisfies the control.
	RelationshipEqual ControlRelationship = "equal"
	// RelationshipSubsetOf means the check is narrower than the control
	// (satisfies part of it; other checks cover the rest).
	RelationshipSubsetOf ControlRelationship = "subset_of"
	// RelationshipSupersetOf means the check is broader than the control.
	RelationshipSupersetOf ControlRelationship = "superset_of"
	// RelationshipIntersects means partial overlap, neither subset nor superset.
	RelationshipIntersects ControlRelationship = "intersects"
)

type Envelope

type Envelope struct {
	FormatVersion string            `json:"format_version"`
	ProducedAt    time.Time         `json:"produced_at"`
	Records       []EvidenceRecord  `json:"records"`
	Signature     EnvelopeSignature `json:"signature"`
}

Envelope is the signed wrapper around a batch of EvidenceRecords. Every envelope is independently verifiable: the public key and signature live inside the envelope, so an auditor with one envelope file and nothing else can verify it offline.

type EnvelopeSignature

type EnvelopeSignature struct {
	Algorithm string `json:"algorithm"`
	PublicKey []byte `json:"public_key"`
	Value     []byte `json:"value"`
}

EnvelopeSignature is the Ed25519 signature over canonical JSON of {format_version, produced_at, records}. The keypair is ephemeral — generated per envelope at write time and discarded the instant the signature is computed.

type EvidenceMode added in v0.23.0

type EvidenceMode string

EvidenceMode declares whether a policy is evaluated via automated API collection or via a manually-uploaded PDF. It is an explicit first-class field on every policy spec; the evaluator branches on it and nothing else.

const (
	// EvidenceModeAutomated means the policy binds API source plugins and
	// evaluates via the pass_when: DSL (Path B) or a rule: escape-hatch (Path C).
	EvidenceModeAutomated EvidenceMode = "automated"
	// EvidenceModeManual means the policy expects a customer-uploaded PDF at a
	// catalog-defined path. The evaluator runs the universal PDF presence check
	// (Path A); pass_when: and rule: are ignored entirely.
	EvidenceModeManual EvidenceMode = "manual"
)

type EvidenceRecord

type EvidenceRecord struct {
	Type        string          `json:"type"`
	ID          string          `json:"id"`
	IdentityKey string          `json:"identity_key,omitempty"`
	Payload     json.RawMessage `json:"payload"`
	SourceID    string          `json:"source_id"`
	CollectedAt time.Time       `json:"collected_at"`
	// Scope records which account/region/project this observation was
	// collected from. Sovereignty buyers are multi-account/multi-region
	// by regulatory necessity (data residency), so scope is a
	// first-class dimension of evidence identity — two records with the
	// same ID but different Scope are distinct observations. Optional
	// and pointer-typed so records that don't set it serialize
	// byte-identically to pre-scope envelopes (no signature churn);
	// source plugins populate it incrementally as they gain
	// scope-awareness. Scope stays vault-side — it never crosses the
	// aggregation boundary into the Cloud SubmissionPayload.
	Scope *RecordScope `json:"scope,omitempty"`
}

EvidenceRecord is one observation produced by a source plugin. IdentityKey is set only when the evidence type has a meaningful cross-source identity (e.g. an email for user_record across aws.iam and okta); leaving it empty disables cross-source dedup.

type EvidenceType

type EvidenceType struct {
	ID      string          `json:"id"`
	Version int             `json:"version"`
	Schema  json.RawMessage `json:"schema"`
}

EvidenceType is a versioned evidence-shape schema. The Schema is a JSON Schema document; collected records are validated against it before being wrapped in an Envelope.

type Framework

type Framework interface {
	ID() string
	Version() string
	Controls() []Control
	Policies() []PolicyRef
}

Framework is a shipped or extension compliance framework — the catalog of controls plus the set of policies that contribute to each control.

type FrameworkRunSummary added in v0.21.0

type FrameworkRunSummary struct {
	SchemaVersion string         `json:"schema_version"`
	RunID         string         `json:"run_id"`
	Framework     string         `json:"framework"`
	PeriodID      string         `json:"period_id"`
	CompletedAt   time.Time      `json:"completed_at"`
	Policies      []PolicyResult `json:"policies"`
}

FrameworkRunSummary is the per-run, framework-level summary persisted at {framework}/{periodID}/run_*/summary.json. This shape is vault-side and may carry full-fidelity per-policy references; the privacy-bounded SubmissionPayload is built independently by the aggregator from the same []PolicyResult.

Auditors read this file for the run; the framework's separate period summary (one per (framework, periodID), rebuilt every run inside that period) aggregates these into the period view.

type Manifest

type Manifest struct {
	SchemaVersion     string             `json:"schema_version"`
	RunID             string             `json:"run_id"`
	Framework         string             `json:"framework,omitempty"`
	PeriodID          string             `json:"period_id,omitempty"`
	StartedAt         time.Time          `json:"started_at"`
	CompletedAt       time.Time          `json:"completed_at"`
	FileHashes        map[string]string  `json:"file_hashes"`
	ExceptionsApplied []AppliedException `json:"exceptions_applied,omitempty"`
	Signature         ManifestSignature  `json:"signature"`
}

Manifest is the per-run signed integrity root. A single Ed25519 signature over FileHashes binds every file in the run folder; any later modification to a hashed file invalidates the manifest.

M5 shipped the minimal shape sufficient to sign and round-trip; later milestones layer optional fields on top (framework + period for reporting/integrity, exceptions_applied for the exception register view). See docs/architecture/05-vault-layout.md §Per-run manifest.

type ManifestSignature

type ManifestSignature struct {
	Algorithm string `json:"algorithm"`
	PublicKey []byte `json:"public_key"`
	Value     []byte `json:"value"`
}

ManifestSignature mirrors EnvelopeSignature: a fresh ephemeral Ed25519 keypair is generated at signing time, the private key is zeroed immediately, and the public key is embedded so the manifest is independently verifiable.

type ParameterSpec

type ParameterSpec struct {
	Type        string
	Default     any
	Min         any
	Max         any
	Enum        []any
	Pattern     string
	Description string
}

ParameterSpec describes a tunable per-project value on a policy. Default/Min/Max/Enum are typed `any` because the value's static type is named by the Type field (bool, int, string, duration, …).

type PassWhenClause added in v0.23.0

type PassWhenClause struct {
	// Slot names the slot whose records are evaluated.
	Slot string
	// Quantifier determines the aggregation semantics.
	Quantifier PassWhenQuantifier
	// Condition is the per-record test applied to records that pass the Filter.
	Condition *PassWhenCondition
	// Filter is an optional pre-condition. Records not satisfying the Filter
	// are excluded from the quantifier evaluation entirely (neither pass nor
	// fail). Nil means all records are evaluated.
	Filter *PassWhenCondition
	// ViolationMsg is a template for per-record violation messages.
	// Supports {{.field.path}} substitution against the record context.
	ViolationMsg string
	// IdentityKey is an optional field path used to deduplicate violations.
	// When set, only the first violation per unique value of this field is
	// emitted. Defaults to "id".
	IdentityKey string
	// MinPercentage is the minimum percentage (0–100) of records that must
	// satisfy the Condition. Only meaningful when Quantifier == QuantifierCount.
	MinPercentage *float64
}

PassWhenClause is one slot's evaluation rule within a pass_when: block.

type PassWhenCondition added in v0.23.0

type PassWhenCondition struct {
	// Op is the comparison operator: eq, neq, lt, lte, gt, gte, in,
	// not_in, is_set, all_of, any_of.
	Op string
	// Field is the dot-path to the field to compare, e.g. "payload.mfa_enabled"
	// or "id". Ignored for all_of / any_of. Field is always a record path;
	// "$params.<name>" is resolved only on the Value (RHS) side, not here —
	// a "$params.*" Field would be looked up as a record path and fail.
	Field string
	// Value is the RHS of the comparison for leaf operators. May be a scalar
	// (bool, int, float64, string) or a []interface{} for in / not_in.
	// A string value of the form "$params.<name>" is resolved to the parameter
	// value at evaluation time.
	Value any
	// Conditions is the sub-condition list for all_of / any_of operators.
	Conditions []*PassWhenCondition
}

PassWhenCondition is a single boolean expression in the pass_when: DSL. It is either a leaf condition (Op + Field + Value) or a compound condition (Op == "all_of" or "any_of" + Conditions list).

type PassWhenQuantifier added in v0.23.0

type PassWhenQuantifier string

PassWhenQuantifier determines how the DSL aggregates per-record results.

const (
	// QuantifierAll passes iff every included record satisfies the condition.
	QuantifierAll PassWhenQuantifier = "all"
	// QuantifierNone passes iff no included record satisfies the condition
	// (i.e. all records must fail it).
	QuantifierNone PassWhenQuantifier = "none"
	// QuantifierAny passes iff at least one included record satisfies the condition.
	QuantifierAny PassWhenQuantifier = "any"
	// QuantifierCount passes iff at least MinPercentage% of included records
	// satisfy the condition.
	QuantifierCount PassWhenQuantifier = "count"
)

type PassWhenSpec added in v0.23.0

type PassWhenSpec struct {
	Clauses []PassWhenClause
}

PassWhenSpec is the declarative evaluation DSL for automated policies. It contains one or more clauses — one clause per slot for multi-slot policies, exactly one for the common single-slot case. All clauses must pass for the policy to pass.

See docs/architecture/03-policy-spec.md §pass_when.

type PeriodAggregate added in v0.21.0

type PeriodAggregate struct {
	EvaluationsInPeriod          int           `json:"evaluations_in_period"`
	PassCount                    int           `json:"pass_count"`
	FailCount                    int           `json:"fail_count"`
	LongestFailureStreak         time.Duration `json:"longest_failure_streak,omitempty"`
	FirstEvaluationAt            time.Time     `json:"first_evaluation_at,omitempty"`
	LastEvaluationAt             time.Time     `json:"last_evaluation_at,omitempty"`
	LongestGapBetweenEvaluations time.Duration `json:"longest_gap_between_evaluations,omitempty"`
}

PeriodAggregate is the per-policy timeline summary for sub-period cadences. When a policy's cadence is shorter than the audit period (e.g., daily MFA check inside a quarterly period), the summary's "latest status" alone does not answer the auditor's real question — "did this control hold continuously throughout the period?"

PeriodAggregate, when present on a policy result, answers it: number of evaluations, pass/fail breakdown, longest failure streak, longest gap between evaluations. Absent for period-aligned cadences (quarterly inside a quarterly period — there is only one data point).

NOTE: PeriodAggregate is populated by `sigcomply audit-ledger` which scans the run-folder history for the period. The single-run path does not compute it (a single run cannot know its own period history without scanning the vault). Reserved as a typed field on PolicyResult so the audit-ledger command can populate it without schema-bumping later.

type Policy

type Policy struct {
	ID          string
	Controls    []ControlRef
	Description string
	Remediation string
	Severity    Severity
	Category    string
	Cadence     string
	OnPush      bool
	Slots       map[string]Slot
	Parameters  map[string]ParameterSpec
	RuleRef     string
	Tags        []string

	// EvidenceMode is the explicit first-class field that determines the
	// evaluator path: automated (Path B/C) or manual (Path A). Required.
	EvidenceMode EvidenceMode
	// PassWhen is the declarative condition DSL for automated policies.
	// Non-nil when evidence_mode: automated and no rule: escape-hatch.
	// Exactly one of PassWhen / RuleRef must be set for automated policies.
	PassWhen *PassWhenSpec
	// CatalogEntry is the evidence_catalog_id for manual policies. It
	// identifies which entry in the manual evidence catalog resolves to the
	// expected PDF path. Required when EvidenceMode == EvidenceModeManual.
	CatalogEntry string
}

Policy is the fully-resolved spec used by the planner and evaluator. Specs are loaded from policy.yaml files at L0; this is the in-memory shape with the rule reference still as a string ID — the registry resolves it to a Rule implementation at plan time.

type PolicyRef

type PolicyRef struct {
	PolicyID string
}

PolicyRef points from a framework to a registered policy by ID. The PolicyRegistry resolves the ID to the full Policy spec.

type PolicyResult

type PolicyResult struct {
	PolicyID           string
	Controls           []ControlRef
	Status             PolicyStatus
	Severity           Severity
	Category           string
	EffectiveParams    map[string]any
	Violations         []Violation
	ResourcesEvaluated int
	ResourcesFailed    int
	EvidenceEnvelopes  []string
	RuleVersion        string
	Diag               map[string]any

	// ConfiguredCadence is the cadence string in effect at this run
	// (e.g., "daily" or "every:6h"). Captured on every result so the
	// framework summary and the audit ledger can show the cadence
	// without recomputing it. Same value as in PolicyState.
	ConfiguredCadence string

	// PolicyContentHash is the SHA-256 of the canonicalized policy
	// spec plus referenced evidence-type schemas at this run.
	// Auditors comparing two runs can detect a policy/schema update
	// by comparing this field.
	PolicyContentHash string

	// NextDueAt is the wall-clock time after which the policy is due
	// to be re-evaluated under its configured cadence. Zero for
	// "continuous" / "every:0s" cadences (always due) and for
	// policies whose most recent terminal status was not pass (in
	// which case the on_fail_retry rule applies on the next run).
	NextDueAt time.Time

	// CarryForward is non-nil iff Status is StatusCarriedForward. It
	// references the prior evaluation that this run inherits from.
	// The reference is hash-tied to the original signed envelope so
	// an auditor can verify it offline without trusting the CLI.
	CarryForward *CarryForwardRef

	// PeriodAggregate, when non-nil, summarizes every evaluation of
	// this policy across the current audit period. Populated by
	// `sigcomply audit-ledger`, not by the single-run path — a
	// single run cannot know its period history without scanning
	// the vault. See PeriodAggregate's doc for the rationale.
	PeriodAggregate *PeriodAggregate
}

PolicyResult is the per-policy outcome with full fidelity — includes violation details and resource IDs. This shape is what L7 (vault) persists. L6 (aggregator) projects it into the privacy-bounded AggregatedPolicy before any cloud submission.

PolicyResult is vault-side; nothing in this struct crosses the aggregation boundary directly.

type PolicyState added in v0.21.0

type PolicyState struct {
	SchemaVersion string `json:"schema_version"`

	PolicyID  string `json:"policy_id"`
	Framework string `json:"framework"`

	// LastRunAt is the run-start timestamp of the most recent run that
	// evaluated this policy (pass, fail, error, or skipped-with-fresh-
	// data). Carried-forward runs do NOT update LastRunAt — by
	// definition they did not evaluate.
	LastRunAt time.Time `json:"last_run_at"`

	// LastPassAt is the run-start timestamp of the most recent run that
	// evaluated this policy and produced StatusPass. Cadence gating
	// uses this field, not LastRunAt — so a failed policy is "due"
	// again on the next run regardless of nominal cadence (the
	// on_fail_retry rule).
	LastPassAt time.Time `json:"last_pass_at,omitempty"`

	// LastFailAt is the run-start timestamp of the most recent run that
	// evaluated this policy and produced StatusFail.
	LastFailAt time.Time `json:"last_fail_at,omitempty"`

	// LastRunStatus is the terminal status of the most recent
	// evaluation. One of: pass, fail, error, skip, na, waived. Empty
	// when this is a freshly-allocated state with no run history.
	LastRunStatus PolicyStatus `json:"last_run_status,omitempty"`

	// LastPeriodID is the period_id that the most recent evaluation
	// belongs to. Carry-forward results reference this period via the
	// envelope under LastEnvelopeRef.
	LastPeriodID string `json:"last_period_id,omitempty"`

	// LastRunID is the run_id of the most recent evaluation. The
	// concurrent-write rule uses it as a tiebreaker when two runs
	// share LastRunAt to the second.
	LastRunID string `json:"last_run_id,omitempty"`

	// LastPolicyHash is the SHA-256 of the canonicalized policy spec
	// at the time of the most recent evaluation. A bundle update that
	// changes the policy text invalidates this hash, marking the
	// policy due on the next run regardless of cadence. The actual
	// hash for the current run is computed at plan time via
	// PolicyContentHash.
	LastPolicyHash string `json:"last_policy_hash,omitempty"`

	// LastEnvelopeRef is the run-relative path of the signed evidence
	// envelope produced by the most recent evaluation. Carry-forward
	// results emit a reference to this path so an auditor can
	// hash-verify the original envelope independently.
	LastEnvelopeRef string `json:"last_envelope_ref,omitempty"`

	// NextDueAt is the wall-clock time after which the policy is
	// considered due again under its configured cadence. Pre-computed
	// at end of each run so the next planner pass is a single field
	// read per policy. For "continuous" / "every: 0s" cadences this
	// is the zero time (always due).
	NextDueAt time.Time `json:"next_due_at,omitempty"`

	// ConfiguredCadence is the cadence string in effect at the most
	// recent evaluation ("daily", "every: 6h", etc.). Captured so a
	// subsequent run can detect a configuration change and re-
	// evaluate even when the time-since-last-pass would suggest
	// otherwise. Currently informational; the planner gates on
	// LastPolicyHash for content changes.
	ConfiguredCadence string `json:"configured_cadence,omitempty"`
}

PolicyState is the per-policy operational record kept in the vault at state/{framework}/policies/{policy_id}.json. It is mutable scheduling state — never part of any signed manifest, never an audit deliverable, never trusted by an auditor (signed envelopes are the trust anchor).

The state's only consumer is the planner, which uses it to decide whether a policy is due to evaluate in the current run. Loss of a state shard is recoverable: the planner treats a missing shard as "first run" and evaluates the policy. The shard is rewritten after every evaluation.

See docs/architecture/10-cadence-model.md for the model.

func (*PolicyState) IsFirstRun added in v0.21.0

func (p *PolicyState) IsFirstRun() bool

IsFirstRun reports whether this policy has never been evaluated. The planner uses this to emit the first-run warning and to mark a policy as immediately due regardless of cadence.

type PolicyStatus

type PolicyStatus string

PolicyStatus is the per-policy outcome of a run.

const (
	StatusPass           PolicyStatus = "pass"
	StatusFail           PolicyStatus = "fail"
	StatusSkip           PolicyStatus = "skip"
	StatusError          PolicyStatus = "error"
	StatusNA             PolicyStatus = "na"
	StatusWaived         PolicyStatus = "waived"
	StatusCarriedForward PolicyStatus = "carried_forward"
)

PolicyStatus values. `pass`/`fail` are the rule outcomes; `skip` / `error` / `na` / `waived` are evaluator/exception-driven states. `carried_forward` indicates the policy was NOT evaluated in this run — the result references a prior signed envelope. See docs/architecture/10-cadence-model.md §Carry-forward.

type RecordScope added in v0.27.0

type RecordScope struct {
	Account string `json:"account,omitempty"`
	Region  string `json:"region,omitempty"`
	Project string `json:"project,omitempty"`
}

RecordScope is the account/region/project an EvidenceRecord was collected from. Every field is omitempty so a partially-known scope (e.g. region but no project) still serializes minimally. The names are deliberately cloud-neutral: Account covers an AWS account ID, a GCP project number, an Azure subscription, or an Okta org; Region covers any geographic locality; Project covers a finer subdivision (GCP project ID, Azure resource group) when one applies.

type Repository

type Repository struct {
	Provider string `json:"provider"`
	NameSlug string `json:"name_slug"`
	URL      string `json:"url,omitempty"`
}

Repository is git-repo identity. Every field listed here is already public via the git remote URL.

type Rule

type Rule interface {
	ID() string
	Evaluate(ctx context.Context, in RuleInput) (RuleResult, error)
}

Rule is the evaluation logic for a policy. Implementations may be hand-written Go, Rego (run via OPA), or a YAML DSL transpiled to Rego. Rules are pure functions over RuleInput — no I/O permitted.

type RuleInput

type RuleInput struct {
	PolicyID string
	Slots    map[string][]EvidenceRecord
	Params   map[string]any
	Now      time.Time
}

RuleInput is the read-only context handed to a rule per policy evaluation. Records arrive grouped by slot name.

type RuleResult

type RuleResult struct {
	Status     PolicyStatus
	Violations []Violation
	Diag       map[string]any
}

RuleResult is what a rule returns to the evaluator. Lives vault-side; the aggregator (L6) projects this into the privacy-bounded AggregatedPolicy that crosses the boundary.

type RunSummary

type RunSummary struct {
	PoliciesTotal          int     `json:"policies_total"`
	PoliciesPassed         int     `json:"policies_passed"`
	PoliciesFailed         int     `json:"policies_failed"`
	PoliciesSkipped        int     `json:"policies_skipped"`
	PoliciesError          int     `json:"policies_error"`
	PoliciesNA             int     `json:"policies_na"`
	PoliciesWaived         int     `json:"policies_waived"`
	PoliciesCarriedForward int     `json:"policies_carried_forward"`
	ComplianceScore        float64 `json:"compliance_score"`
}

RunSummary is the counts-only run-level summary. ComplianceScore is the applicable-pass ratio computed in the aggregator: (policies_passed + policies_waived + policies_carried_forward) / (policies_total - policies_skipped - policies_na). Skipped and N/A policies are removed from the denominator so they don't drag the score down; waived policies count toward the numerator on the basis that an accepted exception is operationally equivalent to a pass. Carried-forward policies also count toward the numerator: a carry-forward only happens when the prior terminal status was pass (the cadence decision rule re-evaluates any non-pass), so the policy is still passing — it simply wasn't re-evaluated this run. Carried- forward policies are the normal steady state for sub-period cadences, so omitting them from the numerator would systematically understate the score on most real runs.

type Severity

type Severity string

Severity is the policy's display severity. Set on the policy spec; a rule cannot override it. If a single policy needs variable severity, split it into multiple policies.

const (
	SeverityInfo     Severity = "info"
	SeverityLow      Severity = "low"
	SeverityMedium   Severity = "medium"
	SeverityHigh     Severity = "high"
	SeverityCritical Severity = "critical"
)

Severity values, in ascending order of urgency.

type Slot

type Slot struct {
	Accepts     []string
	Cardinality SlotCardinality
	Required    bool
	Description string
}

Slot is a named typed input on a policy — the interface between the policy and the source plugins that fulfill it.

Accepts lists every evidence type that satisfies this slot. The planner binds a source to the slot when source.Emits() shares at least one type with Accepts; multiple bindings can target the same slot (subject to Cardinality) and may emit different types from the Accepts list. Rules receive evidence grouped by slot name and may switch on record.Type when behavior differs per type.

A slot listing more than one type is how cross-cloud / cross-source substitutability is expressed: an "object storage encrypted at rest" policy lists {s3_bucket, gcs_bucket, azure_blob_container} and is satisfied by any source that emits any of them — no policy fork, no source-side LCD payload, no normalization step. See docs/architecture/03-policy-spec.md §Slots.

type SlotCardinality

type SlotCardinality string

SlotCardinality declares how many sources a slot may bind.

const (
	SlotExactlyOne SlotCardinality = "exactly-one"
	SlotAtMostOne  SlotCardinality = "at-most-one"
	SlotOneOrMore  SlotCardinality = "one-or-more"
	SlotOptional   SlotCardinality = "optional"
)

SlotCardinality values. See 03-policy-spec.md §Slots for the project-binding rules each cardinality imposes.

type SlotRequest added in v0.19.0

type SlotRequest struct {
	PolicyID      string
	AcceptedTypes []string
	SlotName      string
	Params        map[string]any
}

SlotRequest is the per-binding call into a plugin's Collect.

PolicyID is for diagnostics only — plugins must not branch behavior on it. AcceptedTypes is the intersection of the slot's Accepts list with the plugin's Emits(); the plugin returns records of any AcceptedTypes element (typically just one, since most plugins emit a single type). The planner enforces non-empty intersection at plan time, so AcceptedTypes is always at least one element on a real invocation. Params carries optional per-binding slot_params from the project config (rare; most bindings have no params).

func (SlotRequest) Accepts added in v0.20.4

func (r SlotRequest) Accepts(typeID string) bool

Accepts reports whether typeID is one of the slot's accepted evidence types. Source plugins use it to dispatch on which of their emitted types the slot actually wants.

type SourcePlugin

type SourcePlugin interface {
	ID() string
	Emits() []string
	Init(ctx context.Context, cfg map[string]any) error
	Collect(ctx context.Context, req SlotRequest) ([]EvidenceRecord, error)
}

SourcePlugin produces EvidenceRecords for slots it can fulfill. Shipped plugins are compiled in; project-local plugins under .sigcomply/plugins/ are compiled in by `sigcomply build` (M16).

Implementations must:

  1. Sort emitted records by ID lexicographically before returning from Collect — keeps envelope bytes stable across runs when source state is stable.
  2. Set IdentityKey on EvidenceRecord when the evidence type has a meaningful cross-source identity (see 03-policy-spec.md §Cross-source dedup).
  3. Avoid embedding wall-clock timestamps in record payloads beyond what the source itself provides; use EvidenceRecord.CollectedAt for fetch time.

type SubmissionPayload

type SubmissionPayload struct {
	Schema string `json:"schema"`

	RunID     string `json:"run_id"`
	Framework string `json:"framework"`
	PeriodID  string `json:"period_id"`

	CommitSHA  string    `json:"commit_sha"`
	CommitTime time.Time `json:"commit_time"`
	Branch     string    `json:"branch"`

	Repository  Repository    `json:"repository"`
	Environment CIEnvironment `json:"environment"`

	CLIVersion  string    `json:"cli_version"`
	StartedAt   time.Time `json:"started_at"`
	CompletedAt time.Time `json:"completed_at"`

	Summary  RunSummary         `json:"summary"`
	Policies []AggregatedPolicy `json:"policies"`
}

SubmissionPayload is the wire format crossing the aggregation boundary from the customer environment to the cloud. Every field is concretely typed and represents either a count, an enum status, or already-public metadata.

SECURITY: This type is the structural privacy guarantee of the non-custodial architecture. Adding any field that could carry resource identity (ARNs, emails, IDs, file hashes, hostnames) is a non-custodial regression. Required for any such change:

  1. Written justification on the PR.
  2. Demonstration that the field cannot carry identity in any deployment.
  3. Review by >=2 maintainers including the security owner.
  4. Schema-version bump (current: sigcomply.cloud.v3).

The reflection test in cloud_test.go enforces "no freeform fields" structurally — adding interface{}, json.RawMessage, or map[string]any (transitively) will fail the build.

type Vault

type Vault interface {
	Init(ctx context.Context) error
	PutEnvelope(ctx context.Context, path string, e *Envelope) error
	PutJSON(ctx context.Context, path string, body any) error
	PutBinary(ctx context.Context, path string, body []byte, meta map[string]string) error
	GetBinary(ctx context.Context, path string) ([]byte, error)
	List(ctx context.Context, prefix string) ([]string, error)
}

Vault is the customer-side persistence layer. Append-only per run; the CLI writes, auditors and dashboards read. Implementations are backend-specific (local, s3, gcs, azure_blob) but all conform to this interface.

The CLI never reads from the vault during the same run — vault is one-way for write-side flows.

type Violation

type Violation struct {
	ResourceID string
	Reason     string
	Details    map[string]any
}

Violation is one record-level failure produced by a rule. Lives vault-side only — never crosses the aggregation boundary.

Jump to

Keyboard shortcuts

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