Documentation
¶
Overview ¶
Package specgate decides whether somebody may write a spec revision, for the parts of a spec that app.spec.edit alone does not cover: an app's egress rules (R-182 – R-184), its resource limits (R-241) and automatic deploys on an app whose deploys need approval (R-158).
One function, called by every path that writes a revision from content the caller supplied, so that no surface — console, CLI, MCP — can save a spec another surface would refuse (R-261). Paths that copy the pinned spec and change something else (routing, a slot, a volume, a pinned commit) carry its egress, limits and auto-deploy settings unchanged and need nothing from here.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Approval ¶
type Approval struct {
Required bool `json:"required"`
Reasons []ApprovalReason `json:"reasons"`
}
Approval is whether deploying a spec would need approval, and why (R-154). What the plan shows before anybody asks for the deploy.
type ApprovalReason ¶
type ApprovalReason struct {
Reason approval.Reason `json:"reason"`
Message string `json:"message"`
}
ApprovalReason is one reason a deploy needs approval, with what it means for the person reading the plan.
type Authorizer ¶
type Authorizer interface {
Allows(ctx context.Context, p authz.Principal, appID string, verb authz.Verb) (bool, error)
CheckControl(ctx context.Context, p authz.Principal, appID string, verb authz.Verb) error
}
Authorizer is the two questions the gate asks. *authz.Authorizer answers both: Allows without auditing, CheckControl auditing a denial (design 06 §6).
func Quiet ¶
func Quiet(az Previewer) Authorizer
Quiet is an Authorizer for a dry run: every question is answered as it would be for a real save, and no refusal is written to the audit log, because nothing was attempted.
type Change ¶
type Change struct {
AppID string
// Policy is host policy as it stands now. The gate judges a change by the
// rules in force when it is saved; the planner judges the deploy again
// under the rules in force then (R-183).
Policy policy.Document
// Pinned is the app's pinned spec, nil for an app that has none yet. A
// change is measured from it.
Pinned *spec.AppSpec
// Next is the revision being written.
Next *spec.AppSpec
// Latest is the app's newest revision, or nil. Limits it already sets are
// not a change somebody makes by keeping them: they were allowed when it
// was written (R-241).
Latest *spec.AppSpec
// HostResources is the installation's default limits (R-240). An app
// running on exactly these overrides nothing, so the first revision,
// written with the defaults, asks nothing, and nor does a spec that leaves
// its limits out after the host's defaults have changed.
HostResources spec.Resources
}
Change is one revision somebody wants to write.
type Previewer ¶
type Previewer interface {
Allows(ctx context.Context, p authz.Principal, appID string, verb authz.Verb) (bool, error)
PreviewControl(ctx context.Context, p authz.Principal, appID string, verb authz.Verb) error
}
Previewer answers CheckControl's question without auditing a denial. *authz.Authorizer is one, through PreviewControl.
type Result ¶
type Result struct {
// EgressChanged is whether the change moves the app's egress at all.
EgressChanged bool `json:"egress_changed"`
// Loosenings is what the change loosens that the pinned spec did not
// (policy.NewLoosenings).
Loosenings []policy.Loosening `json:"loosenings,omitempty"`
// NeedsApproval is whether those loosenings make the app's next deploy
// need approval (R-154): policy gates loosening by approval.
NeedsApproval bool `json:"needs_approval"`
}
Result is what the gate found about a change it allowed.
func Check ¶
Check refuses a change the principal may not make, and otherwise says what it found.
- Any change to egress needs app.egress.tighten or app.egress.loosen (R-184). Holding app.egress.loosen covers every egress change, so it is asked first and without auditing a denial: somebody who holds loosen and not tighten is not denied anything.
- A loosening the pinned spec did not already carry is judged by policy (R-183): forbidden refuses it, verb needs app.egress.loosen, approval lets it through for the deploy to be approved.
- Any change to the app's resource limits, or to a workload's own, needs app.resources.override (R-241, R-241a).
- Automatic deploys are refused on an app whose deploys need approval whatever they change (R-158).