info-tech-canon/infospace/patterns/ExplicitUnsafeDiagnosticMode.md

236 lines
11 KiB
Markdown
Raw Normal View History

---
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.