The gateway now ships in the
praisonai-bot package. praisonai serve gateway still works exactly as documented here; for a standalone install see praisonai-bot Migration.Quick Start
1
Single-operator (no scopes — unchanged)
Today’s setup keeps working. Authenticated clients receive all scopes when no policy is configured.
2
Multi-operator (scoped tokens)
Map each operator token to the scopes they need in
gateway.yaml, then run your agent as usual.When no
auth_scopes policy is configured, every successfully authenticated client is granted all scopes — identical to today’s binary auth behaviour. Single-operator setups need no changes.How It Works
- Client connects with a bearer token.
- Gateway resolves scopes via
GatewayConfig.resolve_scopes(token). - Each HTTP route and WebSocket action checks the required scope.
- Outbound events are filtered — approval events only reach clients with the
approvalsscope.
authorize_method gate before any handler runs.
Scope Reference
admin implies every scope, but approvals and pairing do not imply each other — they are sibling capabilities. Only admin grants both. A method requiring both (via field escalation) therefore resolves to admin. See Incomparable scopes escalate to ADMIN.Which scope should this operator have?
The
APPROVALS scope is coarse — any holder may attempt to resolve any pending request. To bind a specific request to a specific operator inside that scope, use per-request reviewer custody.Per-user attribution under a shared token
Operators sharing one gateway token collapse to a single audit identity by default. A proxy or SSO layer can stamp theX-Operator-Id header per user so each decision is still attributed to an individual on the audit trail.
operator:alice on the audit trail. See Approval Attribution for the full identity derivation order.
Configuration
YAML — structured (recommended)
YAML — flat mapping
Python
Scope-Gated Routes
For programmatic (WebSocket / RPC) methods, see Method → Scope Registry below.
The WebSocket
message requirement comes from the Method Scope Registry below — the dispatcher calls resolve_required_scope("message") (and resolve_required_scope("agent.message")) rather than hard-coding the check per route.
Method Scope Registry
New gateway methods are closed until classified — you cannot accidentally ship an unauthenticated control-plane call. Every method maps to aGatewayMethodDescriptor in the module-level GATEWAY_METHODS registry. resolve_required_scope looks up that descriptor and returns the effective scope for a call.
As of PraisonAI PR #5217, the WebSocket dispatcher calls the registry directly — every inbound method goes through a central
authorize_method(method, client_scopes, params) gate before any handler runs. resolve_required_scope() is the single source of truth. A plugin-registered or newly-added method is gated automatically; a method nobody classified requires admin by omission rather than being reachable ungated. See Central authorization gate below.Quick lookup
Registering a plugin method
Plugin authors adding a new dispatcher method register its required scope once, at import time.register_gateway_method refuses duplicates — pass replace=True to override an existing entry.
Per-field escalation
Escalation raises the required scope when a sensitive field is present; a plain call stays at the baseline.Strict fields (fail-closed on unknown payload)
strict_fields=True escalates any structural field not explicitly listed as safe, so unexpected payload keys fail closed.
APPROVALS and PAIRING are sibling scopes — neither implies the other. A method whose baseline is one but whose payload escalates to the other resolves to admin, its common upper bound, so a single-scope check can never be satisfied by holding only one of the two capabilities.Core method classification
Core methods are classified at import; anything unlisted defaults toadmin.
Connection-lifecycle frames (
hello, join, leave) run before a client has negotiated any scope, so they classify as read — the lowest meaningful level. abort (and its wire alias message_abort) mutates the turn just like sending one, so both carry the same write requirement as message.
server_reply is unique — the handler escalates to approvals for approval-kind replies (parity with approvals.resolve), so a WRITE-only role can answer choice/input but not approval prompts. See Gateway Server→Client Request Channel.
Descriptor fields
Descriptor fields
GatewayMethodDescriptor is a frozen dataclass; its escalate_fields and safe_fields collections are defensively copied at construction, so mutating the originals after registration cannot change resolution.Central authorization gate
Every gateway method now flows through one default-deny gate before any handler runs. Nothing changes for end users — the gateway gates dispatch for you.- Called once, centrally, at the top of
_handle_client_message— no per-handler wiring needed. - Default-deny: an unclassified method requires
admin. adminimplies every scope.readis the baseline lifecycle scope — any operator holding at least one actionable scope (write/approvals/pairing/admin) satisfiesread, so awrite-only operator can still completehello/join/leave/session.status. An empty scope set cannot satisfy evenread.- A malformed
method(non-string / unhashable) fails closed toadminrather than raisingTypeError. - Transport
PING/PONGframes bypass the gate (handled earlier in the wire loop).
GatewayUnauthorized is a PermissionError subclass carrying .method: str and .required: OperatorScope. str(exc) renders as method 'X' requires scope 'Y', and the dispatcher maps it to the insufficient_scope wire envelope with required_scope set.Method → Scope Registry
Every gateway method declares its required scope in one place — a declarative registry — instead of scattered checks per endpoint. New methods are closed until explicitly classified; an unregistered method requiresadmin.
Core method classification
Core methods are classified at import inpraisonaiagents.gateway.protocols:
Connection-lifecycle frames (
hello, join, leave) run before a client has negotiated any scope, so they classify as read — the lowest meaningful level. abort (and its wire alias message_abort) mutates the turn just like sending one, so both carry the same write requirement as message.
Registering a plugin method
A plugin that adds a new gateway surface registers its scope at import time:Per-payload-field escalation (raise, never lower)
A descriptor can raise the required scope when a specific field appears in the params — an otherwisewrite method that mutates configuration should require admin when the config field is present:
Strict-fields mode (fail closed on unknown fields)
For high-risk methods, enablestrict_fields=True with an allowlist of known-safe fields. Any field not in safe_fields or escalate_fields escalates to admin:
Incomparable scopes escalate to ADMIN
approvals and pairing are sibling capabilities — neither implies the other. When a method’s baseline is one and a field escalates to the other, the combined requirement escalates to admin rather than silently picking one (a single-scope check cannot satisfy both):
API reference
register_gateway_method is safe to call at plugin import time. The core-method classification runs at praisonaiagents.gateway.protocols import, so any plugin registration you do afterwards adds to the same registry.Common Patterns
Read-only dashboard viewer —[read] for status and transcripts without send or approve rights.
Send but not approve — [read, write] for operators who reply to users but cannot resolve tool approvals.
Approvals-only on-call — [read, approvals] for security-sensitive approval resolution without channel admin rights.
Full admin — [admin] for SREs who need pause/resume/reconnect plus all other capabilities.
Error Handling
HTTP 403 when scope check fails:message without write scope:
Best Practices
Default to read and add scopes as needed
Default to read and add scopes as needed
Start every operator with
[read] and expand only when their role requires it.Rotate per-token secrets independently
Rotate per-token secrets independently
Issue separate tokens per operator so you can revoke one role without rotating everyone.
Pair approvals with the allowlist
Pair approvals with the allowlist
Combine
approvals scope with /api/approval/allowlist for defence-in-depth on tool execution.Use admin sparingly
Use admin sparingly
Prefer explicit scope lists over
[admin] unless the operator truly needs channel control.Classify every new gateway method
Classify every new gateway method
An unregistered gateway method defaults to
admin (fail closed). When a plugin adds new surface, always call register_gateway_method(name, scope=...) at import time so the requirement matches the intent — not just whatever the current default happens to be.Related
Bind-Aware Auth
Token requirements when binding to external interfaces
Gateway Overview
Multi-channel gateway architecture and setup
Scoped Approvals
Resolve tool-execution approvals with the
approvals scope
