chart/docs/plan_diagnostics_improvements.md

122 lines
4.5 KiB
Markdown

# 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:
```bash
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:
```bash
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:
```bash
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`.
## 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.