No description
  • Go 87.6%
  • JavaScript 8.2%
  • HTML 2.2%
  • CSS 2%
Find a file
2026-05-14 20:13:28 +02:00
internal/app permissions are hard 2026-05-14 20:13:28 +02:00
web retry repo create 2026-05-14 20:09:30 +02:00
.env.example config 2026-05-14 19:48:42 +02:00
.gitignore init 2026-05-14 19:21:15 +02:00
go.mod init 2026-05-14 19:21:15 +02:00
go.sum init 2026-05-14 19:21:15 +02:00
main.go init 2026-05-14 19:21:15 +02:00
README.md retry repo create 2026-05-14 20:09:30 +02:00

Project Box

Project Box is a Go + vanilla web app for managing user projects.

Features:

  • Workspace model mapping each app-user workspace to a host Unix account
  • Create projects per authenticated user
  • Auto-create Forgejo repository or onboard an existing repository URL
  • Auto-create Caddy reverse-proxy route via Admin API (optional)
  • Manage project env vars
  • Auto-generate user-scoped systemd service per project
  • PostgreSQL persistence with startup migration
  • Authentik forward-auth header support + dev mode demo user
  • HTTP server on TCP and optional Unix socket
  • Request/access logs

Run

  1. Copy config:
    cp .env.example .env
    
  2. Start PostgreSQL and create database box.
  3. Start app:
    go run .
    
  4. Open http://localhost:8080.

Environment Variables

See .env.example.

Important:

  • DEV_MODE=true uses a demo user and bypasses forward-auth headers.
  • DEV_MODE=false expects auth headers from your reverse proxy:
    • X-authentik-username
    • X-authentik-email
  • FORGEJO_BASE_URL + FORGEJO_TOKEN enable repository creation.
  • FORGEJO_ORG switches creation from personal repos to org repos.
  • UNIX_SOCKET_PATH enables unix socket listener in addition to LISTEN_ADDR.
  • CADDY_ROUTE_ENABLED=true enables route provisioning.
  • CADDY_ADMIN_URL points to the Caddy Admin API (default http://localhost:2019).
  • CADDY_SERVER_ID is the HTTP server object id under apps.http.servers (default srv0).
  • CADDY_DOMAIN_SUFFIX controls generated host as <slug>.<suffix> (if empty: <user>-<slug>.local).
  • WEBHOOK_BASE_URL sets absolute webhook URLs returned by API/UI (recommended behind reverse proxy).

Caddy API Behavior

On project creation, when Caddy routing is enabled, the app appends a route to:

  • POST /config/apps/http/servers/<CADDY_SERVER_ID>/routes

Route shape:

  • match.host = [<project host>]
  • handle[0].handler = reverse_proxy
  • handle[0].upstreams[0].dial = 127.0.0.1:<target_port>

Workspaces and Unix Users

  • Create one or more workspaces per authenticated user.
  • Each workspace stores a unix_user (must exist on the host).
  • Project creation requires selecting a workspace.
  • Service/env files are written under that Unix user's home:
    • ~/.config/systemd/user/<service>.service
    • ~/.config/project-manager/<app-user>/<project>/service.env
  • The app attempts to run user-systemd commands as that Unix user via:
    • sudo -n -u <unix_user> systemctl --user ...

Existing Repos

  • In project creation, set repo_url (or fill "Existing Repo URL" in UI) to onboard an existing repo.
  • If repo_url is provided, Forgejo repo creation is skipped.
  • If repo_url is empty, the app tries to create a Forgejo repo when Forgejo env vars are configured.

Auto Deploy via Webhook

  • Each project gets a unique deploy webhook URL, exposed as webhook_url in project API responses and shown in UI.
  • For repos hosted on the configured Forgejo instance, the app now auto-provisions the repository webhook via Forgejo API.
  • If auto-provisioning cannot apply (for example external non-Forgejo repo), configure a webhook manually to call webhook_url.
  • Deploy runs only when payload ref equals refs/heads/main.
  • Deploy behavior:
    1. Clone repo to <PROJECTS_ROOT>/<app-user>/<workspace>/<project>/repo if missing.
    2. Otherwise fetch origin main and reset --hard origin/main.
    3. Restart the project user service via systemctl --user restart.

Notes:

  • WEBHOOK_BASE_URL should be set to an externally reachable base URL so Forgejo can call webhook endpoints.

Runtime Status and Logs

  • Per-project runtime endpoints:
    • GET /api/projects/:id/status
    • GET /api/projects/:id/logs?lines=200
    • POST /api/projects/:id/reprovision (retry provisioning for failed/pending projects)
  • Status is read via systemctl --user show <service>.
  • Logs are read via journalctl --user -u <service>.
  • Both commands are executed as the workspace Unix user via sudo -n -u <unix_user> ....
  • The app also auto-runs loginctl enable-linger <unix_user> (via sudo) to keep user systemd available without active login.

Systemd

For each project in workspace <ws> owned by unix user <unix_user>, the app writes:

  • env file: <PROJECTS_ROOT>/<app-user>/<workspace>/<project>/.env
  • unit file: <home-of-unix-user>/.config/systemd/user/projectmgr-<app-user>-<project>.service

Then it attempts as that unix user:

  • systemctl --user daemon-reload
  • systemctl --user enable --now <service>

The generated .env is synced from the project env-var configuration and always includes:

  • LISTEN_NETWORK=unix
  • LISTEN_ADDRESS=<PROJECTS_ROOT>/<app-user>/<workspace>/<project>/app.sock

If user systemd is not available in the runtime environment, project creation still succeeds and unit files are still generated.