Policy-as-Code
Steel evaluates authorization policies on every function call. Policies are written in Rego, loaded at runtime, and can be changed without redeploying your application.
What is Policy-as-Code?
Instead of hardcoding authorization logic in your application, Steel externalizes it into Rego policies. This means:
- Policies are version-controlled, reviewable, and auditable.
- Change authorization rules without redeploying code.
- Apply different policies per environment, per user, or per plugin.
- Every authorization decision is explicit and traceable.
How Policies Work
When a plugin function is called, Steel constructs an input record from the call's metadata and arguments, then evaluates a Rego rule against it.
// Steel constructs this input for every call
{
"object_id": 42,
"facet_id": 1234567890,
"method_id": 9876543210,
"method_name": "execute",
"positional": ["ls", "-la", "/tmp"],
"named": { "verbose": true }
} Your Rego policy can inspect any of these fields and decide whether to allow or deny the call.
Writing Policies
Allow a specific command
package data.steel.auth.allow
default allow = false
allow {
input.method_name == "execute"
input.positional[0] == "ls"
} Restrict by role
package data.steel.auth.allow
default allow = false
allow {
input.method_name == "execute"
input.named.role == "admin"
} Limit argument count
package data.steel.auth.allow
default allow = false
allow {
input.method_name == "execute"
input.positional[0] == "ls"
count(input.positional) <= 3
} Allow by stable method ID
package data.steel.auth.allow
default allow = false
allow {
input.method_id == 1234567890
} Default-Deny
Steel uses a default-deny model. If no policy rule exists for a call, it is denied. You must explicitly allow every capability you want to expose. There is no implicit permissiveness.
This is the opposite of most systems, where everything is allowed unless explicitly blocked. With Steel, you start from zero and grant only what you need.
Per-Plugin Policy Namespaces
Each plugin has its own policy namespace derived from its WIT world name. This means different plugins can have different authorization rules without conflicts.
// Policy for "steel:execute" world
package data.steel.execute.allow
// Policy for "steel:sanitize" world
package data.steel.sanitize.allow Dynamic Policy Loading
Policies are loaded into the policy engine at runtime. This means:
- Load policies from files, strings, or databases.
- Update policies per-session without affecting other users.
- Apply different policy sets for different environments.
- Hot-reload policies without restarting the application.
Full Type Coverage
Steel includes a complete bidirectional conversion between its value types and the Rego engine. Every Wasm Component Model type — records, variants, lists, maps, tuples, flags, enums, options, results, and fixed-length lists — passes through Rego policies without loss.
This means your policies can inspect complex structured arguments, not just simple strings and numbers.
Rego Engine
Steel uses regorus, a pure-Rust implementation of the Rego
policy language. No external dependencies, no network calls, no CGO. The
policy engine runs entirely in-process with predictable performance.