Skip to main content
Gateway control commands are gated by content provenance: a message only counts as a command when it is a real interactive human turn, so an untrusted producer can never drive /stop, /new, or /compress even under a privileged identity.

Quick Start

1

Interactive human turn β€” command runs

A real user typing into a live chat keeps the default (allow_control=True), so a slash-prefixed message is classified as a command.
2

Untrusted producer β€” command is neutralised

A webhook, relay, or wake producer marks its message allow_control=False, so the same text is handled as plain text instead of a privileged command.

How It Works

A producer sets allow_control=False on non-interactive content; is_command short-circuits to False before the gateway ever classifies the message, so it is dispatched as plain text.

The Two-Gate Model

Command classification passes through two independent gates β€” content-trust fires first, identity ACL second. Identity alone is not enough: untrusted content is often attributed to an allowed identity β€” a webhook bound to the owner, a relayed or proactive event on the bot’s own session. Identity ACL passes, but content-trust does not, so the command never executes.

What This Prevents

Content provenance is now a first-class part of the contract, so a /-prefix in an untrusted body no longer drives privileged session control.

For Adapter Authors

When you write an adapter or a webhook/relay/proactive producer, set allow_control=False on the BotMessage unless the content is a real, interactive human turn on that channel. The default (True) keeps today’s behaviour for interactive turns; the primitive is safe by narrowing it for untrusted sources.
to_dict() and from_dict() carry allow_control. from_dict defaults it to True when the key is absent, so older serialised payloads still deserialize as trusted.
Content-trust is the first gate. CommandAccessPolicy.can_run(user_id, cmd) still runs afterward for messages that pass content-trust, so identity gating (admin_users, user_allowed_commands) is unchanged.

Configuration Reference


Backward Compatibility

The default is True, so today’s interactive traffic is unchanged. from_dict defaults the field to True when absent, so pre-PR serialised messages still deserialize as trusted. There is no config flag, CLI flag, or YAML change β€” this is a contract primitive, not a user-facing feature knob.

Untrusted Request Fencing

Prompt-plane counterpart β€” wraps untrusted payload content as data

Bot Command Access Control

Identity-based ACL that layers on top of content-trust

Webhook Channel

The primary untrusted producer

Gateway Inbound Hooks

HTTP-triggered agent runs with the same trust posture

Bot Chat Commands

What the gated commands actually do

Inbound Message Gate

Admission after is_command classifies