controller

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Overview

Package controller is the only authority that changes what the gateway enforces: it owns the database and the audit log of a deployment, compiles policy generations and publishes them, runs Confirmed Policy Activation and Temporary Policy Overrides with their deadlines and startup recovery, and publishes trust keys, blocked accounts and blocked addresses.

Every change goes through one of its exported operations, which serialise under one lock, record a durable transition and audit it; publishing a generation, committing a transition and settling a deadline are unexported. Publish, which installs a whole policy without activation, exists for tests and the fault-injection build's test route only. The controller never serves the data plane and never reads the clock except through compile's trusted time. The gateway wiring and the Admin API call it.

Index

Constants

View Source
const (
	DefaultAuditWait = 2 * time.Second
	MinAuditBytes    = 4 << 10
	MaxAuditBytes    = 1 << 30
	MaxAuditFiles    = 1000
	DefaultIdle      = 15 * time.Minute
	DefaultLifetime  = 8 * time.Hour
	MinSession       = time.Second
	MaxSession       = 24 * time.Hour
)

Defaults and bounds of the controller's settings, which the configuration file check applies too.

View Source
const ShutdownWait = 5 * time.Second

ShutdownWait bounds how long Stop waits for admin requests in progress; the native gateway waits that long for the requests of both its listeners together.

Variables

This section is empty.

Functions

func CandidateGeneration

func CandidateGeneration(p policy.Policy, parent int64, overrides []store.Override) (*compile.Generation, error)

effectiveGeneration compiles the active base with the active overrides into one generation. It expires at the earliest of the provisional deadline (if any) and the overrides' expiry times, so a generation never serves past the first of them. Its identity names the base revision and every override. CandidateGeneration compiles p, stored with parent revision parent, with overrides applied, as an activation would publish it; it publishes nothing.

Types

type Change

type Change struct {
	ID       string          `json:"transition_id"`
	Expected int64           `json:"expected_revision"`
	Policy   json.RawMessage `json:"policy"`
}

Change is one whole-policy publication, used by the test publication hook. The transition ID makes retries safe: a repeated ID returns the recorded outcome instead of applying again.

type Config

type Config struct {
	Database             string
	AuditLog             string
	AdminListen          string
	AdminIdleTimeout     time.Duration
	AdminSessionLifetime time.Duration
	AuditTimeout         time.Duration
	AuditMaxBytes        int64
	AuditMaxFiles        int
	JWT                  pipeline.TrustConfig

	// ReloadTrust reads the trust settings again from the startup
	// configuration, never from HTTP; nil refuses every key reload.
	ReloadTrust func() (pipeline.TrustConfig, error)
}

Config is the controller's configuration. Zero limits take their defaults.

func (Config) Check

func (cfg Config) Check() error

Check checks the settings the controller needs before it opens anything.

type Controller

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

Controller is the only component that changes the effective policy. It owns the database, compiles generations and publishes them by atomic pointer swap. Request handlers only read the current pointer.

Prepare creates it, Open opens its deployment, Start serves its Admin API and Stop releases everything it owns. The application that calls them owns its lifecycle.

func Prepare

func Prepare(cfg Config, logger *slog.Logger) (*Controller, error)

Prepare checks cfg and builds the controller, opening nothing; its errors are about the settings only.

func (*Controller) Activate

func (c *Controller) Activate(ctx context.Context, t store.TransitionRecord, candidate int64, checksum string, seconds int64) (Outcome, int)

Activate makes a validated candidate the provisional active base. The candidate must have been prepared against the current confirmed revision, which becomes its rollback target, and its checksum must match the one the administrator confirmed. It is decoded and compiled again in full before anything is committed. Its deadline is persisted with the activation, so a restart can neither extend nor forget it, and it is never extended.

func (*Controller) Addresses

func (c *Controller) Addresses() *store.BlockedAddresses

Addresses returns the published blocked address list.

func (*Controller) Admission

func (c *Controller) Admission() string

Admission reports whether protected admission can run at this moment: "ready" when a published generation is unexpired and the audit log accepts acknowledgements, otherwise why it cannot (the stop, the deployment mode, "unavailable", "generation_expired" or "audit_failed"). A ready gateway may still refuse every request its policy denies, and the next request may still fail; this is an observation, not a promise.

func (*Controller) Audit

func (c *Controller) Audit() *audit.AuditLog

Audit returns the audit log the controller opened; it stays open until the stop.

func (*Controller) BeginStop

func (c *Controller) BeginStop()

BeginStop ends protected admission and every change of authority: the published generation is withdrawn, so a request that has not captured one yet is refused; mutations and the deadline timer change nothing any more; Start is refused. A request that captured a generation earlier runs to its end with it. Stop begins with it; the native gateway calls it before it stops its data plane.

func (*Controller) Block

Block adds t's subject to the blocked accounts, or keeps an entry with the same reason, and records t's receipt. Each method here sets t's kind itself, so a caller cannot run one change under another's name.

func (*Controller) BlockAddress

BlockAddress adds entry's range to the blocked address list.

func (*Controller) Blocked

func (c *Controller) Blocked() *store.BlockedAccounts

Blocked returns the published blocked accounts.

func (*Controller) Capture

Capture reads the generation in force and the stored authority together, under the lock, without settling a deadline or publishing anything. The generation is nil when none is in force or it expired; the authority is then not read.

func (*Controller) ChangePassword

func (c *Controller) ChangePassword(ctx context.Context, admin store.Administrator, passwordHash string) (Outcome, int)

ChangePassword replaces an administrator's password, provided the credential is still the one the caller verified, and ends every session of that administrator.

func (*Controller) CreateOverride

func (c *Controller) CreateOverride(ctx context.Context, t store.TransitionRecord, request OverrideRequest) (Outcome, int)

CreateOverride creates and publishes a temporary override of the confirmed base. Nothing is committed unless every check passes and the complete effective policy with the override compiles.

func (*Controller) Current

func (c *Controller) Current() *compile.Generation

Current returns the published generation, or nil when nothing may be served.

func (*Controller) Enrol

func (c *Controller) Enrol(ctx context.Context, e store.Enrolment, passwordHash string) (Outcome, int)

Enrol consumes an enrolment and stores the administrator's new credential. The password hash is computed by the caller, outside the controller lock. Every existing session of the administrator ends.

func (*Controller) EstablishBase

func (c *Controller) EstablishBase(ctx context.Context, kind, actor string, candidate int64, checksum string) (Outcome, int)

EstablishBase confirms a validated deny-all candidate as the first confirmed base of an installation (genesis) or as the base that ends a declared recovery. Each has one server-chosen identity, so a retried or duplicated request can never create a second base.

func (*Controller) Expired

func (c *Controller) Expired(g *compile.Generation)

Expired is called by the request path when the generation it captured has expired. It starts at most one check at a time, so a late or missing timer cannot keep the gateway refusing every request.

func (*Controller) Gauges

func (c *Controller) Gauges() (revision int64, overrides, blocked int)

Gauges reads the published policy revision, active temporary rules and blocked accounts for the metrics listener.

func (*Controller) Observations

func (c *Controller) Observations() *monitor.Observations

Observations returns the in-memory observations of requests and events.

func (*Controller) Open

func (c *Controller) Open() error

Open takes the deployment lock, opens the database and the audit log and publishes the effective policy; it opens no listener, Start does. Its errors are store errors (store.ErrHeld when another process holds the deployment); on one, nothing it opened is left open. A database without usable authority is not an error: the controller serves no protected request, and its health and audit log say why.

func (*Controller) Publish

func (c *Controller) Publish(ctx context.Context, actor string, change Change) (Outcome, int)

Publish applies a complete policy as the new confirmed base. Production binaries expose no route to it: whole-policy activation belongs to the confirmed-activation lifecycle, and only the test publication hook calls it. It cannot create a provisional base.

func (*Controller) Reads

func (c *Controller) Reads() store.Reads

Reads returns the store's read methods; nil while no database is open.

func (*Controller) ReloadKeys

ReloadKeys reads the verification keys again and publishes them when the issuer and audience are unchanged.

func (*Controller) Resolve

func (c *Controller) Resolve(ctx context.Context, t store.TransitionRecord, provisional int64, disposition string) (Outcome, int)

Resolve confirms or rolls back the current provisional base on an administrator's explicit request. The request names the provisional revision it means and the confirmed revision it was prepared against; a request for any other state is refused. At or after the deadline neither is possible: the request is refused and the automatic rollback runs instead, however late its timer is.

func (*Controller) RevokeOverride

func (c *Controller) RevokeOverride(ctx context.Context, t store.TransitionRecord, id int64) (Outcome, int)

RevokeOverride ends an active override on an administrator's request. It is allowed while a provisional base is pending. An override that has already ended, including one whose expiry time has passed, keeps its recorded disposition.

func (*Controller) Sessions

func (c *Controller) Sessions() *session.Table

Sessions returns the administrator sessions, which the controller revokes when an administrator's credentials change.

func (*Controller) Settings

func (c *Controller) Settings() Config

Settings returns the configuration with its defaults applied.

func (*Controller) StageCandidate

func (c *Controller) StageCandidate(ctx context.Context, t store.TransitionRecord, document []byte) (Outcome, int)

StageCandidate validates a complete policy with the canonical compiler and stores it as an immutable candidate. Nothing is published: the effective policy and its generation are unchanged.

func (*Controller) Start

func (c *Controller) Start(admin http.Handler) error

Start serves admin, the Admin API, on its loopback listener. Protected admission does not wait for it: it follows the generation published since Open. Start succeeds at most once, and never once Stop has begun.

func (*Controller) State

func (c *Controller) State() string

State returns the deployment mode, or the reason no authority is served.

func (*Controller) Stop

func (c *Controller) Stop() error

Stop begins the stop (see BeginStop), shuts the Admin API down, waiting at most ShutdownWait for admin requests in progress, then closes the audit log, waiting at most auditDrainWait for the events queued until then to be written, and the database. It returns nil only when every step completed. It also releases a controller that was never started. Stop may be called any number of times, also concurrently: the controller stops once, and every call returns that stop's result.

func (*Controller) StopWithin

func (c *Controller) StopWithin(ctx context.Context) error

StopWithin is Stop with ctx bounding the wait for admin requests in progress; the native gateway shares that bound with its data plane.

func (*Controller) Unblock

Unblock removes t's subject from the blocked accounts.

func (*Controller) UnblockAddress

func (c *Controller) UnblockAddress(ctx context.Context, t store.TransitionRecord, entry store.BlockedAddress) (json.RawMessage, int)

UnblockAddress removes entry's range from the blocked address list.

func (*Controller) ValidateOverride

func (c *Controller) ValidateOverride(ctx context.Context, actor string, expected int64, request OverrideRequest) (Outcome, int)

ValidateOverride answers whether a proposed override would be created now, without creating it. It runs every check creation runs, including compiling the effective policy, and records the answer in the audit log only.

func (*Controller) Verifier

func (c *Controller) Verifier() *pipeline.Verifier

Verifier returns the published token verifier.

func (*Controller) WithdrawCandidate

func (c *Controller) WithdrawCandidate(ctx context.Context, t store.TransitionRecord, id int64) (Outcome, int)

WithdrawCandidate marks a validated candidate withdrawn.

type Outcome

type Outcome struct {
	Transition     string    `json:"transition_id"`
	Outcome        string    `json:"outcome"`
	Reason         string    `json:"reason,omitempty"`
	Field          string    `json:"field,omitempty"`
	Revision       int64     `json:"revision,omitempty"`
	Generation     string    `json:"generation,omitempty"`
	Candidate      int64     `json:"candidate,omitempty"`
	Provisional    int64     `json:"provisional,omitempty"`
	RollbackTarget int64     `json:"rollback_target,omitempty"`
	Deadline       time.Time `json:"deadline,omitzero"`
	Disposition    string    `json:"disposition,omitempty"`
	// For a temporary override: its identifier, the control and target it
	// governs and its expiry time. Override also names the active override a
	// refused candidate conflicts with.
	Override int64     `json:"override,omitempty"`
	Target   string    `json:"target,omitempty"`
	Expires  time.Time `json:"expires_at,omitzero"`
}

Outcome is the controller receipt for a mutation. For a provisional base, Revision is the active base after the transition, Provisional the revision the lifecycle concerns and RollbackTarget the confirmed revision it returns to.

type OverrideRequest

type OverrideRequest struct {
	Type             string  `json:"type"`
	Control          string  `json:"control"`
	Operation        string  `json:"operation"`
	Role             *string `json:"role"`
	Count            *int64  `json:"count"`
	ExpiresInSeconds int64   `json:"expires_in_seconds"`
}

OverrideRequest is an administrator's proposed override. Role and Count are pointers so that a member the control does not take is refused rather than ignored.

Jump to

Keyboard shortcuts

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