Documentation
¶
Overview ¶
Package plugin is the public API a project-local Go extension compiles against. It is the only non-internal Go package in this module, and it exists for one narrow reason.
Go refuses an import of a path containing an "internal" element from outside the tree rooted at that element's parent. `sigcomply build` compiles a customer's .sigcomply/ extensions inside the CUSTOMER's module — readModulePath in cmd/sigcomply/build.go reads the project's own go.mod, and `go build` runs from the project directory. So every interface and registry a Go extension has to reach — core.SourcePlugin, sources.RegisterFactory, vault.RegisterBackend, manual.RegisterReader — sat behind a wall the customer could not climb: their package passed `sigcomply build`'s discovery, validation and vet, and then failed the compile that same command triggered, with "use of internal package … not allowed". Authoring a Go plugin project-locally meant forking the CLI. This package is the door, and nothing more than the door.
Everything here is a compatibility promise ¶
A re-export is a commitment: it is what customers compile against and what a major version has to keep working (see docs/architecture/07-extensibility.md §Compatibility guarantees). The surface is therefore deliberately small — exactly what the three plugin axes documented in docs/architecture/00-three-plugin-axes.md need, and not one identifier more. Adding to it is a decision, not a convenience; the cost of a too-wide façade is paid years later, by whoever cannot change an internal type because someone once found it handy here.
- Axis C, source plugins: SourcePlugin, Env, SourceFactory, RegisterSource, plus the evidence values a Collect implementation must name (SlotRequest, EvidenceRecord, RecordScope) and the optional caveat interface (CaveatedSource, SourceCaveat).
- Axis B, vault backends: Vault, Envelope, VaultConfig, VaultFactory, RegisterVaultBackend.
- Axis A, manual-evidence backends: ManualReader, ManualFileInfo, ManualReaderFactory, RegisterManualReader, ErrManualNotFound.
Aliases, not wrappers ¶
Every type here is a type ALIAS of the internal type, so a value written against this package IS the internal type — identical, not convertible. A plugin built through RegisterSource satisfies core.SourcePlugin with no adapter, records it emits are the same structs the collector validates, and no copy-and-translate layer exists to fall out of step. The registration functions are thin forwarders only because Go has no such thing as a function alias; they add no behavior, and plugin_test.go asserts that a registration made here lands in the registry the orchestrator actually reads.
What is deliberately NOT public ¶
- Frameworks, policies, the pass_when DSL, the evaluator and the rule registry. Framework specs are curated — an auditor has to trust that the SOC 2 spec being measured against is the canonical one. Custom policies are authored as project-local YAML, and custom rule logic as Rego; neither needs Go.
- Evidence types. They are registered from JSON Schema — embedded in-tree, or project-locally from .sigcomply/evidence_types/*.json — and never from a Go init(). There is no hook to expose.
- The aggregation contract (core.SubmissionPayload and friends). That is the privacy boundary: the wire type is structurally incapable of carrying identifiers, and widening it is an upstream change with a security review, never a customer extension.
- The vault layout, the manifest, and the signing keys. Auditors and the verification SPA depend on those shapes being fixed.
- The host side of each registry — sources.Build, sources.Lookup, sources.IDs, vault.FromConfig, manual.LookupReader. An extension registers itself; it never dispatches, enumerates or replaces its peers. Keeping the read side internal is what stops a plugin from quietly reaching across the seam into another one.
- Convenience helpers that happen to be exported internally (sources.StringOpt, the instance-ID grammar, core.EnvelopeSignature). They are one-line idioms or fields reachable through a value that is already public; promising them forever buys nothing.
If something you need is missing, that is a conversation about the contract, not a gap to route around: open an issue rather than vendoring or forking. The data-driven routes (YAML policies, Rego rules, JSON evidence types) need no Go at all and load at every `sigcomply check` — reach for those first.
Index ¶
- Variables
- func RegisterManualReader(id string, factory ManualReaderFactory)
- func RegisterSource(id string, factory SourceFactory, configKeys ...string)
- func RegisterVaultBackend(id string, factory VaultFactory)
- type CaveatedSource
- type Env
- type Envelope
- type EvidenceRecord
- type ManualFileInfo
- type ManualReader
- type ManualReaderFactory
- type RecordScope
- type SlotRequest
- type SourceCaveat
- type SourceFactory
- type SourcePlugin
- type Vault
- type VaultConfig
- type VaultFactory
Constants ¶
This section is empty.
Variables ¶
var ErrManualNotFound = manual.ErrNotFound
ErrManualNotFound is the sentinel a ManualReader returns when the requested key does not exist. Returning it is not optional: it is what separates missing-but-expected evidence (the policy fails with a structured message telling the operator which folder is empty) from a transport failure (the policy becomes status=error). Any other error is read as transport.
It is a variable rather than a constant only because Go has no error constants; it is the same value as the sentinel the manual.pdf source compares against, so errors.Is works across the seam. Do not reassign it.
Functions ¶
func RegisterManualReader ¶
func RegisterManualReader(id string, factory ManualReaderFactory)
RegisterManualReader registers a manual-evidence backend under id, to be called from the extension package's init(). Duplicate IDs panic at process start, for the same reason RegisterSource's do.
func RegisterSource ¶
func RegisterSource(id string, factory SourceFactory, configKeys ...string)
RegisterSource registers a source-plugin factory under id, to be called from the extension package's init(). Duplicate IDs panic at process start: an in-tree duplicate is a programming error, and a project-local plugin claiming a reserved ID is a misconfiguration the build should not let through.
configKeys names every key the factory reads out of Env.Config. It is optional and fail-open — a factory that declares none is never warned about — but declaring them is what lets the planner report a typo in `sources:` instead of silently collecting nothing, because the inner config bag is an untyped map the config loader cannot check.
func RegisterVaultBackend ¶
func RegisterVaultBackend(id string, factory VaultFactory)
RegisterVaultBackend registers a vault backend under id, to be called from the extension package's init(). Duplicate IDs panic at process start, for the same reason RegisterSource's do.
Types ¶
type CaveatedSource ¶
type CaveatedSource = core.CaveatedSource
CaveatedSource is the optional interface a SourcePlugin implements to declare its SourceCaveats. A plugin that implements nothing declares no caveats, which is the correct default.
type Env ¶
Env carries everything a SourceFactory needs: Config is the raw map from the project config's `sources:` entry for this plugin, and FrameworkExtras is the escape hatch for framework-supplied data (the manual catalog, for instance). There is deliberately no Vault field — a plugin that needs storage configures its own backend rather than borrowing the run's evidence vault.
type Envelope ¶
Envelope is the signed wrapper around a batch of EvidenceRecords that a Vault's PutEnvelope receives. It is self-contained — public key and signature travel inside it — so an auditor holding one file can verify it offline; a backend stores the bytes and must not rewrite them.
type EvidenceRecord ¶
type EvidenceRecord = core.EvidenceRecord
EvidenceRecord is one observation a plugin returns from Collect. The Payload is validated against the registered JSON Schema of Type before it is wrapped in a signed envelope; the first non-conforming record fails the binding.
type ManualFileInfo ¶
ManualFileInfo describes one file returned by ManualReader.List. Key is the full path as passed to Get — prefix-relative, not a bare filename — so a List result feeds straight back into Get.
type ManualReader ¶
ManualReader is the read-only interface the manual.pdf source uses to fetch customer-uploaded evidence files. It is named ManualReader rather than Reader because this package holds all three axes at once, and an unqualified Reader beside a Vault and a SourcePlugin would not say which storage it reads.
type ManualReaderFactory ¶
type ManualReaderFactory = manual.ReaderFactory
ManualReaderFactory builds a ManualReader from the raw `manual.pdf` config map, returning the reader plus the (scheme, bucket, prefix) triple used to build the URI recorded in emitted evidence records.
type RecordScope ¶
type RecordScope = core.RecordScope
RecordScope is the account/region/project an EvidenceRecord was collected from — provenance for an auditor reading a single envelope, not a dimension of record identity. Optional; most records leave it nil.
type SlotRequest ¶
type SlotRequest = core.SlotRequest
SlotRequest is the per-binding call into Collect. AcceptedTypes is the intersection of the slot's accepts list with this plugin's Emits(), so a plugin emitting several types dispatches on it (via its Accepts method) rather than on PolicyID, which is diagnostics only.
type SourceCaveat ¶
type SourceCaveat = core.SourceCaveat
SourceCaveat is a plugin's own declaration that a field it emits is a safe default rather than a measurement — the vendor publishes no API for it, or the configured credential cannot reach the one that exists. It is advisory: a caveat never changes a status, a count, or the wire payload.
type SourceFactory ¶
SourceFactory builds a configured plugin instance. It runs once per registered source ID at the start of every `sigcomply check`; an error here is a configuration error (exit code 3).
type SourcePlugin ¶
type SourcePlugin = core.SourcePlugin
SourcePlugin is the interface a source plugin implements: ID, Emits, Init, Collect. Implementations must sort emitted records by ID, set IdentityKey where the evidence type has a cross-source identity, and leave fetch time to EvidenceRecord.CollectedAt — the full contract is documented on core.SourcePlugin.
type Vault ¶
Vault is the customer-side persistence layer a backend implements: append-only per run, written by the CLI and read by auditors and dashboards. The CLI never reads back from the vault during the same run.
type VaultConfig ¶
type VaultConfig = spec.VaultConfig
VaultConfig is the project config's `vault:` section: Backend selects the registered backend ID and Config carries every other key through untouched, so a new backend reads its own settings with no typed field and no central validation switch to edit. Str and Bool read Config.
type VaultFactory ¶
VaultFactory builds a fully Init'd Vault from the project's vault config. Backends translate VaultConfig into their own options, then construct and initialize before returning.