Go backend powering the Tidefly API, deployment engine, and worker management.
The recommended way to install Tidefly is via the TUI setup wizard:
curl -fsSL https://raw.githubusercontent.com/tidefly-oss/tidefly-tui/main/scripts/install.sh | bash- Go 1.24+
- Docker
- Task —
go install github.com/go-task/task/v3/cmd/task@latest - Wire —
go install github.com/google/wire/cmd/wire@latest - Air —
go install github.com/air-verse/air@latest
git clone https://github.com/tidefly-oss/tidefly-plane
cd tidefly-plane
task setup # generates manifest/development/.env with secrets
task dev:up # starts Postgres, Redis, Caddy
task dev # starts backend with hot reloadBackend available at http://localhost:8181.
All configuration is done via environment variables. Run task setup to generate a .env with secure defaults.
See .env.example for all available options.
task setup Generate dev .env and secrets
task dev Start backend with hot reload
task dev:up Start dev infra (Postgres, Redis, Caddy)
task dev:down Stop dev infra
task dev:reset Stop dev infra and delete all volumes
task wire Regenerate Wire DI bindings
task build Build production binary
task build:docker Build Docker image
task lint Run golangci-lint
task test Run all tests
task tidy go mod tidy
The codebase follows a feature-first flat layout based on the Uber Go Style Guide. Every feature lives in its own package directly under internal/ — no layered handlers/services/repositories nesting.
cmd/tidefly-plane/ entry point (main.go)
internal/
├── platform/ cross-cutting concerns (no business logic)
│ ├── ca/ internal mTLS certificate authority
│ ├── config/ environment config + validation
│ ├── crypto/ AES-256-GCM encryption helpers
│ ├── eventbus/ in-process pub/sub (WebSocket fanout)
│ ├── logger/ structured logging + audit + DB log
│ ├── metrics/ Prometheus registry
│ ├── secret/ secret management
│ └── version/ build version (set via ldflags)
│
├── infra/ adapters for external systems
│ ├── caddy/ Caddy Admin API client
│ ├── database/ GORM connect + AutoMigrate
│ ├── ingress/ ingress adapter interface + Caddy impl
│ ├── redis/ Redis connect
│ └── runtime/ Docker/Podman abstraction
│
├── access/ shared access control — label constants, network/container filtering
├── bootstrap/ Wire DI providers + wire_gen.go + App wiring
├── middleware/ HTTP + Huma middleware (auth, rate limiting, logging, CORS…)
├── models/ GORM models (shared across features)
├── queue/ asynq enqueue helpers (zero internal imports — breaks cycles)
├── logmon/ container log monitor (background service)
│
└── [feature packages — one per domain, flat under internal/]
├── admin/ user + settings management
├── agent/ gRPC server, client, registry + HTTP handler
├── auth/ JWT, sessions, password hashing
├── backup/ S3 backup
├── container/ container lifecycle + SSE streams + exec
├── converter/ manifest/compose/dockerfile → ServiceManifest
├── dashboard/ aggregated overview endpoint
├── deploy/ deploy orchestration (Deployer)
├── events/ SSE event stream
├── git/ Git integrations (GitHub, GitLab, Gitea, Bitbucket)
├── image/ container image management
├── log/ app + audit log endpoints
├── manifest/ ServiceManifest handler + manager
├── network/ Docker network management
├── notification/ in-app + external notifications
├── project/ project management
├── setup/ initial admin setup
├── system/ health, metrics, version check, self-update
├── template/ service template loader (live from GitHub)
├── volume/ Docker volume management
├── webhook/ webhook CRUD + inbound receiver
└── ws/ WebSocket handler
deploy/
development/ docker-compose + .env for local dev
production/ Dockerfile + docker-compose + .env
We follow the Uber Go Style Guide with these project-specific rules:
Package naming
- Lowercase, singular, no stuttering (
containernotcontainers,gitnotgitservice) - Short and descriptive (
infranotinfrastructure,logmonnotlogwatcher) - No
http,service,handler,repositorysub-packages — everything lives flat in the feature package
Package structure (per feature)
handler.go—Handlerstruct +NewHandler()store.go— DB queries (replacesrepository.go)routes.go—RegisterRoutes()withhttpx.V1constanterrors.go— huma error helpers if needed- Additional files named after what they do (
integrations.go,stream.go)
Constructors
- Always
NewHandler(), neverNew()— avoidsgit.New()ambiguity
Handler methods
- Unexported (lowercase) —
h.list,h.create,h.delete - Input/Output types also unexported —
listInput,createOutput
Routes
- Always use
httpx.V1constant:httpx.V1+"/containers"not"/api/v1/containers"
Import cycles
queue/has zero internal imports — all enqueue helpers live hereaccess/has zero dependency onmiddleware/— bootstrap wires viaaccess.SetUserReader()middleware/has zero dependency onauth/— bootstrap wires viajwtValidator()adapterplatform/packages never import feature packages
Label constants
- Always use
access.LabelManaged,access.LabelInternaletc. — never hardcode"tidefly.internal"strings
See contributing.md for setup instructions and guidelines.
Please do not open public issues for security vulnerabilities. Use GitHub Private Security Advisories instead.
AGPLv3 — see LICENSE