mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 00:26:29 +00:00
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>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
9312e27019
commit
3cfb3da498
@@ -0,0 +1,66 @@
|
||||
//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)
|
||||
}
|
||||
}
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup-diagnostic-key.sh — one-time key generation for the encrypted
|
||||
# diagnostic export feature. Run this once as the project maintainer.
|
||||
#
|
||||
# Output:
|
||||
# keys/private/diagnostic SSH ed25519 private key (gitignored)
|
||||
# keys/private/diagnostic.pub Matching public key (gitignored — copy in keys/public/)
|
||||
# keys/public/diagnostic.pub Public key in version control
|
||||
#
|
||||
# After running this script:
|
||||
# 1. Add the public key to your GitHub account SSH keys so it appears
|
||||
# at https://github.com/<you>.keys — this lets users verify the key.
|
||||
# 2. Update the DiagnosticPublicKey constant in
|
||||
# pkg/service/export/encrypt.go to match keys/public/diagnostic.pub.
|
||||
# 3. Commit keys/public/diagnostic.pub and the updated constant.
|
||||
# 4. Keep keys/private/diagnostic somewhere safe (the .gitignore protects
|
||||
# it from accidental commits, but it is NOT backed up by git).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
KEY_DIR="$REPO_ROOT/keys"
|
||||
PRIVATE_DIR="$KEY_DIR/private"
|
||||
PUBLIC_DIR="$KEY_DIR/public"
|
||||
|
||||
mkdir -p "$PRIVATE_DIR" "$PUBLIC_DIR"
|
||||
|
||||
KEY_FILE="$PRIVATE_DIR/diagnostic"
|
||||
|
||||
if [[ -f "$KEY_FILE" ]]; then
|
||||
echo "Key already exists at $KEY_FILE — delete it first to regenerate."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ssh-keygen -t ed25519 \
|
||||
-C "aftertouch-diagnostic@gesellix" \
|
||||
-N "" \
|
||||
-f "$KEY_FILE"
|
||||
|
||||
cp "$KEY_FILE.pub" "$PUBLIC_DIR/diagnostic.pub"
|
||||
cp "$KEY_FILE.pub" "$REPO_ROOT/pkg/service/export/diagnostic.pub"
|
||||
|
||||
echo
|
||||
echo "Keys generated:"
|
||||
echo " Private : $KEY_FILE (gitignored — keep safe)"
|
||||
echo " Public : $PUBLIC_DIR/diagnostic.pub (canonical — add to GitHub)"
|
||||
echo " Embed : pkg/service/export/diagnostic.pub (compiled into binary)"
|
||||
echo
|
||||
echo "Public key:"
|
||||
cat "$PUBLIC_DIR/diagnostic.pub"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo " 1. Add the public key to your GitHub account:"
|
||||
echo " https://github.com/settings/ssh/new"
|
||||
echo " 2. Commit keys/public/diagnostic.pub and pkg/service/export/diagnostic.pub."
|
||||
Reference in New Issue
Block a user