security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul

Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation

Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)

UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet

Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-19 12:44:31 +00:00
co-authored by Claude Opus 4.6
parent 28763c4f09
commit 84a56c80de
77 changed files with 2485 additions and 966 deletions
@@ -130,7 +130,7 @@
v-model="openForm.peerUri"
type="text"
placeholder="pubkey@host:port"
class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30"
class="w-full input-glass"
/>
<p class="text-white/40 text-xs mt-1">Format: pubkey@host:port</p>
</div>
@@ -141,14 +141,14 @@
type="number"
min="20000"
placeholder="100000"
class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30"
class="w-full input-glass"
/>
<p class="text-white/40 text-xs mt-1">Minimum 20,000 sats</p>
</div>
</div>
<div v-if="openError" class="mt-3 p-2 bg-red-500/20 border border-red-500/30 rounded-lg">
<p class="text-red-300 text-xs">{{ openError }}</p>
<div v-if="openError" class="mt-3 alert-error">
<p class="text-xs">{{ openError }}</p>
</div>
<div class="flex gap-3 mt-6">
@@ -169,15 +169,15 @@
<div class="glass-card p-6 w-full max-w-sm mx-4">
<h2 class="text-lg font-bold text-white mb-2">Close Channel?</h2>
<p class="text-white/60 text-sm mb-4">This will cooperatively close the channel with peer {{ closeTarget.remote_pubkey.slice(0, 16) }}...</p>
<div v-if="closeError" class="mb-3 p-2 bg-red-500/20 border border-red-500/30 rounded-lg">
<p class="text-red-300 text-xs">{{ closeError }}</p>
<div v-if="closeError" class="mb-3 alert-error">
<p class="text-xs">{{ closeError }}</p>
</div>
<div class="flex gap-3">
<button @click="closeTarget = null" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">Cancel</button>
<button
@click="closeChannel"
:disabled="closingChannel"
class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-red-500/20 border-red-500/30"
class="flex-1 glass-button glass-button-danger px-4 py-2 rounded-lg text-sm font-medium"
>
{{ closingChannel ? 'Closing...' : 'Close' }}
</button>