Authorization
Decide which principals may make which calls through Orca AI Gateway, using static ACLs, an external policy engine, or your own service.
An authorizer runs after the principal has been established and before any budget or content check. It answers one question: may this principal make this call?
plugins:
authorizers:
- name: acl
kind: yaml_acl
required: true
failure_mode: deny
rules:
- effect: allow
action: invoke
resource: { kind: model, name: "gpt-*" }Authorizers are a list, so you can layer a fast local check in front of an external policy engine.
Available kinds
kind | What it does |
|---|---|
yaml_acl | Evaluates static rules declared inline in the plugin config. |
opa_http | Delegates the decision to an Open Policy Agent endpoint. |
ext_proc | Delegates to your own gRPC service. |
allow_all | Permits everything. Intended for development. |
The shipped binary has no usable cedar authorizer. orca-gateway check accepts kind: cedar,
but the gateway then fails at boot with unsupported kind "cedar". Use opa_http for
policy-engine authorization.
yaml_acl
The simplest option, and the right one when your rules are a modest, static list. Rules match on
scope dimensions and the request. Declare them inline: the server does not load a file field for
this authorizer.
Because it evaluates in-process with no network call, yaml_acl adds no meaningful latency, which
makes it a good first layer even when a policy engine handles the harder cases.
A matching deny always wins over a matching allow; no matching allow denies the request:
plugins:
authorizers:
- name: inline-acl
kind: yaml_acl
rules:
- effect: deny
principal_kind: anonymous
reason: authentication required
- effect: allow
action: invoke
scope:
workspace_id: ws_42
resource:
kind: model
name: "gpt-*"
- effect: allow
action: invoke
scope:
workspace_id: ws_42
resource:
kind: route
name: "*"Model traffic is authorized twice: once for the requested model and again for the route selected by the routing policy. A production ACL needs matching allow rules for both resources.
Each rule can match principal_kind, scope, resource, action, and conditions. Resource names
support exact, prefix, suffix, contains, and * glob patterns. Match scope only on dimensions that
every accepted credential binds through a verified claim or API-key attribute; see
Identity and scope.
Deny anonymous callers
A request that carries no credential reaches the authorizer as an anonymous principal. Add a
deny rule for principal_kind: anonymous to every production ACL, as the example above does.
Because a matching deny always wins, the gateway answers every request without a credential with
403 forbidden and the rule's reason. With opa_http or ext_proc, make your policy deny
anonymous principals.
opa_http
Delegates to Open Policy Agent, which is the right choice once policy needs data the gateway does not hold - group membership, cost centers, an approval workflow.
plugins:
authorizers:
- name: opa
kind: opa_http
policy_url: "http://opa.svc:8181/v1/data/orca/allow"
timeout_ms: 2000
required: true
failure_mode: denyThe gateway posts { "input": { "principal", "resource", "action", "scope", "request_id", "trace_id" } } and expects { "result": { "allow": true|false, "reason": "..." } }.
Missing result is a deny.
An external authorizer puts a network call on the critical path of every request. Keep OPA close
to the gateway, and think carefully about failure_mode: deny means an OPA outage stops all
traffic, while allow means an outage silently disables authorization. Neither is free.
ext_proc
For authorization logic you want to own outright, run a gRPC service and point the gateway at it.
The wire contract is the ext_proc protocol adapted from Envoy.
plugins:
authorizers:
- name: custom
kind: ext_proc
endpoint: "http://authz-sidecar:9000"
timeout_ms: 500Running it as a sidecar rather than a shared service keeps the call local and removes a cluster-wide dependency from the request path.
allow_all
plugins:
authorizers:
- name: dev
kind: allow_allEvery request is permitted. Use it to get a development gateway running before policy exists, and make sure it never reaches a production config.
Choosing a failure mode
failure_mode decides what a runtime error from the authorizer means, not what a deny means - an
intentional deny always refuses the request.
denyis the correct default. An authorizer that cannot reach its policy source should not let traffic through.allowtrades correctness for availability, and is only defensible when another layer is already enforcing the same rule.
Initialization errors currently fail gateway boot regardless of required. The flag is carried in
configuration but does not change startup behavior or ongoing readiness aggregation.