--- id: practice-pattern/explicit-unsafe-diagnostic-mode title: ExplicitUnsafeDiagnosticMode type: practice-pattern scheme: practice-pattern/0.1 status: candidate version: "0.1" summary: Keep an unsafe, field-revealing diagnostic capture opt-in per invocation, refused in production, and confined to a local owner-controlled sink. aliases: - OrwellLoggingDiagnostics uses: - model/observability - model/data - model/devsecops related_patterns: [] known_uses: - tmux-amq-orwell-flag --- # ExplicitUnsafeDiagnosticMode ## Intent Let a rare local debugging session capture fields that normal logging must omit, without turning that capture into a standing capability: it is selectable only per invocation, refused wherever a production policy applies, written only to a sink the invoking owner controls, and never projected to any remote or shared destination. ## Context Use this pattern where a component's normal `Log`/`LogRecord` output (`model/observability`) must omit fields carrying credentials, message bodies, or other data a `DataClassification`/`Sensitivity` policy restricts (`model/data`), but an engineer diagnosing a specific local failure occasionally needs exactly those fields to see what happened. The component runs, or can run, in more than one `EnvironmentPromotion` stage (`model/devsecops`), and at least one of those stages is production or production-adjacent. ## Problem Sensitive-field omission and diagnosability compete. Omitting the fields by default protects them, but it can also hide the one detail that explains a transport or protocol failure. Relaxing omission to help diagnosis risks either leaving the relaxed mode enabled by default, letting it run in production, or letting the captured fields leave the local machine through normal telemetry, log shipping, or shared CI artifacts — at which point the exception has become a standing disclosure. ## Forces - Diagnosis needs the exact fields normal policy omits, not a summary of them. - The unsafe mode must never become the default; defaults drift toward whatever is easiest to leave on. - A production environment cannot honor a request to disclose these fields regardless of who or what asks for it. - Local-only retention is only useful if it is still discoverable and removable by the person who created it. - Any path that projects general logs, metrics, or CI artifacts elsewhere becomes a path this data must not travel, even accidentally. - A warning that is easy to miss does not function as a control. ## Solution Therefore, provide a single opt-in diagnostic switch with these properties: 1. Safe logging is the default at every verbosity. Raising verbosity alone must not disclose fields the classification policy restricts. 2. An explicit per-invocation option selects the unsafe mode. Configuration files, inherited profile defaults, and background startup must not enable it silently — it is asked for anew, in the invocation, every time. 3. The production `EnvironmentPromotion` stage refuses the option outright. Wherever it is accepted, emit a visible warning before any additional field is captured. 4. The mode writes only to a sink the invoking owner controls, at a permission level that excludes other accounts (for example, a owner-only-readable local file). It is never added to a `LogStream`, metrics pipeline, State Hub event, or shared CI artifact. 5. Document precisely which fields the mode can capture. Prefer synthetic reproduction data over live captures. The invoking owner keeps the smallest useful capture and removes it after diagnosis, using whatever procedure that sink's owner defines. 6. Tests verify default omission, the per-invocation opt-in, refusal or absence of the option in production, sink permissions, and separation from any remote projection. ## Structure ```text Invocation -> unsafe-mode option (explicit, per-call) -> EnvironmentPromotion check: production? -> refused -> visible warning -> Logger (model/observability) -> default path: LogStream, filtered by DataClassification -> unsafe path: local owner-only sink, never a LogStream sink ``` The environment check and the routing decision happen at the same configuration point the normal logger is built from; there is no second place the unsafe path could be reintroduced. ## Dynamics An operator invokes the component with the unsafe-mode option because a specific failure needs fields normal logs omit. The component checks the current `EnvironmentPromotion` stage; in production the option is rejected or ignored with an explicit error, and diagnosis proceeds without it. Elsewhere, the component warns, opens the local sink at a restrictive permission mode, and logs at full detail for the remainder of that invocation only — the option does not persist to the next invocation. After diagnosis, the operator reads the local sink directly and removes it; nothing from this path reaches any aggregated or shared destination. ## Invariants 1. The unsafe mode is off by default and cannot be enabled by configuration inheritance, only by an explicit per-invocation option. 2. A production `EnvironmentPromotion` stage refuses the option. 3. Enabling the option always produces a visible warning before capture. 4. The sink is local, owner-controlled, and restricted to the owner's account (for example, mode `0600`). 5. Nothing captured under the unsafe mode reaches a `LogStream`, metrics pipeline, State Hub projection, or shared CI artifact. 6. The captured field set is documented, not open-ended. ## Evidence The practice should produce: - a test that the default path omits the restricted fields; - a test that the option is per-invocation and does not persist or inherit; - a test — or, where the component has no production/non-production distinction at all, an explicit statement of that absence — that the option is refused in production; - a test that the sink is created at a restrictive permission mode; and - a test or code-path review confirming no remote or shared sink can receive the unsafe-mode output. A known use that verifies only some of these should say plainly which ones, rather than implying full coverage. ## Consequences Diagnosis gets access to fields it otherwise could not see, without turning that access into a standing capability or a silent default. The cost is one more configuration branch to test, a local file an operator must remember to remove, and — for components with no existing environment distinction — the need to add one before the production refusal invariant can be verified at all. ## Failure Modes - **QuietDefault:** the option is honored from a config file or an inherited profile instead of being asked for at each invocation. - **NoProductionCheck:** the component has no way to refuse the option in production because it has no environment distinction to check against. - **SharedSink:** captured output lands in the same file, stream, or aggregator the default logs use. - **UnboundedCapture:** the mode is undocumented as to which fields it reveals, so it grows to cover more than diagnosis needs. - **SilentWarning:** the mode activates without a visible notice, so an operator can leave it running without noticing. ## When Not to Use Do not use this pattern to justify collecting sensitive fields the component could avoid needing in the first place, and do not use it where a component has no local, owner-controlled place to write a sink at all — in that case the safer answer is to improve error messages and structured non-sensitive diagnostics, not to add an unsafe capture mode with nowhere safe to put it. ## Known Uses ### tmux-amq `--orwell` flag tmux-amq implements the option at commit `04de219`, in `src/tamq/diagnostics.py` (the `configure()` entry point) and `src/tamq/cli.py` (the global `--orwell` flag, plumbed through the CLI's `configure(args.orwell, ...)` call and a matching `purge --orwell` cleanup subcommand). `tests/test_diagnostics.py::test_orwell_log_is_private` observes two of the invariants directly: enabling the option prints a warning containing `--orwell`, and the resulting log file is created at permission mode `0600` (invariant 3 and invariant 4). The per-invocation opt-in (invariant 1) is observed by construction — `configure()` takes `orwell` as a call argument with no persisted state — but has no dedicated negative test. This known use does **not** demonstrate invariant 2. tmux-amq has no production/non-production `EnvironmentPromotion` distinction to refuse the option against; it is a local CLI tool run directly by its operator, and `configure()` accepts `orwell=True` unconditionally. Invariant 5 (no remote projection) holds by tmux-amq's general architecture — it has no telemetry export path at all — rather than by a check specific to this mode. Invariant 6 (documented field scope) is not enforced in code; the flag raises the overall log level to `DEBUG` and adds a file handler rather than gating a named field list. Because one known use verifies a subset of the invariants and the pattern has not yet been observed in a component with a real production stage to refuse, this pattern stays at `candidate`. A second known use in a component with an `EnvironmentPromotion` distinction is the evidence that would test invariant 2 and could support promotion to `active`. ## Related Patterns None yet. A future pattern for field-level redaction policy (which fields a `DataClassification` restricts, independent of capture mode) would be a natural neighbor once more than one component needs it stated generically. ## Adoption Checklist - [ ] Confirm the component has, or can meaningfully check, a production/non-production distinction. - [ ] Add the per-invocation option; do not wire it to configuration files. - [ ] Refuse the option outright when the production stage is detected. - [ ] Emit a visible warning before capturing any additional field. - [ ] Route unsafe-mode output only to a local, owner-restricted sink. - [ ] Document the captured field set. - [ ] Give the operator a removal procedure for the sink. - [ ] Test default omission, opt-in scope, production refusal, sink permissions, and absence of remote projection. ## Evolution Version 0.1 generalizes the practice coordination-engine prepared as `OrwellLoggingDiagnostics` (`INFO-DEC-2026-003`, disposition adapt) from its one known use in tmux-amq. The name, canon-shape sections, and imports from `model/observability`, `model/data`, and `model/devsecops` are new in this version; the six practice points and the consumer boundary are carried over from the candidate. The next useful evidence is a known use in a component that actually has a production stage to refuse the option against.