236 lines
11 KiB
Markdown
236 lines
11 KiB
Markdown
|
|
---
|
||
|
|
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.
|