chart/docs/plan_diagnostics_improvements.md

4.8 KiB

Diagnostic Access Improvements

Goal

Make it practical for an agent on a separate SSH machine to diagnose browser and production failures without granting broad production control or asking the user to paste console output.

Browser Captures

Diagnostic mode (?diag=1) offers Capture diagnostic. Upload and metadata remain authenticated. The PNG URL at /api/debug/captures/{id} is public by its 72-bit id, which is the explicit handoff capability a user shares with an agent.

After inspecting a user-shared capture, the agent must immediately call:

DELETE /api/debug/captures/{id}

The 24-hour expiry and 50-capture cap remain a backstop. Do not inspect capture URLs that the user has not explicitly supplied.

Production Diagnostics

Do not grant an agent a general production shell or Docker-group membership. Docker access is effectively root access, and arbitrary shell access can expose environment variables, OAuth tokens, and mounted volumes.

Instead create a dedicated chart-debug production account with a forced-command SSH wrapper. It accepts only a small, read-oriented command set:

logs --since <duration>
status
container-state
recent-deploy
capture-read <capture-id>
capture-delete <capture-id>

The wrapper must reject arbitrary commands and paths. It should cap output, redact known secret patterns, and log every request. Use a dedicated SSH key that can be revoked without affecting deployment or normal administration.

Expected agent usage:

ssh chart-debug@production logs --since 20m

Installation on the Coolify host

The implementation lives in ops/chart-debug-command and ops/install-chart-debug. The Coolify-side agent must run as root on the Docker host, not inside the application container.

  1. Identify the current chart container and a stable name prefix that survives deploys:

    docker ps --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}'
    

    The Coolify resource UUID is dgvch0xqv8uvjfor7dl8bwl9; a likely anchored pattern is ^dgvch0xqv8uvjfor7dl8bwl9, but the agent must verify it matches exactly one running container before installation.

  2. Run the installer from a checkout containing ops/, using the dedicated public key supplied out-of-band:

    sudo ./ops/install-chart-debug \
      --public-key 'ssh-ed25519 AAAA... chart-debug restricted production diagnostics' \
      --container-pattern '^VERIFIED-STABLE-PREFIX' \
      --url https://chart.amow.com
    
  3. Verify the account is locked, files are root-owned, sudo policy is valid, the selector matches exactly one container, allowed commands work, and an arbitrary command is denied:

    passwd -S chart-debug
    stat -c '%U:%G %a %n' /usr/local/sbin/chart-debug-command \
      /etc/chart-debug.conf /etc/sudoers.d/chart-debug
    visudo -cf /etc/sudoers.d/chart-debug
    sudo -u chart-debug sudo -n /usr/local/sbin/chart-debug-command 'container-state'
    sudo -u chart-debug sudo -n /usr/local/sbin/chart-debug-command 'logs --since 5m'
    if sudo -u chart-debug sudo -n /usr/local/sbin/chart-debug-command 'shell'; then
      echo 'ERROR: arbitrary command was allowed'; exit 1
    else
      echo 'arbitrary command correctly denied'
    fi
    
  4. Confirm SSH permits the chart-debug user. If AllowUsers is configured, add chart-debug; do not enable password authentication. Report the host or IP and SSH port so the client alias can be configured.

Security invariants:

  • Do not add chart-debug to the Docker group.
  • Do not install the private key on production or paste it into chat.
  • Keep /etc/chart-debug.conf, the wrapper and sudoers entry root-owned.
  • Keep the account password locked and the authorized_keys restrict forced command intact; no additional unrestricted keys.
  • The wrapper must match exactly one running container. Zero or multiple matches fail closed.
  • Verify denials as well as successful commands. Audit records are available via journalctl -t chart-debug.
  • Keep secrets off process command lines. The status command supplies CHART_AUTH_TOKEN to curl through stdin (curl --config -), never -H.
  • Assign resolve_container before invoking Docker. Calling it inline through command substitution can trap its exit in a subshell and obscure the intended fail-closed return code.

Observability

Keep browser performance telemetry separate from production access. A future frontend recorder should locally aggregate frame timing, long tasks, tick rate, visible bars, and rendered line/level counts, then periodically upload compact, authenticated summaries. Pair it with server timing for bar handling, level rebuilds, WebSocket serialization, and the existing loop-lag measure.

This separates rendering, feed, transport, and backend pressure without logging prices, drawing text, cursor positions, screenshots, or per-tick event history.