Replaces SSH as the primary git push path — no key generation needed.
# Admin UI: Users → Generate key (shown once)
git remote add hiy http://hiy:API_KEY@myserver/git/myapp
git push hiy main
What was added:
- api_keys DB table (id, user_id, label, key_hash/SHA-256, created_at)
Keys are stored as SHA-256 hashes; the plaintext is shown once on
creation and never stored.
- routes/api_keys.rs
GET/POST /api/users/:id/api-keys — list / generate
DELETE /api/api-keys/:key_id — revoke
- HTTP Smart Protocol endpoints (public, auth via Basic + API key)
GET /git/:app/info/refs — ref advertisement
POST /git/:app/git-receive-pack — receive pack, runs post-receive hook
Authentication: HTTP Basic where the password is the API key.
git prompts once and caches via the OS credential store.
post-receive hook fires as normal and queues the build.
- Admin UI: API keys section per user with generate/revoke and a
one-time reveal box showing the ready-to-use git remote command.
SSH path (git-shell + authorized_keys) is still functional for users
who prefer it; both paths feed the same build queue.
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
Full self-contained git push flow — no GitHub required:
git remote add hiy ssh://hiy@myserver/myapp
git push hiy main
What was added:
- Bare git repo per app (HIY_DATA_DIR/repos/<app-id>.git)
Initialised automatically on app create; removed on app delete.
post-receive hook is written into each repo and calls the internal
API to queue a build using the same pipeline as webhook deploys.
- SSH key management
New ssh_keys DB table. Admin UI (/admin/users) now shows SSH keys
per user with add/remove. New API routes:
GET/POST /api/users/:id/ssh-keys
DELETE /api/ssh-keys/:key_id
On every change, HIY rewrites HIY_SSH_AUTHORIZED_KEYS with
command= restricted entries pointing at hiy-git-shell.
- scripts/git-shell
SSH command= override installed at HIY_GIT_SHELL (default
/usr/local/bin/hiy-git-shell). Validates the push via
GET /internal/git/auth, then exec's git-receive-pack on the
correct bare repo.
- Internal API routes (authenticated by shared internal_token)
GET /internal/git/auth -- git-shell permission check
POST /internal/git/:app_id/push -- post-receive build trigger
- Builder: git-push deploys use file:// path to the local bare repo
instead of the app's remote repo_url.
- internal_token persists across restarts in HIY_DATA_DIR/internal-token.
New env vars:
HIY_SSH_AUTHORIZED_KEYS path to the authorized_keys file to manage
HIY_GIT_SHELL path to the git-shell script on the host
Both webhook and git-push deploys feed the same build queue.
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
Control plane:
- Users and app grants stored in SQLite (users + user_apps tables)
- bcrypt password hashing
- Sessions: HashMap<token, user_id> (in-memory, cleared on restart)
- Bootstrap: first admin auto-created from HIY_ADMIN_USER/HIY_ADMIN_PASS if DB is empty
- /admin/users page: create/delete users, toggle admin, grant/revoke app access
- /api/users + /api/users/:id/apps/:app_id REST endpoints (admin-only)
Deployed apps:
- Every app route now uses Caddy forward_auth pointing at /auth/verify
- /auth/verify checks session cookie + user_apps grant (admins have access to all apps)
- Unauthenticated -> 302 to /login?next=<original URL>
- Authorised but not granted -> /denied page
- Session cookie set with Domain=.DOMAIN_SUFFIX for cross-subdomain auth
Other:
- /denied page for "logged in but not granted" case
- Login page skips re-auth if already logged in
- Cookie uses SameSite=Lax (required for cross-subdomain redirect flows)
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
- New HIY_ADMIN_USER / HIY_ADMIN_PASS env vars control access
- Login page at /login with redirect-after-login support
- Cookie-based sessions (HttpOnly, SameSite=Strict); cleared on restart
- Auth middleware applied to all routes except /webhook/:app_id (HMAC) and /login
- Auth is skipped when credentials are not configured (dev mode, warns at startup)
- Logout link in both dashboard nav bars
- Caddy admin port 2019 no longer published to the host in docker-compose
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
Dashboard now shows:
- System card at top: CPU 1-min load average, RAM used/total, disk used/total
(reads /proc/loadavg, /proc/meminfo, df -k /)
- Two status columns in the apps table:
- "Container" — actual Docker runtime state (running/exited/restarting/not deployed)
via `docker inspect` on each app's hiy-{id} container
- "Last Deploy" — build pipeline status (queued/building/success/failed)
- Auto-refresh now calls /api/status every 5 s and updates both columns
(fixes the previous broken refresh that used app.status which didn't exist)
New API endpoint: GET /api/status → {app_id: {deploy, container}} for all apps
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH