gate

package
v0.59.0 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 4 Imported by: 0

README

gate

import "github.com/kitsunium/sdk/framework/internal/core/gate"

Package gate — the dotted-quad code range this domain owns.

Package gate — what the gate decided, and why.

Package gate — the one refusal this domain mints.

It is one rather than several on purpose. Every fault a policy can carry is the same kind of fault — somebody wrote a policy that cannot do its job — and Validate joins them so a caller fixing one sees all of them. Splitting the range would give a caller several codes to branch on for a condition with a single response: fix the policy and restart.

Package gate decides whether one invocation of a distributed binary may run.

A product that verifies an entitlement at start-up has three questions to answer before it does any work, and they are usually answered by hand, in a root command, in code nobody revisits:

  • Is THIS invocation subject to the check at all? `version` and `help` carry nothing to protect, and a command that repairs the licence must stay reachable when the licence is what is broken.
  • The vendor mandates a newer build. Refuse, upgrade, or warn?
  • When the answer is no, what should the process do about it?

This package owns the first two as VALUES and leaves the third entirely to the caller.

What it does not do

It does not verify anything — that is `entitlement`. It does not upgrade anything — that is `selfupdate`. And it never ends your process: a library that decides on your behalf whether the process should still be alive makes the decision untestable and skips every deferred function, which is the same reason `cli` refuses flag.ExitOnError.

The service's Decide is a pure classification. The caller runs the verifier, reads the decision, and performs whatever the decision names.

The exemption list is the security-relevant part

An exemption is a command that runs WITHOUT the check, so the list is an attack surface and a lockout risk at the same time — too wide and the gate is decoration, too narrow and a machine whose licence lapsed has no way back.

Two failure modes follow, and the shapes here address them separately.

A name-only match is too coarse. `skill` on its own prints install guidance and must not require a licence; `skill install` does real work and must. One list cannot express both, so there are two: [PolicyValue.ExemptExact] matches a whole path, [PolicyValue.ExemptSubtree] matches a path and everything under it.

And the recovery hatch is easy to lose. A policy that gates the very command an operator would run to repair a lapsed licence locks the machine out with no path back — reachable only by reinstalling the binary. That is not left to a comment: [PolicyValue.RecoveryPaths] names those commands, and PolicyValue.Validate REFUSES a policy in which any of them is gated.

Package gate — the policy value: which invocations are exempt, and what a mandated upgrade does.

Package gate — what a mandated upgrade does.

Index

Constants

const CodePolicyInvalid errs.Code = 140289

CodePolicyInvalid identifies a gate policy that could not be used as written — an unset update action, a recovery command that is gated, or no exemption at all. Every one of them is a construction fault, reported at start-up rather than at the first refused invocation.

Code 0.2.36.1

const OutcomeAllow Outcome = 1

OutcomeAllow runs the invocation. Either it was exempt, or the entitlement verified and no floor stood in the way.

const OutcomeRefuse Outcome = 2

OutcomeRefuse stops the invocation. DecisionValue.Cause carries the refusal the verifier produced, so the caller maps it to an exit status and an operator-facing sentence with errs.CodeOf or errors.Is.

const OutcomeUpgrade Outcome = 3

OutcomeUpgrade asks the caller to upgrade and re-run this invocation.

Reached only when the entitlement verified, the vendor mandates a newer build, and the policy said UpdateApply. A caller that cannot upgrade treats it as OutcomeRefuse with the same Cause.

const UpdateApply UpdateAction = 2

UpdateApply asks the caller to upgrade and re-run the invocation.

The gate does NOT perform the upgrade — see the package doc. It reports that the caller should, which keeps the network access, the signature verification and the process replacement in the caller's own control flow where they can be refused, logged or tested.

A caller acting on it must upgrade AT MOST ONCE per invocation: a floor no published release satisfies — a roster asking for v9.9.9 while the mirror serves v1.5.10 — otherwise upgrades to no effect, re-runs, is refused again and loops forever.

const UpdateRefuse UpdateAction = 1

UpdateRefuse stops the invocation and reports the floor. The caller tells the operator which version is required; nothing is downloaded and nothing is replaced.

This is the conservative answer and the right one when the binary may be running somewhere the operator did not put it.

const UpdateWarn UpdateAction = 3

UpdateWarn runs the invocation anyway and reports the floor alongside it.

It trades enforcement for availability on purpose, and it is the only action here that lets an out-of-date build keep working. Choose it when the floor is advice; choose UpdateRefuse when it is a requirement.

type DecisionValue

DecisionValue is what the gate concluded about one invocation.

It is a value and carries no method that acts: the caller performs whatever Outcome names. Nothing here ends a process, opens a socket or writes a file.

type DecisionValue struct {
	// Outcome is what to do.
	Outcome    Outcome
	// Cause is the refusal the verifier produced, or nil.
	//
	// It is carried rather than re-labelled so the caller keeps everything the
	// domain said: errors.Is answers on entitlement's sentinels and
	// errs.CodeOf on its codes, through this field, exactly as if the caller
	// had held the verifier's error itself. A gate that summarised the cause
	// would force every consumer to match on text.
	//
	// Non-nil with OutcomeUpgrade too: the floor refusal is what SAYS which
	// version is required, and a caller that upgrades needs to read it.
	Cause      error
	// Exempt records that this invocation was never checked, as opposed to
	// checked and allowed.
	//
	// The two are the same Outcome and a very different fact. A daemon that
	// seeds a watchdog from a verification must not seed it from an exemption:
	// no verification happened, so there is no grant, no deadline and nothing
	// to age against.
	Exempt     bool
	// FloorUnmet records that the vendor mandates a newer build, whatever the
	// policy decided to do about it.
	//
	// It is separate from Outcome because UpdateWarn allows the invocation and
	// the caller still has to say so. Reading Outcome alone would lose that.
	FloorUnmet bool
}

type Outcome

Outcome is what the caller should do with an invocation.

The zero value is unclaimed for the same reason UpdateAction's is: a decision nobody made must not read as "allow". A caller switching on this and falling through its cases gets OutcomeUnset, which is the refusing direction.

type Outcome uint8

func (Outcome) String
func (Outcome) String() string

String names the outcome for a diagnostic, spelling the unclaimed zero value "unset" rather than inventing a name for it.

type PolicyValue

PolicyValue is one product's gate policy: the commands that run unchecked, the commands that must never be gated, and what happens when the vendor mandates a newer build.

It is a VALUE, copied by the caller and never mutated by this package. The zero value is not usable — see Validate — because none of its three fields has a safe default: an empty exemption list locks out recovery, and neither "refuse" nor "upgrade" is a guess this package may make on a vendor's behalf.

type PolicyValue struct {
	// ExemptExact lists command paths, relative to the root, that run without
	// the check — matched WHOLE, so a same-named command elsewhere in the tree
	// is not silently exempted with them.
	//
	// The empty string is the bare root invocation: a binary run with no
	// subcommand shows its help, and showing help must not require an
	// entitlement any more than `help` itself does.
	//
	// Paths are space-joined, e.g. "skill install". A command name cannot
	// contain a space, so the joining is lossless.
	ExemptExact      []string
	// ExemptSubtree lists command paths whose whole subtree runs without the
	// check — the path itself and every command under it.
	//
	// Use it when the exemption is about a CAPABILITY rather than a command:
	// everything under `license` repairs the licence, so gating any of it would
	// be gating the repair. Use ExemptExact when a parent and its children
	// differ, which they usually do.
	ExemptSubtree    []string
	// RecoveryPaths lists the commands an operator runs to repair a refused
	// entitlement. Every one of them MUST be exempt, and Validate refuses a
	// policy where one is not.
	//
	// It exists because the failure it prevents is silent and total: a policy
	// that gates its own repair command leaves a machine whose licence lapsed
	// with no path back short of reinstalling the binary, and nothing about the
	// policy LOOKS wrong — the exemption list is simply missing an entry.
	// Naming them separately turns that into a construction-time refusal.
	RecoveryPaths    []string
	// OnUpdateRequired is what happens when the entitlement verifies but the
	// vendor mandates a newer build than this one.
	//
	// It has no default. See UpdateAction.
	OnUpdateRequired UpdateAction
}

func (PolicyValue) Exempt
func (*PolicyValue) Exempt(path []string) bool

Exempt reports whether the command at path runs without the check.

It takes the command path relative to the root, with the root itself NOT included. A nil path, an empty one, and a single empty element — which is what splitting an empty string yields — are all the bare root invocation.

func (PolicyValue) Validate
func (*PolicyValue) Validate() error

Validate reports every way this policy is unusable, rather than the first.

A caller fixing a policy wants the whole list: the checks are independent, and a policy corrected one refusal at a time takes as many start-ups as it has faults.

It returns ONE error naming them all rather than an errors.Join of several, and that is the difference between a promise and a delivered one: a joined error has no single Private detail, so errs.PrivateOf reports the first fault's and a structured-logging caller would still see them one at a time.

It returns one typed refusal whose private detail names every fault, or nil.

type UpdateAction

UpdateAction is what the gate does when the entitlement verifies but the vendor mandates a newer build than the running one.

The zero value is deliberately unclaimed. Every other field of PolicyValue has a defensible empty meaning — no exemptions, no recovery paths — but this one does not: refusing and upgrading are opposite answers, and a product that silently picked either on a vendor's behalf would be wrong for half of them. A binary distributed to machines an operator does not own must not replace itself uninvited; one distributed to a fleet that expects to track the floor must not stop working instead. Validate refuses the unset value rather than guessing (ADR 0031).

type UpdateAction uint8

func (UpdateAction) String
func (UpdateAction) String() string

String names the action for a diagnostic, spelling the unclaimed zero value "unset" rather than inventing a name for it.

func (UpdateAction) Valid
func (UpdateAction) Valid() bool

Valid reports whether this action was set to one of the three answers. It is false for the unclaimed zero value, and false for anything past the last action — a value from a newer build of this package that this one cannot honour.

Generated from docs/api by tools/genindex.

Documentation

Overview

Package gate — the dotted-quad code range this domain owns.

Package gate — what the gate decided, and why.

Package gate — the one refusal this domain mints.

It is one rather than several on purpose. Every fault a policy can carry is the same kind of fault — somebody wrote a policy that cannot do its job — and Validate joins them so a caller fixing one sees all of them. Splitting the range would give a caller several codes to branch on for a condition with a single response: fix the policy and restart.

Package gate decides whether one invocation of a distributed binary may run.

A product that verifies an entitlement at start-up has three questions to answer before it does any work, and they are usually answered by hand, in a root command, in code nobody revisits:

  • Is THIS invocation subject to the check at all? `version` and `help` carry nothing to protect, and a command that repairs the licence must stay reachable when the licence is what is broken.
  • The vendor mandates a newer build. Refuse, upgrade, or warn?
  • When the answer is no, what should the process do about it?

This package owns the first two as VALUES and leaves the third entirely to the caller.

What it does not do

It does not verify anything — that is `entitlement`. It does not upgrade anything — that is `selfupdate`. And it never ends your process: a library that decides on your behalf whether the process should still be alive makes the decision untestable and skips every deferred function, which is the same reason `cli` refuses flag.ExitOnError.

The service's Decide is a pure classification. The caller runs the verifier, reads the decision, and performs whatever the decision names.

The exemption list is the security-relevant part

An exemption is a command that runs WITHOUT the check, so the list is an attack surface and a lockout risk at the same time — too wide and the gate is decoration, too narrow and a machine whose licence lapsed has no way back.

Two failure modes follow, and the shapes here address them separately.

A name-only match is too coarse. `skill` on its own prints install guidance and must not require a licence; `skill install` does real work and must. One list cannot express both, so there are two: PolicyValue.ExemptExact matches a whole path, PolicyValue.ExemptSubtree matches a path and everything under it.

And the recovery hatch is easy to lose. A policy that gates the very command an operator would run to repair a lapsed licence locks the machine out with no path back — reachable only by reinstalling the binary. That is not left to a comment: PolicyValue.RecoveryPaths names those commands, and PolicyValue.Validate REFUSES a policy in which any of them is gated.

Package gate — the policy value: which invocations are exempt, and what a mandated upgrade does.

Package gate — what a mandated upgrade does.

Index

Constants

View Source
const CodePolicyInvalid errs.Code = 0x00_02_24_01 // 0.2.36.1

CodePolicyInvalid identifies a gate policy that could not be used as written — an unset update action, a recovery command that is gated, or no exemption at all. Every one of them is a construction fault, reported at start-up rather than at the first refused invocation.

Variables

This section is empty.

Functions

This section is empty.

Types

type DecisionValue

type DecisionValue struct {
	// Outcome is what to do.
	Outcome Outcome
	// Cause is the refusal the verifier produced, or nil.
	//
	// It is carried rather than re-labelled so the caller keeps everything the
	// domain said: errors.Is answers on entitlement's sentinels and
	// errs.CodeOf on its codes, through this field, exactly as if the caller
	// had held the verifier's error itself. A gate that summarised the cause
	// would force every consumer to match on text.
	//
	// Non-nil with OutcomeUpgrade too: the floor refusal is what SAYS which
	// version is required, and a caller that upgrades needs to read it.
	Cause error
	// Exempt records that this invocation was never checked, as opposed to
	// checked and allowed.
	//
	// The two are the same Outcome and a very different fact. A daemon that
	// seeds a watchdog from a verification must not seed it from an exemption:
	// no verification happened, so there is no grant, no deadline and nothing
	// to age against.
	Exempt bool
	// FloorUnmet records that the vendor mandates a newer build, whatever the
	// policy decided to do about it.
	//
	// It is separate from Outcome because UpdateWarn allows the invocation and
	// the caller still has to say so. Reading Outcome alone would lose that.
	FloorUnmet bool
}

DecisionValue is what the gate concluded about one invocation.

It is a value and carries no method that acts: the caller performs whatever Outcome names. Nothing here ends a process, opens a socket or writes a file.

type Outcome

type Outcome uint8

Outcome is what the caller should do with an invocation.

The zero value is unclaimed for the same reason UpdateAction's is: a decision nobody made must not read as "allow". A caller switching on this and falling through its cases gets OutcomeUnset, which is the refusing direction.

const (
	// OutcomeAllow runs the invocation. Either it was exempt, or the
	// entitlement verified and no floor stood in the way.
	OutcomeAllow Outcome = iota + 1
	// OutcomeRefuse stops the invocation. DecisionValue.Cause carries the
	// refusal the verifier produced, so the caller maps it to an exit status
	// and an operator-facing sentence with errs.CodeOf or errors.Is.
	OutcomeRefuse
	// OutcomeUpgrade asks the caller to upgrade and re-run this invocation.
	//
	// Reached only when the entitlement verified, the vendor mandates a newer
	// build, and the policy said UpdateApply. A caller that cannot upgrade
	// treats it as OutcomeRefuse with the same Cause.
	OutcomeUpgrade
)

func (Outcome) String

func (o Outcome) String() string

String names the outcome for a diagnostic, spelling the unclaimed zero value "unset" rather than inventing a name for it.

type PolicyValue

type PolicyValue struct {
	// ExemptExact lists command paths, relative to the root, that run without
	// the check — matched WHOLE, so a same-named command elsewhere in the tree
	// is not silently exempted with them.
	//
	// The empty string is the bare root invocation: a binary run with no
	// subcommand shows its help, and showing help must not require an
	// entitlement any more than `help` itself does.
	//
	// Paths are space-joined, e.g. "skill install". A command name cannot
	// contain a space, so the joining is lossless.
	ExemptExact []string
	// ExemptSubtree lists command paths whose whole subtree runs without the
	// check — the path itself and every command under it.
	//
	// Use it when the exemption is about a CAPABILITY rather than a command:
	// everything under `license` repairs the licence, so gating any of it would
	// be gating the repair. Use ExemptExact when a parent and its children
	// differ, which they usually do.
	ExemptSubtree []string
	// RecoveryPaths lists the commands an operator runs to repair a refused
	// entitlement. Every one of them MUST be exempt, and Validate refuses a
	// policy where one is not.
	//
	// It exists because the failure it prevents is silent and total: a policy
	// that gates its own repair command leaves a machine whose licence lapsed
	// with no path back short of reinstalling the binary, and nothing about the
	// policy LOOKS wrong — the exemption list is simply missing an entry.
	// Naming them separately turns that into a construction-time refusal.
	RecoveryPaths []string
	// OnUpdateRequired is what happens when the entitlement verifies but the
	// vendor mandates a newer build than this one.
	//
	// It has no default. See UpdateAction.
	OnUpdateRequired UpdateAction
}

PolicyValue is one product's gate policy: the commands that run unchecked, the commands that must never be gated, and what happens when the vendor mandates a newer build.

It is a VALUE, copied by the caller and never mutated by this package. The zero value is not usable — see Validate — because none of its three fields has a safe default: an empty exemption list locks out recovery, and neither "refuse" nor "upgrade" is a guess this package may make on a vendor's behalf.

func (*PolicyValue) Exempt

func (p *PolicyValue) Exempt(path []string) bool

Exempt reports whether the command at path runs without the check.

It takes the command path relative to the root, with the root itself NOT included. A nil path, an empty one, and a single empty element — which is what splitting an empty string yields — are all the bare root invocation.

func (*PolicyValue) Validate

func (p *PolicyValue) Validate() error

Validate reports every way this policy is unusable, rather than the first.

A caller fixing a policy wants the whole list: the checks are independent, and a policy corrected one refusal at a time takes as many start-ups as it has faults.

It returns ONE error naming them all rather than an errors.Join of several, and that is the difference between a promise and a delivered one: a joined error has no single Private detail, so errs.PrivateOf reports the first fault's and a structured-logging caller would still see them one at a time.

It returns one typed refusal whose private detail names every fault, or nil.

type UpdateAction

type UpdateAction uint8

UpdateAction is what the gate does when the entitlement verifies but the vendor mandates a newer build than the running one.

The zero value is deliberately unclaimed. Every other field of PolicyValue has a defensible empty meaning — no exemptions, no recovery paths — but this one does not: refusing and upgrading are opposite answers, and a product that silently picked either on a vendor's behalf would be wrong for half of them. A binary distributed to machines an operator does not own must not replace itself uninvited; one distributed to a fleet that expects to track the floor must not stop working instead. Validate refuses the unset value rather than guessing (ADR 0031).

const (
	// UpdateRefuse stops the invocation and reports the floor. The caller
	// tells the operator which version is required; nothing is downloaded and
	// nothing is replaced.
	//
	// This is the conservative answer and the right one when the binary may be
	// running somewhere the operator did not put it.
	UpdateRefuse UpdateAction = iota + 1
	// UpdateApply asks the caller to upgrade and re-run the invocation.
	//
	// The gate does NOT perform the upgrade — see the package doc. It reports
	// that the caller should, which keeps the network access, the signature
	// verification and the process replacement in the caller's own control
	// flow where they can be refused, logged or tested.
	//
	// A caller acting on it must upgrade AT MOST ONCE per invocation: a floor
	// no published release satisfies — a roster asking for v9.9.9 while the
	// mirror serves v1.5.10 — otherwise upgrades to no effect, re-runs, is
	// refused again and loops forever.
	UpdateApply
	// UpdateWarn runs the invocation anyway and reports the floor alongside it.
	//
	// It trades enforcement for availability on purpose, and it is the only
	// action here that lets an out-of-date build keep working. Choose it when
	// the floor is advice; choose UpdateRefuse when it is a requirement.
	UpdateWarn
)

func (UpdateAction) String

func (a UpdateAction) String() string

String names the action for a diagnostic, spelling the unclaimed zero value "unset" rather than inventing a name for it.

func (UpdateAction) Valid

func (a UpdateAction) Valid() bool

Valid reports whether this action was set to one of the three answers. It is false for the unclaimed zero value, and false for anything past the last action — a value from a newer build of this package that this one cannot honour.

Jump to

Keyboard shortcuts

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