fides

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package fides talks to a Fides server — the compliance system Hecate records promotions in and asks for permission.

Only the endpoints a promotion needs are here. Fides has a large API; Hecate uses three parts of it:

  • verify a trail's attestation chain, so the tamper-evidence claim is checkable by whoever relies on it rather than asserted in documentation;
  • check an environment's policy against a trail;
  • check whether an artifact is on an environment's allowlist.

The last two are why a Gate has to name a Fides environment at all.

Index

Constants

View Source
const (
	// RoleApprover is a reviewer's sign-off.
	RoleApprover = "approver"
	// RoleDeployer is the identity that triggers the deployment. Distinct from
	// the approver on purpose: Fides refuses a trail where they are the same
	// person, which is the whole point of four-eyes.
	RoleDeployer = "deployer"
)

Approval roles, as Fides names them when it evaluates segregation of duties.

Variables

This section is empty.

Functions

func IsAuth

func IsAuth(err error) bool

IsAuth reports whether Fides rejected the credentials.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether Fides says the thing is not there.

Types

type Anchor

type Anchor struct {
	Anchored bool `json:"anchored"`
	// HeadMatches is false when the chain has moved on since it was anchored,
	// or when it was rewritten under the anchor.
	HeadMatches bool      `json:"head_matches"`
	TSAURL      string    `json:"tsa_url,omitempty"`
	AnchoredAt  time.Time `json:"anchored_at,omitempty"`
}

Anchor is the external timestamp over a chain head.

type Approval

type Approval struct {
	// By is the human the approval belongs to. Required.
	//
	// Sent as `on_behalf_of`, because Hecate authenticates to Fides with a
	// service token: without it every approval Hecate records would carry the
	// service account's identity, every identity would be equal, and
	// segregation of duties would evaluate one person having done everything.
	// Fides honours the delegation only when it is configured to and the email
	// is a known user in the organisation — so this can be refused, and a
	// refusal is a real answer rather than a transport failure.
	By string
	// Role is RoleApprover or RoleDeployer.
	Role string
	// Reason is free text stored with the approval.
	Reason string
}

Approval is one identity signing off on a trail in one role.

type Artifact

type Artifact struct {
	// SHA256 identifies it. The `sha256:` prefix is stripped before sending —
	// Fides keys artifacts on lowercase hex.
	SHA256 string
	// Trail links the artifact to the evidence recorded against it.
	Trail string
	// Name is human-readable, e.g. the image repository.
	Name string
	// Type is what kind of thing it is, e.g. container-image.
	Type string
	// Tags are arbitrary labels.
	Tags map[string]string
}

Artifact is a thing Fides can hold evidence about.

type Attestation

type Attestation struct {
	// Name is what this evidence is, e.g. "promotion".
	Name string
	// Type is the attestation type an environment policy asks for by name, so
	// `policy check` can require it. Getting this wrong means a policy that
	// requires "deployment" never sees one.
	Type string
	// ArtifactSHA256 ties the evidence to what was deployed.
	ArtifactSHA256 string
	// Payload is the evidence body, stored and hashed into the chain.
	Payload any
	// SignedBy is who or what produced it.
	SignedBy string
}

Attestation is one piece of evidence on a trail.

type Chain

type Chain struct {
	// Valid is false when the chain has been tampered with, reordered, or had
	// an entry deleted.
	Valid bool `json:"valid"`
	// Count is how many attestations are on the trail.
	Count int `json:"count"`
	// BrokenAt is the index of the first bad entry, or -1 when valid.
	BrokenAt int `json:"broken_at"`
	// Reason says what was wrong, when something was.
	Reason string `json:"reason,omitempty"`
	// ExternalAnchor reports whether the chain head was timestamped by an
	// external authority. A valid chain that only we vouch for is a weaker
	// claim than one an RFC3161 authority saw at a point in time.
	ExternalAnchor *Anchor `json:"external_anchor,omitempty"`
}

Chain is the verdict on a trail's attestation chain.

Fields mirror Fides' own response rather than being renamed, so an operator comparing `hecate verify` with `fides verify-chain` sees the same words.

type ChangeVerdict

type ChangeVerdict struct {
	// Recommendation is "approve" or "hold".
	Recommendation string `json:"recommendation"`
	// Approved is Fides' own boolean, true only when every control is satisfied
	// and a human has signed off.
	Approved bool `json:"approved"`
	// RiskScore is 0-100, higher being worse.
	RiskScore int `json:"risk_score"`
	// RiskLevel is "low", "medium" or "high".
	RiskLevel string `json:"risk_level"`
	// Passed names the controls that were satisfied, by key alone — Fides sends
	// strings here and objects for the two below, and that asymmetry is real.
	Passed []string `json:"passed,omitempty"`
	// Failed and MissingEvidence name the controls that stopped it, so a held
	// crossing can say what would unblock it.
	Failed          []Control `json:"failed,omitempty"`
	MissingEvidence []Control `json:"missing_evidence,omitempty"`
	// Waived are the controls a human has excused, with who excused them and
	// until when. A waiver is a governed exception rather than a pass, so it is
	// reported separately: an auditor's first question about a green gate is
	// which of it was waived.
	Waived []Control `json:"waived,omitempty"`
	// Attestations counts the evidence on the trail.
	Attestations struct {
		Total        int `json:"total"`
		NonCompliant int `json:"non_compliant"`
	} `json:"attestations"`
	// Approvals is who signed off, and whether that satisfied four-eyes.
	Approvals struct {
		Count          int      `json:"count"`
		HumanApprovers int      `json:"human_approvers"`
		FourEyes       bool     `json:"four_eyes"`
		Approvers      []string `json:"approvers,omitempty"`
		Deployers      []string `json:"deployers,omitempty"`
	} `json:"approvals"`
	// SoD is Fides' segregation-of-duties finding: committer, approver and
	// deployer must be three distinct people.
	SoD *SegregationOfDuties `json:"segregation_of_duties,omitempty"`
	// Summary is Fides' own sentence about the verdict.
	Summary string `json:"summary,omitempty"`
}

ChangeVerdict is the evidence-backed change-approval decision for a trail.

func (ChangeVerdict) Blockers

func (v ChangeVerdict) Blockers() []string

Blockers lists what is standing in the way, for a message a human can act on.

func (ChangeVerdict) Held

func (v ChangeVerdict) Held() bool

Held reports whether the verdict withholds approval.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a Fides API client.

func New

func New(cfg Config) (*Client, error)

New builds a client.

func (*Client) Allowlisted

func (c *Client) Allowlisted(ctx context.Context, environment, sha256 string) (bool, error)

Allowlisted reports whether an artifact digest is approved for an environment. The digest is the bare sha256 hex, as Fides stores it.

func (*Client) Assert

func (c *Client) Assert(ctx context.Context, sha256, policy string) (*Compliance, error)

Assert checks an artifact digest against a named policy. The policy may be empty, which asks Fides for every policy that applies.

func (*Client) Attest

func (c *Client) Attest(ctx context.Context, trail string, a Attestation) error

Attest records evidence on a trail.

func (*Client) ChangeGate

func (c *Client) ChangeGate(ctx context.Context, trail string) (*ChangeVerdict, error)

ChangeGate reads the change-approval verdict for a trail.

func (*Client) PolicyCheck

func (c *Client) PolicyCheck(ctx context.Context, environment, trail string) (*PolicyVerdict, error)

PolicyCheck evaluates an environment's policy against a trail.

Both identifiers are UUIDs: the environment comes from the Gate, the trail from the crossing being judged.

func (*Client) RecordApproval

func (c *Client) RecordApproval(ctx context.Context, trail string, a Approval) error

RecordApproval records a sign-off on a trail, so Fides can evaluate segregation of duties over the identities involved.

Fides derives its verdict from the trail's committer tag plus the recorded approvals: committer, approver and deployer must be three distinct people. It treats a missing role as non-compliant rather than absent, so recording only one of them leaves the change gate holding for a reason that reads like a policy failure.

func (*Client) ReportArtifact

func (c *Client) ReportArtifact(ctx context.Context, a Artifact) error

ReportArtifact records an artifact against a trail.

**It refuses to report without a trail, and that is a safety rule rather than validation.** Fides upserts on the digest with `ON CONFLICT (sha256) DO UPDATE SET trail_id = EXCLUDED.trail_id`, so reporting a digest with an empty trail would overwrite the link CI made when it attached the SBOM and the scans — silently detaching exactly the evidence a change gate exists to read. An artifact Hecate cannot link is one it should leave alone.

func (*Client) TrailForArtifact

func (c *Client) TrailForArtifact(ctx context.Context, sha256 string) (string, error)

TrailForArtifact finds the trail an artifact digest was built on.

The trail that matters already exists: CI opened it when it built the image and recorded the SBOM and scan attestations there. Those attestations are exactly what an environment policy and the change gate judge, so a crossing has to gate on *that* trail. A trail Hecate opened fresh would carry none of them, and both checks would refuse every promotion — a gate that always says no is a gate somebody switches off.

Returns "" when Fides has never seen the digest, which is not an error: it means CI did not register the artifact, and the caller decides whether that is disqualifying.

ponytail: Fides has no by-digest lookup that also returns the trail — its /search/artifacts filters by sha but omits trail_id, and /artifacts returns trail_id but takes no filter — so this reads the org's artifacts and matches here. One call per crossing.

The ceiling is lower than "grows with the artifact count" suggests, which is what this comment used to say: /artifacts also runs a per-row query for each artifact's SBOM and embeds the payload, so the response is the size of every SBOM in the organisation. Hundreds of megabytes is reachable, to learn one 36-byte trail id.

The cheap upgrade is upstream and two lines: /search/artifacts already filters by sha, already has LIMIT 100 and already joins trails, so adding a.trail_id to its SELECT is the whole change. This function then becomes one filtered call with no loop. Tracked in #111.

func (*Client) VerifyChain

func (c *Client) VerifyChain(ctx context.Context, trail string) (*Chain, error)

VerifyChain checks a trail's tamper-evidence chain.

type Compliance

type Compliance struct {
	Compliant  bool     `json:"compliant"`
	Violations []string `json:"violations,omitempty"`
}

Compliance is the answer to `assert`: does this artifact satisfy a policy?

type Config

type Config struct {
	// BaseURL is the server root, e.g. https://fides.acme.io. Required: there
	// is no public Fides to default to.
	BaseURL string
	// Token is a Fides API key, sent as a bearer token.
	Token string
	// Timeout bounds a single call. Zero uses 30s.
	Timeout time.Duration
}

Config is what a client needs to reach a Fides server.

type Control

type Control struct {
	Key  string `json:"control"`
	Name string `json:"name"`
	// Reasons are Fides' own phrasings, e.g. "missing sbom".
	Reasons []string `json:"reasons,omitempty"`
	// WaivedReasons is what the waiver excused, present only on Waived.
	WaivedReasons []string `json:"waived_reasons,omitempty"`
	Reason        string   `json:"reason,omitempty"`
	ApprovedBy    string   `json:"approved_by,omitempty"`
	ExpiresAt     string   `json:"expires_at,omitempty"`
}

Control is one control the change gate judged.

Sent as an object rather than a bare key because the reasons are the useful part: "CC7.2" is a code to look up, "CC7.2 Vulnerability scanning: failed vuln-scan" is a thing to go and fix.

func (Control) Describe

func (c Control) Describe() string

Describe names the control the way a person would read it.

type Error

type Error struct {
	Status int
	Method string
	Path   string
	Body   string
}

Error is a refusal from the server, with the status kept so a caller can tell a rejected token from a trail that is not there.

func (*Error) Error

func (e *Error) Error() string

type PolicyResult

type PolicyResult struct {
	Policy string `json:"policy"`
	// Applies is false when the policy is conditional on a flow tag the trail
	// does not carry. A policy that did not apply is not a policy that passed.
	Applies bool `json:"applies"`
	// Missing lists the attestation types the policy required and the trail
	// does not have a compliant one of.
	Missing []string `json:"missing,omitempty"`
}

PolicyResult is one environment policy's verdict on the trail.

type PolicyVerdict

type PolicyVerdict struct {
	Compliant bool           `json:"compliant"`
	TrailID   string         `json:"trail_id,omitempty"`
	Results   []PolicyResult `json:"results,omitempty"`
}

PolicyVerdict is the answer to "does this trail satisfy the environment's policy?"

Field names mirror Fides' own response. `compliant` rather than `passed`, and a per-policy breakdown rather than a flat list of violations, because a Gate that refuses a crossing has to be able to say which policy refused it.

func (PolicyVerdict) Unmet

func (v PolicyVerdict) Unmet() []string

Unmet lists the policies that refused, so a failure can name them.

type SegregationOfDuties

type SegregationOfDuties struct {
	Committer  string   `json:"committer,omitempty"`
	Approvers  []string `json:"approvers,omitempty"`
	Deployers  []string `json:"deployers,omitempty"`
	Compliant  bool     `json:"compliant"`
	Violations []string `json:"violations,omitempty"`
}

SegregationOfDuties is Fides' four-eyes finding for a trail.

Jump to

Keyboard shortcuts

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