From 43230d19a8a89564177da5709f5d1b2ecb87cf24 Mon Sep 17 00:00:00 2001 From: JohnnyT Date: Fri, 2 Oct 2026 06:00:54 -0600 Subject: [PATCH] Says what still carries the token at :debug statifier_router 0.10.0 keeps the location token out of its own query log, so the hold desk's moduledoc, the controller's moduledoc and the guide no longer give the router's binds as the reason to keep :debug out of production. They name what remains: the position writes, which Ecto prints with the blob cut short, and the query telemetry event, which carries the position writes and the desk post job's insert. Oban runs its own statements with log: false, so the job insert prints no query log line. The guide and the moduledoc also say where the location is stored - the router's location table, the execution's position_blob in the clear, and the desk post job's arguments - and that whoever can read those tables holds the capability. This app configures no Oban pruner and Oban prunes nothing by default, so the job rows stay. The filter_parameters comment says that "token" also redacts any other param whose key contains it, the CSRF token among them. No config value changes. Refs: se-2poc --- config/config.exs | 5 +- docs/guides/basichttp-front.md | 51 ++++++++++++++----- lib/statifier_examples/hold_desk.ex | 29 +++++++++-- .../controllers/basic_http_controller.ex | 7 +-- 4 files changed, 73 insertions(+), 19 deletions(-) diff --git a/config/config.exs b/config/config.exs index d0ad034..64f3cd3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -122,7 +122,10 @@ config :logger, :default_formatter, config :phoenix, :json_library, Jason # A BasicHTTP location's token is a bearer capability, so a params log -# names it filtered, beside Phoenix's own default. +# names it filtered, beside Phoenix's own default. Phoenix filters every +# param whose key contains a listed string, so "token" also redacts any +# other token-named param in those logs, the CSRF token (`_csrf_token`) +# among them. config :phoenix, :filter_parameters, ["password", "token"] # OpenTelemetry. `opentelemetry_statifier` brings only the API, so the SDK's diff --git a/docs/guides/basichttp-front.md b/docs/guides/basichttp-front.md index 4bf65c5..b7a9d04 100644 --- a/docs/guides/basichttp-front.md +++ b/docs/guides/basichttp-front.md @@ -20,17 +20,39 @@ which drives the whole of it through the controller. Anyone who holds a location can post events to that execution, and the router authenticates nothing beyond possession of it (ruled by the operator, 2026-09-30). The hold hands its location to the one desk its -request names and to nobody else. No request line or dispatch log carries -it: the endpoint's `Plug.Telemetry` logs no request line under -`/basichttp` (`StatifierExamplesWeb.Endpoint.log_level/1`), the route is -`log: false`, and `:filter_parameters` names `token`. Ecto's query log at -`:debug` prints bound parameters, and the router binds the token to look -a location up and to store it, so a host keeps `:debug` out of -production; at `:info`, the production level here, no query is logged. -A host serves -the base URL over TLS and rotates a location that may have leaked with -`StatifierRouter.BasicHTTP.rotate_location/2`, after which the old one -answers 404. +request names. No request line or dispatch log carries it: the endpoint's +`Plug.Telemetry` logs no request line under `/basichttp` +(`StatifierExamplesWeb.Endpoint.log_level/1`), the route is +`log: false`, and `:filter_parameters` names `token`. + +The location is also stored, and whoever can read where it is stored +holds the capability as surely as the desk does. The router's location +table holds the token, which the front looks the location up by. The +location is part of the execution's persisted state: `_ioprocessors` +names it, and the execution's `position_blob` carries it in the clear, +because `StatifierExamples.Persistence` stores the blob as `:binary`. +And the `StatifierExamples.HoldDesk.DeskPost` job that carries the POST +holds it in its arguments, in a job row this app never prunes (see the +end of "The outbound send runs in the executor"). + +A host keeps `:debug` out of production; at `:info`, the production level +here, no query is logged. `statifier_router` 0.10.0 runs its own three +statements that bind the token with Ecto's `log: false`, so the router's +query log no longer prints it, but other statements still bind it. +`statifier_persistence` writes the position on the create and on every +step, and Ecto's `:debug` query log prints those writes with the blob cut +short by its inspect limit: the token is unreadable in the line, not +absent from its parameters. Ecto's query telemetry event carries every +statement's bound parameters whatever the `log` option says, so a handler +on it receives the position writes and the desk post job's insert, whose +own log line Oban suppresses. The router's +`docs/adr/0002-addressing.md`, the Note of 2026-10-02 on the location +token and the query log, records the router's half of this and the +persisted state. + +A host serves the base URL over TLS and rotates a location that may have +leaked with `StatifierRouter.BasicHTTP.rotate_location/2`, after which +the old one answers 404. ## The pins @@ -135,7 +157,12 @@ database. The job's arguments carry the planned POST as it was planned, body included, so the `reply_to` location the body hands the desk is written to -the jobs table with it, and stays there until the host prunes the job. +the jobs table with it. This app configures no Oban pruner, and Oban +prunes nothing unless one is configured, so the row and the location in +it stay, after the job completes or is cancelled, until a host deletes +them; anyone who can read the jobs table holds the capability (see "A +location is a bearer capability" above). A host that prunes sets Oban's +`:pruner`. ## The front diff --git a/lib/statifier_examples/hold_desk.ex b/lib/statifier_examples/hold_desk.ex index f558cf2..7ce98ae 100644 --- a/lib/statifier_examples/hold_desk.ex +++ b/lib/statifier_examples/hold_desk.ex @@ -16,9 +16,32 @@ defmodule StatifierExamples.HoldDesk do **A location is a bearer capability** (ruled by the operator, 2026-09-30): anyone who holds it can post events to that execution, and the router authenticates nothing beyond possession of it. This module - hands the location to the desk the hold request names and to nobody - else, and never logs it; `StatifierExamplesWeb.BasicHTTPController` - says what keeps it out of the request log. + hands the location to the one desk the hold request names and writes + no log line of its own that carries it; + `StatifierExamplesWeb.BasicHTTPController` says what keeps it out of + the request log. Whoever can read this app's database holds the + capability too: the router's location table holds the token; the + location is part of the execution's persisted state, in + `_ioprocessors` inside the execution's `position_blob`, in the clear + (`StatifierExamples.Persistence` stores the blob as `:binary`); and the + arguments of the `StatifierExamples.HoldDesk.DeskPost` job that carries + the POST hold it, in a job row this app never prunes. + + **The `:debug` limit.** `statifier_router` 0.10.0 runs its own three + statements that bind the token - the front's lookup, the location + insert at create and the rotation upsert - with Ecto's `log: false`, + so the router's query log no longer prints it. The token still reaches + a `:debug` repo through statements that are not the router's. + `statifier_persistence` binds the execution's position on the create + and on every step, and Ecto's query log prints those writes with the + blob cut short by its inspect limit, which makes the token unreadable + in the line but not absent from the parameters. And Ecto's query + telemetry event carries every statement's bound parameters whatever + the `log` option says, so a handler on it receives the position writes + and the desk post job's insert, whose own log line Oban suppresses. So + this app keeps `:debug` out of production, as the router's ADR-0002 + advises in its Note on the location token and the query log, and + rotates a location that may have leaked. `config/0` is a router configuration of its own, separate from `StatifierExamples.RoutedWorkflow.config/0`: only this configuration diff --git a/lib/statifier_examples_web/controllers/basic_http_controller.ex b/lib/statifier_examples_web/controllers/basic_http_controller.ex index a9067b3..a02382a 100644 --- a/lib/statifier_examples_web/controllers/basic_http_controller.ex +++ b/lib/statifier_examples_web/controllers/basic_http_controller.ex @@ -14,9 +14,10 @@ defmodule StatifierExamplesWeb.BasicHTTPController do nothing else. No request line or dispatch log carries it: `StatifierExamplesWeb.Endpoint.log_level/1` skips the request line for `/basichttp`, the route is `log: false`, and `:filter_parameters` names - `token`. At `:debug` Ecto's query log prints bound parameters, and the - router's location lookup binds the token, so a host keeps `:debug` out - of production. + `token`. The router's own lookup no longer prints the token at `:debug` + (`statifier_router` 0.10.0 runs it with Ecto's `log: false`); + `StatifierExamples.HoldDesk` says what still carries the token at that + level, and why a host keeps `:debug` out of production. """ use StatifierExamplesWeb, :controller