Pairs with pangolin-teardown.sh. For instances that were phone-home
registered with a CCP, this script removes the CCP-side Instance row
during teardown so the slug is freed for re-registration.
Without it, tearing down a CCP-registered instance leaves a stale
Instance row in CCP's DB. The next phone-home-registration with the
same slug hits the unique-constraint violation we saw in marcelle
testing.
Matching: by agentUrl (default from .env CCP_AGENT_URL), --slug, or
--instance-id. Dry-run by default; --yes to execute. CCP admin token
via --token or CCP_ADMIN_TOKEN env var.
Added to build-release.sh whitelist so release tarballs include it
alongside pangolin-teardown.sh and validate-env.sh.
Bunker Admin
scripts/build-release.sh --upload now checks for an existing release
at the given tag before POSTing a new one. If found and --replace is
not set, errors out with a clear message.
This prevents the silent-overwrite problem: a user on v2.9.7 running
./scripts/upgrade.sh sees "no update available" when the v2.9.7
release's tarball contents have silently changed. Version tags should
be immutable once published.
--replace is still available for deliberate test-bench iteration
(DELETEs the existing release, then POSTs). Documented as destructive
in the --help output and DEV_WORKFLOW.md.
Bunker Admin
Fixes surfaced by three rounds of fresh-install testing on marcelle:
- config.sh: add host-port preflight check (ss -tln) to catch
cockpit-on-9090 style collisions before compose up; add
--skip-port-check escape hatch; add --install-watcher /
--no-install-watcher / --install-backup-timer /
--no-install-backup-timer flags; -y --enable-all now installs both
systemd units by default (previously silently skipped); print
resolved admin email in Configuration Complete block.
- scripts/validate-env.sh: new section 5b "Host Port Availability"
using ss-based detection, with process-name surfacing when run as
root.
- scripts/pangolin-teardown.sh: new wrapper. Reads credentials from
.env or takes --api-url/--api-key/--org-id flags. Dry-run by
default; --yes to execute. Deletes resources before sites (avoids
orphans). --keep-site-ids for safety.
- scripts/build-release.sh: include validate-env.sh and
pangolin-teardown.sh in release tarball whitelist.
- CCP instances.service.ts: deleteInstance() now calls
teardownTunnel() before composeDown when pangolinSiteId is set.
Previously an admin clicking "Delete Instance" orphaned the
Pangolin site + all its resources. Best-effort with try/catch
matching the existing Docker-cleanup tolerance pattern.
- CLAUDE.md: sync drift — 44 → 50 migrations, 186 → 192 models,
40 → 44 modules.
Bunker Admin
Six independent fixes surfaced during the v2.9.1 → v2.9.2 admin-UI
upgrade validation today. Together they make a clean install on a new
box work end-to-end without in-session patching.
- Fix 1: scripts/validate-compose-parity.sh + build-release.sh hook —
fail release builds when api/admin/media-api/nginx healthcheck
blocks drift between docker-compose.yml and docker-compose.prod.yml.
Previous boot-race fix had to be applied to both files manually.
- Fix 2: scripts/systemd/install.sh chowns logs/ to the install user
(the API container creates subdirs there as root, locking the
host-side watcher out), pre-creates logs/upgrade-watcher.log, and
changemaker-upgrade.service adds StartLimitIntervalSec=0 so a
single transient failure can't wedge the .path unit permanently.
- Fix 3: /api/upgrade/status now returns a `watcher` sub-object that
flags the host systemd watcher as stalled when trigger.json has
been pending >30s. Admin SettingsPage SystemUpgradeTab renders a
warning Alert with the systemctl recovery command when unhealthy.
- Fix 4: scripts/upgrade.sh write_result() — prefer head -1 VERSION
over `git rev-parse HEAD` so release-mode upgrades report the new
tag in result.json instead of "unknown".
- Fix 5: admin container healthcheck start_period 20s → 60s in both
compose files, same class as the earlier api fix. Matches Gancio
convention.
- Fix 7: /api/pangolin/sync now detects resources bound to a stale
siteId (common after --pangolin-site new rotations), deletes and
recreates them against the current site, and reports them under
a new `reassigned` response field.
Bunker Admin
- Add scripts/gitea-init.sh: runs migrations + creates admin user on
first boot, replacing the manual installation wizard
- Set GITEA__security__INSTALL_LOCK=true in both compose files
- Add NocoDB auth bridge (nginx) + /api/services/nocodb-auth proxy
endpoint so the admin iframe auto-authenticates
- Update NocoDBPage.tsx to fetch token and use auth bridge flow
- Fix docker-compose.prod.yml missing Gitea env vars for API container
(GITEA_URL, GITEA_API_TOKEN, GITEA_ADMIN_PASSWORD, etc.)
- Pass NC_ADMIN_EMAIL/PASSWORD to API for NocoDB auth proxy
- Increase Gitea auto-setup retries from 3 to 6 with admin auth check
- Update config.sh non-interactive mode to set GITEA_ADMIN_USER
- Include gitea-init.sh in release tarball (build-release.sh)
Bunker Admin
GITEA_API_TOKEN is for the local platform Gitea (docs comments, user
provisioning, SSO). New GITEA_REGISTRY_API_TOKEN is for the remote
registry at gitea.bnkops.com (release uploads via build-release.sh).
Previously both contexts shared one variable, causing auth failures
when the token for one instance was used against the other.
Bunker Admin
GITEA_URL points to the internal Docker hostname (gitea-changemaker:3000),
which is unreachable from the host. Derive external URL from GITEA_REGISTRY
instead, which already contains the external hostname.
Bunker Admin
New install method: curl one-liner downloads a lightweight release
tarball (~9 MB) and runs the config wizard. No git clone needed,
no TypeScript compilation — pulls pre-built images from Gitea registry.
- docker-compose.prod.yml: production compose without build blocks or
source code volume mounts; IMAGE_TAG defaults to latest
- scripts/install.sh: curl-friendly installer (downloads tarball,
extracts, runs config.sh)
- scripts/build-release.sh: creates release tarball from dev repo
with only runtime files (configs, scripts, docs, empty data dirs)
- config.sh: release-mode detection (VERSION file + no .git dir),
auto-sets IMAGE_TAG=latest and NODE_ENV=production
- upgrade.sh: release-mode upgrade path (downloads new tarball from
Gitea Releases API instead of git pull, always uses registry mode)
- upgrade-check.sh: release-mode version check via Gitea API
- .gitignore: exclude releases/ and api/dist/
- Docs: updated getting-started with pre-built install instructions
Bunker Admin