No description
- Go 87.6%
- JavaScript 8.2%
- HTML 2.2%
- CSS 2%
| internal/app | ||
| web | ||
| .env.example | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| README.md | ||
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
- Copy config:
cp .env.example .env - Start PostgreSQL and create database
box. - Start app:
go run . - Open
http://localhost:8080.
Environment Variables
See .env.example.
Important:
DEV_MODE=trueuses a demo user and bypasses forward-auth headers.DEV_MODE=falseexpects auth headers from your reverse proxy:X-authentik-usernameX-authentik-email
DB_PROVISION_ENABLED=trueprovisions a dedicated PostgreSQL role/database per app.DB_PROVISION_ADMIN_URLis the admin DSN used for provisioning (defaults toDATABASE_URL).FORGEJO_BASE_URL+FORGEJO_TOKENenable repository creation.FORGEJO_GIT_USERNAMEis used withFORGEJO_TOKENfor non-interactive HTTPS git clone/fetch (defaultoauth2).FORGEJO_ORGswitches creation from personal repos to org repos.UNIX_SOCKET_PATHenables unix socket listener in addition toLISTEN_ADDR.CADDY_ROUTE_ENABLED=trueenables route provisioning.CADDY_ADMIN_URLpoints to the Caddy Admin API (defaulthttp://localhost:2019).CADDY_SERVER_IDis the HTTP server object id underapps.http.servers(defaultsrv0).CADDY_DOMAIN_SUFFIXcontrols generated host as<slug>.<suffix>(if empty:<user>-<slug>.local).WEBHOOK_BASE_URLsets 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_proxyhandle[0].upstreams[0].dial = unix/<unix-user-home>/projects/<app>/app.sock
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_urlis provided, Forgejo repo creation is skipped. - If
repo_urlis 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_urlin 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
refequalsrefs/heads/main. - Deploy behavior:
- Clone repo to
~/projects/<app>/repoif missing. - Otherwise
fetch origin mainandreset --hard origin/main. - Build binary to
~/projects/<app>/bin/app. - Restart the project user service via
systemctl --user restart.
- Clone repo to
Notes:
WEBHOOK_BASE_URLshould 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/statusGET /api/projects/:id/logs?lines=200POST /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:
<unix-user-home>/projects/<app>/.env - binary path used by systemd:
<unix-user-home>/projects/<app>/bin/app - unit file:
<home-of-unix-user>/.config/systemd/user/projectmgr-<app-user>-<project>.service
Then it attempts as that unix user:
systemctl --user daemon-reloadsystemctl --user enable --now <service>
The generated .env is synced from the project env-var configuration and always includes:
LISTEN_NETWORK=unixLISTEN_ADDRESS=<unix-user-home>/projects/<app>/app.sockDATABASE_URL=postgres://<app_db_user>:<generated_password>@.../<app_db_name>
If user systemd is not available in the runtime environment, project creation still succeeds and unit files are still generated.