Install flow
- api/rpc/package/install.rs: always append the literal image URL as a
last-resort pull candidate in do_pull_image, so images not carried by
any configured mirror (docker.io/bitcoin/bitcoin:28.4) still install
instead of masquerading as a generic pull failure across every mirror.
- api/rpc/package/install.rs: write_bitcoin_conf now skips on any stat
error, not just "file exists". Once bitcoin-knots' first-boot chowns
/var/lib/archipelago/bitcoin into the container's user namespace (700
perms, UID 100100/100101), the archipelago daemon can't even traverse
in — try_exists returns Err which unwrap_or(false) treated as "not
present" and drove a doomed write. Now errors out of the directory
traversal are treated as "conf already owned by container user" and
the write is skipped. Mirrors the lnd.conf pattern.
- api/rpc/package/install.rs: drop the hardcoded `prune=550` from the
conf default. Operators with multi-TB drives shouldn't be silently
pruned; users who want a pruned node can set it in bitcoin.conf
themselves. Full archive is the only honest default.
- api/rpc/package/config.rs: bitcoin-core now passes explicit
-server/-rpcbind/-rpcallowip/-rpcport/-printtoconsole/-datadir CLI
args. Vanilla bitcoin/bitcoin:28.4 has no entrypoint wrapper and
reads conf + argv only; without these the RPC listens on 127.0.0.1
inside the container and rootlessport can't reach it, so the
bitcoin-ui companion gets 502 on every /bitcoin-rpc/ call.
Bitcoin Knots keeps its own entrypoint-driven defaults.
- container/docker_packages.rs: split bitcoin-core out of the shared
AppMetadata arm. bitcoin-core now surfaces as "Bitcoin Core" with
bitcoin-core.svg and a Reference-implementation description; the
bitcoin + bitcoin-knots ids keep the Knots branding. Fixes the home
card showing "Bitcoin Knots" for a Core install.
Bitcoin node UI (docker/bitcoin-ui)
- index.html: impl name/tagline/logo now dynamic. applyImplBranding()
reads subversion from getnetworkinfo — /Satoshi:X/Knots:Y/ resolves
to Bitcoin Knots, plain /Satoshi:X/ resolves to Bitcoin Core. Both
get their own icon and subtitle. Settings modal replaced its
hardcoded Regtest/txindex=1/port-18443 placeholders with live values
from getblockchaininfo + getindexinfo + getzmqnotifications.
- index.html: new Storage info card (Full Archive · X GB /
Pruned · X GB from blockchainInfo.pruned + size_on_disk) visible on
the main dashboard, same level as Network. Settings modal mirrors it
with the prune height when applicable.
- Dockerfile + assets/: bitcoin-core.svg, bitcoin-knots.webp, and the
bg-network.jpg used by the dashboard are now COPY'd into the image
under /usr/share/nginx/html/assets. Previously the <img src> pointed
at paths that 404'd into the SPA fallback and the onerror handler
hid the broken logo silently.
Frontend
- appSession/appSessionConfig.ts: add bitcoin-core to APP_PORTS (8334),
HTTPS_PROXY_PATHS (/app/bitcoin-ui/), and APP_TITLES (Bitcoin Core).
Without these the AppSessionFrame showed "No URL found for
bitcoin-core" and the home/app-list title fell through to the raw id.
- settings/AccountInfoSection.vue: backfill What's New entries for
v1.7.31 through v1.7.37 that had been missed in earlier cuts.
Release plumbing
- releases/v1.7.37-alpha/: binary + frontend tarball.
- releases/manifest.json: v1.7.37-alpha, sha256/size refreshed.
- Cargo.toml / package.json: version bumps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was seeing "upload failed: 413" on mesh attachment sends between
federated nodes — a ~7MB image becomes ~10MB base64 in the
typed_envelope wire and hit the 10m client_max_body_size on
/archipelago/, /content/, and /dwn/. Bumped those six locations
(two per server block, regular + HTTPS) to 256m so modern
attachments/blobs don't trip the proxy. /rpc/ stays at 1m —
internal JSON-RPC calls are small and don't need the headroom.
Applied to all 4 fleet nodes live; ISO source config updated so
fresh installs get the same limits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plumbs the BlobStore from blobs.rs into ApiHandler. The HMAC capability
key is derived from the node's Ed25519 signing key via a domain-separated
SHA-256 — rotating the identity rotates every outstanding cap (intentional
so a replaced node cannot honour old tokens).
New routes (added to nginx config in both server blocks):
- POST /api/blob — session-authenticated raw upload, returns
{cid, size, mime, filename, self_test_url}. The self_test_url is a
pre-signed cap pointing at the local node so the UI can verify the
round-trip without needing a peer pubkey.
- GET /blob/<cid>?cap=<hex>&exp=<epoch>&peer=<pubkey> — peer-facing,
HMAC-verified in constant time, expiry-checked, then streams bytes.
Mesh.vue gets a minimal "Attachment test (blob store)" section: file
picker → upload → cid display → "Verify round-trip" and "Open in new
tab" buttons. This validates Phase 3a end-to-end before we layer the
ContentRef typed envelope variant on top.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
App catalog served from Gitea repos (app-catalog) with 35 apps.
Nodes fetch catalog dynamically — new apps appear without frontend
rebuild. Test app added and removed to verify pipeline.
Gitea manifest updated with internal_port/nginx_proxy for iframe.
Updated catalog.json, nginx configs, app session configs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- IndeedHub container port changed from 7777 to 7778 (7777 used by nostr-relay)
- Nginx proxy updated to route to 7778
- Backend config.rs port mapping updated
- Podman registries.conf switched to v2 format (fixes mixed v1/v2 error)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FIPS stays in the marketplace as an installable container app.
NostrVPN is the native system service; FIPS is a separate optional app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Convert NostrVPN from container app to native systemd service
- Auto-configure VPN with node's Nostr identity after onboarding
- Add nostr-vpn.service with proper capabilities (NET_ADMIN, NET_RAW)
- Remove FIPS from marketplace, container config, nginx, image-versions
(consolidated into NostrVPN — same mesh VPN concept)
- Add AIUI inclusion step to dev CI workflow
- AIUI installed on VPS build server for ISO inclusion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three new marketplace apps:
- Routstr (v0.4.3): Decentralized AI inference proxy with Cashu payments
- Nostr VPN (v0.3.4): Mesh VPN with Nostr signaling + WireGuard tunnels
- FIPS (v0.1.0): Self-organizing encrypted mesh network
Includes status UI dashboards for headless apps (nostr-vpn-ui, fips-ui)
with usage instructions, node identity display, and container logs.
Nostr identity injected via env vars for all three apps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical:
- BUILD_VERSION was hardcoded as "1.3.0-alpha" — now reads from Cargo.toml
This caused ALL ISOs to show v1.3.0 regardless of actual binary version
Kiosk:
- Remove --disable-gpu flags (broke display scaling on some monitors)
- Add --start-fullscreen --window-size for reliable fullscreen
New apps:
- Nostr VPN, FIPS, Routstr, noStrudel, BotFights, NWNN, 484 Kitchen,
Call the Operator, Arch Presentation, Syntropy Institute, T-0
Rust: suppress dead_code and unused_assignments warnings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backport from .228 live server:
- AIUI: use SPA fallback (try_files → /aiui/index.html) for client-side routing
- Remove cookie_session gates from AIUI proxies (API key managed by proxy)
- Apply to both HTTP and HTTPS server blocks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Container stability:
- Merge scan results instead of full replacement (prevents UI flapping)
- Absence threshold: 3 consecutive missed scans before removing from state
- container-list RPC uses cached scanner state for consistency
- Increased Podman API timeout 30s → 60s (scanner + health monitor)
- Keep crashed containers visible as "exited" instead of podman rm -f
- Resolve host-gateway IP via ip route (podman 4.3.x compatibility)
ISO build fixes:
- AIUI web app inclusion: searches 5 paths + CI step to copy from build server
- Claude API proxy: systemctl enable with symlink fallback
- AIUI nginx: try_files =404 (was /aiui/index.html redirect loop)
- Build version set to 1.3.0
Container fixes:
- lnd-ui: nginx listens on 8080 (was 80, Permission denied in rootless)
- first-boot: image-versions.sh sourced from correct path with validation
- first-boot: host-gateway resolved to actual gateway IP
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>
- R1: Add health RPC endpoint with crash recovery status, uptime, and version
- R2: Wrap all 5 Nostr client.connect() calls in 10s timeout
- R3: Make backup restore atomic with staging dir and rollback on failure
- I1: Add rate limiting, body size, and proxy timeouts to unauthenticated nginx endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The LND UI runs on port 8081 (separate nginx container) but fetches
/lnd-connect-info from port 80. This is cross-origin, so browsers
block reading the response without CORS headers. Added dynamic
Access-Control-Allow-Origin from $http_origin.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New Discover.vue (app store redesign)
- Fleet.vue dashboard for .228
- MeshMap.vue component
- Fixed Discover.vue type errors (unused var, type predicate)
- Various UI updates (Apps, Dashboard, Marketplace, Mesh, Web5)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fedimintd serves JSON-RPC API on 8174 and Guardian web UI on 8175.
Updated all port mappings: frontend AppSession, nginx HTTP/HTTPS
proxies, PodmanClient static map.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add https: to CSP frame-src so external site iframes (BotFights,
484 Kitchen, etc.) load without being blocked by Content-Security-Policy
- Show spinner + "Starting..." on marketplace cards for containers that
are booting up, preventing users from re-installing running apps
- Add spinner to transitional state badges (starting/stopping/installing)
on installed app cards in Apps view
- Add "What's New" button to Settings version card with release notes
modal covering recent highlights in layman-friendly language
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CSP: removed unsafe-eval, tightened frame-src to self + host ports,
added frame-ancestors, base-uri, form-action directives
- X-Frame-Options: SAMEORIGIN added after proxy_hide_header on all app proxies
- HSTS: max-age=31536000; includeSubDomains on all server blocks
- Rate limiting: 20r/s on /rpc/ with burst=40, 3r/s auth zone
- Added X-DNS-Prefetch-Control, Permissions-Policy payment=() header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch IndeedHub to staging API, add _next asset caching in nginx,
simplify NostrIdentityPicker component, and update Apps/Web5/Marketplace views.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Simplify DHT encoding: use JSON instead of DNS packets (drop simple-dns)
- Fix mainline crate API: SigningKey takes 32 bytes, get_mutable returns Result
- Add missing dht_did field to IdentityRecord constructor
- Store DID Document as JSON in DHT (DNS encoding deferred)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add /content and /dwn proxy locations to nginx config (both HTTP and HTTPS)
so peer requests reach the backend instead of the SPA catch-all
- Update content_file_path() to check FileBrowser data dir as fallback when
files aren't in the dedicated content/files/ directory
- Populate size_bytes from actual file metadata in content.add
- Filter out availability:nobody items from the public catalog endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added nostrudel.ninja as a web-only app in Marketplace (community category).
Configured nginx reverse proxy at /ext/nostrudel/ with NIP-07 provider
injection in both HTTP and HTTPS blocks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`.
- Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27.
- Removed the `backup.rs` file as it is no longer needed.
- Introduced tests for configuration and credential management.
- Enhanced the `identity` module to generate W3C compliant DID documents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace sh -c echo with tokio::fs::write for bitcoin.conf generation
- Add client_max_body_size 1m to /rpc/ in both HTTP and HTTPS nginx blocks
- Document full audit findings in docs/security-audit-2026-03-11.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security headers already present in nginx config (X-Content-Type-Options,
X-Frame-Options, CSP, Referrer-Policy, Permissions-Policy). Removed
Access-Control-Allow-Origin: * from electrs-status — CORS is now handled
by the backend with origin validation. Deployed and verified all headers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set http1_keep_alive(false) on hyper server to prevent connection
reuse issues with nginx reverse proxy
- Clean up nginx proxy config: remove upstream block, use direct
proxy_pass to 127.0.0.1:5678
- Update AppLauncherOverlay and appLauncher store with UI fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The credential issuance and verification handlers used
Handle::block_on() directly inside the tokio runtime, causing a
deadlock. Wrapped with block_in_place() to properly yield the
runtime thread.
Also completed full feature verification across all 25 test groups
(~175 checks) on live server.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>