Files
homelab/knecht/README.md
2026-04-04 15:17:33 +02:00

96 lines
3.6 KiB
Markdown

# knecht
CLI + TUI for managing Portainer stacks from the local `services/` directory.
## Installation
```sh
cd knecht
go build -o knecht .
# move to somewhere on your PATH, e.g.:
mv knecht /usr/local/bin/knecht
```
## Configuration
`~/.config/knecht/config.toml`:
```toml
url = "https://portainer.example.com"
token = "ptr_..."
ignore = ["portainer"] # stack names to hide (e.g. self-managed containers)
# Optional
endpoint = "local" # Portainer environment name, auto-detected if omitted
services_path = "/path/to/services" # defaults to git root / services
```
The API token is generated in Portainer under **Account → Access tokens**.
## Commands
| Command | Description |
|---------|-------------|
| `knecht` | Launch the TUI |
| `knecht list` | Print all stacks with status and drift summary |
| `knecht deploy <stack>` | Deploy a new stack from `services/<stack>/docker-compose.yml` |
| `knecht update <stack>` | Update an existing stack, preserving Portainer env vars |
| `knecht restart <stack>` | Stop then start a stack |
| `knecht diff <stack>` | Print compose and env key drift |
| `knecht logs [container]` | Open Dozzle in the browser; deep-links to a specific container if given |
`<stack>` always maps to a folder name under `services/`.
## TUI
Run `knecht` with no arguments to launch the interactive TUI.
```
┌─ stacks ──────────────┐ ┌─ detail ──────────────────────────────┐
│ ● traefik live │ │ rrr │
│ ● rrr live ~ │ │ │
│ ● jellyfin live │ │ status: running │
│ dummy not deploy │ │ drift: compose ~3 lines │
│ │ │ │
│ │ │ [u] update [d] diff [r] refresh │
└───────────────────────┘ └──────────────────────────────────────┘
```
### Keybinds
| Key | Action |
|-----|--------|
| `↑` / `↓` | Navigate stacks |
| `u` | Update selected stack (preserves Portainer env vars) |
| `D` | Deploy selected stack (only available when not deployed) |
| `d` | Toggle diff view |
| `e` | Generate or append `.env.example` from Portainer env keys |
| `r` | Refresh all stacks |
| `esc` | Return to summary view |
| `q` / `ctrl+c` | Quit |
## Drift Detection
knecht compares the local `services/<stack>/docker-compose.yml` against what is deployed in Portainer, and the keys in `services/<stack>/.env.example` against the env vars set in Portainer.
| Indicator | Meaning |
|-----------|---------|
| `~` next to stack name | Drift detected |
| `! missing: KEY` | Key is in `.env.example` but not set in Portainer |
| `? unknown: KEY` | Key is in Portainer but not in `.env.example` — press `e` to append |
| `no .env.example` | Portainer has env vars but no example file exists — press `e` to generate |
Stacks with no `.env.example` file are not penalised for env drift — absence of the file means "no opinion".
## Stack Convention
Each stack is a directory under `services/` containing:
```
services/<name>/
├── docker-compose.yml # required
└── .env.example # optional — documents required Portainer env vars
```
`knecht deploy <name>` / `knecht update <name>` read these files directly. Env var *values* are never stored locally — only the keys in `.env.example`, as documentation.