grantgw

package
v0.2.37 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MPL-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package grantgw is the grant gateway: the one place a request made with a run grant is decided. Work that was dispatched somewhere else (a command in a workspace) opens a SANDBOX — a named bundle, declared by the stack, of what a program may hold (chassis/sandbox). Whichever way it was opened — by the rule that started the command (`WITH grant, sandbox`) or by the program itself (`txco sandbox`) — the request arrives here, and for each secret the sandbox names the same things happen in the same order:

  1. HARD CHECKS, the chassis's alone. The grant is genuine and unexpired, its row is live, its generation is current, its tenant is live, and it names the sandbox. A request that fails one is refused and no stack runs.
  2. THE PROPOSAL, the chassis's own answer. The run grant names the secret, the principal holds a standing grant to release it, the secret's pull policy admits this node, budget remains. All yes: propose to allow.
  3. THE TENANT'S `_grant` STACK. The request and the proposal are presented to it as an ordinary pipeline run, so admission, tracing and usage apply as they do to every inlet. A rule may write `@grant.res.allow` either way. No rule writes it: the proposal stands.
  4. THE VERDICT. Refused: one answer, whatever the reason. Allowed: the budget is charged, and only then is the secret handed over.

A sandbox opens whole or not at all: one secret refused, and the program is handed nothing. What was already released is discarded; what was charged stays charged.

Everything fails closed. An error, a timeout or a refusal of admission in the run is a refusal, whatever was proposed.

The trace of the `_grant` run is the record of a request. The gateway keeps no journal of its own.

Index

Constants

View Source
const (
	ViaExec     = "exec"     // the rule that starts the command: `WITH grant, sandbox`
	ViaLauncher = "launcher" // the program itself: `txco sandbox`
	ViaHTTP     = "http"     // a capability call: POST /v1/cap/<name> with the token
)

The ways a request is made, stamped as `@grant.via`: a sandbox is opened by the rule that starts the command or by the program itself; a capability is called over HTTP (chassis/server/capgw).

View Source
const (
	EnvRun    = grantwire.EnvRun
	EnvToken  = grantwire.EnvToken
	EnvSocket = grantwire.EnvSocket
	EnvBin    = grantwire.EnvBin
)

The variables a command is handed with its run grant (chassis/grantwire owns the names: the launcher reads them too).

Variables

View Source
var ErrHandoff = errors.New("grant")

ErrHandoff wraps every reason a run grant cannot be handed to a command; the message says which.

Functions

func RunOnBus added in v0.2.33

func RunOnBus(ctx, life context.Context, bus chan<- *event.Envelope, payload string) (string, error)

RunOnBus sends one envelope through the bus and waits for its final payload: the round trip every inlet makes. Exported for the capability inlet, which runs the tenant's `_cap` stack the same way. The result channel is buffered and, when the wait is abandoned, drained to its end — the processor sends on it without a select, so an unread channel would hold its goroutine for good.

Types

type Answer

type Answer struct {
	Allowed bool
	// Env is what the sandbox hands over, variable → value, when Allowed.
	// The caller zeroes every value (secrets.Zero) once it has handed them
	// on.
	Env map[string][]byte
	// Unavailable: the chassis could not decide — a store was down, the
	// chassis is stopping. Not a refusal, and not to be retried forever.
	Unavailable bool
	// Reason is why not. For the log and for tests; never for the program.
	Reason string
}

Answer is what the presentation gets back. It tells the program one of three things and nothing more: what it holds, a refusal, or that the chassis could not answer.

func (Answer) Zero

func (a Answer) Zero()

Zero overwrites every value the answer holds.

type Call added in v0.2.33

type Call struct {
	Token string
	Name  string          // the capability, by its bare name
	Input json.RawMessage // what the caller sent, presented to the rules as data
}

Call is one capability call made with a run grant.

type Config

type Config struct {
	// DecideTimeout bounds the `_grant` run. Past it the request is refused.
	DecideTimeout time.Duration
	// RefusalWindow is how long a refusal is remembered. 0 turns that off.
	RefusalWindow time.Duration
}

Config is what New needs beyond the stores.

type Gateway

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

Gateway decides requests made with run grants.

func New

func New(ctx context.Context, pu *processor.Unit, ids *authn.Store, signer *rungrant.Signer, cfg Config) *Gateway

New builds the gateway over the identity store and the chassis's processor. signer may be nil (no master key on this node): every request that presents a token is then unavailable, and no grant can be handed to a command.

func (*Gateway) ForExec

func (g *Gateway) ForExec(ctx context.Context, tenant, stack, workspaceID, grantID string, sandboxes []string) (map[string]string, [][]byte, error)

ForExec hands run grant grantID to one command. It opens each named sandbox — every secret decided, traced and charged as a request of the grant, `via: exec` — and returns the variables the sandboxes set, with the values that must never appear in the command's output; and always the run's name and a token signed here, which is how the command presents itself: to this node's grant socket (`txco sandbox`, when this node has one — the socket and the binary are handed over too), or to a parent chassis's capability inlet from a node (`cap://`), where no socket could reach.

tenant and stack are the dispatching rule's own, and workspaceID is the workspace the command is about to run in. The grant must be live, minted by that stack, and minted for that workspace — so a rule cannot hand a command another stack's grant, or carry a grant to a machine it was not meant for.

A sandbox that is refused is an error naming the sandbox, never the reason: the trace has that. Nothing is handed over then, and the command must not start.

This is the only place a token is made, and it goes into the command's environment and nowhere else: no op returns one, so none reaches an envelope or a trace.

func (*Gateway) Invoke added in v0.2.33

func (g *Gateway) Invoke(ctx context.Context, c Call) Verdict

Invoke decides one capability call and, when it is allowed, charges the grant one request. It runs nothing: the caller does, with the grant the verdict names.

func (*Gateway) OpenSandbox

func (g *Gateway) OpenSandbox(ctx context.Context, o Open) Answer

OpenSandbox decides one request to open a sandbox and, when every secret it names is allowed, hands them all over under the sandbox's variable names. One refused: nothing is handed over, and the answer is that refusal.

func (*Gateway) ServeSocket

func (g *Gateway) ServeSocket(path string)

ServeSocket records where the launcher reaches this chassis. "" turns it off. Called by the socket's controller once it is listening.

func (*Gateway) SetClock

func (g *Gateway) SetClock(now func() time.Time)

SetClock replaces the gateway's clock (tests).

type Open

type Open struct {
	Token    string
	TenantID string
	GrantID  string
	Sandbox  string
	Via      string
}

Open is one request to open a sandbox. Exactly one of Token and GrantID says which grant: the token the work was handed (the launcher presents it), or the row the chassis itself holds (the exec names it, with the tenant it belongs to).

type Verdict added in v0.2.33

type Verdict struct {
	Allowed bool
	// Held: a rule held the call for a person to decide. Not a refusal to
	// the caller, which may end its run saying so.
	Held bool
	// Unavailable: the chassis could not decide.
	Unavailable bool
	// Reason is why not. For the log and for tests; the caller is told
	// only the class of the answer.
	Reason string
	// Grant and Tenant identify the run that asked, when the token was
	// genuine: what the `_cap` run is stamped with.
	Grant  authn.RunGrant
	Tenant string // slug
}

Verdict is what the inlet gets back: allowed, and then who is asking, or why not.

func (Verdict) Identified added in v0.2.33

func (v Verdict) Identified() bool

Identified reports whether the token named a live grant: the hard checks passed, whatever was decided after. An inlet answers 401 when they did not.

Jump to

Keyboard shortcuts

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