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 d1b48388fb
commit 1a74a930f7
77 changed files with 2485 additions and 966 deletions
+28 -1
View File
@@ -80,6 +80,15 @@
</div>
</div>
<!-- App icon -->
<img
v-if="stepIconUrl(step)"
:src="stepIconUrl(step)"
:alt="step.title"
class="w-7 h-7 rounded-md object-contain shrink-0 mt-0.5"
@error="($event.target as HTMLImageElement).style.display = 'none'"
/>
<!-- Step content -->
<div class="flex-1 min-w-0">
<h3 class="text-base font-semibold text-white/90 mb-1">{{ step.title }}</h3>
@@ -141,7 +150,7 @@
<!-- Action error toast -->
<Transition name="fade">
<div v-if="actionError" class="fixed bottom-20 left-1/2 -translate-x-1/2 z-50 max-w-md w-full px-4" role="alert" aria-live="assertive">
<div class="bg-red-500/20 border border-red-500/40 backdrop-blur-sm rounded-lg px-4 py-3 text-red-200 text-sm flex items-center justify-between gap-3">
<div class="alert-error backdrop-blur-sm rounded-lg px-4 py-3 text-sm flex items-center justify-between gap-3">
<span>{{ actionError }}</span>
<button @click="actionError = ''" class="text-red-300 hover:text-white shrink-0">&times;</button>
</div>
@@ -159,6 +168,24 @@ import { useGoalStore } from '@/stores/goals'
import { getGoalById } from '@/data/goals'
import type { GoalStep } from '@/types/goals'
/** Map appId to its icon file path under /assets/img/app-icons/ */
const APP_ICON_MAP: Record<string, string> = {
'bitcoin-knots': '/assets/img/app-icons/bitcoin-knots.webp',
lnd: '/assets/img/app-icons/lnd.svg',
'btcpay-server': '/assets/img/app-icons/btcpay-server.png',
immich: '/assets/img/app-icons/immich.png',
nextcloud: '/assets/img/app-icons/nextcloud.webp',
fedimint: '/assets/img/app-icons/fedimint.png',
mempool: '/assets/img/app-icons/mempool.webp',
electrs: '/assets/img/app-icons/electrs.svg',
'nostr-rs-relay': '/assets/img/app-icons/nostr-rs-relay.svg',
}
function stepIconUrl(step: GoalStep): string | undefined {
if (!step.appId) return undefined
return APP_ICON_MAP[step.appId]
}
const { t } = useI18n()
const route = useRoute()
const router = useRouter()