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
- Variables
- func AdminETag(rev int64) string
- func CanonicalJSON(v any) ([]byte, error)
- func Digest(c Config, opts ...RedactOption) string
- func ETagForRevision(rev int64, revisionRowID uuid.UUID, agentID uuid.UUID) string
- func EnvRefs(s string) []string
- func EscapePointerToken(token string) string
- func IsForbiddenEnvName(name string) bool
- func IsOCISource(s string) bool
- func MatchIfNoneMatch(header, current string) bool
- func MatchOverridableConfigFlag(rc RemoteConfig, plugin, key string) bool
- func MatchTrustedSource(rc RemoteConfig, source string) bool
- func MergePatch(target, patch []byte) ([]byte, error)
- func ParseRevisionIfMatch(header string) (rev int64, ok bool)
- func ParseSchedule(expr string) (sched cron.Schedule, err error)
- func Pointer(segments ...string) string
- func RedactDocument(doc json.RawMessage) (out json.RawMessage, changed bool, err error)
- func ScrubSecretText(s string) (string, bool)
- func SplitPointer(ptr string) []string
- func StripLocked(overlay []byte) (stripped []byte, removed []string, err error)
- func UnescapePointerToken(token string) string
- func ValidateOverlay(overlay json.RawMessage) error
- func WillApply(rc RemoteConfig, changes []Change) (bool, string)
- type APIAuth
- type APIConfig
- type Change
- type Config
- type DiffEntry
- type EvidenceConfig
- type FieldError
- type OverlayDocument
- type Plugin
- type PluginReport
- type RedactOption
- type RemoteConfig
- type Report
- type Safety
- type SourceKind
- type ValidationErrors
Constants ¶
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).
const ( WillApplyReasonModeOff = "mode-off" WillApplyReasonModeReport = "mode-report" )
WillApply reasons besides ReasonUnsafeChanges / ReasonForbiddenChanges.
const ( DiffOpAdd = "add" DiffOpRemove = "remove" DiffOpReplace = "replace" )
Diff operations.
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.
const ( ModeOff = "off" ModeReport = "report" ModeApplySafe = "apply_safe" ModeApplyAll = "apply_all" )
Apply modes (remote_config.mode).
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 )
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.
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.
const DigestPrefix = "sha256:"
DigestPrefix prefixes config digests.
Variables ¶
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") )
var AgentStatuses = []string{StatusApplied, StatusRejected, StatusFailed, StatusNotApplicable}
AgentStatuses are the statuses an agent may report.
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).
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.
var Modes = []string{ModeOff, ModeReport, ModeApplySafe, ModeApplyAll}
Modes is the remote_config.mode vocabulary.
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.
var Reasons = []string{ ReasonUnsafeChanges, ReasonForbiddenChanges, ReasonInvalidConfig, ReasonInvalidType, ReasonUnknownField, ReasonDownloadFailed, ReasonEnvMissing, ReasonUnsupportedByAgent, ReasonCacheCorrupt, ReasonInternal, }
Reasons is the full report reason vocabulary.
Functions ¶
func AdminETag ¶
AdminETag returns the admin-facing ETag for a revision: the plain revision number, quoted (`"7"`) (R7).
func CanonicalJSON ¶
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 ¶
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 ¶
EnvRefs returns the variable names referenced in s, in order of first appearance and deduplicated.
func EscapePointerToken ¶
EscapePointerToken escapes one reference token for an RFC 6901 JSON Pointer ("~" -> "~0", "/" -> "~1").
func IsForbiddenEnvName ¶
IsForbiddenEnvName reports whether a variable may never be referenced (CCF_API_AUTH_*, case-insensitive).
func IsOCISource ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
SplitPointer splits an RFC 6901 JSON Pointer into unescaped segments. "" yields nil.
func StripLocked ¶
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 ¶
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 ¶
HasAuth reports whether both client_id and client_secret are set (non-blank).
func (*APIConfig) HasPartialAuth ¶
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 ¶
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.
- 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.
- 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.
- 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 ¶
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 ¶
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 ¶
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.
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.
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 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.