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
- func PolicyCadences(p *Policy) []string
- func PolicyContentHash(p *Policy, schemaDigests map[string]string) string
- func PrimaryControlID(refs []ControlRef) string
- type AggregatedPolicy
- type AppliedException
- type CIEnvironment
- type CarryForwardRef
- type CloudClient
- type Control
- type ControlRef
- type ControlRelationship
- type Envelope
- type EnvelopeSignature
- type EvidenceMode
- type EvidenceRecord
- type EvidenceType
- type Framework
- type FrameworkRunSummary
- type Manifest
- type ManifestSignature
- type ParameterSpec
- type PassWhenClause
- type PassWhenCondition
- type PassWhenQuantifier
- type PassWhenSpec
- type PeriodAggregate
- type Policy
- type PolicyRef
- type PolicyResult
- type PolicyState
- type PolicyStatus
- type RecordScope
- type Repository
- type Rule
- type RuleInput
- type RuleResult
- type RunSummary
- type Severity
- type Slot
- type SlotCardinality
- type SlotRequest
- type SourcePlugin
- type SubmissionPayload
- type Vault
- type Violation
Constants ¶
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.
const PolicyStateSchemaVersion = "policy-state.v1"
PolicyStateSchemaVersion is the schema stamp on the per-policy state shard. Bumped on any wire-format change.
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
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
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 ¶
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.
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:
- Sort emitted records by ID lexicographically before returning from Collect — keeps envelope bytes stable across runs when source state is stable.
- Set IdentityKey on EvidenceRecord when the evidence type has a meaningful cross-source identity (see 03-policy-spec.md §Cross-source dedup).
- 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:
- Written justification on the PR.
- Demonstration that the field cannot carry identity in any deployment.
- Review by >=2 maintainers including the security owner.
- 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.