Raxol. AgentClientProtocol. Ext. AttachPolicy. Runner
(Raxol Agent Client Protocol v0.1.0)
Copy Markdown
View Source
THE single fail-closed admission funnel. There is exactly ONE funnel:
authorize/2. No second try/catch wrapper exists anywhere — a second funnel
with weaker timeout/crash semantics would be a dual-ownership hole.
authorize/2 :: {:ok, %Grant{}} | {:denied, reason_atom}. The bus/reattach
layer treats ANY {:denied, _} as deny and answers with the single deny wire
envelope (-32000 "attach denied", NO data); the specific reason atom goes
to telemetry/Logger ONLY — a remote attacker gets one bit (anti-oracle).
The exhaustive outcome table
| Policy did… | Runner returns |
|---|---|
{:ok, %Grant{}} well-formed (actor["id"] binary, scope :attach), sid match | {:ok, grant} |
{:ok, %Grant{}} session_id != ctx.session_id | {:denied, :grant_session_mismatch} |
{:ok, %Grant{}} scope ∉ [:attach] | {:denied, :scope_not_allowed} |
{:ok, %Grant{}} actor w/o binary "id" | {:denied, :malformed_grant} |
{:ok, not-a-Grant / nil field} | {:denied, :malformed_grant} |
{:error, _} | {:denied, :policy_error} |
| any other term | {:denied, :non_conforming_return} |
| raised / threw / exited | {:denied, :policy_crash} |
hung past policy_timeout_ms | {:denied, :policy_timeout} (task brutally killed) |
| Task.Supervisor missing/dead | {:denied, :policy_infra} (never raises) |
Summary
Functions
Run policy.authorize_attach(ctx) inside the fail-closed funnel.
The canonical wire projection of a deny. ALL {:denied, _} reasons
collapse to this single frame (-32000 "attach denied", no data) so the
wire observable is byte-identical across reasons (anti-oracle).
The transport layer owns emitting it; exposed here so the collapse is provable
in one place.
Functions
@spec authorize(module(), map()) :: {:ok, Raxol.AgentClientProtocol.Ext.AttachPolicy.Grant.t()} | {:denied, atom()}
Run policy.authorize_attach(ctx) inside the fail-closed funnel.
Options:
:task_supervisor— theTask.Supervisorname/pid (default the package-levelRaxol.AgentClientProtocol.Ext.AttachPolicy.TaskSupervisor, overridable per endpoint viaconfig :raxol_agent_client_protocol, :attach_task_supervisor).:timeout_ms— the bounded policy timeout (default 5000;config :raxol_agent_client_protocol, :attach_policy_timeout_ms).
@spec authorize(module(), map(), keyword()) :: {:ok, Raxol.AgentClientProtocol.Ext.AttachPolicy.Grant.t()} | {:denied, atom()}
The canonical wire projection of a deny. ALL {:denied, _} reasons
collapse to this single frame (-32000 "attach denied", no data) so the
wire observable is byte-identical across reasons (anti-oracle).
The transport layer owns emitting it; exposed here so the collapse is provable
in one place.