- VPS-hop attic push now runs via sudo so attic reads root's config
(~/.config/attic) which has the imrnes-ts server (Tailscale). Without
it the push ran as the CI user whose config only has pub ->
'Server imrnes-ts does not exist', silently skipping the cache upload.
- Direct push retried 3x (attic push is idempotent): a transient 502
(e.g. atticd restart mid-push, Traefik blip) no longer aborts the
whole closure upload before falling back to VPS-hop.
- Restore $VPS_USER in the 3 ssh:// nix copy fallbacks (was committed
as masked '***' -> nix copy would ssh as user '***' and fail).
Root causes found by reproducing the 2026-08-10 run:
- attic 'gmw' cache was created private -> every narinfo/nix-cache-info
read returned 401, so the VPS could never actually substitute from
attic ('Substituted from Attic cache' was a false positive when the
store path happened to be already present locally).
- VPS nix.conf used extra-trusted-substituters, which Determinate Nix
never merges for nix-store CLI clients; extra-substituters (all
users, no trust gate) fixes substitution (verified end-to-end:
delete path -> nix-store --realise pulls from attic over HTTPS).
- runner bootstrap of the attic client depended on nix copy --from
ssh:// (fragile, failed on runner); now the prebuilt attic client
closure lives in the attic cache itself and the runner pulls it over
HTTPS via extra-substituters configured in the Install Nix step.
Also surfaces bootstrap stderr on fallback for future debugging.
The old Push-to-Attic step SSH-copied the full closure (~794MB gateway) to
the VPS on every new store path before attic push — at ~500KB/s that took
25+ minutes (observed 40min+ in-flight run). The runner can now push
straight to the public attic endpoint (https://attic.asepharyana.my.id,
token auth validated) after pulling the prebuilt attic client closure
(52MB) from the VPS via nix copy --from. Falls back to the VPS-hop flow
whenever the direct path fails.
is-active after sleep 3 false-fails when the unit is still activating
(e.g. Next standalone boot >3s) — exit 3 flagged the deploy red even though
the service came up fine. Poll is-active up to 30s and only fail if it never
reaches 'active'.
Sebelumnya frontend hanya static export yang di-serve nginx di dalam package
proxy. Sekarang frontend = runtime mandiri (Next.js standalone :4017) yang
nginx proxikan ('/' -> Next server, '/api' + '/ws' -> backend :4001). Tambah
'frontend' ke matrix deploy agar dideploy + memulai unit gmw-frontend.
Sebelumnya CI hanya build nix -> deploy tanpa verifikasi — placeholder
tests sempat rusak berbulan-bulan tanpa terdeteksi. Job 'test' baru:
- pnpm install + tsc --noEmit + vitest run untuk backend & discord-gateway
- biome check (errors fail, warnings pass)
- build-and-deploy now needs: test