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
- func BuildArgv(cmd *cmdregistry.Command, r Request) ([]string, error)
- func BuildArgvFor(cmd *cmdregistry.Command, r Request, transport string) ([]string, error)
- func CheckFreeForm(reg *cmdregistry.Registry, cmd *cmdregistry.Command, r Request) error
- func DefaultAuditPath() (string, error)
- func ExposedFlags(cmd *cmdregistry.Command, transport string) []cmdregistry.Flag
- func Exposure(cmd *cmdregistry.Command, transport string, set map[string]bool) (bool, string)
- func FreeFormArgv(reg *cmdregistry.Registry, cmd *cmdregistry.Command) bool
- func IsLeaf(reg *cmdregistry.Registry, cmd *cmdregistry.Command) bool
- func ParamsSchema(cmd *cmdregistry.Command) map[string]any
- func ParamsSchemaIn(reg *cmdregistry.Registry, cmd *cmdregistry.Command) map[string]any
- func RequestID(path string, cmd *cmdregistry.Command, r Request) string
- func SecretValues(cmd *cmdregistry.Command, r Request) []string
- func SelfExecutable() (string, error)
- func SetFlags(r Request) map[string]bool
- type Confirmation
- type Decision
- type Guard
- type Options
- type Request
- type Result
- type Spec
Constants ¶
const ( DecisionRun = "run" DecisionConfirm = "confirmation_required" DecisionRefuse = "refused" )
Decision kinds; they are also the audit "decision" values.
const ( ConfirmKindPlan = "plan" ConfirmKindRequest = "request" )
Confirmation kinds.
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.
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).
const ( 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" )
Environment names (EPIC D2, D16).
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 ¶
BuildArgvFor is BuildArgv for a transport: only TransportHTTPStream accepts the stream-override flags.
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 ¶
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 ¶
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 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 ¶
SelfExecutable resolves the binary to run: NSELF_MCP_EXEC_OVERRIDE (tests), else this process's own executable. It never resolves a bare "nself" on PATH.
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.
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.
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
// 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 ¶
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
}
Result is the outcome of Exec and Invoke.
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
// 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
}
Spec is one child execution.