Critical:
- fix: container installs fail with "statfs: no such file or directory"
Root cause: NoNewPrivileges=yes in systemd blocks sudo inside backend.
Fix: use std::fs::create_dir_all + podman unshare chown (no sudo needed)
- fix: Tor services.json never written — \$ARCHY_TOR_DIR escaping bug
- fix: kiosk white screen — increase health wait to 60s, add --disable-gpu
Improvements:
- feat: LUKS encryption badge in Server disk stats (backend detects dm-crypt)
- fix: GRUB theme text scaling on 4:3 monitors — explicit fonts, wider menu
- fix: suppress default Debian MOTD (custom profile.d welcome is enough)
- fix: install error messages now show "Failed to pull/start" instead of
generic "Operation failed" (middleware.rs allowlist expanded)
- fix: container-tests CI — source cargo env before running tests
- docs: interactive container architecture diagram (HTML)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- fix: login disconnect — verify session before WebSocket connect
- fix: 403 on app install — distinguish CSRF vs RBAC errors, only retry CSRF
- fix: health monitor now watches ALL containers (removed skip list for
backend services like nbxplorer, databases, UI containers)
- fix: server.get-state added to CSRF-exempt list (read-only)
- fix: ISO build includes container-specs.sh and lib/common.sh in rootfs
so reconcile actually works on fresh installs
- fix: gamepad nav — improved Server tab zone nav, focus styles, autofocus
- chore: move L484 web-only apps to Services tab
- chore: install store for cross-view install tracking
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Peer rows: tabindex + role=button + Enter handler for D-pad selection
- Zone attributes: mesh-left, mesh-chat, mesh-tools for cross-panel nav
- Actions row: data-controller-container for Up from peers
- Right from peers → chat input, Right from chat → tools tabs (wide)
- Down from tabs → panel fields/buttons in grid fashion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Down from Identity name input now lands on Personal button (closest)
instead of Continue (wider overlap but further away).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Passphrase input autofocused on mount
- "Download Backup" renamed to "Backup to Continue"
- Continue button autofocused after successful backup download
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Path screen: Continue autofocused after 500ms (was 400ms, missed transition)
- Identity screen: name input autofocused on mount
- path-action-button now shows orange focus glow (removed from suppression list)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove path-action-button from focus-visible suppression list
- Orange glow now shows on Continue when autofocused
- Bump autofocus delay to 500ms to clear slide transition
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- glass-button gets orange glow on focus-visible (was suppressed)
- Input fields get orange border on focus-visible
- Restore link made focusable (tabindex, role, keydown.enter)
- Gamepad nav sounds play via existing fallback handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sidebar Right now polls every 100ms (up to 1s) for containers to
appear, instead of a single 200ms retry that missed animations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Only recall container elements (not nav bar buttons) from focus memory
- Retry after 200ms when containers aren't rendered yet (async route)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Up/Down from input: try containers as fallback when spatial nav fails
- Left/Right from input: exit field when cursor is at start/end
(e.g. Left from search bar at position 0 → category buttons)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mode-switcher-btn-active gets orange glass (bg, border, glow).
mode-switcher-btn:focus-visible gets orange ring on gamepad focus.
Sidebar nav-tab-active reverted to original white/black glass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Nav-tab-active now uses orange glass (bg, border, glow, gradient)
- Sidebar focus-visible uses matching orange tint
- Enter on containers skips uninstall button, finds primary action
- Down/Right from grid edges falls back to all focusable elements
- Global fallback for standalone buttons in empty/error states
- Full gamepad nav map for all onboarding screens + login modes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical fixes:
- Remove ensure_default_user() — no more auto-creating user with
password123. Login page now shows "Create Password" form on first
boot. User sets their own password during onboarding flow.
- CSRF 403: increased retry delay from 300ms to 500ms for stale
cookie recovery after remember-me session restore.
- Reboot: multiple fallback methods (/sbin/reboot, sysrq, kill init)
when USB is pulled and /usr/sbin isn't available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Version per build:
- Health endpoint returns "1.2.0-alpha-{git_hash}" using GIT_HASH env
- CI passes git hash to cargo build
FileBrowser auto-login:
- filebrowser-client.ts: include CSRF token + credentials:include
- First-boot: generate random password, store at secrets/filebrowser/
- Set FileBrowser admin password to match after container creation
Nostr relay:
- Use docker.io/scsibug/nostr-rs-relay:0.9.0 (not in our registry)
UID mappings:
- Added electrumx (UID 1000), mysql-mempool, archy-btcpay-db, nextcloud-db
522 tests pass, Rust compiles clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Login.vue now shows "Starting server..." until health check passes.
Tests need to mock server.echo and auth.isSetup RPCs and flush
promises before asserting on the rendered form.
522 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add orchestration_tests.rs + mock_podman.rs (container unit tests)
- Add container-tests.yml CI workflow
- Add dev-container-test.sh for local testing
- MASTER_PLAN.md: add TASK-49 (P0) with 6-phase plan
- Login.vue: minor fixes from user testing
- AppCard.vue: enter key handler fix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UEFI boot:
- xorriso now uses -append_partition with ESP type GUID
(C12A7328-F81F-11D2-BA4B-00A0C93EC93B) instead of -isohybrid-gpt-basdat
which only creates "basic data" partitions. Strict UEFI firmware
requires the correct ESP type to find BOOTX64.EFI.
- Uses Arch Linux ISO approach: -append_partition + appended_part_as_gpt
WebSocket/login from LAN browser:
- HTTPS nginx /ws block was missing proxy_set_header Cookie $http_cookie
Session cookie wasn't forwarded → backend returned 401 → WS failed
Password UX:
- Renamed "Change Password" → "Set Password" with description explaining
default password is password123
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move installingApps from local refs in Marketplace/Discover to the
global server store. Install progress now persists when navigating
between views. My Apps shows installing overlay with progress bar
for apps being installed from the Marketplace.
Changes:
- server.ts: add installingApps Map + helpers to store
- Marketplace.vue: use store's installingApps instead of local ref
- Discover.vue: same
- Apps.vue: pass isInstalling + installProgress to AppCard
- AppCard.vue: add amber installing overlay with progress bar
522 tests pass, vue-tsc clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- OnboardingDone: "Go to Login" → "Set Password" with context text
- Reboot: lazy-unmount live FS before USB removal prompt, suppress
kernel SquashFS messages, auto-reboot after 10s countdown
- Initramfs: filter "Possible missing firmware" warnings (cosmetic)
- ISOLINUX: menu centered at bottom (VSHIFT 18, HSHIFT 32, WIDTH 18)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend:
- Router guard checks isOnboardingComplete before redirecting to /login.
Fresh installs now go to /onboarding/intro instead of stuck on login.
- Login.vue: autocomplete="off" — fixes Enter key focusing button
instead of submitting the form.
ISO build:
- Added uidmap, slirp4netns, fuse-overlayfs to rootfs (required for
rootless Podman, lost to --no-install-recommends)
- Tor setup: mkdir + chmod 700 for hidden service dirs before starting
(Tor refuses 750/setgid permissions)
CI:
- QEMU headless boot test step after smoke test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add identity.create + server.echo to UNAUTHENTICATED_METHODS
- Clear web/dist before frontend build to prevent stale artifacts
- Add autocomplete attrs to login inputs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CI: configure root podman with insecure registry so FileBrowser
image can be pulled during ISO build
- CI: chmod u+rwX on workspace and act cache to fix cleanup failure
- ISO: auto-login on tty1 (no password prompt on console)
- Frontend: add console.log debug output for onboarding routing,
health checks, and 401 redirects to diagnose session issues
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ISO build: configure insecure registry for root podman so FileBrowser
image can be pulled during build (was failing with HTTPS error)
- Auto-login on tty1 so no password prompt on console
- RootRedirect: persistent debug logging to sessionStorage
(view in DevTools > Application > Session Storage > archipelago_boot_log)
- Logs: health check, onboarding state, routing decisions, 401 handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Kiosk: show cursor when active (removed -nocursor from Xorg),
unclutter hides after 3s idle. X11 on VT7 for Ctrl+Alt+F1/F7 switching.
- Kiosk: keep getty@tty1 running so MOTD is accessible via Ctrl+Alt+F1
- Kiosk: disable Chromium password save overlay (--password-store=basic)
- Esc: don't navigate back from top-level pages (dashboard, login, kiosk)
to prevent dead-end at root redirect
- PWA: suppress install prompt in kiosk mode (/kiosk path)
- Gamepad: Enter in text fields moves focus to next element (submit button)
instead of submitting the form
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- rpc-client: don't redirect to /login on 401 during onboarding flow,
which caused session expired kicks on fresh installs
- style.css: add translateZ(0) + isolation:isolate to glass-card,
glass-strong, path-option-card to fix Chromium compositor bug where
backdrop-filter + animated fixed overlays cause black rectangles
- App.vue: pause background animations when tab hidden, force
compositor layer rebuild on tab return to prevent stale renders
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All app images now pull from 80.71.235.15:3000/archipelago/
instead of Docker Hub / ghcr.io. Insecure registry config
baked into ISO for fresh installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Dashboard.vue: move DashboardMobileNav outside <main> so position:fixed
isn't broken by will-change:transform on the perspective container
- Add container-specs.sh and reconcile-containers.sh utility scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- F29-F32: Split 4 views (Marketplace 1293→505, Server 1132→486, Home 1059→394, AppDetails 1036→386)
- F20: Add aria-current="page" to Dashboard nav links
- F21: Add 150ms search debounce in Marketplace and Apps views
- F22: Reduce backdrop-filter blur to 8px on mobile for GPU performance
- F23: Track and clear WebSocket connect check interval in all paths
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- S10: Add warnings to silent health check failures in deploy scripts
- S11: Add trap cleanup for temp dirs in deploy and tailscale scripts
- S12: Quote 20+ critical unquoted variables across deploy scripts
- S13: Extract hardcoded IPs to deploy-config-defaults.sh
- S15: Add --memory=256m to UI container runs
- F16: Remove in-memory JWT, use cookie-only auth in filebrowser client
- F17: Add meta tag fallback for CSRF token in RPC client
- F19: Track and clear setTimeout in AppSession on unmount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- F4: Fetch fresh server state after WebSocket reconnect
- F5: Guard message polling timer with auth check, stop on logout
- F6: Remove NIP-07 listener in appLauncher close()
- F7: Initialize audio player once to prevent listener stacking
- S3: Pin all container images to specific versions, create image-versions.sh
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- F1: Guard connectWebSocket against concurrent calls with isWsConnecting flag
- F2: Serialize mesh send operations with sendQueue to prevent fetchMessages races
- F3: Add global Vue error handler with toast notification
- S1: Replace sudo podman with podman across all scripts (rootless Podman)
- S2: Add health-cmd to all 40 container run commands in first-boot-containers.sh
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AppDetails.vue now checks Bitcoin sync progress for LND, ElectrumX,
BTCPay, and Mempool. Shows orange warning banner with sync progress
bar and block height when Bitcoin is still syncing. Users see clear
feedback instead of broken wallet connect pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Messages persisted to disk (messages.json) — survive restarts
- Sent messages stored on backend via node-store-sent RPC
- Message deduplication (same pubkey + message within 30s)
- Max 200 messages in circular buffer
- Direction field (sent/received) for proper UI display
- Container doctor: prefer system Tor, remove archy-tor container
- Deploy torrc generator: read from tor-config/services.json,
web apps map port 80→local port for clean .onion URLs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major changes:
- Full Tor hidden service management via systemd path unit pattern
(tor-helper.sh + archipelago-tor-helper.path/service) — respects
NoNewPrivileges=yes, no sudo needed from backend
- Container doctor: prefer system Tor over container, remove archy-tor
- Deploy script: fix torrc generation (read correct services.json path),
web apps map port 80→local port, enable both tor and tor@default
- Federation: server rename pushes name to peers via background sync
- Server name: fix root-owned file, optimistic store update
- Mesh: local echo for sent messages, sendingArch loading state
- Web5: Message button → Mesh redirect, node name lookup in messages
- PeerFiles: show DID not onion in header
- Connected Nodes: flex-1 instead of fixed max-h
- Toast notifications route to Mesh
- Deploy script: fix single-quote syntax in SSH block
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ShareModal: strip leading / from filepath (was causing "absolute paths not allowed")
- Server.vue: Tor status in Local Network section now uses same source as header
- Both fixes needed for file sharing and Tor to work consistently
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Public Channel:
- "Archipelago" channel in Mesh — broadcasts to all federation peers over Tor
- Shows received messages from all peers with pubkey label
- Auto-polls every 15s for new messages
- Orange-branded channel icon with unread badge
- Send handler routes to Tor broadcast when arch channel is active
FileBrowser Auto-Login:
- All filebrowser-client methods now call ensureAuth() before requests
- Auto-authenticates with default credentials if not logged in
- Fixes "files don't work when FileBrowser hasn't been logged into"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>