Security Model
Steel enforces authorization on every function call. There are no implicit permissions, no trusted paths, and no shortcuts. Every execution is verified.
Zero-Trust Execution
In most plugin systems, once a plugin is loaded, it can call any import the host provides. Steel flips this: every call starts denied, and must earn permission through a policy evaluation.
This means even a compromised or malicious plugin cannot do more than the policy allows — and the policy can inspect the exact arguments, the target function, and the calling user's context.
Per-Call Authorization
Every function call passes through the policy engine before execution. Steel evaluates a Rego rule with full access to the call's metadata:
| Input Field | What It Contains |
|---|---|
object_id | Which plugin object is being called |
facet_id | Which interface is being invoked |
method_id | Which specific function is being called |
method_name | The human-readable function name |
positional | The positional arguments passed to the call |
named | Named arguments passed to the call |
Policies can inspect any combination of these fields. A policy might allow a function only when called with specific arguments, or restrict it based on the calling user's role.
Default-Deny
If no policy rule exists for a call, it is denied. If the rule returns
false, it is denied. If the rule errors, it is denied. Only an
explicit true permits execution.
This means you must opt in to every capability. There is no background permission set, no "allow all" fallback, and no way for a plugin to escalate its own privileges.
Per-User Session Isolation
When a user authenticates, Steel creates an isolated execution environment for them. Each session gets its own:
Wasm Sandbox
Each user's Wasm instance has its own linear memory. One user cannot read or write another user's data, even within the same plugin.
Linker
Each user's linker only exposes the imports their role permits. Even if the sandbox were somehow escaped, the linker blocks unauthorized calls.
Policy Engine
Each session can have its own policy engine instance. Per-user policies can be loaded without affecting other users.
Defense in Depth
Steel provides two independent layers of enforcement:
- Policy engine — evaluates Rego rules on every call. This is the primary authorization layer.
- Linker — controls which imports are available. This is the structural enforcement layer.
These layers are independent. Even if one is bypassed, the other still enforces the security boundary. This is defense in depth: no single point of failure.
Example Policy
A Rego policy that only allows listing files when the caller has admin role and passes no more than 3 arguments:
package data.steel.auth.allow
default allow = false
allow {
input.method_name == "execute"
input.named.role == "admin"
count(input.positional) <= 3
} If this policy is not loaded, all calls are denied. If it is loaded, only calls matching all three conditions are permitted. Everything else is blocked.
Content-Addressed Integrity
Plugins are distributed as OCI artifacts with SHA-256 content addressing. Every plugin layer has a cryptographic digest. Steel verifies integrity on load — you always know exactly what code is running.