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

invoke

package
v1.4.15 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package invoke is the one execution core that MCP and HTTP share (contract:cli.machine-request v1, EPIC P7-SURF D2).

Purpose: turn a machine request (JSON object) for a registry command into

argv, run `nself <path> --json` as a child of this binary in v1.5
mode, and return the child's single JSON document and exit code.

Inputs: a *cmdregistry.Registry (the machine registry), a command path, a

Request, Options (transport, project dir, timeout).

Outputs: Result: the document bytes and exit code. Every refusal is itself

a v1 error envelope: E420 invalid request, E421 not exposed, E422 no
single JSON document, E423 timed out, E424 confirm mismatch, E425
invoker-owned flag set by the caller; a confirmation is a success
envelope.

Constraints: no shell, ever: one request value is exactly one argv element

and positional values sit after "--". The child is never run
in-process. The child environment drops NSELF_MCP_TOKEN. Remote and
destructive commands pass the confirmation gate (gate.go, EPIC D3):
a plan id or a server-issued single-use nonce held by a Guard, and
every non-read invocation and refusal is appended to the machine audit
log (audit.go). This package imports no cmd/ package; layer L2.

Index

Constants

View Source
const (
	DecisionRun     = "run"
	DecisionConfirm = "confirmation_required"
	DecisionRefuse  = "refused"
)

Decision kinds; they are also the audit "decision" values.

View Source
const (
	ConfirmKindPlan    = "plan"
	ConfirmKindRequest = "request"
)

Confirmation kinds.

View Source
const (
	TransportMCP        = "mcp"
	TransportHTTP       = "http"
	TransportHTTPStream = "http-ndjson"
)

Transports. TransportHTTPStream is an HTTP request answered as NDJSON; it is the only transport that may run a command whose effective output is stream.

View Source
const (
	// DefaultTimeout bounds a document invocation (--timeout on mcp and admin serve).
	DefaultTimeout = 300 * time.Second
	// MaxStdoutBytes caps the child's captured stdout; the rest is drained and dropped.
	MaxStdoutBytes = 32 << 20
	// StderrRingBytes is the stderr kept from the child (the tail).
	StderrRingBytes = 64 << 10
	// CauseTailBytes is the stderr tail an E422 envelope carries in cause.
	CauseTailBytes = 2 << 10
	// MaxRequestBytes bounds DecodeRequest input.
	MaxRequestBytes = 1 << 20
	// MaxElementBytes and MaxArgvBytes bound one argv element and the whole argv.
	MaxElementBytes = 32 << 10
	MaxArgvBytes    = 128 << 10
)

Limits and timings (EPIC D2, D16).

View Source
const (
	// SelfExecOverrideEnv points the re-exec at another binary; honoured only
	// in a `go test` binary, ignored by release builds (D-0288).
	SelfExecOverrideEnv = "NSELF_MCP_EXEC_OVERRIDE"
	TokenEnv            = "NSELF_MCP_TOKEN"
	InvokedByEnv        = "NSELF_INVOKED_BY"
	DeadlineEnv         = "NSELF_INVOKE_DEADLINE_MS"
	// PathEnv pins the command path the invoker classified; the child refuses
	// to run when its own command lookup resolves a different command (E421).
	PathEnv = "NSELF_INVOKE_PATH"
	// SecretsEnv carries the secret values of a request to the child as JSON
	// (SecretBundle), so they never sit on its argv (D-0468).
	SecretsEnv = "NSELF_INVOKE_SECRETS"
)

Environment names (EPIC D2, D16).

View Source
const MaxSecretEnvBytes = 96 << 10

MaxSecretEnvBytes bounds the SecretsEnv value: one environment string is capped near 128 KiB on Linux, and a bigger one would make exec fail.

View Source
const Redacted = "[REDACTED]"

Redacted replaces secret values in ids, audit records and stderr tails.

Variables

This section is empty.

Functions

func BuildArgv

func BuildArgv(cmd *cmdregistry.Command, r Request) ([]string, error)

BuildArgv builds the argv for a document-mode request (stream-override flags are rejected). Exposure must be checked first.

func BuildArgvFor

func BuildArgvFor(cmd *cmdregistry.Command, r Request, transport string) ([]string, error)

BuildArgvFor is BuildArgv for a transport: only TransportHTTPStream accepts the stream-override flags. A secret value is a placeholder in the result (secretenv.go); a caller that runs the argv uses BuildArgvSecrets.

func CheckFreeForm

func CheckFreeForm(reg *cmdregistry.Registry, cmd *cmdregistry.Command, r Request) error

CheckFreeForm vets a request for a command that takes (or may be given) free-form argv. A non-empty Argv on a command that is a plugin parent is refused (E421); an item before "--" naming a root persistent flag is refused (E420).

func DefaultAuditPath

func DefaultAuditPath() (string, error)

DefaultAuditPath is ~/.nself/logs/machine-audit.jsonl.

func ExposedFlags

func ExposedFlags(cmd *cmdregistry.Command, transport string) []cmdregistry.Flag

ExposedFlags lists the flags a request may set, sorted by name. It excludes json, help and the other invoker switches, hidden, deprecated and cli_only flags, consent and plan-id flags, flags of a type with no request form, and, unless transport is TransportHTTPStream, every flag carrying the `output: stream` override (HTTP NDJSON is the only place it is accepted).

func Exposure

func Exposure(cmd *cmdregistry.Command, transport string, set map[string]bool) (bool, string)

Exposure reports whether cmd may be invoked on transport with the flags in set (the names the request carries). A refusal names the rule in reason.

func FreeFormArgv

func FreeFormArgv(reg *cmdregistry.Registry, cmd *cmdregistry.Command) bool

FreeFormArgv reports whether cmd takes the free-form argv member: a plugin command that declares no args and no flags and has no subcommand. A mounted plugin node parses no flags, so the cobra tree in the child reads the first word of a free-form argv as a subcommand name; on a parent that would run a node the exposure rules never checked.

func IsLeaf

func IsLeaf(reg *cmdregistry.Registry, cmd *cmdregistry.Command) bool

IsLeaf reports whether no registry command sits below cmd. A nil registry is taken as a registry of leaves.

func ParamsSchema

func ParamsSchema(cmd *cmdregistry.Command) map[string]any

ParamsSchema is the JSON Schema of the machine request for cmd in document mode (MCP tool input, HTTP body). A command with a confirm block also accepts the confirm member; a free-form plugin command takes argv instead of args and flags.

func ParamsSchemaIn

func ParamsSchemaIn(reg *cmdregistry.Registry, cmd *cmdregistry.Command) map[string]any

ParamsSchemaIn is ParamsSchema for a command of reg: the free-form argv member is offered only on a leaf command (FreeFormArgv). A nil reg takes the command as a leaf.

func RequestID

func RequestID(path string, cmd *cmdregistry.Command, r Request) string

RequestID is the lowercase hex sha256 of the canonical JSON {"args":[..],"argv":[..],"command":"<path>","flags":{..}}: keys sorted, no whitespace, confirm excluded, absent members empty, secret values replaced (RedactRequest). path may carry the "nself " prefix; cmd may be nil.

func RestoreSecretArgv added in v1.4.14

func RestoreSecretArgv(argv []string) (out []string, values []string, err error)

RestoreSecretArgv is the child side of the placeholder scheme. It always unsets SecretsEnv. It returns argv unchanged unless the invoker started this process (InvokedByEnv set) and passed a bundle; then every placeholder element, or the text after "--name=", is replaced by its value. values lists every restored secret, for masking in the command log.

func SecretValues

func SecretValues(cmd *cmdregistry.Command, r Request) []string

SecretValues lists every secret value the request carries, as the strings a child could echo (longest first), for scrubbing stderr tails.

func SelfExecutable

func SelfExecutable() (string, error)

SelfExecutable resolves the binary to run: this process's own executable. A test binary also honours NSELF_MCP_EXEC_OVERRIDE, so tests can point the re-exec at a built nself; a release build ignores it, so whoever sets the server env cannot redirect machine invocations. It never resolves a bare "nself" on PATH.

func SetFlags

func SetFlags(r Request) map[string]bool

SetFlags returns the names of the flags the request names.

Types

type Confirmation

type Confirmation struct {
	ConfirmationRequired bool   `json:"confirmation_required"`
	Kind                 string `json:"kind"`
	Confirm              string `json:"confirm"`
	SideEffect           string `json:"side_effect"`
	Reason               string `json:"reason"`
	Plan                 any    `json:"plan"`
}

Confirmation is the success-envelope data an invoker returns when a command needs confirmation (contract:cli.machine-request v1; produced by Gate). Field order is the contract's. Plan is the child's change-plan object for kind plan and null for kind request.

type Decision

type Decision struct {
	// Kind is DecisionRun, DecisionConfirm or DecisionRefuse.
	Kind string
	// SideEffect is the effective side effect (flag escalation applied).
	SideEffect string
	// ConfirmKind is plan or request when a confirmation was asked for or
	// consumed, else empty.
	ConfirmKind string
	// Flags are the invoker-owned argv items to add before the "--" separator
	// (consent flags, --<id_flag>=<id>) when Kind is DecisionRun.
	Flags []string
	// Confirmation is the document to return when Kind is DecisionConfirm.
	Confirmation *Confirmation
	// Refusal is the invoker error when Kind is DecisionRefuse and Result is nil.
	Refusal error
	// Result, when set with DecisionRefuse, is the response to return as is:
	// the child's error envelope from the plan exec, or an E422/E423 for it.
	Result *Result
}

Decision is the outcome of Gate.

func Gate

Gate applies EPIC D3 to req for cmd. The error return is a context error only.

type Guard

type Guard struct {
	// Now is the clock; tests inject a fixed or advancing one. Set it before
	// the Guard is shared between goroutines.
	Now func() time.Time
	// contains filtered or unexported fields
}

Guard is the gate state of one server process: the nonce store, the audit path and the clock. Build it once with NewGuard and pass it in Options.

func NewGuard

func NewGuard(auditPath string) *Guard

NewGuard returns a Guard that audits to auditPath (DefaultAuditPath is the production value). An empty path disables the audit file.

type Options

type Options struct {
	// Transport defaults to TransportMCP.
	Transport string
	// Dir is the project directory the child runs in.
	Dir string
	// Timeout of 0 selects DefaultTimeout for document transports and no
	// timeout for TransportHTTPStream.
	Timeout time.Duration
	// KillDelay overrides the SIGTERM grace period; zero keeps the 5 s default.
	KillDelay time.Duration
	// Stdout, with TransportHTTPStream, receives the child's NDJSON.
	Stdout io.Writer
	// Guard holds the confirmation nonces and the audit path (gate.go,
	// confirm.go, audit.go). One Guard per server process. A nil Guard fails
	// closed: remote and destructive commands are refused (E421), writes run
	// and nothing is audited.
	Guard *Guard
	// Surface labels the audit line: mcp-stdio, mcp-sse, mcp-http or http.
	// Empty selects http for TransportHTTP and TransportHTTPStream and
	// mcp-stdio for TransportMCP.
	Surface string
}

Options configures Invoke.

type Request

type Request struct {
	// Args are the positional values, one argv element each.
	Args []string `json:"args,omitempty"`
	// Flags maps a flag name to bool, integer, number, string or []string.
	Flags map[string]any `json:"flags,omitempty"`
	// Argv is the verbatim argv of a plugin command that declares no args or flags.
	Argv []string `json:"argv,omitempty"`
	// Confirm is a plan id or a server-issued nonce (consumed by the gate).
	Confirm string `json:"confirm,omitempty"`
}

Request is the machine request (every member optional).

func DecodeRequest

func DecodeRequest(data []byte) (Request, error)

DecodeRequest parses one request document. Empty input is the empty request.

func RedactRequest

func RedactRequest(cmd *cmdregistry.Command, r Request) Request

RedactRequest returns a copy of r with every registry-secret arg and flag value replaced by Redacted. A nil cmd (unknown path) redacts all of them.

type Result

type Result struct {
	// Stdout is the document bytes: the child's stdout (Exec) or the validated
	// document / error envelope (Invoke).
	Stdout []byte
	// Stderr is the raw, unredacted stderr tail. Exec only: Invoke clears it.
	// Never forward it to a client.
	Stderr []byte
	// ExitCode is the child's exit status, or the exit class of an E42x envelope;
	// -1 when the child was killed by a signal.
	ExitCode int
	// TimedOut is true when the invocation deadline ended the child.
	TimedOut bool
	// StdoutTruncated is true when the child wrote more than MaxStdoutBytes.
	StdoutTruncated bool
	// RequestID is the redacted request id (Invoke only).
	RequestID string
	// ErrorCode is the E42x code when Stdout is an envelope written by this
	// package (Invoke only); empty when Stdout is the child's own document.
	ErrorCode string
	// Confirmation is true only when Invoke issued a gate confirmation.
	// Child output cannot set this trusted transport status metadata.
	Confirmation bool
}

Result is the outcome of Exec and Invoke.

func Exec

func Exec(ctx context.Context, s Spec) (Result, error)

Exec runs the nself binary with s.Argv. A non-zero exit is not an error: it is Result.ExitCode. The error is for a failure to start, and for the parent context ending (its error is returned; the child has been stopped).

func Invoke

func Invoke(ctx context.Context, reg *cmdregistry.Registry, path string, req Request, o Options) (Result, error)

Invoke runs the registry command at path for req. With a Guard in o the invocation is gated (Gate) and every mutation and refusal is audited.

type SecretBundle added in v1.4.14

type SecretBundle struct {
	Nonce  string   `json:"nonce"`
	Values []string `json:"values"`
}

SecretBundle is the secret values of one child run: Values[n] is what the placeholder with index n stands for.

func BuildArgvSecrets added in v1.4.14

func BuildArgvSecrets(cmd *cmdregistry.Command, r Request, transport string) ([]string, *SecretBundle, error)

BuildArgvSecrets is BuildArgvFor plus the bundle of the secret values that the argv carries as placeholders (D-0468): a registry-secret positional and the value of a secret flag never sit on the child argv. The bundle is nil when the request has no secret value. Limits apply to the real values.

type Spec

type Spec struct {
	// Argv is everything after the binary: path tokens, --json, flags, "--", args.
	Argv []string
	// Dir is the child's working directory (the server's project dir); empty inherits.
	Dir string
	// Transport is TransportMCP, TransportHTTP or TransportHTTPStream.
	Transport string
	// Path, when set, is the classified command path the child must resolve
	// to (PathEnv); empty sets no pin.
	Path string
	// Timeout of 0 means none (NDJSON): only context cancellation ends the child.
	Timeout time.Duration
	// KillDelay overrides the SIGTERM grace period; zero keeps the 5 s default.
	KillDelay time.Duration
	// Stdout, when set, receives the child's stdout as it is produced (NDJSON);
	// the child's output is then not captured and Result.Stdout is nil.
	Stdout io.Writer
	// Secrets holds the values the Argv placeholders stand for; nil when none.
	Secrets *SecretBundle
}

Spec is one child execution.

Jump to

Keyboard shortcuts

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