Apps default to private (require login). Marking an app public bypasses
the forward_auth check so anyone can access it without logging in.
Changes:
- db.rs: is_public INTEGER NOT NULL DEFAULT 0 column (idempotent)
- models.rs: is_public: i64 on App; is_public: Option<bool> on UpdateApp
- Cargo.toml: add reqwest for Caddy admin API calls from Rust
- routes/apps.rs: PATCH is_public → save flag + immediately push updated
Caddy route (no redeploy needed); caddy_route() builds correct JSON for
both public (plain reverse_proxy) and private (forward_auth) cases
- builder.rs: pass IS_PUBLIC env var to build.sh
- build.sh: use IS_PUBLIC to select route type on deploy
- ui.rs + app_detail.html: private/public badge + toggle button in subtitle
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
- db.rs: add nullable git_token column (idempotent ALTER TABLE ADD COLUMN)
- models.rs: git_token on App (#[serde(skip_serializing)]), CreateApp, UpdateApp
- routes/apps.rs: encrypt token on create/update; empty string clears it
- builder.rs: decrypt token, pass as GIT_TOKEN env var to build script
- build.sh: GIT_TERMINAL_PROMPT=0 (fail fast, not hang); when GIT_TOKEN is
set, inject it into the HTTPS clone URL as x-token-auth; strip credentials
from .git/config after clone/fetch so the token is never persisted to disk
Token usage: PATCH /api/apps/:id with {"git_token": "ghp_..."}
Clear token: PATCH /api/apps/:id with {"git_token": ""}
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
Two silent failure modes:
1. lines() drops any output chunk not terminated with \n — a script
that crashes mid-line (or any final output without a newline) was
silently swallowed. Switched to raw 4KB chunk reads which stream
incrementally and capture everything.
2. A non-zero exit with no output (e.g. bash exit 127 'command not
found') left the log completely empty. Now always appends
'[hiy] exit code: N' after the process finishes so there is always
at least one diagnostic line regardless of script output.
Exit code lookup:
exit code: 0 -> success
exit code: 1 -> script hit 'set -e' on a failing command
exit code: 127 -> bash could not find the script or a command in it
exit code: 126 -> script found but not executable (chmod +x missing)
exit code: signal -> process killed by OS signal
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
When run_build() returned an Err (e.g. spawn failure because the
build script path doesn't resolve) the error was only written to
tracing, leaving the deploy log empty and the user with no clue.
- build_worker now appends the Rust error message to the deploy log
before setting status=failed, so it appears in the UI.
- run_build logs CWD, resolved script path, exists=true/false, build
dir, and env file path before attempting spawn, so there is always
at least one diagnostic line in the log even if spawn itself fails.
- spawn() error is wrapped with the attempted path for clarity.
https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH