A single-binary deployment tool for running bash scripts against environments. Define projects, write deployment steps, manage environment-scoped variables, create immutable releases, and deploy them with live log streaming in the browser.
*** This Application is a test of "Vibe Coding" and what can and cannot be done with AI ***
- Projects - Organize deployments into projects
- Environments - Define deployment targets (dev, staging, prod) with tags
- Steps - Ordered bash scripts that run sequentially during deployment
- Variables - Key/value pairs scoped to environments, resolved at deploy time
- Releases - Immutable snapshots of steps + variables with version numbers
- Deployments - Execute releases against environments with live SSE log streaming
- Approvals - Manual gate for production deployments requiring admin sign-off
- Notifications - Event-driven Slack, Email, Gotify, and Discord alerts for deployment status
- Cancel - Stop running deployments mid-execution
npm install
make buildThis produces a single durpdeploy binary.
./durpdeployServer starts on http://localhost:8080. A durpdeploy.db SQLite file is created automatically on first run.
- Create a project - Navigate to Projects → New Project
- Add steps - On the project detail page, add bash script steps in order
- Create environments - Navigate to Environments → New Environment (e.g., "Production" with tag
prod) - Add variables - On the project detail page, click Variables. Add key/value pairs scoped to environments
- Create a release - On the project detail page, click Releases. Enter a version (e.g.,
1.0.0) - Deploy - On the release, select an environment and click Deploy. Watch logs stream in real time.
DurpDeploy exposes a JSON REST API at /api/v1/*. All requests must authenticate with a bearer token:
curl -H "Authorization: Bearer ddp_pat_<token>" http://localhost:8080/api/v1/projectsAPI tokens are created per-user from the /settings/tokens page or via the CLI:
durpdeploy tokens create --user admin@example.com --name ciThe full API reference is available at /api/swagger/ in a running server (no auth required).
cmd/server/main.go Entry point
internal/
handler/ HTTP handlers (chi routes)
repository/ Thin wrapper around sqlc-generated queries
runner/ Deployment execution engine + SSE log broker
server/ Router setup
migrate/ Goose migration runner
migrations/ SQL schema migrations
queries/ sqlc query definitions
views/ Templ templates (pages, components, layouts)
static/ Embedded static assets (JS, CSS)
Stack: Go + chi + SQLite (modernc.org/sqlite) + sqlc + goose + Templ + HTMX + Alpine.js + Tailwind CSS + DaisyUI
# Generate templ files
make templ-generate
# Build Tailwind CSS
make tailwind-build
# Full build
make build
# Run with hot-reload (requires air or similar)
make devFor a small team deployment, DurpDeploy runs as a single Go process behind
Caddy, which terminates HTTPS and reverse-proxies to localhost:8080. The
binary ships with argon2id password hashing, DB-backed session auth, CSRF
protection on every state-changing request, and an audit log. See
docs/deploy.md for the full runbook — provisioning a fresh
Debian 12 VM end to end takes about 20 minutes.
The first admin user is created with a one-shot CLI command (no server running
required). The password is hashed with argon2id and stored in the users
table; nothing in the DB is plaintext:
durpdeploy admin create --email admin@example.com --password '<strong-password>'The database path is configurable via the DURPDEPLOY_DB env var; it defaults
to durpdeploy.db in the current directory for local dev. Production sets it
to /var/lib/durpdeploy/durpdeploy.db via the systemd unit.
Three roles, set at user-creation time and stored in users.role:
| Role | Reads | Writes | Sees audit log |
|---|---|---|---|
admin |
Everything | Everything | Yes (/admin/audit) |
deployer |
Everything | Everything — same writes as admin |
No |
viewer |
Everything | Nothing — every POST/PUT/PATCH/DELETE returns 403 | No |
Per-project authorization is enforced — every project has a project_members
row for each user who can read or write to it. Global admins bypass the
check; non-admins must be a member. A non-member hitting a project-scoped
route gets 404 (to hide existence) or 403 (depending on the route). The
practical "least privilege" is to make non-admins viewer if they don't
need to trigger deploys. Full details in docs/roles.md.
The threat model — what DurpDeploy defends against, what it doesn't, and the
five-minute hands-on attack drill — is documented in
docs/attack-drill.md. The summary:
- Defends against: unauthenticated deploys, CSRF on a teammate's browser,
password DB leak (argon2id, per-user salt, ~100ms per guess), cross-project
write access (per-project
project_members— P1-1), secret-at-rest exposure (AES-256-GCM forvariablesandrelease_variables.value— P1-3), rogue step scripts (dedicated user + cgroup sandbox + minimal env — P1-4), naive log redaction (regex-based scrubber for literal secrets, common credential patterns, and split writes — P1-5), unrecoverable data loss (Litestream continuous WAL replication + monthly restore drill — P1-6), and unauthorized approval ofpending_approvaldeployments (admin-only gate on/deployments/{id}/approve— P2-1; the storedrequired_approver_roleis descriptive only, the real gate is the handler check). - Partially defends against: backup monitoring (P2-4 — the binary polls
a user-supplied check command and fires
BackupUnhealthyon failure, but does not enforce the originally planned 36h age threshold). Audit retention (P2-5 —audit prune --days Nships and preserves rows tied to live deployments/releases, but the default 180-day window and the daily systemd timer are operator-deployed, not auto-installed).
- No remote deployment targets or SSH
- No parallel step execution
- No CI/build features
- No Kubernetes or cloud integrations
- No PowerShell support (bash only)
MIT
