Skip to content

HTTP trace redaction

An article app may send credentials and receive an authentication token. Declare which values are sensitive so HTTP traces can show the request's progress without recording those values.

;; cf. examples/core/login/model.cljc — sensitive login request
(ns app.article-auth
  (:require [re-frame.core :as rf]
            [re-frame.http.managed]
            [re-frame.schemas]))

(def LoginResponse
  [:map
   [:token {:sensitive? true} :string]
   [:user-id :int]])

(rf/reg-event :auth/login
  {:sensitive [[:password]]}
  (fn [_ [_ credentials]]
    {:fx [[:rf.http/managed
           {:request    {:method :post :url "/api/login"
                         :body credentials :request-content-type :json}
            :sensitive? true
            :decode     LoginResponse
            :on-success [:auth/logged-in]
            :on-failure [:auth/login-failed]}]]}))

(rf/reg-event :auth/logged-in
  {:sensitive [[:value :token]]}
  (fn [{:keys [db]} [_ {:keys [value]}]]
    {:db (-> db
             (assoc-in [:auth :token] (:token value))
             (assoc-in [:auth :status] :authenticated))
     :sensitive [[:auth :token]]}))

(rf/reg-event :auth/login-failed
  (fn [{:keys [db]} _]
    {:db (assoc-in db [:auth :status] :error)}))

Each event registration classifies its own arguments; HTTP redaction does not carry over to them. Managed HTTP appends the success envelope to [:auth/logged-in], so the token's event path is [:value :token].

:sensitive? true redacts the request body, params and all URL query values, and the response payload, in HTTP traces. LoginResponse also marks the token by field: when that schema is used without a whole-request flag, the token is redacted while :user-id remains visible. Your receiving handler gets the real token. Its :sensitive effect classifies the copy stored in app-db: a response's classification does not automatically apply to the values your handler writes.

Add day8/re-frame2-schemas and require re-frame.schemas when using schema marks. A request with marks but without that artefact is refused before sending with :rf.error/schemas-artefact-missing.

Headers and URL parameters

Managed HTTP already redacts sensitive header names, including Authorization and Cookie, in every HTTP trace. No per-request flag is needed. It also redacts values of known secret URL parameters, such as access_token and api_key, while preserving other parameters and the endpoint's address. Matching is case-insensitive.

For names specific to your API, add them once on the effect registration:

(rf/reg-fx :rf.http/managed
  {:carriers {:headers      ["X-Article-Token"]
              :query-params ["article_token"]}}
  re-frame.http.managed/managed-handler)

This keeps the shipped handler and adds the names to its built-in redaction lists. Register it after requiring re-frame.http.managed, before issuing requests. The classification reference lists the built-in names and exact declaration forms.

Response bodies

Use a Malli schema in its EDN vector form to mark individual response fields. :sensitive? replaces a field with :rf/redacted; :large? replaces it with a size marker. Unmarked siblings remain visible unless the request's :sensitive? flag redacts the whole payload. Field marks also apply when the request is not flagged.

A keyword decoder such as :json, a decoder function, a registry-keyword schema reference or a compiled schema does not expose field marks to the classification walker. The response's shape is then unknown, so the body is omitted from exports rather than sent outside the app unclassified.

Local traces and exports

A non-2xx response keeps its raw error body for the app to inspect. It never passes through :decode, so its fields have no schema classification. HTTP exports therefore omit that body; a local dev trace may still contain it. Decode an API's structured error in the failure handler when needed, and classify any secret you then store in app-db.

Keep secrets out of traces covers classification on events and durable state, along with export policy. HTTP classification applies to dev traces and is removed with tracing in production.

Troubleshooting

Symptom Cause Fix
:rf.error/schemas-artefact-missing and no request The decode schema has marks, but the schemas artefact is not loaded Add day8/re-frame2-schemas and require re-frame.schemas
:rf.error/bad-classification naming :rf.http/managed Its :carriers declaration is malformed Use vectors of names under :headers and :query-params, as above
A token is redacted in HTTP traces but visible in app-db history The receiving handler wrote it to an unclassified path Return :sensitive for each stored path
An error body is missing from an export The body has no schema classification Inspect it locally; exports omit raw error bodies