█████████ █████████ ██████████ ██████████ ███████████
███▒▒▒▒▒███ ███▒▒▒▒▒███ ▒▒███▒▒▒▒███ ▒▒███▒▒▒▒▒█▒▒███▒▒▒▒▒███
███ ▒▒▒ ▒███ ▒███ ▒███ ▒▒███ ▒███ █ ▒ ▒███ ▒███
▒███ ▒███████████ ▒███ ▒███ ▒██████ ▒██████████
▒███ █████ ▒███▒▒▒▒▒███ ▒███ ▒███ ▒███▒▒█ ▒███▒▒▒▒▒███
▒▒███ ▒▒███ ▒███ ▒███ ▒███ ███ ▒███ ▒ █ ▒███ ▒███
▒▒█████████ █████ █████ ██████████ ██████████ █████ █████
▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒
A lightweight terminal log viewer for Docker containers. The agent runs on your server beside the Docker daemon and streams parsed, structured logs over QUIC to a TUI client on your machine.
┌──────────────────────────┐ ┌──────────────────┐
│ Docker │ │ │
│ └──► gader-agent │ ───── QUIC ───── │ gader (TUI) │
│ │ │ │
└──────────────────────────┘ └──────────────────┘
server your machine
- Live log streaming over QUIC with a shared-secret handshake
- Structured log parsing per service (Immich, Vaultwarden)
- Service filter (
Tab) — pre-filtered server-side to save bandwidth - Level filter (
l) — ALL / DEBUG / INFO / LOG / WARN / ERROR - Live grep search (
s) — searches message and context fields - Detail view (
e) — full message with wrapping - Scroll + follow mode with
Spaceto jump to latest - Automatic retry when a watched container restarts
- Self-signed TLS cert generated and persisted to
~/.gader/ - TUI trace logs written to
~/.gader/tui_logs
gader_agent/ — server-side binary: watches Docker, serves clients
gader_tui/ — client-side binary: TUI log viewer
gader_common/ — shared types (LogEntry, NetworkPacket)
Requires Rust nightly (see rust-toolchain.toml).
cargo build --releaseBinaries will be at:
target/release/gader_agenttarget/release/gader_tui
Both the agent and TUI use ~/.gader/ for config. On first run, each creates a
default secret file if one doesn't exist:
| Side | File |
|---|---|
| Agent | ~/.gader/server_secret |
| Client | ~/.gader/client_secret |
Set both files to the same secret before connecting:
# on the server
echo "your-secret-here" > ~/.gader/server_secret
# on your machine
echo "your-secret-here" > ~/.gader/client_secretOn first start, the agent generates a self-signed cert and key:
~/.gader/server.cert
~/.gader/server.key
These are reused on subsequent starts. Delete them to force regeneration.
Note: The TUI currently skips server certificate verification. TOFU cert pinning is planned.
gader-agent [OPTIONS]
Options:
-l, --listen <ADDR> Listen address for TUI clients [default: 0.0.0.0:23456]
-d, --docker <URL> Docker HTTP endpoint [default: http://127.0.0.1:2375]
--history-size <N> Log ring buffer size [default: 150]
--log-level <LEVEL> Log level (RUST_LOG takes priority) [default: info]The agent watches the immich_server and vaultwarden Docker containers by default.
Forward Docker's socket over SSH and run the agent locally against it:
# forward the remote Docker socket to a local port
ssh -N -L 2375:/var/run/docker.sock your-server
# run the agent locally, pointing at the forwarded socket
gader-agent --docker http://127.0.0.1:2375 --listen 127.0.0.1:23456# on the server
gader-agent --listen 0.0.0.0:23456
# forward the agent port to your machine
ssh -N -L 23456:localhost:23456 your-server
# on your machine
gadergader [OPTIONS]
Options:
-s, --server <ADDR> Agent address to connect to [default: 127.0.0.1:23456]
--log-level <LEVEL> Log level (RUST_LOG takes priority) [default: info]| Key | Action |
|---|---|
↑ / ↓ |
Navigate log entries |
| Scroll | Navigate log entries |
Space |
Jump to latest (re-enable follow mode) |
Tab |
Cycle service filter |
l |
Cycle level filter (All/DEBUG/INFO/WARN/ERROR) |
s |
Open search bar (live grep) |
Esc |
Close search / clear query / back |
Enter |
Close search bar (keep query active) |
e |
Expand selected entry to detail view |
Backspace |
Back to table from detail view |
q |
Quit |
- Implement the
LogParsertrait ingader_agent/src/parsers/:
pub struct MyParser;
impl LogParser for MyParser {
fn parse(&self, line: &str) -> Option<LogEntry> {
// parse `line`, return Some(LogEntry { .. }) or None
}
}- Register it in
gader_agent/src/main.rsalongside the existing watchers:
let _task = tokio::spawn(async move {
spawn_watcher(docker, "my_container", MyParser::new(), tx, c_token, state).await;
});All files live in ~/.gader/ on the respective machine.
| File | Machine | Description |
|---|---|---|
server_secret |
Server | Shared secret (agent side) |
client_secret |
Client | Shared secret (TUI side) |
server.cert |
Server | Self-signed TLS certificate |
server.key |
Server | TLS private key |
tui_logs |
Client | TUI trace log file |
Claude was used to generate boilerplate code for the tui.
Contributions are welcome!