Files
chanora/apps/chanora_flutter/lib/l10n/app_en.arb
T
EdisonJwa d2d9ba0a5b feat(diagnostics): A.3 — redacted in-memory log sink + user-initiated export
Replaces the diagnostics scaffold with the production redaction
policy + a user-initiated export path that satisfies DEC-016 (no
automatic uploads).

* `chanora_diagnostics::Redactor` applies the six policy rules to
  every captured log line: `$HOME` paths → `[home]`; IPv4 + IPv6
  literals → `[ip]`; email-shaped strings → `[email]`; long
  base64-ish tokens → `[token]`; substrings registered with
  `KnownSecretRegistry` → `[REDACTED]`. The registry implements
  SS-AUD-003 defence-in-depth: storage adapters can register
  secrets as they cross out of the keyring so an accidental
  `Debug` print is still scrubbed at write time.
* `InMemoryLogSink` is a bounded ring buffer (cap 500 lines in the
  bridge) that always passes lines through the redactor before
  storing them. `RedactingLogLayer` plugs it into `tracing-
  subscriber` alongside the existing logcat / fmt layers.
* `DiagnosticExport::from_sink` builds a plaintext blob — already
  redacted — combining free-form metadata (crate version, target
  os/arch) with the retained log tail. `bridge::api::
  export_diagnostics()` is the Flutter-facing entrypoint
  (`#[frb(sync)]`).
* `bridge_init` now installs the redaction layer on both Android
  and desktop hosts, switching from the global `fmt::init()`
  shortcut to a layered `Registry` so the in-memory sink can sit
  side-by-side with the platform sink.
* Flutter adds a bug-report icon to the AppBar; tapping it opens a
  scrollable monospace dialog with Copy and Close actions. New
  `diagnosticsAction` / `copyAction` / `closeAction` strings land
  in `app_en.arb` + `app_zh.arb`.

Tests cover the redaction matrix (IPv4, IPv6, email, long tokens,
known secret), the ring buffer capacity, and the full
`DiagnosticExport::to_text()` round-trip — 9/9 green.

Live-verified on Moto G: the dialog rendered a multi-line transcript
with `[ip]`, `[token]`, `[home]` substitutions, the metadata block
showed `target_os=android` `target_arch=aarch64`, and Copy placed
the same text on the clipboard.
2026-05-15 01:26:49 +08:00

64 lines
1.8 KiB
Plaintext

{
"@@locale": "en",
"@@x-source-of-truth": "DEC-015 (register v0.9.5). Template ARB for English. Server-provided content is NOT translated (ADR-008 / DEC-015).",
"appTitle": "Chanora",
"homeNotProductionReadyBanner": "Beta build — voice in/out wired; not production ready.",
"fieldServerHost": "Server address",
"fieldNickname": "Nickname",
"connectAction": "Connect",
"disconnectAction": "Disconnect",
"refreshAction": "Refresh",
"diagnosticsAction": "Diagnostics",
"copyAction": "Copy",
"closeAction": "Close",
"startAudioAction": "Start audio",
"pttHoldToTalk": "Hold to talk",
"pttTransmitting": "Transmitting…",
"statusIdle": "Not connected",
"statusConnecting": "Connecting…",
"statusConnected": "Connected to {server}",
"@statusConnected": {
"placeholders": { "server": { "type": "String" } }
},
"statusError": "Error: {message}",
"@statusError": {
"placeholders": { "message": { "type": "String" } }
},
"statusReconnecting": "Reconnecting… attempt {attempt}, in {delay}s",
"@statusReconnecting": {
"placeholders": {
"attempt": { "type": "int" },
"delay": { "type": "int" }
}
},
"statusConnectionLost": "Connection lost: {reason}",
"@statusConnectionLost": {
"placeholders": { "reason": { "type": "String" } }
},
"audioStatsLine": "TX {sent} frames • RX {received} frames • PTT {ptt}",
"@audioStatsLine": {
"placeholders": {
"sent": { "type": "int" },
"received": { "type": "int" },
"ptt": { "type": "String" }
}
},
"channelsHeading": "Channels",
"clientsHeading": "Online clients",
"countChannelsAndClients": "{channels} channels • {clients} online",
"@countChannelsAndClients": {
"placeholders": {
"channels": { "type": "int" },
"clients": { "type": "int" }
}
}
}