ruleexec

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultMaxSteps   = 10000
	DefaultMaxDepth   = 64
	DefaultMaxNesting = 64
)

Default budgets (to be published with the scheme spec; see docs/database-scheme-design.md §7).

View Source
const (
	MaxStepsCeiling   = 10000
	MaxDepthCeiling   = 64
	MaxNestingCeiling = 64
)

Published ceilings (see budget-defaults.json). Implementations MUST NOT relax these values; a file that asks for more is rejected.

Variables

This section is empty.

Functions

func CheckStaticBounds

func CheckStaticBounds(f Flow, b *Budget) error

CheckStaticBounds walks a flow BEFORE execution and enforces a zero-cost structural rule: nesting depth must fit the budget. Loops and retries were removed from the execution language (2026-09-10), so a flow is a finite tree of op / if / seq: termination is structural and a single authorization can trigger at most one execution of each operation (no amplification factor).

func EvalCondition

func EvalCondition(c Condition, ctx map[string]any, b *Budget, depth int) (bool, error)

EvalCondition evaluates a condition against a context map with a budget. Depth is tracked to prevent nesting-based abuse.

func GenSignerCert

func GenSignerCert(dir string) (certPath, keyPath string, cert *x509.Certificate, err error)

GenSignerCert generates a self-signed rule-signer certificate and key.

This is a compatibility re-export. The implementation lives in internal/rulesigner; the wrapper exists because moving an *exported* symbol into internal/ silently breaks every downstream module — Go forbids importing internal/ from outside this module, so consumers such as the gateway module's end-to-end fixtures (http/ruleplugins_test.go, e2e_real_test.go, e2e_matrix_test.go, gateway_rules_test.go) stopped compiling.

Callers that need the AIC-carrying variants (so that RuleWithinSignerGrant can pass) should use the internal helper from inside this module.

func GenSignerCertWithCapabilities added in v0.2.0

func GenSignerCertWithCapabilities(dir string, caps []pki.Capability) (certPath, keyPath string, cert *x509.Certificate, err error)

GenSignerCertWithCapabilities generates a signer certificate that carries an AIC extension whose grant covers caps.

Rule *publication* requires this: the loader enforces RuleWithinSignerGrant, so a rule signed by a certificate without an AIC extension (the plain GenSignerCert above) is refused with "signer certificate has no AIC extension". Downstream modules — e.g. the gateway module's end-to-end fixtures — need this exported variant to build signers that can actually publish rules.

func GenSignerCertWithGrant added in v0.2.0

func GenSignerCertWithGrant(dir string, caps, constraints []pki.Capability) (certPath, keyPath string, cert *x509.Certificate, err error)

GenSignerCertWithGrant is GenSignerCertWithCapabilities plus constraints.

func GenerateSelectSQL

func GenerateSelectSQL(raw json.RawMessage) (string, []any, error)

GenerateSelectSQL renders the MySQL SQL for a database-v1 query:SELECT rule (v1 contract: exactly one table). The params must already have passed Validate (rule.go); this function is the "translator" that turns the structured contract into a PARAMETERIZED statement.

All values are returned as placeholders (?) plus args, in statement order: no value is ever spliced into SQL text. Identifiers (table/column names) are still quoted with backticks, and are validated against the rule's declared tables/columns before rendering.

func MapHTTPRequest

func MapHTTPRequest(ctx *pki.PluginContext) map[string]any

MapHTTPRequest converts gateway-core PluginContext facts (including the optional HTTP request fields) into the request map consumed by rule conditions: request.method / request.path / request.query / request.headers plus target/client_cn/agent_id/roles.

HTTP-facing gateways populate ctx.Method/Path/Query/Headers; the TCP admission path only provides Target/ClientCN/Roles/AgentId.

func PublishManifestJSON

func PublishManifestJSON(m *PublishManifest) (string, error)

PublishManifestJSON renders the manifest for output.

func RegisterRulePluginsFromDir

func RegisterRulePluginsFromDir(reg *pki.PluginRegistry, dir string, trustRoots []*x509.Certificate, handler OpHandler, sreg *register.Registry) ([]string, error)

RegisterRulePluginsFromDir loads signed rules from a published rule directory (outDir/<scheme>/default.json [+ .p7s], as produced by PublishRules) and registers one RulePlugin per scheme into reg. Returns the registered scheme list. Any signature or validation failure aborts the whole registration (fail-closed). reg is the gateway registry; sreg is the CLC scheme registry used by Rule.Validate (fail closed, must not be nil).

func RuleWithinSignerGrant added in v0.2.0

func RuleWithinSignerGrant(rule *Rule, signer *x509.Certificate) error

RuleWithinSignerGrant enforces the PUBLICATION BOUNDARY of a signed rule: the capability a rule declares MUST be covered by the signer's own AIC grant (CLC-v1 entailment, semantics.Entails), and every constraint the rule declares MUST also be declared by the signer.

Without this check a holder of any trusted signing certificate could publish a rule that exceeds its own authority. The check is fail-closed: a signer without an AIC extension, or without a covering capability, is rejected.

func RunFlow

func RunFlow(f Flow, ctx *FlowContext, b *Budget) error

RunFlow executes a flow with the given budget.

func ValidateStructure

func ValidateStructure(r *Rule) error

ValidateStructure performs schema-level validation of a rule: field formats, condition operators, flow step kinds and nesting.

Types

type Budget

type Budget struct {
	MaxSteps   int64
	MaxDepth   int
	MaxNesting int
	Deadline   time.Time
	// contains filtered or unexported fields
}

Budget enforces the execution budget for conditions and flows. Since loops and retries were removed (2026-09-10) a flow is a finite tree, so the budget is a defence-in-depth guard rather than the termination argument.

func BudgetFromDefaults

func BudgetFromDefaults(d *BudgetDefaults) *Budget

BudgetFromDefaults builds a Budget from published defaults.

func NewBudget

func NewBudget() *Budget

NewBudget returns a budget with the specification defaults.

func (*Budget) Enter

func (b *Budget) Enter(depth int) error

Enter tracks recursion/nesting depth.

func (*Budget) Exit

func (b *Budget) Exit()

Exit leaves a nesting level.

func (*Budget) Stats

func (b *Budget) Stats() (steps int64)

Stats returns the current counters (for audit output).

func (*Budget) Step

func (b *Budget) Step() error

Step charges one execution step.

func (*Budget) Steps

func (b *Budget) Steps() int64

Steps returns the current step count.

type BudgetDefaults

type BudgetDefaults struct {
	MaxSteps   int `json:"max_steps"`
	MaxDepth   int `json:"max_depth"`
	MaxNesting int `json:"max_nesting"`
}

BudgetDefaults is the published execution budget, distributed with the scheme spec (see budget-defaults.json and docs/database-scheme-design.md §7). Implementations MUST NOT relax these values.

func LoadBudgetDefaults

func LoadBudgetDefaults(path string) (*BudgetDefaults, error)

LoadBudgetDefaults reads a published budget-defaults.json.

type BudgetError

type BudgetError struct {
	Kind BudgetKind
	Used int64
	Max  int64
}

BudgetError is returned when an execution budget is exceeded.

func (*BudgetError) Error

func (e *BudgetError) Error() string

type BudgetKind

type BudgetKind int

BudgetKind identifies which budget was exceeded.

const (
	KindSteps BudgetKind = iota
	KindDepth
	KindNesting
	KindTimeout
)

func (BudgetKind) String

func (k BudgetKind) String() string

type Condition

type Condition struct {
	Op     string      `json:"op"`
	Path   string      `json:"path,omitempty"`
	Value  any         `json:"value,omitempty"`
	Window []string    `json:"window,omitempty"` // between
	Items  []Condition `json:"items,omitempty"`  // and / or / not
}

Condition is the structured condition AST (the "mini language"). Leaf ops compare a resolved path against a value; and/or/not combine sub-conditions.

type Constraint

type Constraint struct {
	Scheme string          `json:"scheme"`
	ID     string          `json:"id"`
	Params json.RawMessage `json:"params,omitempty"`
}

Constraint references a registered constraint type.

type Decision

type Decision struct {
	Allow  bool
	Reason string
	Steps  int64
}

Decision is the phase-two outcome.

type DecisionInput

type DecisionInput struct {
	AgentID       string
	PrincipalID   string
	Mode          string
	EffectiveCaps []string
	Request       map[string]any
	Now           time.Time
}

DecisionInput mirrors the facts a gateway phase-two plugin receives after AIC validation: EffectiveCaps (P∩C) plus request context (see gateway-core AdmissionResult.EffectiveCaps).

type Flow

type Flow struct {
	Steps []Step `json:"steps"`
}

Flow is the minimal workflow AST (Python/C-style flow control).

type FlowContext

type FlowContext struct {
	Vars    map[string]any
	Request map[string]any
	Handler OpHandler
}

FlowContext carries task-local variables, the request facts, and the operation handler (the gateway's fixed capability executor).

type HTTPGateway

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

HTTPGateway is the reference "rule -> gateway -> mysql-api" chain: it simulates the gateway admission path (mTLS identity from the verified peer certificate of the terminating TLS connection), runs the phase-two rule plugin, generates SQL from the rule params, and executes it against the database.

func NewHTTPGateway

func NewHTTPGateway(plugins map[string]*RulePlugin, exec SQLExecutor) *HTTPGateway

NewHTTPGateway builds a gateway with per-user rules and an executor.

func (*HTTPGateway) Handler

func (g *HTTPGateway) Handler() http.Handler

Handler exposes the HTTP handler (for httptest or a real server).

type OpHandler

type OpHandler func(op string, vars, req map[string]any) (map[string]any, error)

OpHandler executes a named operation (e.g. "db:select"). The returned map is merged into task variables.

type PhaseTwo

type PhaseTwo interface {
	Decide(in DecisionInput) (*Decision, error)
}

PhaseTwo is the policy-executor contract the gateway phase-two plugin slot expects (per-scheme routing after EffectiveCaps).

type PublishManifest

type PublishManifest struct {
	Schemes map[string]SchemePublish `json:"schemes"`
}

PublishManifest is the publication manifest.

func PublishRules

func PublishRules(rulesDir, outDir, certPath, keyPath string, reg *register.Registry) (*PublishManifest, error)

PublishRules publishes rule files from rulesDir into outDir:

rulesDir/<scheme>/v<maj>.<min>.json

Each rule is validated against the registry's parameter contract (Rule.Validate, the same check the claims path uses -- not just structure) and PKCS#7 signed. default.json is published as a byte-identical copy of the highest minor version, so its .p7s detached signature stays valid without re-signing. certPath/keyPath are the signer certificate/key (PEM); reg supplies the scheme definitions the rule must satisfy (fail closed, never nil).

type Rule

type Rule struct {
	RuleID      string          `json:"rule_id"`
	Version     string          `json:"version"`
	Scheme      string          `json:"scheme"`
	Capability  string          `json:"capability"`
	Params      json.RawMessage `json:"params"`
	Conditions  *Condition      `json:"conditions,omitempty"`
	Constraints []Constraint    `json:"constraints,omitempty"`
	Flow        *Flow           `json:"flow,omitempty"`
}

Rule is the signed rule file format (draft, see docs/database-scheme-design.md §8).

func LoadRule

func LoadRule(path string) (*Rule, error)

LoadRule reads a rule file.

func LoadRuleBytes

func LoadRuleBytes(data []byte) (*Rule, error)

LoadRuleBytes parses a rule from JSON bytes.

Unknown fields are rejected (fail-closed): a misspelled key such as "condtions" would otherwise be dropped silently and the rule would load — and execute — without the constraint the author meant to write. Rules are machine-generated as often as hand-written, so the loader must not be lenient about field names.

func (*Rule) Validate

func (r *Rule) Validate(reg *register.Registry) error

Validate checks the rule against the scheme registry and validates the rule parameters with the capability semantics (CLC-v1), not with ruleexec-local rules -- see docs/capability-language-layers.md.

type RuleExecutor

type RuleExecutor struct {
	Rule    *Rule
	Budget  *Budget
	Handler OpHandler
}

RuleExecutor binds a signed rule to the phase-two contract.

func (*RuleExecutor) Decide

func (e *RuleExecutor) Decide(in DecisionInput) (*Decision, error)

Decide validates the rule structure, runs the static bounds pre-check, evaluates the conditions against the request, and runs the flow. Authorization itself stays in the AIC layer; this executor only implements the rule's conditions and orchestration.

type RulePlugin

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

RulePlugin adapts a signed rule to the gateway-core phase-two plugin slot (CapabilityPlugin). The admission pipeline calls Execute once per effective capability whose scheme matches Scheme(); a PluginDeny or error denies the connection (fail-closed).

func LoadRulePlugin

func LoadRulePlugin(rulePath string, trustRoots []*x509.Certificate, handler OpHandler, reg *register.Registry) (*RulePlugin, error)

LoadRulePlugin loads a published (PKCS#7 signed) rule file and builds the gateway phase-two plugin for it. Signature verification and registry validation are mandatory (fail-closed): a tampered, unsigned, or parameters-contract-violating rule is rejected. A fresh execution budget is created per plugin so that counters are never shared across executions.

func NewRulePlugin

func NewRulePlugin(scheme string, rule *Rule, budget *Budget, handler OpHandler) *RulePlugin

NewRulePlugin builds a phase-two plugin executing rule for scheme.

func (*RulePlugin) Execute

func (p *RulePlugin) Execute(cap *pki.Capability, ctx *pki.PluginContext) (*pki.PluginResult, error)

Execute maps gateway-core PluginContext facts into a DecisionInput and runs the rule executor (structure -> static bounds -> conditions -> flow). Authorization stays in the AIC layer; this implements the rule's conditions and orchestration only.

func (*RulePlugin) Scheme

func (p *RulePlugin) Scheme() string

Scheme returns the capability scheme this plugin serves.

type RuleVersion

type RuleVersion struct{ Major, Minor int }

RuleVersion is a parsed v<major>.<minor>.json version.

type SQLExecutor

type SQLExecutor func(ctx context.Context, sql string, args ...any) ([]map[string]any, error)

SQLExecutor runs a generated SQL statement and returns rows as JSON objects. The context carries the request's deadline: implementations MUST honour it so a slow/stalled query cannot outlive its HTTP request (audit 2026-09-16, R9). The real implementation talks to MySQL; tests inject a fake.

func DBExecutor

func DBExecutor(db *sql.DB) SQLExecutor

DBExecutor adapts a *sql.DB to SQLExecutor (real MySQL/MariaDB). It enforces a bounded deadline on every query, honouring (but never exceeding) the caller's context.

type SchemePublish

type SchemePublish struct {
	Latest string   `json:"latest"`
	Files  []string `json:"files"`
}

SchemePublish describes one published rule scheme.

type Step

type Step struct {
	Name      string     `json:"name,omitempty"`
	Kind      string     `json:"kind"`
	Op        string     `json:"op,omitempty"`
	Condition *Condition `json:"condition,omitempty"`
	Then      []Step     `json:"then,omitempty"`
	Else      []Step     `json:"else,omitempty"`
	Steps     []Step     `json:"steps,omitempty"`
}

Step kinds: op | if | seq

Jump to

Keyboard shortcuts

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