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
parent d1b48388fb
commit 1a74a930f7
77 changed files with 2485 additions and 966 deletions

View File

@@ -55,7 +55,7 @@
/* Mobile touch targets — ensure tappable elements meet 44px minimum */
@media (max-width: 767px) {
button:not(.mode-switcher-btn):not(.sidebar-nav-item):not([class*="w-9"]):not([class*="w-8"]):not([class*="w-7"]) {
button:not(.mode-switcher-btn):not(.sidebar-nav-item):not([class*="w-9"]):not([class*="w-8"]):not([class*="w-7"]):not([class*="w-10"]):not([class*="w-11"]):not([class*="w-12"]) {
min-height: 44px;
}
}
@@ -81,7 +81,6 @@ button:active:not(:disabled),
[role="button"]:active,
a.glass-card:active,
a.goal-card:active,
.info-card-button:active,
.path-action-button:active {
transform: scale(0.97) !important;
transition: transform 0.1s ease !important;
@@ -244,18 +243,21 @@ input[type="radio"]:active + * {
position: relative;
}
/* On mobile browsers, cap chat height to the dynamic viewport to prevent
content extending behind browser chrome (address bar / toolbar). */
@media (max-width: 767px) {
.chat-fullscreen {
max-height: 100vh;
max-height: 100dvh;
}
}
.chat-mode-pill {
display: none;
position: absolute;
top: 2.25rem;
right: 1.25rem;
z-index: 10;
}
@media (min-width: 768px) {
.chat-mode-pill {
display: flex;
}
}
.chat-iframe {
flex: 1;
@@ -265,10 +267,15 @@ input[type="radio"]:active + * {
background: transparent;
}
/* On mobile, shrink iframe height so AIUI ends above the Archipelago tab bar */
/* On mobile, shrink iframe height so AIUI ends above the Archipelago tab bar.
Use dvh (dynamic viewport height) instead of 100% — on a normal mobile browser,
100% resolves through the parent chain to the large viewport (100vh) which
is taller than the visible area when the browser chrome is showing. dvh
tracks the actual visible viewport. */
@media (max-width: 767px) {
.chat-iframe-mobile {
height: calc(100% - var(--mobile-tab-bar-height, 72px)) !important;
height: calc(100vh - var(--mobile-tab-bar-height, 72px)) !important;
height: calc(100dvh - var(--mobile-tab-bar-height, 72px)) !important;
flex: none;
}
}
@@ -441,6 +448,112 @@ input[type="radio"]:active + * {
min-height: 36px;
}
/* Glass button color variants */
.glass-button-warning {
background: rgba(251, 146, 60, 0.2);
border: 1px solid rgba(251, 146, 60, 0.3);
color: #fdba74;
}
.glass-button-warning:hover {
background: rgba(251, 146, 60, 0.3);
}
.glass-button-danger {
background: rgba(239, 68, 68, 0.2);
border: 1px solid rgba(239, 68, 68, 0.3);
color: #fca5a5;
}
.glass-button-danger:hover {
background: rgba(239, 68, 68, 0.3);
}
.glass-button-success {
background: rgba(74, 222, 128, 0.2);
border: 1px solid rgba(74, 222, 128, 0.4);
color: #bbf7d0;
}
.glass-button-success:hover {
background: rgba(74, 222, 128, 0.3);
}
/* Status badges — inline colored pills */
.status-success {
background: rgba(74, 222, 128, 0.2);
color: #4ade80;
}
.status-error {
background: rgba(239, 68, 68, 0.2);
color: #f87171;
}
.status-warning {
background: rgba(251, 146, 60, 0.2);
color: #fb923c;
}
.status-info {
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
}
/* Alert banners — padded containers with border */
.alert-success {
padding: 0.5rem 0.75rem;
background: rgba(74, 222, 128, 0.1);
border: 1px solid rgba(74, 222, 128, 0.2);
border-radius: 0.5rem;
color: #bbf7d0;
font-size: 0.875rem;
}
.alert-error {
padding: 0.5rem 0.75rem;
background: rgba(239, 68, 68, 0.2);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 0.5rem;
color: #fca5a5;
font-size: 0.875rem;
}
.alert-warning {
padding: 0.5rem 0.75rem;
background: rgba(251, 146, 60, 0.1);
border: 1px solid rgba(251, 146, 60, 0.2);
border-radius: 0.5rem;
color: #fdba74;
font-size: 0.875rem;
}
.alert-info {
padding: 0.5rem 0.75rem;
background: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.2);
border-radius: 0.5rem;
color: #93c5fd;
font-size: 0.875rem;
}
/* Form input focus ring */
.input-glass {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
color: rgba(255, 255, 255, 0.9);
font-size: 0.875rem;
transition: border-color 0.2s ease;
}
.input-glass:focus {
outline: none;
border-color: #fb923c;
box-shadow: 0 0 0 1px #fb923c;
}
/* Toast - glassmorphic, top-right */
.toast-glass {
background-color: rgba(0, 0, 0, 0.65);
@@ -593,22 +706,6 @@ input[type="radio"]:active + * {
z-index: 1;
}
/* Gradient border container for large content areas */
.gradient-border-container {
position: relative;
border-radius: 1.5rem;
padding: 4px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.1) 50%, rgba(0, 0, 0, 0.6) 100%);
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.6);
}
.gradient-border-container-inner {
background: rgba(0, 0, 0, 0.35);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border-radius: 1.25rem;
}
/* Choose Your Path - Main Container */
.path-glass-container {
width: calc(100% - 48px);
@@ -876,6 +973,28 @@ input[type="radio"]:active + * {
}
}
/* Modal transition (Vue <Transition name="modal">) — shared across all modal components */
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-active .glass-card,
.modal-leave-active .glass-card {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .glass-card,
.modal-leave-to .glass-card {
transform: scale(0.95);
opacity: 0;
}
/* Background image */
body {
margin: 0;
@@ -958,10 +1077,10 @@ iframe.iframe-scrollbar-hide {
}
@keyframes caretBlink {
0%, 100% {
border-right-color: #00ffff;
0%, 100% {
border-right-color: #fbbf24;
}
50% {
50% {
border-right-color: transparent;
}
}
@@ -999,15 +1118,6 @@ iframe.iframe-scrollbar-hide {
}
}
@keyframes caretBlink {
0%, 100% {
border-right-color: #fbbf24;
}
50% {
border-right-color: transparent;
}
}
/* Splash screen styles */
.splash-complete .login-card {
animation: fadeUpIn 900ms cubic-bezier(0.22, 1, 0.36, 1) 120ms both;
@@ -1065,9 +1175,9 @@ body::after {
animation-fill-mode: backwards;
}
/* Dashboard: full viewport width, no letterboxing */
/* Dashboard: full viewport width, no letterboxing, no body scroll */
body.dashboard-active {
overflow-x: hidden;
overflow: hidden;
width: 100%;
}
@@ -1590,46 +1700,6 @@ html:has(body.video-background-active)::before {
border: 1px solid rgba(255, 255, 255, 0.08);
}
/* Toggle switch */
.share-toggle {
position: relative;
display: inline-flex;
width: 2.75rem;
height: 1.5rem;
flex-shrink: 0;
cursor: pointer;
}
.share-toggle input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.share-toggle-slider {
position: absolute;
inset: 0;
border-radius: 9999px;
background: rgba(255, 255, 255, 0.15);
transition: background-color 0.2s ease;
}
.share-toggle-slider::before {
content: '';
position: absolute;
width: 1.125rem;
height: 1.125rem;
left: 0.1875rem;
bottom: 0.1875rem;
border-radius: 9999px;
background: white;
transition: transform 0.2s ease;
}
.share-toggle input:checked + .share-toggle-slider {
background: #fb923c;
}
.share-toggle input:checked + .share-toggle-slider::before {
transform: translateX(1.25rem);
}
/* Access type options */
.share-access-options {
display: grid;
@@ -1823,44 +1893,6 @@ html:has(body.video-background-active)::before {
.monitoring-bar-danger {
background: #ef4444;
}
.monitoring-alert-toggle {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
flex-shrink: 0;
}
.monitoring-alert-toggle input {
opacity: 0;
width: 0;
height: 0;
}
.monitoring-alert-toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
transition: background 0.2s ease;
}
.monitoring-alert-toggle-slider::before {
content: '';
position: absolute;
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background: rgba(255, 255, 255, 0.6);
border-radius: 50%;
transition: transform 0.2s ease;
}
.monitoring-alert-toggle input:checked + .monitoring-alert-toggle-slider {
background: rgba(74, 222, 128, 0.4);
}
.monitoring-alert-toggle input:checked + .monitoring-alert-toggle-slider::before {
transform: translateX(16px);
background: #4ade80;
}
.monitoring-threshold-input {
width: 60px;
padding: 4px 8px;
@@ -1872,42 +1904,3 @@ html:has(body.video-background-active)::before {
text-align: right;
}
/* Toggle switch for Tor services and similar on/off controls */
.tor-toggle-label {
position: relative;
display: inline-block;
width: 36px;
height: 20px;
cursor: pointer;
}
.tor-toggle-input {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.tor-toggle-slider {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
transition: background 0.3s ease;
}
.tor-toggle-slider::before {
content: '';
position: absolute;
width: 16px;
height: 16px;
left: 2px;
top: 2px;
background: rgba(255, 255, 255, 0.6);
border-radius: 50%;
transition: transform 0.3s ease, background 0.3s ease;
}
.tor-toggle-input:checked + .tor-toggle-slider {
background: rgba(251, 146, 60, 0.4);
}
.tor-toggle-input:checked + .tor-toggle-slider::before {
transform: translateX(16px);
background: #fb923c;
}