agentconfig

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2026 License: AGPL-3.0 Imports: 19 Imported by: 0

Documentation

Overview

Package agentconfig is the configuration model shared by the CCF agent and the API for remote agent configuration: the declared config types, RFC 7396 merge of an API-stored overlay onto the agent's file config, overlay validation, change-safety classification, redaction, digests, opaque ETags, and the agent<->API wire types.

The package does no I/O and never imports OPA, so importing agentconfig (as sdk/ does) stays light.

Conventions:

  • Config documents are snake_case JSON and are treated as opaque by API envelopes.
  • Every path that addresses a config document is an RFC 6901 JSON Pointer (see Pointer).
  • Overlay null semantics follow RFC 7396: omitting a key keeps the file's value, while null deletes the key from the effective config so the agent's default applies.
  • Only ValidateOverlay decodes strictly. Merge, Validate, ValidateEditable, Classify, Redact and Digest never reject unknown fields or weakly-typed values in a base.

Design references: comments across this package, the agentcfg service and the agent config handlers cite design IDs. They are defined in the compliance-framework/local-dev repository:

  • docs/agent-remote-config-design.md: D<n> decisions (§2) and R<n> resolutions (§12, §13).
  • docs/agent-remote-config-lld-api.md: A<n> work packages and the O<n> overlay validation rules (A1).

Index

Constants

View Source
const (
	ChangeReasonLockedKey             = "locked-key"
	ChangeReasonLogging               = "logging"
	ChangeReasonDataOnly              = "data-only"
	ChangeReasonReducesScope          = "reduces-scope"
	ChangeReasonAlreadyUsed           = "already-used"
	ChangeReasonTrustedSource         = "trusted-source"
	ChangeReasonUntrustedSource       = "untrusted-source"
	ChangeReasonLocalSourceNotAllowed = "local-source-not-allowed"
	ChangeReasonNewLocalSource        = "new-local-source"
	ChangeReasonOverridableConfigFlag = "overridable-config-flag"
	ChangeReasonConfigNotOverridable  = "config-not-overridable"
	ChangeReasonNewEnvReference       = "new-env-reference"
	ChangeReasonForbiddenEnvReference = "forbidden-env-reference"
	ChangeReasonReenablePlugin        = "reenables-plugin"
)

Change reason codes (stable; translated by the UI).

View Source
const (
	WillApplyReasonModeOff    = "mode-off"
	WillApplyReasonModeReport = "mode-report"
)

WillApply reasons besides ReasonUnsafeChanges / ReasonForbiddenChanges.

View Source
const (
	DiffOpAdd     = "add"
	DiffOpRemove  = "remove"
	DiffOpReplace = "replace"
)

Diff operations.

View Source
const (
	FieldCodeUnknownField = "unknown-field" // key not in the schema (O4)
	FieldCodeInvalidType  = "invalid-type"  // wrong JSON type, e.g. a non-string config/labels value (O5)
	FieldCodeInvalidValue = "invalid-value" // right type, out of range / not in enum
	FieldCodeLockedKey    = "locked-key"    // api / daemon / remote_config in an overlay (O3)
	FieldCodeSize         = "size"          // overlay size limit (O2)
	FieldCodePattern      = "pattern"       // plugin name or glob pattern (O6)
	FieldCodeCron         = "cron"          // schedule does not parse (O7)
	FieldCodeDuration     = "duration"      // interval / poll_interval
	FieldCodeSource       = "source"        // empty plugin source or policy entry (O8)
	FieldCodeEnvLocation  = "env-location"  // ${env:} outside plugins.*.config (O9)
	FieldCodeForbiddenEnv = "forbidden-env" // ${env:CCF_API_AUTH_*} (O9)
	FieldCodeEnvMissing   = "env-missing"   // ResolveEnv: variable unset (agent only)
	FieldCodeMaskedValue  = "masked-value"  // "••••" submitted (O10)
	FieldCodeRequired     = "required"      // missing required field (api.url, plugin source, ...)
	FieldCodeParse        = "parse"         // not a JSON object
)

FieldError codes (R43). The agent maps them to report reasons without its own pre-decode: unknown-field -> reason unknown-field; invalid-type -> invalid-type; env-missing -> env-missing; locked-key / forbidden-env -> forbidden-changes; everything else -> invalid-config.

View Source
const (
	ModeOff       = "off"
	ModeReport    = "report"
	ModeApplySafe = "apply_safe"
	ModeApplyAll  = "apply_all"
)

Apply modes (remote_config.mode).

View Source
const (
	// MaskedValue replaces redacted values. It is exactly this string (R25) and the API
	// rejects it on write, so a redacted view can never round-trip into an overlay.
	MaskedValue = "••••"

	// MaxOverlayBytes bounds the compact JSON of an overlay.
	MaxOverlayBytes = 256 << 10
	// MaxReportBytes bounds a config report body.
	MaxReportBytes = 4 << 20

	// DefaultPollInterval is the remote_config.poll_interval default.
	DefaultPollInterval = 60 * time.Second
	// MinPollInterval is the smallest accepted remote_config.poll_interval.
	MinPollInterval = 15 * time.Second
)
View Source
const (
	StatusApplied       = "applied"
	StatusRejected      = "rejected"
	StatusFailed        = "failed"
	StatusNotApplicable = "not-applicable"
	StatusPending       = "pending" // server-derived only
	StatusUnknown       = "unknown" // server-derived only
)

Instance statuses. The agent sends applied, rejected, failed or not-applicable (R10); the server additionally derives pending and unknown.

View Source
const (
	ReasonUnsafeChanges      = "unsafe-changes"
	ReasonForbiddenChanges   = "forbidden-changes"
	ReasonInvalidConfig      = "invalid-config" // overlay-merged config invalid, OR the local file is invalid on reload (agent keeps last-known-good, R42)
	ReasonInvalidType        = "invalid-type"   // R27 (agent strict decode)
	ReasonUnknownField       = "unknown-field"  // R27
	ReasonDownloadFailed     = "download-failed"
	ReasonEnvMissing         = "env-missing"          // R24
	ReasonUnsupportedByAgent = "unsupported-by-agent" // overlay uses a feature this agent version lacks
	ReasonCacheCorrupt       = "cache-corrupt"
	ReasonInternal           = "internal"
)

Report reasons (R42). The API rejects any other value.

View Source
const DigestPrefix = "sha256:"

DigestPrefix prefixes config digests.

Variables

View Source
var (
	// ErrEnvMissing is returned by ResolveEnv when a referenced variable is unset.
	ErrEnvMissing = errors.New("environment variable is not set")
	// ErrEnvForbidden is returned by ResolveEnv for a forbidden variable name.
	ErrEnvForbidden = errors.New("environment variable may not be referenced")
)

AgentStatuses are the statuses an agent may report.

View Source
var EnvRefPattern = regexp.MustCompile(`\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}`)

EnvRefPattern matches a ${env:NAME} placeholder. Placeholders are resolved only in plugins.*.config values (whole or embedded), in the file and in the overlay (R24).

View Source
var LockedKeys = []string{"api", "daemon", "remote_config"}

LockedKeys are the top-level keys an overlay may never set (D3). They always come from the agent's local config.

Modes is the remote_config.mode vocabulary.

View Source
var PluginNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]{0,62}$`)

PluginNamePattern is the name pattern for plugins named in an overlay (R28, O6). Viper lowercases file plugin names, so upper case would silently create a second plugin. ValidateOverlay applies it to every plugin an overlay sets, including file plugins it only changes, so a file plugin whose name does not match cannot be changed remotely.

Reasons is the full report reason vocabulary.

Functions

func AdminETag

func AdminETag(rev int64) string

AdminETag returns the admin-facing ETag for a revision: the plain revision number, quoted (`"7"`) (R7).

func CanonicalJSON

func CanonicalJSON(v any) ([]byte, error)

CanonicalJSON returns the canonical encoding of v: json.Marshal, decode to any with UseNumber, re-encode with SetEscapeHTML(false) and no trailing newline. Object keys are sorted. It is the encoding Digest hashes.

It intentionally duplicates the API's internal/artifact.CanonicalJSON: artifact forms are pinned by golden tests, and this public package must not import internal/.

func Digest

func Digest(c Config, opts ...RedactOption) string

Digest returns "sha256:" + hex(sha256(canonical(Redact(c, opts...) with API = nil))). Callers pass the SAME options they used to redact the reported effective config, so the digest matches the reported document (R55). Canonical JSON is described on CanonicalJSON.

func ETagForRevision

func ETagForRevision(rev int64, revisionRowID uuid.UUID, agentID uuid.UUID) string

ETagForRevision returns the agent-facing opaque ETag (R7), including the quotes: `"r<rev>-<revisionRowID>"` for rev >= 1 and `"r0-<agentID>"` for rev 0, so a DB reset, re-registration or agent re-creation never yields a false 304. Server-side only: clients store the raw ETag they received and send it back verbatim.

func EnvRefs

func EnvRefs(s string) []string

EnvRefs returns the variable names referenced in s, in order of first appearance and deduplicated.

func EscapePointerToken

func EscapePointerToken(token string) string

EscapePointerToken escapes one reference token for an RFC 6901 JSON Pointer ("~" -> "~0", "/" -> "~1").

func IsForbiddenEnvName

func IsForbiddenEnvName(name string) bool

IsForbiddenEnvName reports whether a variable may never be referenced (CCF_API_AUTH_*, case-insensitive).

func IsOCISource

func IsOCISource(s string) bool

IsOCISource reports whether s parses as an OCI tag with strict validation, which is what the agent's downloader supports. The agent's internal.IsOCI delegates here (R3).

func MatchIfNoneMatch

func MatchIfNoneMatch(header, current string) bool

MatchIfNoneMatch reports whether any entry of an If-None-Match header (strong, W/, bare, a comma-separated list, or "*") equals the CURRENT opaque tag. Entries are compared as text after stripping W/ and quotes. An empty header never matches.

func MatchOverridableConfigFlag

func MatchOverridableConfigFlag(rc RemoteConfig, plugin, key string) bool

MatchOverridableConfigFlag reports whether plugins.<plugin>.config.<key> may be changed remotely. An entry containing ':' is "<plugin-glob>:<key-glob>" (split at the first ':'); otherwise it is "<key-glob>" for any plugin. Matching is path.Match, case-sensitive; base keys are lowercased by viper (R28).

func MatchTrustedSource

func MatchTrustedSource(rc RemoteConfig, source string) bool

MatchTrustedSource reports whether source matches one of rc.TrustedSources using path.Match semantics: case-sensitive, and '*' does not cross '/'.

func MergePatch

func MergePatch(target, patch []byte) ([]byte, error)

MergePatch applies an RFC 7396 JSON merge patch. target may be nil/empty (treated as null). A non-object patch replaces the target. Objects merge recursively, null deletes a key and arrays replace wholesale. Numbers are preserved exactly. The result is canonical JSON (sorted keys, no HTML escaping).

func ParseRevisionIfMatch

func ParseRevisionIfMatch(header string) (rev int64, ok bool)

ParseRevisionIfMatch parses an admin If-Match header holding a plain revision number: `"7"`, `W/"7"` or `7`. Exactly one non-negative value in canonical decimal form is accepted; anything else (a list, "*", a sign such as "+7", leading zeros such as "07") yields ok=false.

func ParseSchedule

func ParseSchedule(expr string) (sched cron.Schedule, err error)

ParseSchedule parses a plugin schedule the way the agent does: standard 5-field cron or a descriptor such as "@hourly". It is NOT the API's 6-field internal scheduler format.

robfig/cron v3.0.1 panics on a TZ= or CRON_TZ= prefix with no space after it (it slices past the end of the spec), so that form is rejected first, and any other parser panic is returned as an error.

func Pointer

func Pointer(segments ...string) string

Pointer builds an RFC 6901 JSON Pointer from unescaped segments, e.g. Pointer("plugins", "x", "config", "a/b") == "/plugins/x/config/a~1b". No segments yields "" (the whole document).

func RedactDocument

func RedactDocument(doc json.RawMessage) (out json.RawMessage, changed bool, err error)

RedactDocument re-applies Redact's rules to a reported config document (base or effective) WITHOUT decoding it into Config, so fields a newer agent sends are preserved (R51). It removes api.auth.client_secret, masks api.url, plugins.*.source and plugins.*.policies entries when they hold a secret by content, and masks plugins.*.config and plugins.*.policy_data exactly as Redact does (key names, content and placeholder rules). It cannot know the agent's env-sourced pointers (R55), so it is best effort; on a document Redact produced with the same rules it is a no-op. changed reports whether anything was altered. The input must be a JSON object.

func ScrubSecretText

func ScrubSecretText(s string) (string, bool)

ScrubSecretText returns MaskedValue and true when s contains a secret by content (the rule Redact applies whatever the key: a URL with a password, a DSN, a PEM private key, a password=... assignment or a provider token), else s and false. It masks the whole string. Use it on free text such as error messages and plugin sources.

func SplitPointer

func SplitPointer(ptr string) []string

SplitPointer splits an RFC 6901 JSON Pointer into unescaped segments. "" yields nil.

func StripLocked

func StripLocked(overlay []byte) (stripped []byte, removed []string, err error)

StripLocked returns the overlay without the top-level LockedKeys and the sorted list of removed keys. A nil/empty/"null" overlay is returned as "{}". A non-object overlay is an error.

func UnescapePointerToken

func UnescapePointerToken(token string) string

UnescapePointerToken reverses EscapePointerToken.

func ValidateOverlay

func ValidateOverlay(overlay json.RawMessage) error

ValidateOverlay validates an overlay ON ITS OWN (no base) and returns nil or ValidationErrors. It is the only strict decoder in the package (R27, R51): unknown keys are rejected everywhere, every leaf may be null (RFC 7396 delete) and there is no type coercion. Rules:

O1  must be a JSON object ({} allowed)
O2  compact size <= MaxOverlayBytes
O3  no locked key (api, daemon, remote_config), even with a null value
O4  unknown keys are rejected
O5  types: verbosity integer 0-2; agent_evidence.{enabled,emit_on_run_completion} bool,
    interval a Go duration >= 0; plugins.*.config and labels values strings (or null);
    policy_behavior values string arrays; protocol_version 1 or 2 (explicit 0 rejected,
    R9); schedule a string
O6  every non-null plugin key in the overlay matches PluginNamePattern, also for a file
    plugin the overlay only changes (there is no base here); so a file plugin whose name
    does not match (e.g. "_legacy", or longer than 63 characters) cannot be changed
    remotely, only deleted with null
O7  schedule parses with ParseSchedule
O8  source (when non-null) and policy entries are non-empty
O9  ${env:NAME} only in plugins.*.config values; NAME must not be forbidden
O10 no string value equals MaskedValue
O11 no key or string value contains a NUL character (Postgres cannot store it)

func WillApply

func WillApply(rc RemoteConfig, changes []Change) (bool, string)

WillApply decides whether an agent in rc.Mode applies a revision with these changes: off -> (false, "mode-off"); report -> (false, "mode-report"); any Forbidden -> (false, "forbidden-changes") in every apply mode (R23); apply_safe with any Unsafe -> (false, "unsafe-changes"); otherwise (true, "").

Types

type APIAuth

type APIAuth struct {
	ClientID     string `json:"client_id" mapstructure:"client_id"`
	ClientSecret string `json:"client_secret,omitempty" mapstructure:"client_secret"`
}

APIAuth holds the agent service-account credentials.

type APIConfig

type APIConfig struct {
	URL  string   `json:"url" mapstructure:"url"`
	Auth *APIAuth `json:"auth,omitempty" mapstructure:"auth"`
}

APIConfig is the agent's API connection block. It is a locked key.

func (*APIConfig) HasAuth

func (a *APIConfig) HasAuth() bool

HasAuth reports whether both client_id and client_secret are set (non-blank).

func (*APIConfig) HasPartialAuth

func (a *APIConfig) HasPartialAuth() bool

HasPartialAuth reports whether exactly one of client_id and client_secret is set.

type Change

type Change struct {
	Path   string `json:"path"` // RFC 6901 pointer
	Safety Safety `json:"safety"`
	Reason string `json:"reason"`
	Value  string `json:"value,omitempty"` // the source / env name that triggered the class
}

Change is one classified difference between the base and the effective config.

func Classify

func Classify(base Config, overlay json.RawMessage, rc RemoteConfig) ([]Change, error)

Classify diffs Merge(base, overlay) against base and classifies every changed path. rc must be normalized. A locked key in the raw overlay is Forbidden even though Merge strips it. An overlay null is a deletion and is classified as one; an omitted key produces no Change. The result is sorted by Path, then Value.

Re-enabling a plugin the base disables is Unsafe (reenables-plugin) unless its source is trusted (trusted-source). Its other parts are classified as if the plugin were new, since a disabled plugin's sources are not already used: a local plugin source goes through the source rules (Forbidden unless apply_all with allow_local_sources, as for a new plugin), every policy entry it keeps goes through the source rules, and every ${env:} reference in its config is a new reference. By design, apply_safe accepts the rest of a plugin's data without a host opt-in: schedule, labels, policy_data, policy_behavior, protocol_version and disabling a plugin are Safe (data-only), and removing a plugin or policy entries is Safe (reduces-scope). So a remote editor can change the policy inputs that decide pass/fail, or stop a plugin from running, on an apply_safe host.

type Config

type Config struct {
	Daemon        bool               `json:"daemon" mapstructure:"daemon"`
	Verbosity     int32              `json:"verbosity" mapstructure:"verbosity"` // 0/1/2 = Info/Debug/Trace
	API           *APIConfig         `json:"api,omitempty" mapstructure:"api"`
	RemoteConfig  *RemoteConfig      `json:"remote_config,omitempty" mapstructure:"remote_config"`
	Plugins       map[string]*Plugin `json:"plugins" mapstructure:"plugins"`
	AgentEvidence *EvidenceConfig    `json:"agent_evidence,omitempty" mapstructure:"agent_evidence"`
}

Config is the declared form of the agent configuration (file, overlay and effective). The agent keeps its private runtime structs and converts once from this form (R4).

func DecodeConfig

func DecodeConfig(data []byte) (Config, error)

DecodeConfig decodes a reported config document (base or effective) non-strictly: unknown fields are ignored so a newer agent's fields never make the API reject a report (R51).

func Merge

func Merge(base Config, overlay json.RawMessage) (Config, error)

Merge computes the effective config: StripLocked(overlay), then MergePatch onto json.Marshal(base), then a non-strict decode into Config (so fields a newer base carries survive). Locked keys always come from base, as defence in depth (R23).

Merge does no env resolution, applies no defaults and does no validation. It errors when the overlay is not an object or the merged document does not fit the Config types (for example a number where a string map value is expected); ValidateOverlay reports those problems with pointers.

func Redact

func Redact(c Config, opts ...RedactOption) Config

Redact returns a deep copy of c with api.auth.client_secret cleared and secret-like values replaced by MaskedValue. Apply it to the UNRESOLVED config (placeholders intact). It is idempotent, and Digest hashes its output, so both always apply the same rules (R55).

A value is masked whole: the result is exactly MaskedValue, never a partially masked string, so a redacted view can never be resubmitted (ValidateOverlay rejects MaskedValue, O10). The rules, under plugins.*.config and plugins.*.policy_data (any depth):

For a string value, let literal be the value with its ${env:NAME} placeholders removed.

  1. A value whose literal is empty or only whitespace and the separators ":;,|/@=&" (placeholder-only, e.g. "${env:PASS}" or "${env:USER}:${env:PASS}") is kept verbatim.
  2. Else it is masked when it is at a pointer given to WithMaskedPointers, or its key is secret-like (isSecretKey: e.g. password, passphrase, secret, token, credential, api_key, private_key, dsn, connection_string, auth, cookie, session_id; see secretKeyStems and secretKeyWords). Keys that only describe a secret are not secret-like (e.g. secret_name, token_url, password_file, api_key_id, max_tokens; see isNonSecretKeyName). Under a secret-like key, literal text mixed with a placeholder ("lit${env:X}") is masked.
  3. Else it is masked when its literal contains a secret by content, whatever the key (containsSecretValue): a URL with a password in its userinfo (also inside a longer string such as a DSN), a PEM private key, a password=... assignment, or a high-confidence provider token (AWS access key ID, GitHub, GitLab, Slack, Google API key, Stripe, JWT, SendGrid, npm, PyPI, OpenAI, Anthropic, Hugging Face, DigitalOcean, Shopify, Terraform Cloud, Vault, Azure AD client secret, age), or a scheme-less MySQL DSN with a password (user:pass@tcp(host)/db).

A non-string value (number, object, array) at a masked pointer or under a secret-like key is masked whole; booleans and null are never secret and are kept unless at a masked pointer. Strings nested in kept objects and arrays get the same rules, with the nearest enclosing object key as their key. api.url, plugins.*.source and each plugins.*.policies entry are masked when they contain a secret by content (rule 3 only; no key rule). Map keys and labels are never masked.

func ResolveEnv

func ResolveEnv(c Config, lookup func(string) (string, bool)) (Config, error)

ResolveEnv returns a copy of c with ${env:NAME} placeholders in plugins.*.config values replaced by lookup(NAME). Nothing else is resolved. An unset variable yields an error wrapping ErrEnvMissing; a forbidden name yields an error wrapping ErrEnvForbidden. There is no escaping syntax in v1. Agent only: reports, redaction and digests use the unresolved config.

func (Config) EffectiveRemoteConfig

func (c Config) EffectiveRemoteConfig() RemoteConfig

EffectiveRemoteConfig returns the normalized remote_config block of c, using c.API to decide whether the agent has credentials.

func (Config) Validate

func (c Config) Validate() error

Validate is the agent's full check of an effective config: ValidateEditable plus the api block (url required, both or neither credential, client_id a UUID) and remote_config (mode enum, poll_interval >= MinPollInterval, valid glob patterns). File-origin leniency (R34) and the explicit-0 protocol_version file check (R9) are agent concerns: the agent chooses which FieldErrors to downgrade.

func (Config) ValidateEditable

func (c Config) ValidateEditable() error

ValidateEditable checks an effective config except the locked blocks (api, daemon, remote_config). The API uses it on redacted reported bases merged with an overlay, so it never rejects masked values or a missing client secret. Rules: verbosity >= 0; agent_evidence.interval a non-negative duration; every plugin non-nil with a non-empty source, a parseable schedule, protocol_version in {0,1,2} and non-empty policy entries; env references obey O9.

type DiffEntry

type DiffEntry struct {
	Path string          `json:"path"`
	Op   string          `json:"op"` // add | remove | replace
	From json.RawMessage `json:"from,omitempty" swaggertype:"object"`
	To   json.RawMessage `json:"to,omitempty" swaggertype:"object"`
}

DiffEntry is one difference between two JSON documents. Path is an RFC 6901 pointer.

func DiffJSON

func DiffJSON(a, b []byte) ([]DiffEntry, error)

DiffJSON compares two JSON documents. Objects recurse; arrays and scalars are leaves. A key present on one side only is an add or a remove of the whole value; a value that differs (including a type change) is a replace. Empty input is treated as null. The result is sorted by Path.

type EvidenceConfig

type EvidenceConfig struct {
	Enabled             *bool  `json:"enabled,omitempty" mapstructure:"enabled"`
	EmitOnRunCompletion *bool  `json:"emit_on_run_completion,omitempty" mapstructure:"emit_on_run_completion"`
	Interval            string `json:"interval,omitempty" mapstructure:"interval"`
}

EvidenceConfig controls the agent's own evidence.

type FieldError

type FieldError struct {
	Path    string `json:"path"`    // RFC 6901 pointer into the snake_case config ("" = root)
	Code    string `json:"code"`    // stable machine code (R43), one of FieldCode*
	Message string `json:"message"` // human text
}

FieldError is one validation problem in a config document.

type OverlayDocument

type OverlayDocument struct {
	Revision  int64           `json:"revision"`
	Overlay   json.RawMessage `json:"overlay" swaggertype:"object"`
	CreatedAt *time.Time      `json:"created-at,omitempty"` // omitted for revision 0
}

OverlayDocument is the body of GET /api/agent/config (inside {"data": ...}).

type Plugin

type Plugin struct {
	Enabled         *bool               `json:"enabled,omitempty" mapstructure:"enabled"`
	ProtocolVersion int32               `json:"protocol_version,omitempty" mapstructure:"protocol_version"` // 0 = auto (R9)
	Schedule        *string             `json:"schedule,omitempty" mapstructure:"schedule"`
	Source          string              `json:"source" mapstructure:"source"`
	Policies        []string            `json:"policies,omitempty" mapstructure:"policies"`
	Config          map[string]string   `json:"config,omitempty" mapstructure:"config"`
	Labels          map[string]string   `json:"labels,omitempty" mapstructure:"labels"`
	PolicyData      map[string]any      `json:"policy_data,omitempty" mapstructure:"policy_data"`
	PolicyBehavior  map[string][]string `json:"policy_behavior,omitempty" mapstructure:"policy_behavior"`
}

Plugin is one plugin entry.

func (*Plugin) IsEnabled

func (p *Plugin) IsEnabled() bool

IsEnabled reports whether the plugin runs; a nil Enabled means true.

type PluginReport

type PluginReport struct {
	Name   string `json:"name"`             // the plugin's key under plugins in the config
	Source string `json:"source,omitempty"` // the configured source
	// LibVersion is the version of github.com/compliance-framework/agent the plugin binary
	// was built with, from its Go build info. Empty when unknown: no build info, or a
	// replace or devel build.
	LibVersion string `json:"lib-version,omitempty"`
}

PluginReport is one plugin of an instance (R76).

type RedactOption

type RedactOption func(*redactOpts)

RedactOption configures Redact and Digest.

func WithMaskedPointers

func WithMaskedPointers(ptrs ...string) RedactOption

WithMaskedPointers masks the values at these RFC 6901 pointers. The agent passes the plugin config values that came from viper env (CCF_PLUGINS_<P>_CONFIG_<K>) rather than from placeholders (R25, R44). Pass the SAME pointers to Redact and Digest (R55).

type RemoteConfig

type RemoteConfig struct {
	// Mode is off, report, apply_safe or apply_all. Unset means report for an agent with
	// credentials (it reports but never applies), off without (see Normalize).
	Mode                   string   `json:"mode,omitempty" mapstructure:"mode"`
	PollInterval           string   `json:"poll_interval,omitempty" mapstructure:"poll_interval"`
	TrustedSources         []string `json:"trusted_sources" mapstructure:"trusted_sources"`                   // default []
	OverridableConfigFlags []string `json:"overridable_config_flags" mapstructure:"overridable_config_flags"` // default []
	AllowLocalSources      bool     `json:"allow_local_sources" mapstructure:"allow_local_sources"`           // default false
}

RemoteConfig is the agent's remote-configuration policy. It is set locally only (file, host env, CLI), never remotely (R30).

func (RemoteConfig) Normalize

func (rc RemoteConfig) Normalize(hasAuth bool) RemoteConfig

Normalize applies the remote_config defaults (R29):

  • Mode "" becomes report with auth (the agent reports but never applies a revision until the operator opts in with apply_safe or apply_all), off without; no auth always forces off;
  • PollInterval "" becomes "60s";
  • nil TrustedSources / OverridableConfigFlags become [];
  • AllowLocalSources stays false unless set.

type Report

type Report struct {
	Hostname          string          `json:"hostname,omitempty"`      // <= 255
	AgentVersion      string          `json:"agent-version,omitempty"` // <= 64
	Mode              string          `json:"mode"`
	Daemon            bool            `json:"daemon"` // false = one-shot run; pruned after 24h (R10, R37)
	AppliedRevision   *int64          `json:"applied-revision"`
	AttemptedRevision *int64          `json:"attempted-revision,omitempty"`
	Status            string          `json:"status"`
	Reason            string          `json:"reason,omitempty"`
	Error             *string         `json:"error"`               // <= 8 KiB, truncated server-side
	Truncated         bool            `json:"truncated,omitempty"` // agent dropped/trimmed parts to fit MaxReportBytes (R10)
	Warnings          []FieldError    `json:"warnings,omitempty"`  // R41: tolerated file-origin problems
	Base              json.RawMessage `json:"base" swaggertype:"object"`
	Effective         json.RawMessage `json:"effective" swaggertype:"object"`
	EffectiveDigest   string          `json:"effective-digest"`
	Unsafe            []Change        `json:"unsafe,omitempty"`
	RemoteConfig      *RemoteConfig   `json:"remote-config,omitempty"` // normalized; snake_case inside
	// Plugins are the instance's plugins and the agent library each was built with (R76),
	// so the UI can show policy compatibility before a save. Older agents omit it.
	Plugins []PluginReport `json:"plugins,omitempty"`
}

Report is the body of PUT /api/agent/instances/:instanceId/config-report. Envelope keys are kebab-case; Base, Effective and RemoteConfig contents are snake_case config documents.

type Safety

type Safety string

Safety is the class of one effective-config change (§3.6).

const (
	Safe      Safety = "safe"
	Unsafe    Safety = "unsafe"
	Forbidden Safety = "forbidden"
)

type SourceKind

type SourceKind string

SourceKind classifies a plugin source or policy entry.

const (
	SourceKindOCI   SourceKind = "oci"
	SourceKindLocal SourceKind = "local"
)

func KindOf

func KindOf(s string) SourceKind

KindOf classifies s: OCI (strict tag) or, otherwise, a local path.

type ValidationErrors

type ValidationErrors []FieldError

ValidationErrors is a list of FieldErrors; it is the error type returned by the validators.

func (ValidationErrors) Error

func (v ValidationErrors) Error() string

Error implements error.

Jump to

Keyboard shortcuts

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