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

opcore

package
v0.253.0 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package opcore is the urfave/cli-free engine core of the spec-driven verb design: the per-operation descriptor, the request runtime, and the self-guarding Operation.Execute entrypoint. specverb projects this core onto a urfave/cli tree; a non-CLI consumer (ward-mcp) drives Operation.Execute directly and is still fully gated. See docs/specverb.md for the projection shape.

Index

Constants

View Source
const (
	LocationPath  = "path"
	LocationQuery = "query"
	LocationBody  = "body"
	LocationForm  = "form"
)

Location constants label where a Property lowers onto the outgoing request.

View Source
const DefaultUserAgent = "umbra/1.0 (+https://github.com/coilyco/umbra)"

DefaultUserAgent names this client when nothing else does. Go's default is refused outright by some APIs; see docs/specverb-request.md (umbra#303).

View Source
const ItemsAny = "any"

ItemsAny marks an array whose swagger `items` schema is empty: the spec says the list carries more than one type. Forgejo labels. agentic-os#1047

View Source
const OwnerRepoArg = "owner-repo"

OwnerRepoArg is the documented `args` sugar: a single `owner/name` value split across the leaf's `owner` and `repo` path params.

Variables

View Source
var ReservedFlagNames = map[string]bool{
	"dry-run": true, "query": true, "output": true, "body-file": true,
}

ReservedFlagNames are the universal per-leaf engine flags no promoted input (query, body, or form field) may shadow. See docs/specverb-request.md.

Functions

func AnyItem

func AnyItem(token string) any

AnyItem lowers one token of an empty-`items` array: all digits becomes a JSON number, anything else stays a string.

func AssembleBody

func AssembleBody(d Descriptor, body map[string]any) ([]byte, error)

AssembleBody builds one descriptor's outgoing JSON body. Exported so a caller assembling a request by hand cannot order the body modes differently.

func ChainSources

func ChainSources(chain guardfile.ValueChain) []valuesource.Source

ChainSources lowers a guardfile.ValueChain to the []valuesource.Source the shared resolver walks, keeping guardfile free of the resolution layer.

func CheckFlagCollisions

func CheckFlagCollisions(desc Descriptor) error

CheckFlagCollisions rejects reserved or duplicate local inputs and duplicate outgoing query names. Both descriptor sources share this fail-closed check.

func CheckProxyRules

func CheckProxyRules(allow, deny []ProxyRule, args map[string]any) error

CheckProxyRules enforces request-side allow and deny guards over one argument map. Exported so the MCP Apps host refuses on identical regex semantics.

func CoerceItems

func CoerceItems(items string, tokens []string) ([]any, error)

CoerceItems lowers CLI tokens to the JSON element type an array field declares. An empty `items` (ItemsAny) resolves per token, the only both-types encoding.

func CoerceScalar

func CoerceScalar(fieldType, token string) (any, error)

CoerceScalar lowers one CLI token to the JSON type a body field declares, the scalar sibling of CoerceItems. Unknown or empty types stay strings. #328.

func DefaultScheme

func DefaultScheme(baseURL string) string

DefaultScheme prepends https:// to a base-url that carries no scheme, so a Guardfile may write `base-url "host/api/v1"` and rely on TLS by default.

func DestructiveVerb

func DestructiveVerb(verb string) bool

DestructiveVerb reports whether a verb mutates irreversibly, so both the resolved and the inline source flag the same leaves. See docs/specverb.md.

func FillPath

func FillPath(template string, values []string) string

FillPath substitutes the i-th positional value into the i-th `{...}` slot. The caller has already enforced len(values) == number of slots.

func MatchesAnyGlob

func MatchesAnyGlob(val string, globs []string) bool

MatchesAnyGlob reports whether val matches at least one filepath.Match glob. A malformed pattern matches nothing (fail closed), never errors out.

func MethodForVerb

func MethodForVerb(verb string) (string, bool)

MethodForVerb returns the HTTP method a verb maps to by convention; ok is false only for the bare-unknown-verb POST default. See specverb-resolution.md.

func ParseInline

func ParseInline(src []byte) ([]Descriptor, RuntimeConfig, error)

ParseInline states the ward-mcp inline grammar as the same []Descriptor the OpenAPI source feeds, plus the request RuntimeConfig. See docs/opcore-inline.md.

func ParseInlineWithOptions added in v0.247.0

func ParseInlineWithOptions(src []byte, opts InlineOptions) ([]Descriptor, RuntimeConfig, []string, error)

ParseInlineWithOptions is ParseInlineWithWarnings for a consumer stating its options.

func ParseInlineWithWarnings

func ParseInlineWithWarnings(src []byte) ([]Descriptor, RuntimeConfig, []string, error)

ParseInlineWithWarnings is ParseInline plus the non-fatal notes a caller should surface, one per grant whose method was inferred rather than known.

func PathParamsInOrder

func PathParamsInOrder(path string) []string

PathParamsInOrder returns the `{...}` names in path-template order, the order the leaf takes them positionally. Shared with the CLI projection's binder.

func ValidMethod

func ValidMethod(m string) bool

ValidMethod reports whether m is an HTTP method the grammar accepts.

Types

type ArgBinder

type ArgBinder struct {
	PathVals []string

	Query   neturl.Values
	BodyObj map[string]any
	// contains filtered or unexported fields
}

ArgBinder routes one poll arg onto a path param (by name or owner-repo sugar), a query param, or a body field, keeping leaf-request assembly flat.

func NewArgBinder

func NewArgBinder(leaf Descriptor) *ArgBinder

NewArgBinder builds a binder over one leaf's path/query/body flag surface.

func (*ArgBinder) Bind

func (b *ArgBinder) Bind(name, val string) error

Bind routes one resolved arg value; the caller has already proved every arg targets something, so an unmatched name here is a no-op, not an error.

func (*ArgBinder) BindSlice

func (b *ArgBinder) BindSlice(name string, vals []string) error

BindSlice routes one resolved arg whose value is a list. A query target repeats the parameter, a body or form target lands a JSON array in the leaf's own type.

func (*ArgBinder) FieldFor

func (b *ArgBinder) FieldFor(name string) (Field, bool)

FieldFor returns the leaf's declared query/body/form field of that name.

func (*ArgBinder) RequireAllPaths

func (b *ArgBinder) RequireAllPaths() error

RequireAllPaths fails closed if any path param went unbound, so a poll tick never fires an under-bound URL.

type Args

type Args struct {
	Path        map[string]string
	Query       map[string]string
	QueryValues map[string]any
	Body        map[string]any
}

Args splits an Operation's inputs by URL and body location. Query is the compatible scalar surface, while QueryValues carries typed values and arrays.

type BodyMapping

type BodyMapping struct {
	SourcePath []string
	Target     string
	// Type is the JSON type the leaf projects, defaulting to string. A mapped
	// leaf used to be string-only in every configuration. See umbra#312.
	Type string
	// Items is the element type when Type is "array".
	Items string
}

BodyMapping projects one required nested string input path onto one top-level outgoing JSON key. Unmapped input is never forwarded.

type Database

type Database struct {
	Driver string
	DSN    guardfile.ValueChain
}

Database is the wrap-level `database <driver> { value ... }` declaration: a registered database/sql driver name and the chain its DSN resolves through.

func (Database) IsZero

func (d Database) IsZero() bool

IsZero reports whether no database was declared.

type Descriptor

type Descriptor struct {
	VerbName       string         // dotted audit name, e.g. ward.ops.forgejo.repo.create
	Group          string         // CLI group noun, e.g. repo
	Leaf           string         // CLI leaf verb, e.g. create
	Method         string         // HTTP method
	Path           string         // path template, e.g. /repos/{owner}/{repo}
	PathParams     []string       // ordered positional args drawn from the path
	BodyFlags      []Field        // request-body fields, including nested object/array shapes
	BodyMappings   []BodyMapping  // required input paths projected onto fresh top-level body keys
	QueryFlags     []Field        // query params, including typed scalar-array shapes
	QueryExclusive [][]string     // local query-name groups where at most one may be supplied
	FormFlags      []Field        // formData params, where "file" types take a path
	FixedBody      map[string]any // pinned body keys, alone for a state toggle or seeding a mapped body
	// ReturnFields is the optional `returns` declaration a successful response
	// is pruned to before Execute hands it back. See docs/opcore-returns.md.
	ReturnFields []Field
	Destructive  bool     // leaf mutates irreversibly (delete)
	Grant        string   // the authorizing grant sentence, e.g. "can delete repos"
	Describe     string   // optional Guardfile describe "..." note, "" if none
	FailWhen     string   // optional JMESPath response postcondition; truthy rejects a successful call
	Proxy        *Proxy   // non-nil for an inline MCP proxy grant
	GraphQL      *GraphQL // non-nil for a `graphql` grant; owns the whole body
	SQL          *SQL     // non-nil for a `sql` grant; reaches a database, not a URL
	MCP          *MCPCall // non-nil for an `mcp` dialect leaf; reaches a session, not a URL
	// Meta is upstream `_meta`, kept verbatim and never interpreted. A served
	// surface forwards it; MCP Apps addresses its widget there.
	Meta map[string]any
	// RawResponse marks a success response offering no JSON, written through
	// untouched. See docs/specverb-request.md.
	RawResponse bool
	// MethodInferred marks a Method guessed from an unrecognised verb rather
	// than looked up or stated. See docs/opcore-inline.md.
	MethodInferred bool
}

Descriptor is the tiny per-operation payload the generic action binds to, isolated from the static request machinery.

func (Descriptor) InputSchema

func (d Descriptor) InputSchema() Schema

InputSchema projects a Descriptor onto its neutral input surface: path params as required strings, query/body/form as fieldFlagsToCLI reads them, no FixedBody.

func (Descriptor) Label

func (d Descriptor) Label() string

Label names the leaf in operator-facing errors, satisfying stepflow.Leaf so a resolved Descriptor can drive a complex-action step. See pkg/stepflow.

type Field

type Field struct {
	Name         string // local input name and the default outgoing field or parameter name
	UpstreamName string // outgoing query parameter name when it differs from Name
	Type         string // swagger type: string|boolean|integer|number|array|object
	Items        string // scalar element type when Type is "array" and Item is nil
	Item         *Field // nested element schema when Type is "array" and Item is object/array
	Fields       []Field
	Raw          bool
	Required     bool // required in the schema, enforced at request assembly
	Desc         string
	Enum         []any    // permitted values, when the upstream schema names a closed set
	Minimum      *float64 // inclusive numeric lower bound
	Maximum      *float64 // inclusive numeric upper bound
	MinItems     *int     // inclusive array-length lower bound
	MaxItems     *int     // inclusive array-length upper bound
	// ArrayEncode is "" or "repeat" for the bare repeated name, "brackets" for
	// the []-suffixed form. docs/specverb-request.md (umbra#7013).
	ArrayEncode string
	// Keyed marks an object whose key set is the caller's, not a fixed
	// named list. See docs/opcore-body-variants.md.
	Keyed       bool
	EntrySchema *Field
	// Variant is a discriminated union: exactly one Cases entry applies. See
	// docs/opcore-body-variants.md.
	Variant *Variant
}

Field is one spec input. The flat cases lower to CLI flags, while body fields may carry nested shape and query fields may carry numeric or array bounds.

func (Field) QueryName

func (f Field) QueryName() string

QueryName returns the outgoing query parameter name for this field. An empty UpstreamName preserves the historical Name-to-wire mapping.

func (Field) QueryWireName

func (f Field) QueryWireName() string

QueryWireName returns the outgoing parameter name including any array-encoding suffix, which is what actually reaches the URL.

func (Field) TypeLabel

func (f Field) TypeLabel() string

TypeLabel renders the flag's type for help and describe output.

type GraphQL

type GraphQL struct {
	Document  string  // the operation document, sent verbatim as `query`
	Operation string  // operation name, sent as `operationName`; "" when anonymous
	Variables []Field // derived from the document signature, decorated by `variable`
}

GraphQL is one grant's operation: an authored document that never reaches the input schema, plus caller variables derived from its signature.

type InlineOptions added in v0.247.0

type InlineOptions struct {
	// AllowReservedNames lets an input take a name the umbra CLI reserves for its flags.
	// A consumer mounting no CLI sets it. Duplicate inputs stay refused.
	AllowReservedNames bool
}

InlineOptions are the consumer facts the inline grammar cannot read from the file.

type MCPCall

type MCPCall struct {
	Tool string

	// Allow and Deny guard the outgoing arguments, PostCall the returned value.
	// All three match a regex against one named string field.
	Allow    []ProxyRule
	Deny     []ProxyRule
	PostCall []ProxyRule
}

MCPCall marks a leaf of the mcp dialect: the exact upstream tool it fires and the guards over that call. A leaf carrying it assembles no request.

type MCPEnv

type MCPEnv struct {
	Name  string
	Value guardfile.ValueChain
}

MCPEnv is one environment injection on a stdio upstream.

type MCPUpstream

type MCPUpstream struct {
	Kind string // "stdio" | "http"

	Command string
	Argv    []string
	Env     []MCPEnv

	URL      string
	URLValue guardfile.ValueChain
	Auth     guardfile.Auth
}

MCPUpstream is the wrap-level `mcp <transport> { ... }` declaration, with its credential material still symbolic: chains resolve per call, not at parse.

func (MCPUpstream) IsZero

func (u MCPUpstream) IsZero() bool

IsZero reports whether no upstream was declared.

type Operation

type Operation struct {
	Desc Descriptor
	RT   *Runtime
}

Operation is one resolved leaf plus the runtime that fires it: the unit a non-CLI consumer (ward-mcp) drives via the self-guarding Execute.

func (Operation) Execute

func (o Operation) Execute(ctx context.Context, a Args) (Response, error)

Execute runs the leaf under the full security floor (gate, restrict, assemble, base-url, auth, fire) and returns the decoded response, rendering nothing.

func (Operation) Preview

func (o Operation) Preview(a Args) (Request, error)

Preview resolves the request without firing it (same gate/restrict/assembly as Execute) for a dry-run; a value-resolved base-url stays an offline placeholder.

func (Operation) Resolve

func (o Operation) Resolve(ctx context.Context, a Args, dry bool) (Request, error)

Resolve validates, gates, and assembles one request without firing it. Consumers use it when they need the resolved request shape.

func (Operation) ResolveQuery

func (o Operation) ResolveQuery(a Args) (neturl.Values, error)

ResolveQuery validates, aliases, and serializes query inputs without assembling or firing. Encode makes them unable to alter the URL, so no gate.

type Property

type Property struct {
	Type         string // string|boolean|integer|number|array
	Items        string // element type when Type==array
	Item         *Property
	Properties   map[string]Property
	Required     []string
	Raw          bool
	Location     string // path|query|body|form (neutral hint)
	UpstreamName string // outgoing query parameter when it differs from the local property name
	Description  string
	Enum         []any
	Minimum      *float64
	Maximum      *float64
	MinItems     *int
	MaxItems     *int
	// Keyed marks an object whose key set is the caller's, every value shaped
	// like Entry. umbra#8032.
	Keyed   bool
	Entry   *Property
	Variant *PropertyVariant
}

Property is one input in a Schema: its type (or array element type), the neutral where-it-goes hint, its help text, and any nested shape.

type PropertyVariant

type PropertyVariant struct {
	On    string
	Cases map[string]Property
}

PropertyVariant is the schema-facing form of Variant. See opcore-body-variants.md.

type Provider

type Provider = valuesource.Provider

Provider resolves the value at address for one named value source: the shared valuesource.Provider, so the consumer's one registry drives both engines.

type Proxy

type Proxy struct {
	Name     string       // local served tool name
	Upstream UpstreamTool // exact upstream MCP tool mapping
	Allow    []ProxyRule  // request-time allow rules
	Deny     []ProxyRule  // request-time deny rules
	PostCall []ProxyRule  // response-time checks after the upstream call
	Describe string       // optional human note
}

Proxy is one inline MCP proxy grant: local tool, exact upstream mapping, and request/response guards. The consumer resolves the upstream schema at runtime.

type ProxyRule

type ProxyRule struct {
	Field    string
	Patterns []string
}

ProxyRule is one simple regex guard over a string field such as url, text, content, target, element, key, or state.

type Request

type Request struct {
	Method      string
	URL         string
	Body        []byte
	ContentType string
}

Request is a resolved-but-unfired call: the exact method, URL, and body the runtime would send. Preview returns one for a consumer's dry-run.

type Response

type Response struct {
	Decoded any
	Raw     []byte
	Status  string
}

Response is a fired call's outcome: the decoded JSON value, the raw bytes (for a consumer's own rendering), and the HTTP status line.

type Runtime

type Runtime struct {
	BaseURL   string
	Auth      guardfile.Auth
	Providers map[string]Provider
	Client    *http.Client
	Restrict  []guardfile.Restriction

	// AllowMeta names the path params the wrap's `allow-metacharacters` exempts.
	// Query is exempt by construction, never by this list; specverb-request.md.
	AllowMeta []string

	// Headers are wrap-declared request headers, applied before the default
	// User-Agent. See docs/specverb-request.md.
	Headers map[string]string

	// BaseURLValue (zero = static BaseURL) resolves the host through the first
	// available source in its chain once, caching it. See specverb-policy.md.
	BaseURLValue guardfile.ValueChain

	// Database backs `sql` grants. The pool opens once on first use, so a
	// consumer that serves no SQL grant never opens a connection.
	Database Database

	// MCP is the wrap-level upstream backing `mcp` dialect leaves. Its value
	// chains stay symbolic here and resolve per call, never at parse.
	MCP MCPUpstream
	// contains filtered or unexported fields
}

Runtime carries the per-tree request dependencies shared by every leaf, urfave/cli-free: specverb embeds it and a non-CLI consumer drives it directly.

func NewRuntime

func NewRuntime(c RuntimeConfig) *Runtime

NewRuntime assembles a Runtime, defaulting the base-url scheme and the HTTP client; the wrap pipeline and step runner are CLI-layer concerns layered on top.

func (*Runtime) Authorize

func (rt *Runtime) Authorize(ctx context.Context, req *http.Request) error

Authorize applies the runtime's configured auth to req. Blank auth is a no-op.

func (*Runtime) BaseForRequest

func (rt *Runtime) BaseForRequest(ctx context.Context, dry bool) (string, error)

BaseForRequest returns the request base-url: the static base, or (for a `base-url { value }` host) a once-resolved provider value. Dry-run stays offline.

func (*Runtime) CheckRestrictions

func (rt *Runtime) CheckRestrictions(pathParams, values []string) error

CheckRestrictions enforces every wrap-level restriction against a leaf's bound path values, failing closed on an out-of-scope value. See docs/specverb.md.

func (*Runtime) FireCapture

func (rt *Runtime) FireCapture(ctx context.Context, method, url string, body []byte, contentType string) (decoded any, raw []byte, status string, err error)

FireCapture sends one request and returns the decoded JSON value plus raw body, rendering nothing: the "fire and capture" path complex actions feed on.

func (*Runtime) FireCaptureRaw

func (rt *Runtime) FireCaptureRaw(ctx context.Context, method, url string, body []byte, contentType string) (raw []byte, status string, err error)

FireCaptureRaw sends one request and returns the body undecoded, for an op declaring a non-JSON media type. See docs/specverb-request.md.

func (*Runtime) MetaAllowed

func (rt *Runtime) MetaAllowed(param string) bool

MetaAllowed reports whether the wrap opted this path param out of the gate. Exported so the CLI layer's pre-gate agrees with opcore's rather than diverging.

type RuntimeConfig

type RuntimeConfig struct {
	BaseURL      string
	Auth         guardfile.Auth
	Providers    map[string]Provider
	Client       *http.Client
	Restrict     []guardfile.Restriction
	AllowMeta    []string
	Headers      map[string]string
	BaseURLValue guardfile.ValueChain
	Database     Database
	MCP          MCPUpstream
}

RuntimeConfig is the input to NewRuntime: BaseURL is raw (scheme + trim applied), a nil Client defaults to the redirect-guarding client.

type SQL

type SQL struct {
	Statement   string  // sent verbatim; parameters bind, nothing interpolates
	Params      []Field // ordered, binding to $1..$N or ?..? in that order
	MaxRows     int     // result bound for a reading statement
	Reads       bool    // true when the statement returns rows rather than a count
	Placeholder byte    // '$' or '?', the style the statement uses
}

SQL is one grant's declared statement: authored text that never reaches the input schema, plus the parameters a caller binds into its placeholders.

type Schema

type Schema struct {
	Properties        map[string]Property // keyed by field name
	Required          []string            // names that must be supplied, in a stable order
	MutuallyExclusive [][]string          // groups where at most one property may be supplied
}

Schema is the transport-neutral input surface of one Descriptor: every input it accepts, keyed by name, plus the subset that must be supplied. See docs.

func (Schema) JSONSchema

func (s Schema) JSONSchema() []byte

JSONSchema emits the Schema as a generic draft-07 object, never an MCP tool type (that wrapper lives in ward-mcp). The neutral Location hint is omitted.

type UpstreamTool

type UpstreamTool struct {
	Server string
	Tool   string
}

UpstreamTool names the proxied MCP server and upstream tool exactly.

type Variant

type Variant struct {
	On    string
	Cases map[string][]Field
}

Variant is one discriminated-union body shape. See docs/opcore-body-variants.md.

Jump to

Keyboard shortcuts

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