Files
Bose-SoundTouch/scripts/decrypt-diagnostic.go
Tobias Gesellchen 3cfb3da498 feat(export): encrypted diagnostic report for issue reporting
Adds a "Download diagnostic report" button on the Health tab that
produces an age-encrypted .age file the user can attach to a GitHub
issue without exposing sensitive data.

Archive contents (tar.gz, then age-encrypted with the maintainer's
SSH ed25519 public key):
- diagnostic.json         structured health/device summary (no secrets)
- datastore/…/*.xml       raw on-disk XML verbatim for diff vs HTTP
- http/service/…          live service HTTP responses per account/device
- http/speaker/…          live speaker API responses (port 8090)
- ssh/speaker/…           CA bundles + logread (last 20 min, 127.0.0.1
                          filtered) + dmesg fetched via SSH
- system/ca.pem           service CA cert
- system/resolv.conf      host DNS resolver config
- settings.json           service settings (OAuth secrets redacted)
- env.txt                 filtered process environment
- logs/service.txt        in-memory service log buffer

Supporting tooling:
- scripts/setup-diagnostic-key.sh  one-time SSH key-pair generation
- scripts/decrypt-diagnostic.go    go run helper for maintainer decryption
- keys/public/diagnostic.pub       committed public key (matches github.com/gesellix.keys)
- docs/DIAGNOSTIC-EXPORT.md        maintainer setup + user workflow guide
- docs/concepts/ENCRYPTED-EXPORT.md  research notes and architecture rationale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 20:02:57 +02:00

67 lines
1.6 KiB
Go

//go:build ignore
// decrypt-diagnostic.go — maintainer-side helper to decrypt a diagnostic report.
//
// The decrypted content is a .tar.gz archive containing:
// - diagnostic.json structured health/device summary
// - datastore/... raw XML files from the sender's datastore
//
// Usage:
//
// # Decrypt to stdout and extract in one step:
// go run scripts/decrypt-diagnostic.go aftertouch-diagnostic-<timestamp>.age | tar xz
//
// # Or decrypt to a .tar.gz file first:
// go run scripts/decrypt-diagnostic.go aftertouch-diagnostic-<timestamp>.age > report.tar.gz
// tar xzf report.tar.gz
//
// The private key is read from keys/private/diagnostic (relative to the repo root).
package main
import (
"fmt"
"io"
"os"
"filippo.io/age"
"filippo.io/age/agessh"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "usage: go run scripts/decrypt-diagnostic.go <file.age>")
os.Exit(1)
}
keyPath := "keys/private/diagnostic"
privKeyBytes, err := os.ReadFile(keyPath)
if err != nil {
fmt.Fprintf(os.Stderr, "read private key %s: %v\n", keyPath, err)
os.Exit(1)
}
id, err := agessh.ParseIdentity(privKeyBytes)
if err != nil {
fmt.Fprintf(os.Stderr, "parse identity: %v\n", err)
os.Exit(1)
}
f, err := os.Open(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "open %s: %v\n", os.Args[1], err)
os.Exit(1)
}
defer f.Close()
r, err := age.Decrypt(f, id)
if err != nil {
fmt.Fprintf(os.Stderr, "decrypt: %v\n", err)
os.Exit(1)
}
if _, err := io.Copy(os.Stdout, r); err != nil {
fmt.Fprintf(os.Stderr, "write: %v\n", err)
os.Exit(1)
}
}