Instruction file imported from miladbeigi/vaultui (
.cursor/rules/development.mdc). Copyright stays with the author.
VaultUI Development Lifecycle
Environment Setup
- Go 1.25+ required (
go versionto check) - Install golangci-lint:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - Start the local Vault dev server with seed data:
docker compose up -d - Vault is at
http://127.0.0.1:8200with root tokenroot - Verify seed completed:
docker compose logs seed | tail -1should show "Seed data loaded successfully."
Running the App
- Quick run:
VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN=root go run . - Config file:
~/.vaultui.yamlsupports multiple Vault contexts - CLI flags:
--vault-addr,--token,--namespace,--config,--auth-method,--username,--password,--role-id,--secret-id,--auth-mount - Context switching inside the TUI:
:ctxcommand
Making Changes
Vault API methods (internal/vault/)
- One file per engine/feature (e.g.,
secrets.go,pki.go,transit.go,identity.go) - Wrap errors with
fmt.Errorf("context: %w", err) - Use the cache for list operations:
c.cache.Get(key)/c.cache.Set(key, value) - Return typed structs, not raw
map[string]interface{}
TUI views (internal/ui/views/)
- Every view implements the
ui.Viewinterface:Init(),Update(),View(),Title(),KeyHints() - Use
navKeys(defined inengines.go) for keyboard navigation bindings - Use the
components.Tablefor list views andcomponents.Breadcrumbfor path-based views - Never inline Lipgloss colors — always use
styles.*frominternal/ui/styles/theme.go
Wiring new views
- Add
:commandininternal/app/app.goexecuteCommand()method - Add jump key in
internal/app/keys.goif it deserves a number shortcut - Handle the jump key in
app.goUpdate()under thetea.KeyMsgswitch - Update the dashboard quick nav in
internal/ui/views/dashboard.gorenderQuickNav()
Seed data (scripts/seed.sh)
- Add test data for every new feature so it can be tested locally
- After changing seed:
docker compose down && docker compose up -d vault seed - Wait a few seconds, then verify:
docker compose logs seed | tail -1
Testing
- Write unit tests for every new view and API method
- Test files live next to source:
foo_test.goalongsidefoo.go - Use the
newTestClient(t)helper (defined inengines_test.go) for view tests - Cover these states for every view: loading, loaded with data, error, empty, keyboard interactions
- Run tests:
make testorgo test ./... - Run a specific package:
go test -v ./internal/ui/views/ -run TestName
Pre-Commit Checks
Always run before committing:
make ci
This runs all 6 checks that match GitHub Actions CI exactly:
make fmt— gofmt formattingmake vet— go vet static analysismake lint— golangci-lintmake test— all unit testsmake build— binary compiles (with version ldflags)make tidy— go.mod/go.sum are clean
Fix all issues before committing. CI will reject the push otherwise.
Versioning and Releases
- We follow Semantic Versioning:
vMAJOR.MINOR.PATCH- PATCH: bug fixes, doc updates
- MINOR: new features (new engine browser, new command, etc.)
- MAJOR: breaking changes (config format change, removed flags, etc.)
- Version info is embedded via
-ldflagsininternal/version/version.go make buildautomatically injects version, commit, and date- To cut a release:
- Update
CHANGELOG.md— move items from[Unreleased]to a new version section - Commit the changelog
- Run
make release VERSION_TAG=v0.X.0
- Update
- This pushes a git tag; GitHub Actions runs GoReleaser to produce:
- Cross-platform binaries (linux/darwin, amd64/arm64)
- Checksums file
- Docker images at
ghcr.io/miladbeigi/vaultui - GitHub Release with attached assets
- GoReleaser config lives in
.goreleaser.yaml; release workflow in.github/workflows/release.yml
UI Validation (before committing)
After make ci passes, always validate the change in the live TUI before committing. The user runs the app — not the assistant.
- Ensure docker compose is running:
docker compose up -d - Verify seed data:
docker compose logs seed | tail -1 - Give the user the exact command to run and the steps to exercise the feature (which command to type, which key to press, what to look for)
- Wait for the user to confirm that the change works as expected before committing
- Do NOT commit or push until the user explicitly confirms
If the feature involves auth methods, also suggest testing with userpass or approle:
go run . --vault-addr=http://127.0.0.1:8200 --auth-method=userpass --username=testuser --password=testpass
Committing
- Message style: verb + what changed ("Add X browser", "Fix Y navigation", "Remove Z feature")
- Use HEREDOC for multi-line messages:
git commit -m "$(cat <<'EOF' Summary line here Optional body with details. EOF )" - Push after each logical unit of work
- Run
make cibefore every commit - Only commit after user has confirmed the UI validation
Key Project Conventions
docs/DESIGN.mdis the source of truth for the roadmap and phase plan- All Vault API methods go in
internal/vault/with caching - All UI views implement
ui.Viewand go ininternal/ui/views/ - Reusable UI components go in
internal/ui/components/ - Navigation uses a stack-based router (
internal/app/router.go) — Push/Pop/ResetToRoot - The command palette (
:prefix) is handled inapp.goexecuteCommand() - Jump keys (1-6) are defined in
keys.goand handled inapp.goUpdate()