Help us shape the future of Go! Take the 2026 Go Developer Survey

plans

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package plans is a lock's store of plans awaiting a decision: one file per plan in the lock's volume (0600 in a 0700 directory), surviving restarts. A plan is bound to the peer UID that made it, the requesting agent's token, the policy fingerprint and the plan hash; it runs once.

The store keeps the product's plan as an opaque body: the product recomputes the plan hash from it before running (Check), and runs nothing whose binding moved.

One Store owns a store directory: Open locks it (a second Open fails with ErrLocked until Close), so that a decision nonce is accepted once whatever the number of consoles on a volume. A Store is safe for concurrent use.

Index

Constants

View Source
const (
	// MaxPlans bounds the live (not final) plans of a store.
	MaxPlans = 1000
	// MaxBody bounds a plan's body.
	MaxBody = 1 << 20
	// Retention keeps a final plan this long before Prune removes it.
	Retention = 24 * time.Hour
)

Defaults.

Variables

View Source
var (
	// ErrNoSuchPlan: unknown, pruned or expired plan id.
	ErrNoSuchPlan = errors.New("plans: no such plan")
	// ErrWrongPeer: the plan was made by another peer UID.
	ErrWrongPeer = errors.New("plans: plan made by another peer")
	// ErrWrongRequester: the plan was made by another agent (token).
	ErrWrongRequester = errors.New("plans: plan made by another agent")
	// ErrStale: the plan hash or policy fingerprint moved since planning.
	ErrStale = errors.New("plans: plan is stale")
	// ErrUsed: the plan already ran, or is in a final state.
	ErrUsed = errors.New("plans: plan already used")
	// ErrReplay: the decision nonce was consumed before.
	ErrReplay = errors.New("plans: decision nonce already consumed")
	// ErrState: the transition is not allowed from the plan's state.
	ErrState = errors.New("plans: transition not allowed")
	// ErrFull: the store holds MaxPlans live plans.
	ErrFull = errors.New("plans: too many plans")
	// ErrUnsafe: the store directory or a file in it has unsafe
	// permissions; nothing is loaded.
	ErrUnsafe = errors.New("plans: unsafe permissions")
	// ErrLocked: another Store (another console on the same volume) has
	// the directory open.
	ErrLocked = errors.New("plans: the store is open in another process")
)

Functions

func HashToken

func HashToken(token string) string

HashToken is the stored form of an agent token: hex SHA-256. The token itself is never written.

Types

type Binding

type Binding struct {
	UID int `json:"uid"`
	// Agent is the requesting agent's label (display only).
	Agent string `json:"agent"`
	// TokenHash is HashToken of the agent's token ("" without one).
	TokenHash string `json:"token_hash"`
	PolicyFP  string `json:"policy_fp"`
	PlanHash  string `json:"plan_hash"`
}

Binding is what a plan is bound to.

type Caller

type Caller struct {
	UID int
	// Token is the agent's lea token (LEA_AGENT_TOKEN), "" without lea.
	Token string
}

Caller is who presents a plan id.

type Option

type Option func(*Store)

Option configures Open.

func WithClock

func WithClock(now func() time.Time) Option

WithClock sets the store's clock (tests).

type Plan

type Plan struct {
	ID      string  `json:"id"`
	State   State   `json:"state"`
	Binding Binding `json:"binding"`
	// Ticket is the hub ticket id, once pending, and SignedTicket the
	// ticket as the lock signed and pushed it: a decision is matched
	// against it (ticket.Decision.Match), never against one rebuilt.
	Ticket       string          `json:"ticket,omitempty"`
	SignedTicket json.RawMessage `json:"signed_ticket,omitempty"`
	// Nonce is the decision nonce consumed when the plan began executing.
	Nonce string `json:"nonce,omitempty"`
	// Reason says why the plan reached its final state (denial reason,
	// failure, what went stale).
	Reason  string     `json:"reason,omitempty"`
	Created time.Time  `json:"created"`
	Updated time.Time  `json:"updated"`
	Expires *time.Time `json:"expires,omitempty"`
	// Body is the product's plan, opaque to the store.
	Body json.RawMessage `json:"body"`
}

Plan is one stored plan.

func (*Plan) LockTicket

func (p *Plan) LockTicket(pub ed25519.PublicKey) (*ticket.Ticket, error)

LockTicket decodes the signed ticket of a pending plan, verified under the lock's own key pub (the store is the lock's, but a file is only trusted once its signature checks).

type State

type State string

State of a plan.

const (
	// Planned: made, never run.
	Planned State = "planned"
	// Pending: run asked, a ticket awaits the human.
	Pending State = "pending"
	// Executing: a decision was accepted and consumed; the plan is running.
	Executing State = "executing"
	Executed  State = "executed"
	Failed    State = "failed"
	// Stale: the binding moved (plan hash, policy) before it ran.
	Stale     State = "stale"
	Denied    State = "denied"
	Withdrawn State = "withdrawn"
	Expired   State = "expired"
)

func (State) Final

func (s State) Final() bool

Final reports whether s is a final state: nothing leaves it.

type Store

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

Store is a directory of plans.

func Open

func Open(dir string, opts ...Option) (*Store, error)

Open opens or creates the store in dir (created 0700). It refuses a directory, plan or nonce file readable or writable by group or others, and loads every plan.

func (*Store) Begin

func (s *Store) Begin(id string, c Caller, nonce string) error

Begin starts executing plan id under the decision nonce: single use. The nonce is recorded durably before the plan changes state; a nonce seen before is ErrReplay, a plan not Planned or Pending is ErrUsed. Without a hub (standalone), nonce is "".

func (*Store) ByTicket

func (s *Store) ByTicket(ticketID string, c Caller) (*Plan, error)

ByTicket returns the plan waiting on ticket id, for caller c.

func (*Store) Check

func (s *Store) Check(id string, c Caller, policyFP, planHash string) error

Check compares a plan's binding with what the product recomputed just before running it: a different policy fingerprint or plan hash marks the plan Stale and returns ErrStale.

func (*Store) Close

func (s *Store) Close() error

Close releases the store directory for another Open. The Store must not be used after it.

func (*Store) Create

func (s *Store) Create(b Binding, body json.RawMessage, ttl time.Duration) (*Plan, error)

Create stores a new Planned plan bound to b, with the product's body, expiring after ttl unless run (0: never).

func (*Store) End

func (s *Store) End(id string, c Caller, st State, reason string) error

End moves a plan that has not begun to a final state: Denied, Withdrawn, Stale or Expired, with reason. It is how the lock records a decision other than an approval, or a ticket withdrawn.

func (*Store) FindPending

func (s *Store) FindPending(c Caller, planHash string) (*Plan, bool)

FindPending returns the pending plan of caller c (peer UID and agent token) with planHash, if any: a second run of the same plan reuses its ticket instead of opening a new one.

func (*Store) Finish

func (s *Store) Finish(id string, c Caller, ok bool, reason string) error

Finish ends an executing plan: Executed, or Failed with reason.

func (*Store) Get

func (s *Store) Get(id string, c Caller) (*Plan, error)

Get returns plan id as caller c may see it: same peer UID, same agent token. An expired plan is ErrNoSuchPlan.

func (*Store) MarkPending

func (s *Store) MarkPending(id string, c Caller, t *ticket.Ticket) error

MarkPending records that plan id waits on t, the ticket the lock signed for it, until t.Expires (nil: no expiry). t must carry the plan's hash and policy fingerprint.

func (*Store) Pending

func (s *Store) Pending() []*Plan

Pending lists the pending plans, oldest first.

func (*Store) Prune

func (s *Store) Prune() (int, error)

Prune expires plans whose time is up and removes final plans older than Retention. Their nonces stay recorded. It returns how many it removed.

func (*Store) WithdrawAll

func (s *Store) WithdrawAll(reason string) ([]*Plan, error)

WithdrawAll ends every pending plan as Withdrawn (a lock starting without its hub) and returns them.

Jump to

Keyboard shortcuts

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