Skip to main content

Module redaction

Module redaction 

Source
Expand description

The one rule set that makes text a run printed fit to publish.

Evidence carries text nobody wrote for a reader outside the machine it ran on: the reason a case skipped, the tail of a failure, a command’s captured output. Any of it can hold a credential the command echoed or a path that names the account it ran under, and once it is on a public page it cannot be taken back. public_text is the rule for that text; wiring the evidence publication to it is I1955, and until then nothing calls it.

redact_secrets alone is applied today wherever this process hands back or sends text it did not write (I2161): every HTTP error body (http::router::error_response, which http::router::Response::error returns), every JSON-RPC error (mcp::protocol::error) and MCP tool refusal (mcp::surface::Surface::call_from), the request fields the server logs (http::router::Router::foreign_origin, http::server::answer_http), every field of every event and span the log writes, whoever logged it (logging::redact, I2708), a handover body before it is signed into the mesh journal and replicated in clear (mesh::handover::to_body), and the free text of a review as it is signed (mesh::cooperation::redacted, I2708). Two records use it to find a credential rather than to send one: continuity::record::portability refuses a continuity record that carries one, and reasoning::store::redact takes it out of a reasoning record before the record is kept. These are all of its callers; no_answer_carries_a_credential holds this list to the code in both directions.

The credential shapes are not a second table. They are a port of MJ_CAPTURE_SECRETS and MJ_CAPTURE_SECRET_ASSIGN in lib/capture.sh, the table the prompt archive has redacted with from the start, applied the way mj_capture_redact applies it: each shape in table order over the whole text, every leftmost-longest match replaced by a marker naming the shape, and then the assignment rule, which keeps the name a value was assigned to. Two tests hold the port to the table: one reads lib/capture.sh and compares the shapes, and one fixture, test/fixtures/redaction/shapes.tsv, is run through both implementations. The crate has no regular-expression engine, so the shapes are matched by the small hand-written matcher below, which knows exactly the constructs the table uses.

Two neighbours are different concerns and stay apart: execution::redact removes the fields a capability’s input schema marks sensitive, and generate::forbidden_in is the last word on a published artifact, which public_text asks after it has done its work. Before asking, public_text refuses a text that still carries a secret no shape covers: one whose announcing shape fired, in this call or in an earlier redaction whose marker the text carries, and one whose armour or name is left once the shapes have run. Redacting the announcement is what would let the rest past that check.

use majordomus_cli::redaction::redact_secrets;

// assembled at run time, so that no committed file carries a credential's shape
let token = format!("{}{}", "ghp_", "a".repeat(24));
let out = redact_secrets(&format!("pushing with {token} failed"));
assert_eq!(out.text, "pushing with [redacted:github-token] failed");
assert_eq!(out.kinds, ["github-token"]);

Structs§

Redacted
Text after redact_secrets or public_text, with the shapes that fired in it.

Functions§

normalise_machine_paths
The repository root written as <repo> and the home directory as <home>.
public_text
Text fit to publish: machine paths normalised, then credentials redacted, then checked.
redact_secrets
Every credential in text replaced by a marker that names its shape.
shape_names
The names of the credential shapes, in the order they are applied, then assignment.