Compose row gains a 📎 attach button that uploads the file via /api/blob
and calls mesh.send-content for the selected peer. Received content_ref
bubbles render as a caption+filename card with either an inline image
preview or a Download button that calls mesh.fetch-content and swaps in
the returned local_url.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds attachment sharing over the mesh: a ContentRef envelope (variant 19)
carries the blob CID, size, mime, optional thumb/caption, and a per-peer
HMAC capability URL so the recipient fetches the full blob out-of-band via
`GET {sender_onion}/blob/{cid}?cap=..&exp=..&peer=..`. BlobStore is shared
from ApiHandler into RpcHandler so mesh.send-content and mesh.fetch-content
(reqwest via TOR_SOCKS_PROXY) hit the same store and cap_key.
Co-Authored-By: Claude Opus 4.6 (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>
Adds core/archipelago/src/blobs.rs: a SHA-256 content-addressed store
that writes bytes to ${data_dir}/blobs/<cid> with a sibling <cid>.meta
JSON file (mime, filename, size, created_at, optional tiny thumbnail).
BlobStore::put is idempotent, max 64 MiB per blob, and issues HMAC-SHA256
capability tokens scoped to (cid, peer_pubkey_hex, expiry_epoch). Tokens
are verified in constant time and rejected on expiry. This is the
foundation piece for the mesh ContentRef typed envelope — the /blob/<cid>
HTTP route and ContentRef variant will land in a follow-up increment
once the HMAC key is plumbed from node identity.
No consumer yet, so the module compiles with dead_code warnings; these
will clear when the HTTP handler and ApiHandler state wiring land next.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds sender_pubkey + sender_seq fields to MeshMessage so received
messages carry a stable cross-transport identity: (sender_pubkey,
sender_seq) pair. This is the foundation for the upcoming reply,
reaction, edit, and read-receipt variants — they need to target a
message by an ID that is meaningful on every node, not just locally.
Receive-side population lives in dispatch.rs::store_typed_message,
which now looks up the peer's pubkey_hex and copies envelope.seq from
the decoded TypedEnvelope. Sent-side population will land when we
plumb a per-node monotonic seq counter through the RPC layer.
Also adds mesh.debug-dump: a full in-memory state snapshot returning
peers, messages, status, shared-secret peer ids, encrypt_relay flag,
and stego mode — intended for smoke tests and bug investigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
IndeedHub, Bitcoin, and Penpot installs routinely exceed the default
RPC timeout on first pull. Bump package.install specifically to
900s so the frontend doesn't drop the request while the backend is
still downloading images.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds renderers for tx_relay, tx_relay_response, tx_confirmation,
lightning_relay, and lightning_relay_response message types so these
appear as rich cards in the chat stream. sendArchMessage now looks up
our own onion via getTorAddress and skips federation peers that match,
preventing the duplicate "echoed back to self" message we were seeing
on single-node test federations. Empty-federation error message is
also clearer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds message_type + typed_payload (JSON) to MeshMessage so the UI can
render invoice/alert/coordinate/tx/lightning messages as structured
cards in both directions instead of showing raw wire bytes on the
Sent side. RPC handlers now route through send_typed_wire /
send_channel_typed_wire which transmit the binary envelope directly
(no utf8_lossy corruption) and record a rich Sent MeshMessage.
Also: store_message deduplicates echo-back doubles (20-msg lookback,
30s window), from_name is plumbed through the federation Incoming
path, and peer_dest_prefix / send_raw_payload are factored out of
send_message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swapped all registry references: image-versions.sh, marketplaceData.ts,
curatedApps.ts, catalog.json. git.tx1138.com is now fallback only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Registry fallback now only tries DIFFERENT registries (skips original
that already failed). 120s timeout per fallback attempt. WireGuard
keys generated on unbundled first-boot. Gitea ROOT_URL uses port 3001.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gitea image pushed to Archipelago registry. PROXY_APPS stays empty
per user preference - direct port only. Gitea config uses
INSTALL_LOCK + dark theme.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Installs all 7 containers (postgres, redis, minio, relay, api,
ffmpeg, frontend) on indeedhub-net with proper env vars and volumes.
Fixes pull timeout to cover stderr reader. Catalog registry set to
23.182.128.160:3000.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Timeout now wraps stderr reader + wait (was only wrapping wait, so
hung pulls were never killed). 23.182.128.160:3000 is now primary
registry since git.tx1138.com is unreachable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previous timeout used ExitStatus::default() which is success on Linux,
so the fallback never triggered. Now properly kills process, awaits
exit, and forces fallback path on timeout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Image pulls now timeout after 60s and fall through to dynamic registry
fallback instead of hanging forever when primary is unreachable.
Gitea external port corrected to 3001. WireGuard key generation
added to first-boot for fresh installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. registries.conf includes docker.io search + fallback 23.182.128.160
2. First-boot pull_with_fallback() tries primary then fallback registry
3. FileBrowser created with noauth config on persistent volume
4. Backend dynamic registries.json pre-created in ISO
5. Filebrowser password secret created for token flow
Fixes: apps stuck at 0% download, filebrowser not working, dynamic
catalog not loading on fresh installs.
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>
Configurable registry list persisted to config/registries.json.
Image pulls try all registries in priority order — if primary fails,
fallback registries are attempted automatically. RPC endpoints:
registry.list, registry.add, registry.remove, registry.test.
Replaces hardcoded fallback logic with extensible registry system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added gitea to PROXY_APPS so it always routes through /app/gitea/
nginx proxy (same origin as parent page). Fixes X-Frame-Options
SAMEORIGIN rejection when loading via direct port.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When primary registry (git.tx1138.com) fails, image pull automatically
retries from Gitea registry at 23.182.128.160:3000. Tags pulled image
with original name so install continues seamlessly. Gitea added as
external app in app session config.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Videos and audio now stream directly via URL with auth token query
param instead of downloading entire file into a JS blob. Fixes
playback of large videos (170MB+ was timing out). Images still use
blob URLs. streamUrl() added to filebrowser client and cloud store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cloud subpages (Music, Photos, etc.) now show bg-cloud.jpg instead
of falling through to bg-home.jpg.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Video thumbnail in card is pointer-events-none so clicks pass through
to the play handler. Better error messages when preview fetch fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cookie was scoped to /app/filebrowser but Cloud page reads it from
/dashboard/cloud — cookie was invisible. Changed to path=/ so the
auth token is accessible from any page for fetchBlobUrl calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Video fills entire viewport with no padding/border-radius. Double-click
toggles native fullscreen. Reduced padding for all media types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fedimint can use a remote Bitcoin RPC (e.g., over Tailscale or Tor).
Dependency check now logs info instead of blocking installation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MediaLightbox: full glassmorphic redesign with dark backdrop, smooth
transitions, proper video/audio/image support. FileBrowser: noauth
config on persistent volume. Deploy script: fixed sed quoting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cashu ecash protocol (BDHKE blind signatures, cashuA token format,
mint HTTP client) replacing the stub wallet. TollGate-inspired streaming
data payment system with step-based pricing (bytes/time/requests),
session management with incremental top-ups, usage metering, and
Nostr kind 10021 service advertisements.
13 new streaming.* RPC endpoints. Content server now verifies real
Cashu tokens. Profits tracking includes streaming revenue.
Frontend: GlobalAudioPlayer (persistent bottom bar across all pages),
video lightbox with full controls, audio in MediaLightbox, free file
previews (no blur), paid 10% audio/video previews, separated play
vs download buttons in PeerFiles.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents duplicate resolver directive error when both
nginx-archipelago.conf and external-app-proxies.conf are loaded
at http context level.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert botfights from external link to real container app on port 9100.
Add manifest, update marketplace/discover/kiosk/session configs, switch
registry URLs to git.tx1138.com.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add git.tx1138.com to trusted registries (replaces old 80.71.235.15)
- Add botfights app config: port 9100, data volume, JWT_SECRET auto-gen, fight loop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apps are now installed exclusively via the Marketplace UI.
The deploy script handles code sync, backend/frontend builds,
and service restarts only. The legacy container creation code
is wrapped in `if false` to preserve git history.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ISO builder: run npm ci before npm run build to prevent stale UI artifacts
- Unbundled ISO: clean container-images dir to prevent bundled tars leaking
- WireGuard: use After=network.target instead of network-online.target for
faster wg0 startup on install
- VPN status: check actual nvpn0 interface instead of config tunnel_ip to
prevent NostrVPN from showing standalone WireGuard IP
- ContainerApps: filter out not-installed bundled apps (fixes Bitcoin Knots
appearing on clean unbundled installs)
- Kiosk: persist kiosk mode to localStorage before /kiosk redirect so
App.vue can skip remote relay (fixes input doubling with companion app)
- IndeedHub: fix port mapping and X-Forwarded-Prefix passthrough
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>
Nodes can now see v1.3.5 as an available update. Includes registry
migration changelog and component download URLs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Registry migration to git.tx1138.com/lfg2025, version bump for
release testing across nodes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All hardcoded references to the old IP-based registry replaced across
Rust backend, Vue frontend, shell scripts, Dockerfiles, CI, and docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- VPN status: don't show WG IP as NostrVPN IP when tunnel not up
- VPN section polls every 15s so IP updates after pairing
- NostrVPN shows "Pair a device" when service active but no tunnel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Unbundled ISO: first-boot only creates FileBrowser (marker file .unbundled)
Users install apps from Marketplace — no more bitcoin/mempool on clean install
- VPN status: read tunnel IP from config file (instant) instead of nvpn status (22s)
- Kiosk: App.vue skips remote relay on /kiosk path (prevents duplicate input)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VPS runner was sniping jobs and failing instantly (no build env).
Changed runs-on from ubuntu-latest to iso-builder label, which only
the ThinkPad runner has registered.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>