Policy gates
Vetrix evaluates operator-defined policy gates at workflow-schedule
time. Each gate is stored in the policy_gates table, carries an
enforcement mode (warn or block) and a scope tier
(instance / org / repo / environment), and is compiled by a
pluggable Evaluator backend. The engine fetches every gate applicable
to the run's scope ladder, evaluates each against a canonical
PolicyInput, and either blocks the run (at least one block
enforcement returned violations) or attaches banner annotations (warn
enforcement).
This document covers the ship-today state:
- Gate schema + CRUD semantics.
- The default Vetrix native policy language evaluator.
- Reusable-workflow nesting + input type-check enforcement.
A Rego/OPA-backed evaluator is tracked as a focused follow-up ticket so
this page stays concise; the Evaluator interface is the plug-point for
that work.
Schema
policy_gates (
id UUID PK
scope ENUM (instance | org | repo | environment)
scope_id UUID -- NULL for instance; required otherwise
name TEXT -- UNIQUE per (scope, scope_id)
source TEXT -- opaque to the DB; parsed by the evaluator
enforcement ENUM (warn | block)
created_by UUID REFERENCES users(id)
created_at TIMESTAMPTZ
)
The policy_gates table is provisioned by Vetrix's database migrations.
Enforcement
| Mode | Effect on a run that violates the gate |
|---|---|
| warn | Run is scheduled; the detail page shows a banner listing |
| the rule names and messages. | |
| block | Run is not scheduled; workflow_runs.status becomes |
blocked and the description lists the violations. |
ShouldBlock(decisions) returns true iff at least one decision carries
enforcement = block AND a non-empty violations list.
Scope ladder
A run is evaluated against gates in every applicable tier:
- Instance (
scope_id = NULL) - Org (
scope_id = org_id) - Repo (
scope_id = repo_id) - Environment (
scope_id = environment_idwhen the run is bound to one)
Service.Evaluate walks all matching gates, not just the most-specific
tier — an org-level policy cannot be bypassed by adding a narrower
gate.
Vetrix native policy language
# Every rule is one line. `#` comments are stripped before parsing.
#
# Rule: allowed_uses — every `uses:` on a job must match at least one
# glob. * matches [^/]*, ** matches anything including /.
allowed_uses = vetrix/*@*, github.com/allowed/**@*
# Rule: required_attestation — each declared kind must appear on at
# least one artifact attached to the run.
required_attestation = slsa-provenance, sbom
# Rule: deny_image — any job whose image matches the regex fails the
# gate.
deny_image = ^untrusted/.*$
# Optional: override the default violation message for a rule.
rule_msg uses_not_allowlisted = Contact sec-team before adding actions
Sandbox
Parse-time refusal on any of:
http.send, http.post, http.get, io.read, io.write,
os.exec, os.env, fs.read, fs.write
These tokens cover the Rego surface a copy-pasted OPA policy might
smuggle in. The operator sees policy_sandbox_violation at create time
— never at eval time.
Evaluation budget
Each gate runs under a wall-clock budget (default 500 ms). Exceeding
surfaces policy_evaluation_timeout on the run; other gates still run.
Reusable-workflow resolver
uses: owner/repo/.vetrix/automations/foo.yml@SHA is resolved at ingest
time by ResolveReusable:
- Max nesting depth = 1. A reusable workflow that itself references
another reusable workflow surfaces
reusable_workflow_nesting_exceeded. - Input type-check. The caller's
with:values must satisfy the declared types in the callee'son.workflow_call.inputsblock. Mismatched types surfacereusable_workflow_input_type_mismatch; an unknownwith:key surfacesreusable_workflow_unknown_input; missing required inputs surfacereusable_workflow_required_input_missing. - Callee must declare workflow_call. The
on.workflow_calltrigger is mandatory on the callee; otherwise ingest fails.
The resolver is purely source-to-source; the callee's content is
supplied via a LoaderFunc so the resolver has no filesystem or
network access.
Deferred follow-ups
- Full OPA Rego interpreter backend +
opago-module dependency. - Admin CRUD REST surface + UI (
/api/v1/admin/policy-gates). - PgStore implementation (today:
MemStorein tests and dev stacks; theStoreinterface is the plug-point). - Per-env frontend policy editor.