# upaas A simple self-hosted PaaS that auto-deploys Docker containers from Git repositories via Gitea webhooks. ## Features - Single admin user with argon2id password hashing - Per-app SSH keypairs for read-only deploy keys - Per-app UUID-based webhook URLs for Gitea integration - Branch filtering - only deploy on configured branch changes - Environment variables, labels, and volume mounts per app - Docker builds via socket access - Notifications via ntfy and Slack-compatible webhooks - Simple server-rendered UI with Tailwind CSS ## Non-Goals - Multi-user support - Complex CI pipelines - Multiple container orchestration - SPA/API-first design - Support for non-Gitea webhooks ## Architecture ### Project Structure ``` upaas/ ├── cmd/upaasd/ # Application entry point ├── internal/ │ ├── config/ # Configuration via Viper │ ├── database/ # SQLite database with migrations │ ├── docker/ # Docker client for builds/deploys │ ├── globals/ # Build-time variables (version, etc.) │ ├── handlers/ # HTTP request handlers │ ├── healthcheck/ # Health status service │ ├── logger/ # Structured logging (slog) │ ├── middleware/ # HTTP middleware (auth, logging, CORS) │ ├── models/ # Active Record style database models │ ├── server/ # HTTP server and routes │ ├── service/ │ │ ├── app/ # App management service │ │ ├── auth/ # Authentication service │ │ ├── deploy/ # Deployment orchestration │ │ ├── notify/ # Notifications (ntfy, Slack) │ │ └── webhook/ # Gitea webhook processing │ └── ssh/ # SSH key generation ├── static/ # Embedded CSS/JS assets └── templates/ # Embedded HTML templates ``` ### Dependency Injection Uses Uber fx for dependency injection. Components are wired in this order: 1. `globals` - Build-time variables 2. `logger` - Structured logging 3. `config` - Configuration loading 4. `database` - SQLite connection + migrations 5. `healthcheck` - Health status 6. `auth` - Authentication service 7. `app` - App management 8. `docker` - Docker client 9. `notify` - Notification service 10. `deploy` - Deployment service 11. `webhook` - Webhook processing 12. `middleware` - HTTP middleware 13. `handlers` - HTTP handlers 14. `server` - HTTP server ### Request Flow ``` HTTP Request │ ▼ chi Router ──► Middleware Stack ──► Handler │ (Logging, Auth, CORS, etc.) │ ▼ Handler Function │ ▼ Service Layer (app, auth, deploy, etc.) │ ▼ Models (Active Record) │ ▼ Database ``` ### Key Patterns - **Closure-based handlers**: Handlers return `http.HandlerFunc` allowing one-time initialization - **Active Record models**: Models encapsulate database operations (`Save()`, `Delete()`, `Reload()`) - **Async deployments**: Webhook triggers deploy via goroutine with `context.WithoutCancel()` - **Embedded assets**: Templates and static files embedded via `//go:embed` ## Development ### Prerequisites - Go 1.23+ - golangci-lint - Docker (for running) ### Commands ```bash make fmt # Format code make lint # Run comprehensive linting make test # Run tests with race detection make check # Verify everything passes (lint, test, build, format) make build # Build binary ``` ### Commit Requirements **All commits must pass `make check` before being committed.** Before every commit: 1. **Format**: Run `make fmt` to format all code 2. **Lint**: Run `make lint` and fix all errors/warnings - Do not disable linters or add nolint comments without good reason - Fix the code, don't hide the problem 3. **Test**: Run `make test` and ensure all tests pass - Fix failing tests by fixing the code, not by modifying tests to pass - Add tests for new functionality 4. **Verify**: Run `make check` to confirm everything passes ```bash # Standard workflow before commit: make fmt make lint # Fix any issues make test # Fix any failures make check # Final verification git add . git commit -m "Your message" ``` The Docker build runs `make check` and will fail if: - Code is not formatted - Linting errors exist - Tests fail - Code doesn't compile This ensures the main branch always contains clean, tested, working code. ## Configuration Environment variables: | Variable | Description | Default | |----------|-------------|---------| | `UPAAS_PORT` | HTTP listen port | 8080 | | `UPAAS_DATA_DIR` | Data directory for SQLite and keys | ./data | | `UPAAS_DOCKER_HOST` | Docker socket path | unix:///var/run/docker.sock | | `DEBUG` | Enable debug logging | false | | `SENTRY_DSN` | Sentry error reporting DSN | "" | | `METRICS_USERNAME` | Basic auth for /metrics | "" | | `METRICS_PASSWORD` | Basic auth for /metrics | "" | ## Running with Docker ```bash docker run -d \ -p 8080:8080 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v upaas-data:/data \ upaas ``` ## License MIT