# CLAUDE.md Guidance for Claude Code when working in this repository. > User-facing docs are in Portuguese (BR): [README.md](README.md) and [docs/](docs/). > This file is in English and is the technical source of truth for how the code works. ## What this is A terminal UI (TUI) **installer** for the "App do Dono" **client middleware**. The middleware runs on a tenant's infrastructure and brokers communication between the tenant's servers and davinTI's **central server**. The installer is a guided wizard that: checks Docker → logs into the private registry → pulls images → (optionally) sets up a WireGuard tunnel (`vproxy`) when there is no public IP → collects configuration via terminal forms → generates config files (`config.toml`, `envs`) → runs the Docker containers. It is **not** the middleware itself — it only provisions and launches it via `docker`. ## Tech stack - **Go 1.25** - **Bubble Tea v2** (`charm.land/bubbletea/v2`) — Elm-architecture TUI runtime. - **Bubbles v2** — `textinput`, `spinner` components. - **Lip Gloss v2** — styling. - **BurntSushi/toml** — read existing `config.toml` for form defaults. - Module path: `git.davinti.com.br/davinTI/app-dono/tui`. ## Layout ``` cmd/main.go Entry point; starts the Bubble Tea program. internal/tui/ model.go Model struct, ConfigValues, loadConfig (defaults), InitialModel, Init. steps.go step enum — the ordered wizard stages. update.go Update: global keys + per-step dispatch (handlers). view.go View: header + per-step body + help footer (AltScreen). form.go Generic FormStep/FormField component (text/password/number/select). cmds.go Msg types and async tea.Cmd wrappers around docker/file ops. docker.go os/exec wrappers around the `docker` binary. config.go Generates & writes config.toml and envs (+ numeric validation). styles.go Lip Gloss palette/styles. Makefile Multi-arch build + S3 publish. ``` ## Architecture (Model–Update–View) - **Model** ([model.go](internal/tui/model.go)) holds all state: `currentStep`, progress/error flags, one `FormStep` per form, and `configValues` (`ConfigValues`) which accumulates each form's `map[string]string` output by section. - **Update** ([update.go](internal/tui/update.go)) handles global messages (`Ctrl+C`, window size, spinner tick) then dispatches on `currentStep` to per-step handlers. Step transitions happen when a form returns `done == true` or when an async completion message arrives. - **View** ([view.go](internal/tui/view.go)) renders per-step. - **Commands** ([cmds.go](internal/tui/cmds.go)) wrap every blocking op (docker calls, file writes) as a `tea.Cmd` returning a `Msg`: `DockerCheckedMsg`, `ImageDownloadFinishedMsg`, `ConfigFileMsg`, `DockerRunMsg`, `TickMsg`. ## Wizard flow `steps.go` defines the order. See [docs/fluxo.md](docs/fluxo.md) for full detail. 1. `StepCheckDocker` — `exec.LookPath("docker")`. Missing → `StepDockerInstall` (instructs manual install and exits; **never auto-installs Docker**). 2. `StepDockerLogin` → `StepDownloadImage` — `docker login` + `pull` of the app image. These registry credentials are **reused** later for the vproxy image. 3. `StepIPQuestion` — "public IP available?" - **Yes** → jump to `StepAppConfig`. - **No** → vproxy block: `StepWireguardConfig` → `StepGenerateWireguardFile` (writes `envs`) → `StepDownloadWireguard` → `StepRunWireguard`. 4. Config forms: `StepAppConfig` → `StepServerConfig` → `StepDatabaseConfig` → `StepCertConfig`. 5. `StepGenerateFile` — writes `config.toml` (validates numeric fields first). 6. `StepRunDocker` — runs `app-dono-cliente` container. 7. `StepDone`. ## Key constants In [update.go](internal/tui/update.go): - `imageName = hub.davinti.com.br:443/app-dono/app-cliente:latest` - `wireguardImageName = hub.davinti.com.br:443/davinti-vproxy:latest` - `configPath = config.toml`, `wireguardConfigPath = envs` In [docker.go](internal/tui/docker.go): `networkName = app-dono_app` (all containers join this network; created on demand). ## Generated files - **`config.toml`** — app config, bind-mounted at `/app/config.toml`. If present at startup it seeds form defaults via `loadConfig`. Generated by `GenerateConfigTOML`. - **`envs`** — vproxy/WireGuard env vars, passed via `--env-file`. Generated only when there is no public IP. By `GenerateWireguardConfig`. Both are gitignored. ## Containers | Container | Image | Notes | | ------------------ | -------------------- | -------------------------------------------------------- | | `app-dono-cliente` | app-cliente | `:8080`, mounts config.toml + cert dir, `--restart unless-stopped`, runs as host uid:gid. | | `vproxy` | davinti-vproxy | `--cap-add=NET_ADMIN`, `/dev/net/tun`, `--env-file envs`, only without public IP. | `seccomp=unconfined` is added to either container when the "Modo Compatibilidade" (`seccomp_unconfined`) select is `"Sim"` — for old machines. ## Gotchas (read before editing) - **Port:** `config.toml` always writes `port = 8080` (hardcoded in `GenerateConfigTOML`). The form's "Porta" value only sets the **host-side** port in the `docker run` mapping (`:8080`); inside the container the app always listens on 8080. - **Network name** is the `networkName` constant (`app-dono_app`) in [docker.go](internal/tui/docker.go); use it rather than re-typing the literal. - **Registry credentials** are collected once (login form) and reused for the vproxy pull — no second prompt. - Containers run detached and are verified with `docker inspect` for `running`; on failure the last 20 log lines are surfaced. ## Build & run ```bash go run ./cmd # run from source go build -o installer ./cmd # local binary make build # multi-arch (linux/darwin/windows, amd64/arm64) → ./dist make build VERSION=2.0.0 make clean make push S3_BUCKET= VERSION= # sync dist/ to S3 (version + latest) make release S3_BUCKET= VERSION= # per-binary copy with OS/arch naming ``` Tests (`go test ./...`) cover the pure logic in [config_test.go](internal/tui/config_test.go) (config.toml/envs generation + numeric validation) and [form_test.go](internal/tui/form_test.go) (`FormStep.Values()`). The docker wrappers, Update and View are not unit-tested — verify those by running `go build ./...` and exercising the TUI manually. Fully running the installer requires a working Docker daemon and registry access. ## Conventions - User-facing strings in the TUI are **Portuguese (BR)**; keep new ones consistent. - Keep code comments and identifiers matching the surrounding style (mixed English identifiers, Portuguese user messages). - When adding a wizard step: add it to `steps.go`, a handler in `update.go`, a render branch in `view.go`, and any new `Msg`/`Cmd` in `cmds.go`.