Integrated Security Blueprint – Qubes OS + Kicksecure v4.3 Updated 2026 (for high‑sensitivity work environments)

⚡ For automatic application of all settings in this tab:

Use the Helper Script in Tab 11 which automatically applies kernel parameters to dom0 and all VMs (option #1).

chmod +x auto-script.sh && ./auto-script.sh
External HDD

Recommend using an external drive with USB 3.2 support to achieve 450–900 MB/s, allowing the system to run and be portable.

Secure Boot

Officially supported in Qubes 4.3 – can be left enabled, though you may need to sign kernel modules if you run into issues with certain devices.

BIOS/UEFI Setup
Updating dom0

Core system (isolated from the network, updates happen through official channels)

sudo qubes-dom0-update

⚙️ Critical kernel parameters

dom0 parameters are applied in GRUB; VM parameters via qvm-prefs

🖥️ dom0 parameters (applied in GRUB)

🔴 Xen parameters: add to GRUB_CMDLINE_XEN_DEFAULT

bootscrub=1 xen_scrub_pages=1

🔴 Linux parameters: add to GRUB_CMDLINE_LINUX

init_on_free=1 init_on_alloc=1 page_poison=1 lockdown=confidentiality module.sig_enforce=1 slab_nomerge slub_debug=FZ vsyscall=none kernel.kptr_restrict=2 oops=panic debugfs=off quiet loglevel=0 spectre_v2=on page_alloc.shuffle=1 preempt=voluntary pti=on mds=full
⚠️ Note about smt=off , ipv6.disable=1 , and noswap:
  • smt=off – moved to “Expert settings” section (significantly impacts performance, 30–50%).
  • ipv6.disable=1 – not useful in dom0 (dom0 does not directly handle the network card). Apply to VMs via kernelopts.
  • noswap – may cause system freeze. Use vm.swappiness=1 instead.
🖼️ Template & AppVM parameters (applied via qvm-prefs)
⚠️ Important: 512‑character limit in kernelopts!

The qvm-prefs VM kernelopts command is limited to 512 characters. This means you cannot add all the parameters below at once.

Solutions: Use the shortened list (under 300 characters) or enable the in‑VM kernel for templates that need extra protection.

These parameters protect your applications from CPU attacks. In Qubes, VMs use the dom0 kernel but their parameters are set separately and do not inherit GRUB parameters.

xen_scrub_pages=1 init_on_free=1 init_on_alloc=1 page_poison=1 mitigations=auto random.trust_cpu=off random.trust_bootloader=off spec_store_bypass_disable=on tsx_async_abort=full mds=full,nosmt page_alloc.shuffle=1 randomize_kstack_offset=on l1tf=full,force ipv6.disable=1 slab_nomerge vsyscall=none retbleed=stuff spectre_v2_user=on kvm.nx_huge_pages=force
💡 Shortened list (recommended to avoid the 512‑character limit): init_on_free=1 init_on_alloc=1 page_poison=1 slab_nomerge vsyscall=none mitigations=auto random.trust_cpu=off random.trust_bootloader=off spec_store_bypass_disable=on ipv6.disable=1
📖 Parameter explanation
# Parameter Explanation Benefit Applies to
1 bootscrub=1 Scrubs memory during Xen boot Prevents data leakage from a previous session 🔄 Xen
2 xen_scrub_pages=1 Scrubs memory before reusing it between VMs Prevents data leakage from one VM to another 🔄 Xen
3 init_on_free=1 Zeroes memory immediately after it is freed Prevents leakage of passwords and keys 🔄 All
4 init_on_alloc=1 Zeroes memory before it is allocated Prevents a malicious app from reading leftover data from another app 🔄 All
5 page_poison=1 Poison freed memory pages Makes use‑after‑free exploits harder 🔄 All
6 lockdown=confidentiality Prevents even root from modifying the kernel or reading its memory Prevents tampering with the dom0 system 🖥️ dom0
7 module.sig_enforce=1 Prevents loading unsigned kernel modules Blocks rootkit injection 🖥️ dom0
8 ipv6.disable=1 Disables IPv6 entirely Prevents IPv6 data leaks 🔄 All
9 noswap Disables swap entirely Prevents data leakage to disk (⚠️ may cause system freeze) 🖥️ dom0
10 slab_nomerge Prevents merging of similar kernel objects Makes heap exploitation harder 🔄 All
11 slub_debug=FZ Enables memory integrity checks and adds guard zones Detects buffer overflows 🔄 All
12 vsyscall=none Disables the old vsyscall interface Closes an ROP attack surface 🔄 All
13 kernel.kptr_restrict=2 Hides kernel addresses Makes ROP attacks harder 🖥️ dom0
14 oops=panic Causes the kernel to panic on any oops Prevents exploitation of errors to continue an attack 🖥️ dom0
15 debugfs=off Disables debugfs entirely Prevents leakage of kernel structure information 🖥️ dom0
16 spectre_v2=on Enables extra Spectre v2 protections Prevents CPU side‑channel attacks 🖥️ dom0
17 preempt=voluntary Voluntary kernel preemption scheduling More stable than full while maintaining good security 🖥️ dom0
18 pti=on Enables page table isolation Stops Meltdown vulnerability 🖥️ dom0
19 mds=full Enables full protection against Microarchitectural Data Sampling Prevents data leakage between threads 🖥️ dom0
20 quiet loglevel=0 Suppresses kernel messages during boot Prevents information leakage to an attacker 🔄 All
21 page_alloc.shuffle=1 Randomizes memory page allocation Makes heap exploitation harder 🔄 All
22 mitigations=auto Automatically enables vulnerability patches Protects Spectre, Meltdown inside VMs 📱 AppVMs
23 random.trust_cpu=off Do not trust CPU random number generator Prevents key guessing via a compromised RNG 📱 AppVMs
24 random.trust_bootloader=off Do not trust bootloader random number generator Prevents key guessing via a compromised RNG 📱 AppVMs
25 spec_store_bypass_disable=on Disables Spectre v4 vulnerability Prevents data leakage through store buffers 📱 AppVMs
26 tsx_async_abort=full Enables full TSX Async Abort protection Prevents CPU data leakage 📱 AppVMs
27 mds=full,nosmt Full MDS protection with SMT disabled Prevents data leakage between threads 📱 AppVMs
28 l1tf=full,force Enables full L1 Terminal Fault protection Prevents L1 memory data leakage 📱 AppVMs
29 randomize_kstack_offset=on Randomizes kernel stack offset Makes stack‑based exploitation harder 📱 AppVMs
30 retbleed=stuff Protects against Retbleed attack Prevents data leakage via indirect branch prediction 📱 AppVMs
31 spectre_v2_user=on Spectre v2 protection for user space Prevents Spectre v2 attacks from applications 📱 AppVMs
32 kvm.nx_huge_pages=force Enables nested page tables protection Prevents VM escape attacks 📱 AppVMs
33 smt=off Disables Hyper‑Threading (⚠️ experts only) Prevents Spectre/Meltdown attacks between VMs but severely impacts performance (30–50%) 🔴 Expert
🔧 How to apply

🖥️ For dom0: Add the parameters to /etc/default/grub then:

sudo grub2-mkconfig -o /boot/grub2/grub.cfg && sudo reboot

📱 For VMs: Apply to each VM (mind the 512‑character limit):

qvm-prefs VMNAME kernelopts "parameters here"
🔄 Emergency command (if the system won't boot after adding parameters):

From the GRUB menu, press e on the boot entry, find the line starting with linux, delete the parameters you added, then press Ctrl+X or F10 to boot.

After booting, restore the original GRUB file from the backup: sudo cp /etc/default/grub.backup.* /etc/default/grub && sudo grub2-mkconfig -o /boot/grub2/grub.cfg

💡 Or use the Helper Script in Tab 11 for automatic application to everything.

💡 For ephemeral (RAM‑based) encryption inside VMs, see Tab 5 (Amnesic Tmpfs) and Tab 6 (RAM Pool).

Scope of this tab: The lower network layers (sys-net, physical isolation, what each party sees). Tor and gateway settings are in Tab 2.

✅ Recommended solution: WISP router + Ethernet only

🛡️ WISP router + Ethernet only (hides the Qubes device completely)

A small router (e.g., GL.iNet) in WISP mode, connected to the Qubes device via Ethernet only, with wireless broadcasting of the internal network disabled.

💡 Core idea:
  • The router picks up internet from the intermediate provider wirelessly
  • But it does not broadcast any internal WiFi signal in your home
  • The Qubes device is connected to it only via Ethernet
  • Result: The Qubes device does not appear on any wireless network, and others only see an ordinary router

🔧 Setup steps

  1. Buy a small router that supports WISP mode (e.g., GL.iNet or TP‑Link)
  2. Configure the router in WISP mode to connect to the intermediate provider, and enable MAC Randomization
  3. Disable internal wireless broadcasting entirely
  4. Connect the router to the Qubes device via Ethernet cable — it will appear in Qubes as eth0
✅ Final result: The intermediate provider and neighbors see only a single router. The Qubes device is not visible to any party on the local network.

🏗️ Data flow diagram: both setups

❌ Normal setup (direct WiFi)
📡 ISP
🏪 Intermediary
├────────────┬────────────┐
💻 Qubes 👤 Neighbor
⚠️ Qubes exposed to everyone
🔄 Data path:
💻 Qubes
🏪 Intermediary
📡 ISP
🌐 Destination
✅ Preferred setup (WISP + Ethernet)
📡 ISP
🏪 Intermediary
├────────────┬────────────┐
📶 WISP router 👤 Neighbor
│ 🔌 Ethernet
💻 Qubes
✅ Qubes completely hidden
🔄 Data path:
💻 Qubes
📶 Router
🏪 Intermediary
📡 ISP
🌐 Destination
💡 Diagram explanation:
  • Right: Who sees you on the local network? In the normal setup, Qubes is exposed to the intermediary and neighbors. In the preferred setup, the router completely isolates Qubes — nobody sees what is behind it.
  • Left (dotted path): How data actually flows? In both setups, data passes through every hop: Qubes ← Router ← Intermediary ← ISP ← Destination. Qubes never connects directly to the destination.

🧱 Network architecture inside Qubes: sys-net and sys-firewall

🧱 Why two firewalls?

In Qubes, no one connects directly to the internet except sys-net. Then sys-firewall sits as a mandatory mediator for all other qubes.

🌐 Internet / WISP router
│ eth0 (physical interface)
🧱 sys-net
Untrusted — connected to the outside world
│ vif (virtual interface)
🧱 sys-firewall
Trusted — internal firewall for everyone
├──────────────┬──────────────┐
🧅 sys-whonix 📦 any Qube
⚠️ Deadly common mistake: Never connect any Qube directly to sys-net. The correct setup: application ← sys-firewall ← sys-net → internet.

📊 Comparison tables

👁️ Who sees what in each setup?
PartyNormal setup (direct WiFi)WISP + Ethernet setup
Intermediary (middle provider) 🔴 Sees the Qubes device directly (MAC, TCP/IP fingerprint, connection times) 🟡 Sees only a single router (doesn't know the number or type of devices behind it)
Neighbors on the same network 🔴 See the Qubes device (can scan and try to attack it) 🟢 See only a single router (cannot see what is behind it)
Official ISP 🟢 Sees only the intermediary (doesn't see you) 🟢 Sees only the intermediary (doesn't see you)
Final destination (without Tor) 🔴 Sees the official ISP's IP 🔴 Sees the official ISP's IP
Final destination (with Tor) 🟢 Sees a Tor exit node IP 🟢 Sees a Tor exit node IP
Note: The real difference appears only at the local network level. The official ISP and final destination see no difference between the two setups.
📊 Comprehensive comparison: different setups
Criteria🔴 Intermediary + direct WiFi🟡 Official ISP🟢 Intermediary + WISP + Ethernet
Qubes device visible to others?🔴 Intermediary + neighbors🟢 No one🟢 No one
Risk of WiFi driver exploits🔴 High🟢 None🟢 None
What the official ISP sees🟢 Only the intermediary🔴 You directly🟢 Only the intermediary
Router compromise = risk inside your device?🟢 No (external)
CostNothingNothing🟡 ~30–50$
Protection level🔴 Weak🟡 Moderate🟢 Excellent

⚠️ Scenario: what if sys‑net is compromised?

🎭 Worst case: attacker controls the router
✅ What they can do:
  • See traffic (but it's encrypted via Tor)
  • Cut off connection or block sites
  • Attempt to attack sys‑firewall (requires a rare Xen exploit)
❌ What they cannot do:
  • Access dom0 or the user interface
  • See your screen or record keystrokes
  • Access the rest of the virtual machines
  • Know that you are using Qubes (in the WISP setup)
  • Decrypt Tor traffic
🛡️ Bottom line: Even in the worst case, damage remains limited to sys‑net. The rest of the system stays safe thanks to Qubes' isolation.

🔄 Emergency plans: if Ethernet or WISP router is unavailable

📋 Internet connection plans (best to worst)
#PlanSecurityWhen to use?
🟢 AWISP + Ethernet⭐⭐⭐⭐⭐Permanent daily setup
🟢 BWISP + VPN on router + Ethernet⭐⭐⭐⭐⭐Extra protection from the intermediary (VPN before data reaches Qubes)
🟡 CWISP + internal encrypted WiFi (WPA3)⭐⭐⭐If cable is temporarily unavailable
🟡 DUSB tethering from an Android phone⭐⭐⭐Traveling, no WISP router available
🟠 EDirect WiFi + random MAC + firewall⭐⭐Emergency only, last resort
🔴 FOrdinary direct WiFiNever use this
Quick explanation of alternative plans:
  • Plan B: WISP router runs a VPN (WireGuard) — the intermediary doesn't even see ordinary encrypted traffic, only a single VPN connection.
  • Plan D: Android phone connected to intermediary network → USB cable to Qubes → appears as usb0. Better than direct WiFi, but the phone itself could be compromised.

⚙️ Additional settings and warnings

🔒 Extra layers of protection

🔄 MAC address randomization (in sys‑net)

# /usr/lib/NetworkManager/conf.d/31-randomize-mac.conf
[connection]
ethernet.cloned-mac-address=random
wifi.cloned-mac-address=random
wifi.scan-rand-mac-address=yes

In the WISP + Ethernet setup, this is only an extra precaution — the router is the one dealing with the external network.

🚫 Disable IPv6 (to prevent leaks)

qvm-prefs sys-net kernelopts "ipv6.disable=1" qvm-prefs sys-firewall kernelopts "ipv6.disable=1"

⚠️ Basic leak warnings

🔴 WebRTC: Can leak your real IP even with Tor. Solution: Use the built‑in Tor Browser inside Whonix. about:config → media.peerconnection.enabled = false
🔴 DNS leak: Make sure sys-whonix is the only netvm for your browsing qube.
✅ Testing tools: Tor Check | WebRTC Leak | DNS Leak
📚 Network tab summary:
  • Recommended solution: WISP router + Ethernet only + disable internal wireless broadcasting.
  • Qubes architecture: sys‑net (untrusted) ← sys‑firewall (trusted) ← other qubes. Never bypass sys‑firewall.
  • Your real adversary: The intermediary provider and neighbors on the local network — not the official ISP.
  • Data path: Qubes ← WISP router ← Intermediary ← ISP ← destination. Data passes through once in each direction.
  • Compromise scenario: Even if sys‑net is compromised, the attacker cannot reach dom0 or other qubes except through a rare Xen exploit.
  • Additional VPN layer: You can run a VPN on the WISP router itself to protect data even before it reaches Qubes.
  • Sticker cards: Provide privacy from the intermediary and neighbors, but in a formal police investigation they could be tracked through store cameras.

🔗 Next step: Go to Tab 2 to set up Tor, gateways, and connection types.

Whonix version: These commands use version 18. If you are using a newer version, adjust the template name.
🔴 First identity (Persona A) – e.g., daily email
📱 anon-work-A
Workstation
🔐 sys-whonix-A
Gateway A
🔥 sys-firewall-A
Firewall A
🌐 sys-net
🌐 Tor network + full encryption
🚪 Node 1
Entry guard
🔓 Decrypts outermost layer
Knows only the user's identity
🔄 Node 2
Middle relay
🔓 Decrypts middle layer
Knows neither source nor destination
🚪 Node 3
Exit node
🔓 Decrypts innermost layer
Sees the destination only (content is encrypted)
🌐 Internet
Destination
🔒🔒 Full encryption path (HTTPS + Tor):
📱 Workstation: encrypts data with TLS/HTTPS (layer 1) → 🔐 Gateway A: adds 3 Tor layers (layers 2,3,4) → 🔥 Firewall: passes through as‑is → 🔓 Entry guard: peels outermost Tor layer → 🔓 Middle relay: peels middle Tor layer → 🔓 Exit node: peels final Tor layer and reveals TLS‑encrypted data to the destination.
🔵 Second identity (Persona B) – e.g., anonymous document publishing
📱 anon-work-B
Workstation
🔐 sys-whonix-B
Gateway B
🔥 sys-firewall-B
Firewall B
🌐 sys-net
🚪 Three Tor nodes
🌐 Internet
🔒🔒 Same mechanism as identity 1: TLS encryption from workstation → Gateway B adds 3 Tor layers → sequential decryption through three Tor nodes (on a circuit completely independent from identity A).
🔐 Each Gateway has different bridges
🕐 SDWDate hides timing (built into Whonix)
🔥 Two separate firewalls (pass traffic as‑is)
🔒→🔓 4 encryption layers: 1 TLS + 3 Tor
🔐 How layered encryption works (HTTPS + Tor):
  • Step 0: The browser in anon-work encrypts data with TLS/HTTPS.
  • Step 1: The Gateway adds 3 Tor layers on top of TLS (total: 4 layers).
  • Step 2: Entry guard peels the outermost Tor layer (remaining: 1 TLS + 2 Tor).
  • Step 3: Middle relay peels the second Tor layer (remaining: 1 TLS + 1 Tor).
  • Step 4: Exit node peels the last Tor layer and reveals the TLS‑encrypted data it cannot decrypt.

📖 Result: Even if the exit node is malicious, it sees only TLS‑encrypted data (passwords, messages, etc.).

🌐 Tor & VPN connection types – comprehensive comparison

🧅 Tor only

Your device ← Tor network ← internet

ISP sees Tor connection | destination sees Tor exit IP

🌉 Tor + bridges

Your device ← bridge ← Tor ← internet

ISP sees ordinary encrypted connection | for bypassing censorship

🔐 VPN → Tor

Your device ← VPN ← Tor ← internet

ISP sees VPN only | most private

🔄 Tor → VPN

Your device ← Tor ← VPN ← internet

VPN sees your destination | convenience only

🔒 VPN only

Your device ← VPN ← internet

VPN knows your identity and destinations

❌ No protection

Your device ← internet

Everything exposed

Connection type ISP sees Destination sees Privacy Speed Recommendation
No protection Everything Real IP 🔴 None 🟢 Fast ❌ Do not use
VPN only VPN connection VPN IP 🟡 Moderate 🟢 Fast Regular use
Tor only Tor connection Tor exit IP 🟢 High 🟡 Average ✅ Baseline
Tor + bridges Ordinary encrypted connection Tor exit IP 🟢 Very high 🟡 Average 🌉 To bypass censorship
VPN → Tor VPN connection Tor exit IP 🟢 Excellent 🟡 Average 🔐 Recommended
Tor → VPN Tor connection VPN IP 🟡 Good 🟡 Average ⚠️ Convenience only
Criteria VPN Tor (Whonix) Tor + Bridges I2P
Speed 🟢 Fast 🟡 Average 🟡 Average 🟡 Average to slow
Anonymity 🟡 Moderate 🟢 Very high 🟢 Very high 🟢 High
Censorship resistance 🟢 High 🔴 Weak 🟢 Very high 🟡 Moderate
Hides Tor usage ❌ No ❌ No ✅ Yes 🟡 Unknown
Ease of setup 🟢 Easy 🟢 Easy 🟢 Easy 🔴 Hard
Cost ~50€/year Free Free Free
💡 Recommendation summary:
  • For daily ordinary use: VPN only is sufficient (YouTube, news).
  • For basic privacy: Tor only (Whonix) is the baseline.
  • For high privacy: VPN → Tor hides Tor usage from your ISP.
  • If Tor is blocked: Tor + bridges (use Snowflake).
  • To access sites that block Tor: Tor → VPN gives you a fixed IP.
  • For sensitive activities: Never use a direct unprotected connection.

🏗️ Difference between Whonix Gateway and Whonix Workstation

Gateway (GW)Workstation (WS)
FunctionRuns Tor processes and acts as a network bridge for the WSRuns user applications and performs activities safely
VM type in QubesAppVM + provides_network trueAppVM (ordinary)
Contains Tor✅ Yes❌ No
Has entry guards✅ Yes❌ No
Its netvmsys-firewall-Xsys-whonix-X
provides_network✅ true❌ false
Architecture: sys-net ← sys-firewall-A ← sys-whonix-A ← anon-work-A

🛡️ Why Whonix Workstation? A hardened OS

Whonix‑Workstation is based on the Kicksecure distribution, a hardened operating system that provides security layers beyond merely routing traffic through Tor.

  • Stream isolation: Each application gets its own Tor circuit.
  • Keystroke scrambling (kloak): Prevents identification through your typing style.
  • Ephemeral memory encryption: Temporary data on disk stays encrypted.
  • Hardened memory allocator: Protects against memory exploitation.
  • Kernel hardening: Advanced sysctl settings + ability to rebuild the kernel with random symbols.

🛡️ Entry guards are static – solution: multiple gateways

The entry guard is the first Tor node a gateway connects to. Its change cycle lasts weeks or months. If you land on a malicious node, you stay stuck with it. Using a single gateway for all activities means all your identities are linked to the same guard.

❌ Single gateway

Email + document publishing ← same guard ← can be linked
✅ Separate gateway per activity

Email ← Gateway A ← guard A
Publishing ← Gateway B ← guard B
cannot be linked

📝 Commands (copy and paste in order)

🔴 First identity (Persona A)
# 1. Disposable firewall (DispVM)
qvm-create --class DispVM --label orange --template default-dvm sys-firewall-A
qvm-prefs sys-firewall-A provides_network true
qvm-prefs sys-firewall-A netvm sys-net

# 2. Whonix gateway
qvm-create --class AppVM --label red --template whonix-gateway-18 sys-whonix-A
qvm-prefs sys-whonix-A provides_network true
qvm-prefs sys-whonix-A netvm sys-firewall-A

# 3. Workstation
qvm-create --class AppVM --label green --template whonix-workstation-18 anon-work-A
qvm-prefs anon-work-A netvm sys-whonix-A
🔵 Second identity (Persona B)
# Copy the commands above and change A → B
qvm-create --class DispVM --label cyan --template default-dvm sys-firewall-B
qvm-prefs sys-firewall-B provides_network true
qvm-prefs sys-firewall-B netvm sys-net
# ... continue the same way

🖱️ Or via graphical interface (Qube Manager)

  1. Open Qube ManagerCreate New Qube
  2. Firewall: Name: sys-firewall-A | Type: DisposableVM | Template: default-dvm | Networking: sys-net | Enable Provides network
  3. Gateway: Name: sys-whonix-A | Type: AppVM | Template: whonix-gateway-18 | Networking: sys-firewall-A | Enable Provides network
  4. Workstation: Name: anon-work-A | Type: AppVM | Template: whonix-workstation-18 | Networking: sys-whonix-A
  5. For the second identity: repeat the steps changing A to B
⚠️ Important security warnings:
  • Do not use cloneqvm-clone copies the Tor state including the entry guard. Create a new gateway directly from the template.
  • Disposable gateways are not recommended – they break the entry guard selection mechanism.
  • Do not modify default-dvm – any change to it will affect all disposable VMs in the system.
  • The firewall does not decrypt – it passes encrypted traffic as‑is without modification.

🌉 What are bridges?

Bridges are alternative entry points to the Tor network that hide the fact that you are using Tor from your ISP.

🕵️ What does an observer see?

  • Without a bridge: Sees a Tor connection ← may block it
  • With a bridge: Sees ordinary encrypted traffic ← cannot easily distinguish it
📖 Note: Bridges are not a silver bullet. They make discovery harder, but not impossible.

👥 Who needs to use bridges?

Most Whonix users do not need bridges. But three groups might:

GroupGoalSuitable bridge type
1️⃣ Those who are blockedTor is blocked, any way to connectobfs4 or Snowflake
2️⃣ Those who want to hideHide Tor usage from a serious adversaryPrivate bridges
3️⃣ TestersOnly testing bridge connectivityAny type
⚠️ Alert: If you are in a country that blocks Tor (e.g., China, Iran, Russia), you need a bridge. Use Snowflake or meek_azure.

📊 Comparison of bridge types

Type Speed Censorship resistance Mechanism What does the ISP see? Best for
obfs4 🟡 Average 🟢 High Packet obfuscation to remove fixed patterns Random encrypted traffic that looks unusual General use, moderate censorship
Snowflake 🟡 Average to good 🟢 Very high Relay via volunteers’ browsers using WebRTC Video call or WebRTC browsing (e.g., Google Meet) Highly censored environments
meek_azure 🔴 Slow 🟢 Very high Hides inside a large CDN server, appears as normal HTTPS browsing Ordinary visit to a well‑known cloud server (e.g., microsoft.com) China, Middle East, emergency fallback
WebTunnel 🟢 Good 🟢 Very high Hides the connection inside a WebSocket tunnel over HTTPS Normal secure web connection with no strange fingerprints Networks that block unfamiliar protocols
💡 Recommendation: Start with obfs4. If it doesn't work, try Snowflake. In extreme cases, use meek_azure. WebTunnel is a modern and effective option against deep packet inspection.

🎛️ How to enable bridges

🖥️ Graphical method (easiest):

  1. In sys-whonix, click the Tor Control Panel icon in the taskbar.
  2. Click Configure.
  3. Go to the Tor Bridges tab.
  4. Choose bridge type: obfs4 (default), Snowflake, meek_lite, or Custom.
  5. Click Connect and wait until "Connected to Tor" appears.

⌨️ Terminal method:

anon-connection-wizard
💡 To get custom bridges:
  • Send an email to bridges@torproject.org from Gmail or Riseup
  • Paste the bridge into the Custom option

❄️ Snowflake – the recommended bridge for censored regions

Snowflake is a pluggable transport that relays traffic through volunteers' browsers using WebRTC, making it the hardest to detect and block.

✅ Snowflake advantages:

  • Works even in heavily censored environments
  • Uses volunteers worldwide as proxies
  • No central server required
  • Resistant to active probing

🔧 For manual configuration (advanced): add to /usr/local/etc/torrc.d/50_user.conf:

UseBridges 1
ClientTransportPlugin snowflake exec /usr/bin/snowflake-client
Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn
📖 Note: Snowflake requires DNS to be enabled in Whonix‑Gateway. Uncomment #nameserver 10.0.2.3 in /etc/resolv.conf.whonix.

✅ Verify that you are using a bridge (Nyx)

  1. In sys-whonix, open Nyx from the application menu or type nyx in the terminal.
  2. Use the right arrow key to go to page 2 of 5.
  3. Look at the circuit information:

✅ You are using a bridge:

192.168.0.1 UNKNOWN 1 / Guard

❌ You are not using a bridge:

123.45.67.89 RelayName 1 / Guard

To exit Nyx: press q twice.

⚠️ Warning: Bridged Networking ≠ Bridges

Two completely different things despite the similar name:

TermMeaning
Bridges (Tor)Alternative entry points to the Tor network to bypass censorship
Bridged Networking Setting in virtualisation software (VirtualBox) that should not be changed
🚫 Do not change the VirtualBox network settings of Whonix‑Gateway to Bridged Network! This is unsupported, untested, and may break your anonymity.
📚 Quick summary:
  • Gateway = --class AppVM --template whonix-gateway-18 + provides_network true
  • Workstation = --class AppVM --template whonix-workstation-18 + netvm sys-whonix-X
  • Dedicated firewall = each Gateway gets its own firewall
  • Decryption = happens only at the three Tor nodes
  • Whonix hides timing = SDWDate prevents timing attacks
  • Bridges = hide Tor usage from ISP
  • Multiple gateways = create new gateways from the template (not clone)
  • 🔴 Do not run two gateways together (theoretical risk; with bridges it is very small)
🔐 Basic definitions: LUKS, VeraCrypt, and VC+LUKS

Before diving into tools and practical scenarios, it is important to understand the core technologies that this guide relies on for data encryption, as well as the advanced double‑encryption scenario.

LUKS

Linux Unified Key Setup – the disk encryption standard built into the Linux kernel.

  • 🔹 Platform: Linux (native).
  • 🔹 KDF algorithm: LUKS2 + Argon2id (default and recommended).
  • 🔹 Plausible deniability: Not natively supported.
  • 🔹 Integration: Built into Qubes OS and all modern Linux distributions.
  • 🔹 Common use: Encrypting system partitions, external drives, USB flash drives.
  • 🔹 Management: Via cryptsetup and tools like GNOME Disks.

🎯 When to use it? When you need native Linux encryption and want the best protection against GPU‑based attacks thanks to Argon2id. It is the best encryption for everyday Linux use if you don't need deniability.

VeraCrypt

VeraCrypt – TrueCrypt's successor, an open‑source tool for encrypting files and disks.

  • 🔹 Platform: Windows / Linux / macOS (cross‑platform).
  • 🔹 KDF algorithm: PBKDF2 with the ability to raise PIM for increased security.
  • 🔹 Plausible deniability: Supported – hidden volumes.
  • 🔹 Integration: Runs as a separate application in Linux/Qubes.
  • 🔹 Common use: Encrypted containers (files), partitions, full disks, portable USB drives.
  • 🔹 Management: GUI and command‑line tools.
  • 🔹 Download: veracrypt.fr (official site)

🎯 When to use it? When you need plausible deniability or cross‑platform compatibility.

🧠 Argon2id in LUKS2: resistance to GPU attacks

What is Argon2id?

A modern key derivation function designed specifically to consume large amounts of memory, making GPU and ASIC attacks impractical. Its real power lies in forcing an attacker to allocate massive memory for each individual guess, which completely kills the massive parallelism that GPUs rely on.

📊 Documented speeds: password cracking speed by algorithm and memory (latest 2025‑2026 studies)
Algorithm / Setting Memory Speed on modern GPU Security level
SHA‑256 (unsalted) < 1 MB ~10,000,000,000/sec (10 billion) 🔴 Very weak
PBKDF2 (VeraCrypt default) < 1 MB Millions/sec 🔴 Weak
Argon2id (OWASP minimum) 46 MiB ~1,000/sec 🟡 Good
Argon2id (RFC 9106 high) 2 GiB ~20–50/sec 🟢 Excellent
Argon2id (this guide – maximum) 4 GiB less than 25/sec 🟢🔒 Maximum

Numbers from recent academic studies (2025–2026) and OWASP / RFC 9106 recommendations. For the 4 GB setting, the speed collapses because a GPU with 24 GB VRAM cannot run more than ~6 parallel guesses, dropping the rate below 25 guesses/second.

💡 What does this mean in practice?
🔴 Without Argon2id

The attacker can try billions of passwords per second using a single graphics card. If your password is 8 random characters, it can be cracked in hours or days.

🟢 With Argon2id (4 GB)

The same graphics card can only try less than 25 passwords per second. Even with 1000 cards, cracking a 6‑word random passphrase would take thousands of years.

🧠 The real secret: Argon2id's strength is not in slowing down the user, but in making the attack non‑parallelisable. Your password unlocks within ~5 seconds on your machine (with 4 GB dedicated memory), but the attacker cannot run the same attack on 1000 machines unless they have 1000 × 4 GB = 4 TB of dedicated memory – impractical even for the strongest adversaries. The more memory you use, the more parallelism collapses.
✅ Note for 2026: LUKS2 + Argon2id is the default and only recommended choice in all modern Linux distributions. Verify with sudo cryptsetup luksDump /dev/sdX | grep Argon2.
🛡️ Creating a hardened LUKS2 container

Two methods to create a LUKS2 encrypted container with maximum protection against GPU attacks. Choose the one that suits you best: a direct command or an interactive script.

💡 Memory recommendation: If you use 4 GB of RAM for Argon2id, it is advisable to raise the max memory of the storage qube (storage‑vault) to 6 GB or more. This gives it enough room for the 4 GB key derivation + 2 GB for the system.

🟢 Note: The qube will not take the RAM unless it actually needs it. This is merely a maximum limit (maxmem), not a forced reservation. Under normal use, memory consumption will stay far below that.

To apply in dom0: qvm-prefs storage-vault maxmem 6000

🔹 Method 1: Direct command (for experts)

sudo cryptsetup luksFormat --type luks2 --pbkdf argon2id --pbkdf-memory 4194304 --iter-time 8000 /home/user/data.img
🔍 Parameter explanation:
  • --type luks2 – use the modern version of LUKS.
  • --pbkdf argon2id – GPU‑resistant KDF.
  • --pbkdf-memory 4194304 – allocate 4 GB of RAM for key derivation.
  • --iter-time 8000 – derivation time ~8 seconds.

🔹 Method 2: Interactive script (recommended)

Script that asks for the path, size, RAM, and time, then creates the container automatically. Supports creating a new container or encrypting an existing file.

🔹 Method 3: GNOME Disks (for beginners)

You can use the built‑in GNOME Disks tool for quick formatting. But be aware: the tool does not allow you to choose Argon2id (it uses only default settings), so prefer the two methods above for maximum protection.

⚠️ Important warning when using GNOME Disks to open a container: When opening a LUKS partition, a dialog appears with options to save the password. Make sure to choose "Forget password immediately" and not the default "Remember password until you logout" which keeps the password in memory until logout.

📊 LUKS only vs. VeraCrypt only vs. double encryption

Criteria 🔵 LUKS only 🟢 VeraCrypt only 🔴 VC + LUKS
⚡ Read/write speed🟢 1–5 GB/s🟡 100–400 MB/s🟡 similar to VC
🛡️ Data integrity✅ AEAD❌ XTS✅ from LUKS
🔐 GPU attack resistance🟢 Argon2id🟡 PBKDF2🟢✅ both
🕵️ Encryption detectability🔴 LUKS header🟢 random🟢 VC hides
🎭 Plausible deniability❌ not supported✅ hidden volumes✅✅ excellent
🪟 Windows compatibility❌ (LUKS inside)
🐧 Qubes OS compatibility✅ built‑in🟡 manual install🟡 manual install
Bottom line:
LUKS only: fastest and best for 90% of Linux/Qubes users.
VeraCrypt only: for plausible deniability or Windows compatibility.
Double (VC+LUKS): for extreme paranoia – combines VC deniability with LUKS integrity.
💬 Messaging data: The speed difference is unnoticeable (small SQLite). For high sensitivity: VC outer + LUKS inner.
📦 Vault‑in‑a‑box – double storage (VeraCrypt + LUKS)

Idea: Two independent encryption layers: VeraCrypt outer (for plausible deniability) and LUKS inner (for protection and data integrity).

What does a coerced person see?

Decoy password → ordinary files. No trace of sensitive data.

✅ Full plausible deniability.

What really exists?

Real password → inner LUKS containing sensitive data (keys, conversations, scripts).

⚠️ Avoid SSDs if plausible deniability is critical (TRIM).

🧅 Direct VeraCrypt + LUKS setup (without an intermediate filesystem)

Idea: Encrypt a partition/container with VeraCrypt, then format it directly as LUKS. Used inside Whonix RAM with full automation via a script.

🛠️ One‑time setup

Phase 1: Outer VeraCrypt

  1. Start a temporary storage qube, attach the USB drive.
  2. VeraCrypt ← Create VolumeEncrypt a non‑system partition (or file container).
  3. Choose Standard (or Hidden), enter a strong password (outer layer).

Phase 2: Open VC without mounting

  1. Select the partition/container and click MountDo not mount.
  2. It will appear as /dev/mapper/veracrypt1 with no filesystem.

Phase 3: Format directly as LUKS

  1. Use GNOME Disks or cryptsetup on the block device.
  2. Choose Format → Ext4 with LUKS enabled, enter a second, different strong password (inner layer).
  3. Copy your data and scripts into the mounted LUKS container.

▶️ Daily Operation (Correct Automatic Sequence)

1 In the storage qube: open VeraCrypt without mounting (Do not mount) ← /dev/mapper/veracrypt1 appears.
2 In the same storage qube: unlock LUKS on /dev/mapper/veracrypt1 without mounting (Unlock only) ← /dev/mapper/luks-... appears.
3 From dom0: attach the unlocked LUKS block device (Qubes Devices widget) to whonix-work-ram or any Whonix workstation.
4 Automatically: within 10 seconds, the mount-usb.service (previously enabled via option 10 in the helper script) detects /dev/xvdi and immediately mounts it at /home/user/usb.
No manual commands needed – the drive is ready to use.

🔑 Encryption flow:

💾 USB → 🔐 VeraCrypt (password 1) → 🧅 LUKS directly (password 2) → 📁 ext4 → ✅ data
🛠️ Manual disk management: mount-to-whonix.sh

You can also control the disk manually via this script (run from dom0):

  • Path: ~/Desktop/ram-pool-scripts/mount-to-whonix.sh
  • Functions:
    • 🔹 Attach and mount – scans available disks inside the chosen qube and mounts the disk to /home/user/usb.
    • 🔹 Safe detach – runs umount inside the VM and then detaches the device from dom0.
    • 🔹 Show status – executes lsblk inside the VM and shows mount point status.
  • Usage: Run it, it will show a list of running Whonix qubes. Choose the qube and then the desired action.
When do you need it? If you want to quickly attach an extra disk, or safely detach a disk before removal, without having to restart the VM.

⚖️ Direct LUKS 🆚 .img file inside VeraCrypt

Both are two layers. The difference appears after opening VeraCrypt.

🥇 Direct LUKS (recommended)

  • ✅ No leakage (no journaling in‑between)
  • ✅ Stronger deniability
  • ✅ Faster, only two layers
  • ✅ Simpler setup
  • noexec in Whonix
    (solved by the script)

🥈 .img file inside ext4

  • ✅ Executable permissions in Whonix
  • ❌ Journaling may leak data
  • ❌ 3 layers, slower
  • ❌ Complex setup
  • ❌ Weaker deniability (huge file)
⚠️ The noexec issue in Whonix and how the script solves it

Whonix enforces noexec on external devices, which prevents running scripts even after passing the block device.

The script create-ram-vm.sh handles this as follows:

  • It creates a directory /home/user/usb inside whonix-work-ram (an internal path not subject to noexec).
  • It mounts the passed block device onto this internal directory, giving files normal execution permissions.
  • Result: you can run your scripts from /home/user/usb without hindrance, while the data remains in RAM.

💡 Takeaway: The script does not bypass the noexec policy, but cleverly works around it by mounting onto an internal path, achieving both isolation and execution capability.

🎭 Advanced camouflage: USB drive with two partitions (normal + hidden encrypted)

A normal visible partition (FAT/exFAT) occupies most of the space, and a small encrypted partition (VC+LUKS) at the end looks like unformatted space.

Example 32 GB: 30 GB visible + 2 GB encrypted

The encrypted partition has no filesystem signature, it is opened only via VeraCrypt and then LUKS applied directly.

Effective: The drive looks normal and the "unformatted" space raises no suspicion.
💾 SSD warning and secure erasure techniques
⚠️ SSD and TRIM: Do not use hidden volumes with SSDs if you need true plausible deniability (TRIM + wear‑leveling leave traces). Solution: double encryption with separation of storage and execution.

📊 HDD vs. SSD for secure erasure

FeatureHDDSSD
Secure erase✅ Easy (overwriting)❌ Hard (wear‑leveling)
TRIM❌ Not present✅ May reveal unused areas
Data recovery🟡 Possible🟢 Nearly impossible (after TRIM)
Recommended methodwipe / shredATA Secure Erase / Crypto Erase
📖 Terminology: Wear Leveling (distributes writes), TRIM (notifies of deleted pages), Garbage Collection (internal cleanup).
⚠️ Never use shred on an SSD! Use ATA Secure Erase or Crypto Erase instead.

🔐 Separation of storage from execution (to prevent leakage)

Idea: A storage qube (ephemeral) fully decrypts (VC + LUKS) without any mounting, then passes the final block device to a RAM qube (whonix‑work‑ram) which mounts it and works on the data. Data resides only in RAM and disappears when the qube is shut down.

📊 Secure flow (corrected):

📦 storage‑vault (ephemeral) → 🔓 open VC ← 🧅 open LUKS (unlock only, no mount) → 🖱️ pass block device via dom0 → 🧠 whonix‑work‑ram (RAM pool) → ✅ script auto‑mounts onto internal path

VC password + LUKS password are entered only in the storage qube. Data is written to RAM and disappears on shutdown 🧹

🖱️ Practical passing via the dom0 GUI

  1. In storage‑vault: open VeraCrypt (Do not mount) ← open LUKS (Unlock only).
  2. In dom0: Qubes Devices icon ← you will see the block device of the open LUKS.
  3. Select the device → Attach to whonix‑work‑ram.
  4. In whonix‑work‑ram: run the script create-ram-vm.sh and answer y — it automatically mounts to /home/user/usb.
⚠️ Never mount in the storage qube! Only open the encryption and leave it. Mounting is done in the RAM qube via the script.

🧠 Full automation with the script create-ram-vm.sh

The script on the desktop does everything automatically:

  • 📂 ~/Desktop/ram-pool-scripts/create-ram-vm.sh
  • Creates the RAM pool + whonix‑work‑ram + Cwtch/OnionShare settings
  • Prepares the /home/user/usb directory and makes it executable (solves the noexec issue)
  • Interactively asks: do you want to attach a disk? Answer y after passing the block device from dom0
  • Automatically mounts the disk onto the internal directory with full execution permissions

⚠️ Pass the block device from dom0 first, then run the script.

🛡️ Golden rule: Pass the partition (/dev/sdb1), not the whole disk (/dev/sdb). The correct block device is the one that appears after opening LUKS in the storage qube.

💾 Quick example: LUKS USB to Whonix

  1. Attach the USB to storage‑vault ← GNOME Disks ← Unlock LUKS (do not mount).
  2. Qubes Devices icon: Attach to whonix‑work‑ram.
  3. Inside whonix‑work‑ram: run the script ← answer y ← auto‑mount to /home/user/usb.

The LUKS password never leaves storage‑vault. Data is in RAM. 🛡️

🛠️ Additional secure storage tips
  • 🔹 LUKS header separation: Store the header on a separate device for increased security.
  • 🔹 High PIM in VC: Raise PIM to 500+ to resist GPU attacks.
  • 🔹 LUKS header backup: cryptsetup luksHeaderBackup …
  • 🔹 Disable TRIM: When using VC with hidden volumes on an SSD (if possible).
  • 🔹 RAM cleanup: After you finish, stop ram‑work and free the memory.
🛡️ Hardening an existing system LUKS disk without reformatting

Problem: You created your system disk with the default PBKDF settings and want to raise memory consumption and derivation time to protect boot. There is no direct command to modify an existing slot on the system disk.

Important note: This is the system disk – the one Qubes boots from. Any mistake will prevent booting. Make sure you have a live USB for emergency before starting.

Only method: Replace the weak password with a strong one, then delete the old one. Both steps are mandatory each time – even when changing the password later.

luksAddKey (high memory and time) → luksRemoveKey (for old) → only one password left → attacker will go for the weakest
0 Before you start: It is advisable to raise dom0 RAM to get the most benefit

⬆️ Via modifying dom0_mem in GRUB settings, then update and reboot ⬆️

1 Run the script from the main menu

⬆️ Option 9 in the menu. It detects the disk automatically and asks for the desired memory (in GB) and time (in seconds) ⬆️

2 The script will be created on the desktop and run

⬆️ It executes luksAddKey --iter-time with your entered values, then asks before deleting the old key and updating the boot image ⬆️

Never use luksChangeKey for this purpose.

Even with --pbkdf-force-iterations, it never accepts --pbkdf-memory – it will silently revert memory to the default (1 GB).

💡 Takeaway:
Raise dom0 RAM via dom0_mem in GRUB to get the most benefit.
Ask for high memory (e.g., 4 GB) – the system will not crash, it will automatically fall back to a safe limit.
The script executes luksAddKey + luksRemoveKey automatically using the time you specify.
luksChangeKey = silent reversion to default memory.
Only one password – having two makes the weakest the target.
For Qubes system disk: update the boot image with sudo dracut -f.
⚡ For automatic application of all settings in this tab:

Use the Helper Script in Tab 11.

⚠️ Remember: Do not leave two key slots. Right after luksAddKey, run luksRemoveKey. The attacker will always target the easiest.

⚠️ Why all this complexity? (from the Hitchhiker's Guide)

Each card represents a real threat – with a simple explanation and mitigation

"Privacy is when people know who you are but don't know what you do. Anonymity is when people know what you do but don't know who you are."
🎯 Threat model – who is this guide designed for?
✅ Protects against
  • OSINT researchers (e.g., Bellingcat)
  • Trolls on 4chan
  • Mass surveillance / casual monitoring
  • Hackers with limited resources
  • Data analytics and advertising companies
❌ Does NOT protect against
  • Intelligence agencies (NSA, Mossad)
  • Global adversaries
  • The $5 wrench attack (torture)
  • Unknown zero‑day vulnerabilities
🟡 Network
DNS & SNI leakage

Your ISP sees every site you visit, even with HTTPS. They can sell this data or hand it over to authorities.

Solution: sys‑whonix + DNS over Tor (Tab 2)
🟡 Analysis
Timing attacks

What are they? The adversary monitors the timing of data packets entering and leaving the Tor network, and matches the patterns to discover that the source and destination are the same person.

🎯 How it works:

  1. Adversary monitors your internet connection (ISP, WiFi point)
  2. Adversary monitors the destination server (website, service)
  3. They analyse the timing of sent and received packets
  4. If patterns match → they know you are the one contacting the site

📖 Real example: In 2013, the FBI arrested a Harvard student who threatened to bomb. They monitored the university network and saw that the timing of his Tor connection exactly matched the timing of the threat message.

🛡️ How to protect yourself:

  • ✅ Use Whonix – all traffic goes through Tor (no leakage)
  • ✅ Use multiple gateways – separate identities with different Tor circuits
  • Do not connect to the same WiFi that you use for your real identity
  • Avoid identical activity times between your real and anonymous identities
Solution: Whonix + multiple gateways (Tab 2)
🔴 Network
Rogue access points

A small device (Wi‑Fi Pineapple) impersonates a legitimate network and forces your device to connect to it, eavesdropping on traffic.

Protection layers:

  • WISP + Ethernet: hides your Qubes device from the local network – the router is what appears, not your device.
  • sys‑whonix + Tor: even if the attacker eavesdrops, the data is encrypted with 3 Tor layers + TLS.
Solution: WISP + Ethernet (Tab 1) + sys‑whonix (Tab 2)
🟡 Wireless
Wi‑Fi / Bluetooth scanning

Your phone constantly scans networks and devices around you. Google and Apple maintain a database to determine your location.

Solution: Do not carry your smartphone during sensitive activities
🔵 Network
IPv6 leakage

If you use a VPN that supports only IPv4, IPv6 connections may leak directly to your ISP.

Solution: Disable IPv6 in sys‑net (Tab 1)
🔴 Tor
Malicious entry guards

If you land on a malicious entry guard, you stay stuck with it for months, and all your activities can be linked.

Solution: Multiple Whonix gateways (Tab 2)
🔴 Tracking
Browser fingerprinting

Sites collect information about your device: fonts, screen resolution, language. This fingerprint is unique for 99% of users.

Solution: Kicksecure (Tab 2 – Whonix Workstation)
🟡 Browser
WebRTC leakage

Even with Tor, websites can use WebRTC to learn your device's real IP address.

Solution: Disable WebRTC in the browser (Tab 1)
🔴 Browser
Side‑channel attacks (e.g., Leaky Page)

Vulnerabilities like "Leaky Page" allow websites to steal browsing history from other tabs.

Solution: NoScript + identity separation
🔴 Physical
Evil Maid attack

Someone gains physical access to your device in your absence and plants a hardware keylogger or copies the disk.

Solution: Live Mode (OverlayFS) + AEM. See Tab 7 for details.
🔴 Physical
Cold Boot attack

The adversary cools the RAM to extract encryption keys minutes after the device is powered off. This attack is impractical outside a lab.

Solution: BusKill (luksSuspend) or full shutdown + RAM‑wipe. See Tab 7 for details.
🔴 Physical
BadUSB / Rubber Ducky

A USB flash drive impersonates a keyboard and executes malicious commands with root privileges.

Solution: sys‑usb (default) + usbguard (if booting from USB). See Tab 7 for details.
🔴 CPU
Spectre / Meltdown

Hardware vulnerabilities that allow stealing data from memory across virtual boundaries.

Solution: mitigations=auto (Tab 0)
🔴 Firmware
Intel ME / AMD PSP

A hidden management chip inside the CPU that runs even when the device is off, with full privileges.

Solution: AMD CPUs + Live Mode limits its harm
🟡 Physical
TEMPEST

Reconstructing what is shown on a screen or extracting keys through electromagnetic emissions.

Solution: Out of scope for this guide (Faraday cage)
🔵 Data
Metadata / EXIF

Images carry GPS coordinates, camera type, capture date, and author name.

Solution: ExifTool / mat2 (Tab 3)
🔵 Data
Watermarks

Zoom and Adobe add invisible watermarks. Colour printers print faint yellow dots.

Solution: Dangerzone + black‑and‑white laser printer
🟡 Storage
Cloud backups

WhatsApp and iCloud keep unencrypted copies of your conversations that can be handed to authorities.

Solution: Encrypt locally before uploading (VeraCrypt)
🟡 Storage
Deleted file recovery

Deleting files does not actually erase them. On HDDs they can be recovered easily. On SSDs, TRIM and garbage collection make recovery harder but not impossible.

Solution: Ephemeral VM (ephemeral encryption) + RAM‑VM (maximum protection). See Tab 3 (Secure Storage) for details.
🔵 Behavioural
Stylometry

Your writing style is unique and can be used to link your anonymous identities to your real identity.

Solution: Change style + spell checker
🟡 Behavioural
Behavioural biometrics

The way you move your mouse, your typing speed, and how you solve CAPTCHAs are used to identify you.

Solution: Change interaction patterns
🔴 Human
Phishing

The easiest way to compromise you is to trick you with fake emails or malicious links.

Solution: Vigilance + Dangerzone for attachments
🔴 Biometric
Facial recognition

Any photo where you appear can be indexed and linked to you. Clearview AI has billions of images.

Solution: Mask + hat + sunglasses
🟡 Biometric
Gait recognition

Even if your face is covered, you can be identified by the way you walk.

Solution: Loose clothing + change gait
🟡 Wireless
RFID / NFC

Your bank cards and passport contain chips that can be read remotely to track your movements.

Solution: Faraday wallet
🟡 Software
Telemetry

Windows, macOS, and Ubuntu send diagnostic data that can reveal the presence of "suspicious" software.

Solution: Disable telemetry + ShutUp10
🔴 Encryption
Bad cryptography

Some applications use home‑grown encryption protocols that have been proven weak.

Solution: Use well‑audited protocols (Signal Protocol)
🟡 Legal
Fake no‑log policies

VPN services claim not to keep logs, but may be forced to log silently under a court order.

Solution: Don't trust any provider – use Tor
🟡 Financial
Cryptocurrency tracking

Bitcoin and Ethereum are not anonymous. Every transaction is recorded on the public blockchain.

Solution: Use Monero exclusively
🔴 OPSEC
First rule: don't talk about the club

Your biggest enemy is your mouth. Don't tell anyone – not even your closest people – about your sensitive activities or about using this guide.

Solution: Absolute silence. "Loose lips sink ships."
🟡 OPSEC
Don't be weird

If you disappear completely from the digital world, spend all your time on Tor, or have no social media accounts – you draw attention.

Solution: Maintain an "ordinary" digital life alongside your sensitive activities.
🔴 OPSEC
Never mix

The biggest OPSEC mistakes happen when the same email address, the same writing style, or accidentally opening a sensitive link from a personal device occurs.

Solution: Complete isolation. Each identity has its own device, email, and style.
🔴 Legal
Don't talk to the police

In most countries (especially the United States), anything you say to the police can be used against you. Even if you are innocent, you might say something that incriminates you.

Solution: "I want a lawyer. I'll remain silent."
🔵 OPSEC
Lawyer's number always with you

In an emergency, the last thing you want to worry about is finding a lawyer's number. Memorise a trusted lawyer's number or keep it in a secure place.

Solution: Memorise the number. Do not store it on your phone.
🔴 OPSEC
Trap phones (AN0M, EncroChat, Sky ECC)

Cases like AN0M, EncroChat, and Sky ECC have proven that "ultra‑secure" devices sold to criminals are often traps run by intelligence agencies.

Solution: Do not buy "magic solutions". Do it yourself.
🔴 Advanced
Three types of data leaks (Data Leaks)

From the official Qubes documentation: There are 3 types of data leaks:

  • Type 1 (Intentional): Malware creates cooperative covert channels. ❌ No practical complete solution.
  • Type 2 (Intentional Sniffing): Guessing key material via side‑channels. ✅ Solution: Shut down other VMs.
  • Type 3 (Unintentional): Non‑malicious software leaks data. ✅ Solution: Firewall + empty NetVM.
Correction from Qubes docs: Shutting down other VMs does not protect against Type 1. Malware can store data and wait for a VM with network connectivity to reappear.
Solution: Type 1 represents a limitation of Qubes' isolation model.
🔴 Advanced
Cooperative covert channels

The most serious threat to Qubes. Malware in an isolated VM and malware in a VM with network connectivity communicate via CPU cache. Firewalls do not prevent this. Qubes documentation confirms there are no practical complete solutions for this attack.

Solution: No complete technical solution. This is a limit of the isolation model.
🛡️ Advanced Qubes solutions: Ephemeral VM + RAM‑VM + Live Mode

Note: These are the only practical defences currently available in Qubes OS against threats targeting the hard disk (forensic analysis) and the host system (dom0). Pay attention to the difference between Ephemeral VM and RAM‑VM – the former is ephemeral encryption on disk (inherited from a DVM template), the latter is a VM entirely in RAM via tmpfs.

🧠 Ephemeral VM (inherited from DVM)

Mechanism: Set root rw False on the DVM template ← derived qubes automatically inherit ephemeral encryption on disk.

Activation command:

qvm-volume config DVM_TEMPLATE:root rw False

Mitigates:

  • 🗑️ Forensic analysis: Data cannot be recovered after shutdown
  • 📋 Type 3 leaks: User logs disappear
Does not protect against Cold Boot (data is on disk).
🧠 RAM‑VM (tmpfs)

Mechanism: VM entirely in RAM via RAM pool + tmpfs.

Activation command:

qvm-create -P ram_pool -t TEMPLATE VMNAME

🔑 Note: All disposable VMs derived from a RAM‑based VM also run in RAM automatically.

Mitigates:

  • 🗑️ Forensic analysis: No trace on disk
  • 💾 SSD analysis: Does not interact with the disk
  • 🧬 Derived DVMs: Run in RAM automatically
Does not protect against Cold Boot: The attack targets RAM itself. To protect against Cold Boot, use BusKill (luksSuspend) or full shutdown + RAM‑wipe.
⚡ Live Mode (OverlayFS)

Mechanism: dom0 is mounted read‑only. All changes to dom0 are written to RAM and disappear on shutdown.

Activation command: Add rootovl to GRUB_CMDLINE_LINUX in /etc/default/grub.

Mitigates:

  • 🚪 Evil Maid: No persistent malware in dom0
  • 🧠 Compromised dom0: The system returns clean every time
  • 🤖 Intel ME/AMD PSP: Limits their ability to plant rootkits
Does not protect against: An adversary who has root on the compromised dom0 and can access the original disk.
💡 Conclusion: Ephemeral VM (via inheriting root rw False from a DVM template) protects against disk forensic analysis – fastest for daily sessions. RAM‑VM (via RAM pool) provides maximum data protection – no trace on disk, and all disposable VMs derived from it run in RAM automatically. To protect against Cold Boot: use BusKill (luksSuspend) or full shutdown + RAM‑wipe – the attack targets RAM itself. Live Mode (via rootovl in GRUB) protects the host system itself from tampering and persistence attacks. Together, they form a complete shield against persistence attacks and forensics. However, they do not protect against cooperative covert channels, which represent a limit of Qubes' isolation.
"Qubes aspires to maintain a state where an attacker who controls the network, has root in one VM, can exploit the network card, and can clone the disk – still cannot take control of dom0 or see data in other, non‑compromised VMs."
— Jean‑Philippe Ouellet, former Qubes developer
"Trust no one. Not your ISP, not your VPN provider, not the Tor network, not the operating system, not even your own computer. Trust, but verify."
📋 Summary: how does this guide protect you from each threat?
🛡️ Technical 🟡 Network → sys‑whonix (Tab 2) 🔴 Tracking → Kicksecure (Tab 2) 🔴 Physical → Live Mode + encryption 🔵 Data → ExifTool (Tab 3) 🔴 Tor → Multiple gateways (Tab 2) 🔴 CPU → Kernel parameters (Tab 0) 🟡 Financial → Monero only 🧅 IP → Whonix Gateway (Tab 2) 📶 WiFi → WISP+Ethernet (Tab 1)
🧠 Behavioural 🔴 Talking → Absolute silence 🟡 Lifestyle → Be normal 🔴 Mixing → Complete identity isolation 🔴 Legal → Don't talk to police 🔵 Emergency → Keep a lawyer's number 🔴 Traps → No ready‑made solutions ✍️ Writing → Change style
🚨 Emergency 🔥 Burned → Delete‑and‑escape plan 🔍 Tampering → Immediate leave plan 💀 Destruction → BusKill 🧹 Forensics → Ephemeral VM (DVM inheritance) ❄️ Cold Boot → BusKill / Shutdown + RAM‑wipe ⚡ Host → Live Mode (rootovl)
💰 Funding 💵 Cash → iVPN 🪙 Monero → Mullvad / Njalla 🔄 Exchange → sideshift.ai / bisq ❌ Avoid → Bitcoin/Ethereum

💡 Remember: This guide protects you against moderately resourced adversaries. "Trust, but verify."
🧠 Amnesic Tmpfs – directories in RAM that disappear after shutdown

The tmpfs technique makes sensitive directories (logs, cache, user files) reside only in RAM and vanish completely on reboot. This provides an extra layer of privacy and security.

📌 Directories that will be placed in RAM:
  • /var/log – system logs (512 MB)
  • /var/cache – cache (512 MB)
  • /var/tmp – temporary files (256 MB)
  • /tmp – temporary files (512 MB)
  • /home/user – user home directory (2 GB)
⚠️ Important notes:
  • Applying Amnesic Tmpfs to default-dvm makes it inherited by all disposable VMs (DVMs).
  • Applying it to a normal template (e.g., whonix-workstation-18) only affects that template and is not inherited by derived qubes.
  • Directories in RAM consume your memory – make sure you have enough RAM.
  • You can apply or remove these settings at any time.
⚡ Available in the helper script:

To apply or remove Amnesic Tmpfs, go to Tab 11 (Helper Script) and run auto-script.sh.

chmod +x auto-script.sh && ./auto-script.sh

Then choose: 6) Amnesic Tmpfs

You will see a menu:

  • 1) Apply Amnesic Tmpfs to a specific qube
  • 2) Remove Amnesic Tmpfs from a specific qube
RAM Pool (storage in memory) – complete deniability

Idea from the Hitchhiker's Guide: Like Tails, but inside Qubes. Qubes that run in RAM disappear completely on shutdown.

Use case:

You are working on a highly sensitive document. Suddenly, you hear a knock on the door. You hit shutdown. After 60 seconds, even if the device is seized, there is no way to recover what you were working on. "No data = no crime".

This technique allows you to create a file pool on a tmpfs filesystem, then create AppVMs on that pool so that the qube’s volumes reside in memory.

⚠️ Warning: after a reboot, everything disappears (including the qube data itself). Use the auto‑initialisation script if you want a persistent environment.

📌 Basic setup

Raise dom0 memory limit
Important: By default, Qubes OS allocates only 4 GB maximum to the dom0 system. In Live Mode or when running several templates in a RAM pool, you may need to increase this limit.
TypeCommandNotes
Permanent change (edit GRUB) sudo nano /etc/default/grub
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Find the line GRUB_CMDLINE_XEN_DEFAULT and change the dom0_mem value, e.g., dom0_mem=max:8192M
🚀 All RAM Pool scripts are available in the helper script

To avoid duplication, all the full scripts are available in Tab 11 (Helper Script)

⚡ For automatic application: chmod +x auto-script.sh && ./auto-script.sh

Then choose: 5) Create RAM Pool scripts


📋 Scripts that will be created on the desktop:

  • setup-ram-pool.sh – creates the RAM pool in memory
  • create-ram-vm.sh – creates a single qube (with full kernel parameters + DVM template)
  • create-multi-ram-vm.sh – creates several qubes with gateway selection (with full kernel parameters + save capability)
  • cleanup-ram-pool.sh – deletes all qubes and frees the RAM pool
  • ram-pool-status.sh – shows RAM pool status
📂 Script location after running: /home/user/Desktop/ram-pool-scripts/
Manual cleanup (free RAM pool)

To delete all qubes and free the RAM pool manually:

#!/bin/bash
RAM_POOL_PATH="$HOME/ram_pool"
POOL_NAME="ram_pool"

echo "🧹 Cleaning RAM Pool..."

# Delete qubes
for vm in $(qvm-ls --raw-list 2>/dev/null | grep -E "whonix-work-ram|ram-"); do
    echo "  Deleting: $vm"
    qvm-kill "$vm" 2>/dev/null
    qvm-remove --force "$vm" 2>/dev/null
done

# Remove pool
qvm-pool remove "$POOL_NAME" 2>/dev/null

# Unmount
mount | grep -q "$RAM_POOL_PATH" && sudo umount "$RAM_POOL_PATH" 2>/dev/null

# Delete folder
[ -d "$RAM_POOL_PATH" ] && sudo rm -rf "$RAM_POOL_PATH"

echo "✅ Cleanup done"
free -h
Troubleshooting common issues
ProblemCauseSolution
qvm-create fails with "No space left" RAM pool is full or too small df -h /home/$USER/ram_pool then sudo mount -o remount,size=8G /home/$USER/ram_pool
Data does not disappear after reboot (for an AppVM) The pool is not mounted as tmpfs Make sure mount | grep ram_pool shows tmpfs
Kernel parameters not applied The updated script was not used Make sure you use the updated version of the helper script (includes KERNEL_OPTS)
ephemeral_volatile does not work The property requires a pool restart or a new VM After enabling the property, create a new VM; old VMs are not affected

🔒 Advanced Security

Review these measures regularly – security is an ongoing process, not a one‑time setup

🔴 Physical
BadUSB attacks

Do not trust any unknown USB. Any USB can be programmed to act as a keyboard and execute root commands.

Basic protection: Qubes automatically isolates USB in sys-usb (a VM separate from dom0). This prevents any malicious USB device from directly accessing dom0.

If booting from USB: install usbguard in dom0:

sudo qubes-dom0-update usbguard sudo systemctl enable usbguard sudo usbguard generate-policy > /etc/usbguard/rules.conf

The warning "USB in dom0 is not restricted" is normal when booting from USB.

🟡 Network
VPN + Tor together

Threat: Your ISP can see that you are using Tor, even if it cannot see the content of your connection.

Paid solution: Use a trusted VPN before Tor. Path: sys-net → sys-vpn → sys-whonix → anon-whonix. Create a VPN qube and make it the netvm of sys-whonix.

Free alternative: Use Tor + bridges instead of VPN. Bridges hide the fact you are using Tor from your ISP at no cost. See Tab 2 (Whonix) for the bridges section.

Tor alone is sufficient for most users. VPN → Tor is an extra layer for those who need to hide Tor usage itself.
🟢 Maintenance
Security updates

Threat: Neglecting updates leaves known vulnerabilities open to exploitation. Apply updates as soon as they appear.

Basic updates:

  • dom0: sudo qubes-dom0-update
  • Templates: sudo apt update && sudo apt upgrade (inside each template)

Updates over Tor: Qubes uses UpdatesProxy which automatically routes template updates through sys-whonix.

Optional: sudo apt install debsums && sudo debsums -c to verify package integrity.
🔵 Storage
Encrypted backups

Threat: Your disk may fail or be stolen. Without an off‑device backup, you may lose all your data.

Basic solution (Qubes Backup):

qubes-backup --encrypt --passphrase-file /path/to/key /mnt/backup

External alternatives:

  • BorgBackup: incremental backup + encryption + compression
  • Restic: supports encrypted cloud storage
Store the backup outside your device – in a secure location.
🔴 Boot
Do not use dual boot

Threat: Any other operating system (e.g., Windows) can modify your unencrypted /boot and install a bootkit or replace the kernel with a malicious file.

Official sources: Qubes and Kicksecure documentation warn against dual boot because /boot is unencrypted and can be tampered with.

From the Qubes forum: "A bootkit can install itself without the user doing anything wrong."

Safe solutions: A separate external drive for Qubes (physically disconnected), a dedicated computer, or a single‑OS system.
🔴 Physical
USB risk to dom0

Threat: Even if you "pass through" a USB to another VM, dom0 must run USB drivers and read Vendor/Product IDs and the partition table.

What can a malicious USB do?

  • Impersonate a keyboard (Rubber Ducky) and execute root commands
  • Exploit USB driver vulnerabilities (buffer overflows)
  • DMA attacks via Thunderbolt/USB‑C

Your situation (booting from USB): The external drive is trusted, but any other USB is a risk. Complete USB isolation is not possible when booting from USB.

Do not attach any untrusted USB. If possible, use two separate USB controllers: lspci | grep USB.
🔴 Physical
Evil Maid & Cold Boot attacks

① Evil Maid:

Someone gains physical access to your device in your absence and can install a keylogger, modify the bootloader, or implant firmware‑level malware.

Protection: BIOS password + Secure Boot + Anti Evil Maid (AEM) – requires TPM 1.2 and Intel TXT + Live Mode (OverlayFS) – dom0 in RAM, all changes disappear after power‑off.

⚠️ Note: Live Mode does not protect against UEFI/BIOS tampering itself, and using USB for security involves a trade‑off.

② Cold Boot:

The attacker cools the RAM to extract encryption keys after the device is powered off. Data can be recovered for up to 90 minutes with cooling.

What protects:

  • Full shutdown: reduces risk but does not eliminate it – attack is possible within minutes
  • BusKill / Dead Man's Switch: luksSuspend erases LUKS keys from RAM
  • RAM‑wipe: erases memory on shutdown

What does NOT protect:

  • Sleep / Hibernate: keys remain or are stored on disk
  • Ephemeral VM: the key is in RAM during the session

③ Mode comparison:

ModeRAMSpeedCold BootEvil Maid
Sleep🔴 Active🟢 seconds❌ Weak
Hibernate🟢 Dumped🟡 10‑30 s🟡 Partial (image recoverable)
Shutdown🟢 Cleared🔴 30‑60+ s🟡 Reduces risk
Additional tools: qubes‑canary (canary tokens in /boot), tpm‑qubes (TPM for /boot verification), Heads firmware (open‑source BIOS replacement). See Tab 4 for all threats.
💀 Destruction
Self‑destruct tools

Scripts available in Tab 11 (choose 8) Emergency Tools):

① Dead Man's Switch (delayed destruction)

Type: 💀 Complete destruction with a timer

Mechanism: Executes automatically after a timer (you specify the minutes).

Ideal for: Arrest or coercion – if you do not cancel within the timeout, everything is automatically destroyed.

To cancel: sudo /usr/local/bin/cancel before the timeout.

Location: ~/Desktop/emergency/deadman-switch.sh

② BusKill Self‑Destruct (immediate destruction)

Type: 💀 Complete destruction immediate

Mechanism: Executes immediately when run (keyboard shortcut).

Ideal for: Sudden raid or immediate danger requiring fast destruction.

Note: Cannot be cancelled after execution.

Location: ~/Desktop/buskill/buskill-selfdestruct.sh

Shared destruction mechanism (both scripts ① and ②):

  • 🔫 Kill all VMs: qvm-kill --all
  • 🗑️ Delete Qubes from boot menu: Delete the EFI/qubes folder from the EFI partition
  • 💀 Destroy the /boot partition: Overwrite with random data using dd
  • 🔑 Overwrite the LUKS header of all encrypted disks (including the Qubes system disk)
  • 📝 Randomly overwrite the header area (according to LUKS header size)
  • 🧠 Remove keys from memory: luksSuspend for all devices
  • 🔄 Emergency reboot (sysrq‑trigger)

⚠️ Result: Complete irreversible destruction. Even a LUKS header backup alone will not suffice – you also need to restore /boot and EFI/qubes. A full reinstall may be the easiest recovery path.

③ Uninstall Dead Man's Switch (only removes the service)

Type: 🗑️ Delete the service only

Not related to destruction. Its only function is to delete the systemd service of the Dead Man's Switch.

  • 🛑 Stop deadman.service
  • 🚫 Disable the service from starting at boot
  • 🗑️ Delete files: service, nuke, cancel, sudoers permissions

Note: Does not delete BusKill itself (/etc/qubes-rpc/buskill.selfDestruct), only removes the timer.

Location: ~/Desktop/emergency/uninstall-deadman.sh

📊 Quick comparison:

Criteria① Dead Man's② BusKill③ Uninstall
Type💀 Delayed destruction💀 Immediate destruction🗑️ Remove service
TimingAfter timerImmediate
Destruction mechanismFullFullNone
Deletes EFI/qubes
Destroys /boot
Kills VMs
Overwrites LUKS header✅ all disks✅ all disks
luksSuspend✅ all devices✅ all devices
Wipes Qubes
Cancellable✅ before timeout
Recoverability⚠️ need /boot + EFI⚠️ need /boot + EFI
Best forArrest / coercionRaid / immediate dangerSystem cleanup
🔥 Emergency
Emergency plan

① If you are burned (identity exposed):

You have time: Delete the identity from the internet, destroy the SIM and phone (destroy physically and discard), wipe the disk or physically destroy it, keep a lawyer's number, act as if nothing happened.

No time (urgent): Immediately shut down / disconnect power. RAM will fade and data becomes unreadable. Remain silent – don't speak without a lawyer.

② If you discover tampering:

On the laptop: Do not touch the planting – do not use the device. Remove the disk (if encrypted) and leave immediately.

In the room: Assume everything is planted (cameras, recorders). Touch nothing – leave immediately and quietly. Never return.

③ Trick to detect inspection: Take a photo of your desk before leaving and compare when you return. A thin thread or a piece of wax on the drawer.

④ Data recovery: qubes-backup --restore on a fresh, clean device.

Know your rights: World map of encryption laws. If your adversary has physical access, the only option is immediate disappearance.
💡 Tips
Quick security tips

① Basics: Strong, unique passwords for every service. Update the system weekly. Use disposable VMs to open suspicious files.

② Network & browsing: Isolate USB in sys‑usb (default in Qubes). Disable JavaScript or use NoScript. Use the Tor Browser inside Whonix for sensitive activities.

③ Storage & encryption: LUKS2 with integrity for sensitive data. RAM pool for ephemeral operation (no disk trace). Kernel parameters: init_on_free=1, init_on_alloc=1.

Avoid downloading software from untrusted sources. Use official repositories and signed packages only.
🛡️ Physical
Additional physical protection

① Boot protection: Strong BIOS/UEFI password – prevents changing boot settings. Enable Secure Boot. Use TPM to securely store encryption keys.

② Advanced tools: Anti Evil Maid (AEM) – available for Qubes (Intel CPUs). Heads firmware – open‑source BIOS replacement with full TPM verification – stronger than Secure Boot.

③ Specialised hardware: NitroPad (Nitrokey) – laptop with built‑in Heads. Purism Librem – laptop with physical kill switches.

④ While travelling: Do not leave your device unattended. Use a Faraday cage to block wireless communication. Check for strange devices when you return.

If your adversary is a government or intelligence agency, even these measures may not be sufficient.
⚡ For automatic application:

Use the Helper Script in Tab 11 to apply Kicksecure settings automatically to all templates.

chmod +x auto-script.sh && ./auto-script.sh

Then choose: 3) Apply Kicksecure settings

🛡️ Kicksecure – a fully hardened operating system

What is Kicksecure?

Kicksecure is a complete operating system based on Debian, designed from the ground up to be highly hardened.

It is not just "Debian with some extra settings", but a comprehensive security package that applies a multi‑layer security model, starting from the hardened kernel, through user permissions, and all the way to applications and networking.

Result: A system ready for daily use, resistant to viruses and attacks, providing proactive protection instead of reactive defence.

Kicksecure inside Qubes OS

In Qubes OS, Kicksecure acts as a Template to automatically give all your AppVMs its hardening features.

  • Every AppVM automatically inherits all of Kicksecure’s hardening.
  • It can be routed through sys-whonix to get Whonix + Kicksecure together.
  • Complete isolation between different identities.
Best practice: qvm-create --template=kicksecure anon-work && qvm-prefs anon-work netvm sys-whonix
⚠️ Important note for Qubes OS users:

In Qubes OS, all templates share the dom0 kernel and cannot use their own kernel. Therefore, you cannot install a hardened‑kernel inside the template.

Instead, kernel hardening is applied via:

  • GRUB parameters for dom0 (see Tab 0)
  • kernelopts parameters for VMs (see Tab 0)
  • sysctl settings inside the template (user‑space only – shown below)
🧅 Layers of protection in Kicksecure

Kicksecure does not rely on a single protection layer, but applies a multi‑layer defence model that includes:

Kernel hardening

KSPP settings, disable dangerous features, randomise structure layout.

⚠️ In Qubes: applied via GRUB and kernelopts
User isolation

Strict separation of accounts, protects against brute‑force attacks.

Network hardening

Strict firewall, secure time synchronisation.

Fingerprint unification

Unify browser and system information to make your fingerprint statistically indistinguishable from other users.

Encryption and data protection

Full disk encryption, memory scrubbing on shutdown.

Supply chain security

Strict digital signature verification at every stage of development.

Why Kicksecure even with Tor?

Tor hides only your IP address. But websites can identify you through browser fingerprinting: fonts, screen resolution, settings… Kicksecure unifies this information among all its users, making your fingerprint indistinguishable.

📥 Installation and basic setup

Installing the Kicksecure template

Step 1: Install the template from the community repositories:

qvm-template --enablerepo qubes-templates-community install kicksecure-18

Step 2: Install the official time‑management add‑on:

sudo qubes-dom0-update --action=install qubes-core-admin-addon-kicksecure

Step 3: Update the template regularly:

sudo qubes-dom0-update --action=update kicksecure
About stability: The template is very stable and is used by the lead developer personally on Qubes. Issues are very rare and usually revolve around minor, resolvable warnings.
📦 tirdad – for documentation only (not recommended)

What is tirdad? An old tool that aimed to randomise TCP Initial Sequence Numbers (TCP ISN Randomisation).

✅ Why we don't need it?

Modern kernel parameters in this guide (e.g., init_on_free=1, init_on_alloc=1, page_alloc.shuffle=1) provide more comprehensive and deeper protection against the same types of attacks that tirdad tried to mitigate.

⚠️ Why we do not recommend it?
  • Does not work in the default setup: tirdad requires the in‑VM kernel, which is not the Qubes default.
  • Enabling the in‑VM kernel adds extra security risks: it increases the attack surface and consumes more memory.
  • Redundant protection: Current kernel parameters already provide sufficient and better protection.
What does Kicksecure unify?
Font list
Screen resolution
Language settings (en_US.UTF‑8)
Time zone (UTC)
OS version
Browser settings

⚖️ Documented comparison: Kicksecure vs. Whonix Workstation

📊 Comprehensive comparison table
Feature 🛡️ Kicksecure 🧅 Whonix Workstation
Primary goal Security hardening Anonymity
Relationship between them Whonix is built on Kicksecure ← inherits all its security improvements
Network routing ❌ No mandatory routing ✅ Mandatory routing through Tor
System architecture Single OS (monolithic) Two components: Gateway + Workstation

⚙️ Basic security settings

Disable unnecessary services
sudo systemctl mask bluetooth.service cups.service avahi-daemon.service ModemManager.service
These services are unnecessary in a Qubes environment and pose a security risk.
Disable unnecessary hardware
echo "blacklist pcspkr" | sudo tee -a /etc/modprobe.d/blacklist.conf echo "blacklist thunderbolt" | sudo tee -a /etc/modprobe.d/blacklist.conf
Disable core dumps
echo "* hard core 0" | sudo tee -a /etc/security/limits.conf echo "kernel.core_pattern=|/bin/false" | sudo tee -a /etc/sysctl.d/99-core.conf
Reduce Swap
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.d/99-swappiness.conf

🛡️ security‑misc – advanced security settings

Advanced security package from Kicksecure – requires separate installation

Install security‑misc: sudo apt update && sudo apt install security-misc
🔒 Hide kernel and hardware information

This feature hides sensitive information from /sys and /proc, hardware details (lspci, /proc/cpuinfo), and unique device identifiers.

sudo systemctl enable hide-hardware-info.service && sudo reboot
⚠️ Warning: May break some applications. Test before permanent use.
👻 hidepid – hide processes

The hidepid feature restricts non‑root users from viewing process information.

sudo systemctl enable proc-hidepid.service && sudo systemctl start proc-hidepid.service
⚠️ Warning: May break pkexec and some applications.
📋 Summary of security‑misc settings
  • ✅ Hide kernel and hardware information from applications
  • ✅ hidepid – prevent ordinary users from seeing other processes
  • ✅ Advanced sysctl settings (disable bpf, restrict ptrace, etc.)
  • ✅ Disable unsafe network services
  • ✅ Additional protection against side‑channel attacks

🧪 Effectiveness testing

Browser fingerprint testing sites

Expected result: Your fingerprint is indistinguishable within the largest possible group of users.

🔄 Make Kicksecure the default template for default‑dvm

🧬 Why is this important?

sys-usb, sys-net, sys-firewall, and all disposable VMs are derived from default-dvm. Changing their template to Kicksecure will harden all core system components at once.

⚡ For automatic application:

All these steps are automated in the Helper Script in Tab 11.

The rule: Any change to default-dvm is automatically reflected in everything derived from it.

⚠️ Important notes

  • ⚠️ Exception – default‑mgmt‑dvm: Do not change its template to Kicksecure because it breaks passwordless‑sudo, which is necessary for this qube to function. Keep it on the official template.
  • ✅ Other qubes: Kicksecure is a community template specially tailored for Qubes and includes all integration tools. It works successfully with sys-usb, sys-net, and sys-firewall.
  • 🔴 Important: Take a full backup before any change to default-dvm.
✅ Summary of Tab 8 (Kicksecure):
  • Kicksecure is a complete, multi‑layer hardening operating system
  • In Qubes OS: it is used as a template to harden all virtual machines
  • ⚠️ You cannot install a hardened‑kernel inside the template – kernel hardening is applied via GRUB and kernelopts (Tab 0)
  • It unifies browser and system fingerprints to resist tracking
  • Best practice: use it as default‑dvm and route it through sys‑whonix for anonymity

🚀 Qubes Dom0 Live Boot Script

⚠️ This script is not official from the Qubes OS team, but from the Qubes OS community

Comprehensive script to set up a Live Mode environment with memory scrubbing and ephemeral DVMs

⚡ For quick automatic application:

Use the Helper Script in Tab 11 which automatically runs Live Mode (option 7). The full script is located there.

chmod +x auto-script.sh && ./auto-script.sh

💡 Note: The full live.sh code is inside the helper script in Tab 11, not here – to avoid duplication and simplify maintenance.

📋 What this script does
  • 🔹 Creates Live Mode boot options
  • 🔹 Use Overlay mode – it makes the disk read‑only and runs in RAM – more stable but requires at least 16 GB RAM
  • 🔹 Enables memory scrubbing on shutdown (RAM Wipe)
  • 🔹 Disables Swap in dom0 during live session
  • 🔹 Creates ephemeral disposable VMs (ephemeral‑dvm and ephemeral‑whonix‑dvm)
  • 🔹 Enhances kernel settings for protection
⚠️ Important warning:
  • • This script creates new system files (GRUB custom, initramfs).
  • It does not modify the original files – existing boot options remain unchanged.
  • • It adds two new boot options, "Qubes Overlay‑Live Mode" and "Qubes Zram‑Live Mode", to the GRUB menu.
  • Take a full backup before running (just in case).
  • • Execution may take several minutes.
  • • Requires a system reboot to activate the Live Mode options.

🔥 Strongest scenario: combining Live Mode + RAM‑VM

This is the complete shield. dom0 runs in RAM, and sensitive data also runs in RAM. No digital evidence remains after shutdown.

🛡️ Comparison of the three scenarios

Scenario dom0 VM data Protection level Requirements
Live Mode only ✅ in RAM ⚠️ on disk 🟢 Good 16 GB RAM
RAM‑VM only ⚠️ on disk ✅ in RAM 🟢 Good depends on VM size
Live + RAM‑VM ✅ in RAM ✅ in RAM 🔴 Maximum 32 GB+ RAM
💡 Conclusion:

When combined: no trace of dom0 nor of sensitive VM data remains on disk. Once the device is shut down, there is no digital evidence of any activity. This is what makes the system fully amnesic.

For daily use: Live Mode alone + Ephemeral VMs is sufficient for most users. For highly sensitive sessions: combining with RAM‑VM is the strongest choice.

📊 Comprehensive comparison: all types of ephemeral and secure qubes

This is the only reference comparing all types. See the referenced tabs for full details.

📊 Unified comparison table

Feature / Type Ordinary DVM Ephemeral DVM Amnesic Tmpfs VM in RAM Pool Dom0 Live Boot Live + RAM‑VM
Primary storage location Hard disk Hard disk
(with ephemeral encryption)
RAM (tmpfs) RAM (ram_pool)
all volumes in RAM
RAM (full copy) RAM (dom0 + VMs)
Location of /home/user Hard disk (deleted) Hard disk (not RAM) RAM (tmpfs) RAM RAM RAM
Writes to / (root) N/A Diverted to volatile volume
(on disk)
Written to RAM Written to RAM Written to RAM Written to RAM
ephemeral property ✅ enabled
random‑key ephemeral encryption
❌ (not needed) N/A ✅ full
RAM wipe ❌ (data on disk) ❌ (only freed) ✅ yes ✅ yes
Boot speed 🟢 fast 🟢 fast 🟡 moderate 🔴 slower 🔴 slower 🔴 slowest
Ideal use open a file once work session + ephemeral encryption make specific directories disappear max isolation – whole qube in RAM emergency session – dom0 in RAM maximum protection – no trace at all
Reference tab Tab 9 Tab 5 Tab 6 Tab 9 9 + 6
⚠️ Essential clarifications:
  • ❌ Ephemeral DVM does NOT run in RAM. The data (including /home/user) remains on the hard disk.
  • ✅ "Ephemeral" means "ephemeral encryption with a random key", not "storage in RAM".
  • ✅ Amnesic Tmpfs places specific directories in RAM, but the VM itself remains on disk (Tab 5).
  • ✅ The only configurations that run entirely in RAM are RAM Pool and Dom0 Live Boot (Tab 6 + Tab 9).
  • 🔴 Strongest scenario: combine Live Mode + RAM‑VM = dom0 in RAM + VM data in RAM (Tab 9 + Tab 6).

⚖️ Amnesic Tmpfs vs. RAM Pool – when to use each?

Criteria 🧠 Amnesic Tmpfs (Tab 5) 💾 RAM Pool (Tab 6)
What becomes ephemeral? Specific directories inside an existing VM The entire VM (system and storage volumes)
Where is it applied? Inside any existing AppVM At dom0 level (when creating a new VM)
Isolation level 🟡 Partial – only specific directories in RAM 🟢 Full – entire VM in RAM
Best for Cleaning logs and cache after each session Very sensitive work sessions, a Tails‑like environment inside Qubes
💡 Final conclusion:
  • • If you want ephemeral encryption (data encrypted then key lost on shutdown) → use Ephemeral DVM from Tab 9
  • • If you want specific directories cleaned (logs, cache) → use Amnesic Tmpfs (Tab 5)
  • • If you want a whole VM in RAM (maximum isolation, zero data on disk) → use RAM Pool (Tab 6)
  • • If you want a complete emergency session (dom0 itself in RAM) → use Dom0 Live Boot (Tab 9)
  • 🔴 For absolute maximum protection: combine Live Mode + RAM‑VM (Tab 9 + Tab 6). This is the complete shield: dom0 in RAM and VM data in RAM. No trace remains on disk after shutdown.

📱 Portable apps for Whonix / Qubes

What are portable apps?
These scripts allow you to run desktop applications in a fully portable manner, where all application data is stored inside a folder next to the script (not in /home/user). This provides an extra layer of isolation and privacy, and can easily be carried on a USB flash drive.
⚠️ Basic warning: The recommendations below are based on the official Whonix chat page. Applications that require static identifiers (like a phone number) or have known security problems are not recommended for anonymous use.
✅ Before you start – quick checklist
  • ✅ Make sure Whonix‑Gateway is running and connected to the Tor network
  • ✅ Make sure Whonix‑Workstation is running behind the gateway
  • ✅ Prepare a USB flash drive formatted with a filesystem that supports symlinks and permissions (ext4 preferred)
  • ✅ Ensure sufficient free space (2 GB minimum for files and data)
  • ✅ Save all scripts in an organised folder as shown in the file structure below
  • ✅ Make sure torsocks is installed: sudo apt install torsocks -y
📊 Comprehensive comparison: secure messaging and sharing applications

An accurate, up‑to‑date comparison of the most important messaging and sharing applications in terms of security and privacy, aligned with the official Whonix page evaluations (2025):

Criteria 🔷 SimpleX 🔴 Cwtch 🟢 OnionShare 💬 Gajim 📘 Element 💬 XMPP 🟣 Session 🟠 Signal 🟢 Briar ✈️ Telegram
🔹 Basic features
Primary purpose Messaging Messaging File sharing + chat Messaging Messaging Messaging Messaging Messaging Messaging Messaging
Network model Decentralised (SMP servers) Peer‑to‑peer (P2P) over Tor v3 Peer‑to‑peer (P2P) over Tor Federated (XMPP servers) Federated (Matrix servers) Federated (XMPP servers) Decentralised (Oxen network) Centralised (Signal servers) Peer‑to‑peer over Tor/Bluetooth Centralised (Telegram servers)
Encryption protocol Simplex Messaging Protocol (SMP) Tapir Protocol Tor onion protocol OMEMO Olm/Megolm OMEMO Oxen Protocol Signal Protocol Briar Protocol MTProto
E2EE by default ✅ full (default) ✅ full (default) ✅ full (default) 🟡 OMEMO (manual enable) 🟡 optional 🟡 OMEMO (optional) ✅ full ✅ full ✅ full 🟡 partial (Secret Chats only)
🔹 Security features (according to the official Whonix page)
Serverless / Peer‑to‑peer ✅ single‑hop (server does not store) ✅ client ← onion ✅ client ← onion (device is the server) ❌ no ❌ no ❌ no ❌ no ❌ no ✅ client ← onion ❌ no
No onion hosting risks ✅ yes ✅ yes ❌ no (device is the server) ✅ yes ✅ yes ✅ yes ✅ yes ✅ yes ❌ no ✅ yes
E2EE by default ✅ yes ✅ yes ✅ yes ❌ no ❌ no ❌ no ✅ yes ✅ yes ✅ yes ❌ no
Quantum resistance ✅ yes ❌ no ❌ no ❌ no ❌ no ❌ no ❌ no ❌ no ❌ no ❌ no
Verifiability ✅ (SMP) ✅ (Tapir) ✅ (Onion E2EE) ✅ (OMEMO) ⚠️ weak ✅ (OMEMO) ⚠️ unaudited ❌ weak
Plausible deniability ✅ (SMP) ✅ (Tapir) ❌ no ✅ (OMEMO) ❌ no ✅ (OMEMO) ❌ no ❌ no ❌ no ❌ no
Message padding ✅ yes ✅ yes ❓ unknown ✅ (OMEMO) ❓ unknown ✅ (OMEMO) ❓ unknown ❓ unknown ❓ unknown ❌ no
🔹 Additional security features
Perfect Forward Secrecy ✅ (SMP) ✅ (Tor) ✅ (Tor) ✅ (OMEMO) ❌ weak ✅ (OMEMO) ⚠️ (v2 Dec 2025) 🟡 partial
Metadata resistance 🟢 excellent (ephemeral IDs) 🟢 excellent 🟢 excellent 🔴 weak 🔴 weak 🔴 weak 🟡 good 🔴 weak (phone number) 🟢 excellent 🔴 very weak
🔹 Technical features
Open source 🟡 client only
Memory‑safe language ✅ (Haskell, Kotlin) ✅ (Go, Rust) ✅ (Python) ✅ (Python) 🟡 partial 🟡 partial 🟡 partial 🟡 partial 🟡 partial 🟡 partial
Independent security audit ✅ (Trail of Bits: 2022, 2024) ❌ not yet ✅ (Radically Open Security: 2021) 🟡 protocol only (2016) 🟡 protocol only 🟡 protocol only (2016) ❌ not yet ❌ unknown ❌ unknown
Signed releases ❓ unknown ❓ unknown ❓ unknown ❓ unknown ❓ unknown ❓ unknown ❓ unknown
Reproducible builds ❌ no ❓ unknown ❓ unknown ❓ unknown ❓ unknown ❌ no
🔹 Usability features
Requires phone number ❌ no ❌ no ❌ no ❌ no ❌ no ❌ no ❌ no ✅ required ❌ no ✅ required
Requires simultaneous connection ✅ (for private chats) ❌ (mailbox)
File size limits unlimited unlimited ✅ unlimited depends on server depends on server depends on server unlimited 100 MB unlimited 2 GB
Works offline (local network) ✅ (Bluetooth/Wi‑Fi)
iOS support ❌ (Android/Linux only) ❌ (desktop only) ❌ (Linux only) ❌ (Android only)
🔹 Final recommendation
Security recommendation in Whonix ⭐ best choice for anonymous messaging ⭐ excellent for maximum privacy (no audit yet) ⭐ for secure sharing and temporary chat ⚠️ for experts (depends on server) ⚠️ use with caution (forward secrecy issues) ⚠️ use with caution (depends on server) ❌ not recommended (security problems) ❌ not recommended (mandatory phone number) ❌ not recommended (Tor‑over‑Tor) ❌ not recommended for any privacy purpose
💡 Official Whonix final recommendation (ordered by priority):
  1. SimpleX Chat – highest security and anonymity: quantum‑resistant, no static identifiers, audited.
  2. Cwtch – excellent for anonymity with secure deniability, but not yet audited.
  3. OnionShare – ideal for temporary chat and file sharing without accounts.
  4. Gajim – an XMPP option for professionals, but depends on a server (weakens anonymity).

❌ Apps not recommended in Whonix: Signal (mandatory phone number), Session (security problems and lack of PFS), Briar (Tor‑over‑Tor conflict), Telegram (partial encryption and centralised servers), Matrix/Element (forward secrecy problems and metadata leakage).

🔍 Network models – why they matter
🏢 Centralised (Signal, Telegram)
You ← → [central server] ← → your friend
⚠️ The server sees who you talk to and when
🏛️ Federated (XMPP, Matrix)
You ← [your server] ← → [friend's server] → friend
⚠️ Both servers know who you talk to
🔒 Peer‑to‑peer (Cwtch, OnionShare)
You ← ← [Tor network] → → your friend
✅ No central server watching
🧅 Single‑hop (SimpleX)
You → [channel 1] → [server] → [channel 2] → your friend
✅ The server does not know both parties together; ephemeral IDs
🧩 Combining apps together (for maximum protection)

No single app does everything. The optimal strategy is to use the right tool for each task:

📋 Example: a researcher/journalist working with a confidential source
  1. OnionShare ← to receive initial documents from the source (without revealing either party’s identity)
  2. SimpleX Chat ← for ongoing anonymous communication (no phone numbers or emails)
  3. Cwtch ← for highly sensitive conversations (can be plausibly denied later)
  4. Signal ← for communicating with colleagues in the newsroom (easy and fast, anonymity not required here)
SimpleX Chat (⭐ officially recommended – highest security and anonymity)

SimpleX Chat is a decentralised, secure messaging application that does not rely on user identifiers and provides the highest level of privacy. Tested and works excellently in Whonix and Qubes OS without complex configuration.

⭐ Optimal and recommended method: enable SOCKS Proxy inside the app

This is the only method that enables circuit isolation and makes the .onion addresses option appear inside SimpleX. Without it, your connection still goes through Tor but lacks these advanced privacy features. Whonix provides a SOCKS proxy automatically on port 9050.

⚙️ Tor settings inside the SimpleX app (step‑by‑step):
  1. Click your profile picture at the top left ← Settings
  2. Choose Network & Servers
  3. ☑ Enable Use SOCKS proxy
  4. Click SOCKS proxy settings and verify:
    • Host: 127.0.0.1 or localhost
    • Port: 9050
  5. ☑ Enable Use .onion hosts (appears after enabling SOCKS)
  6. Choose Required for maximum privacy (never lets traffic leave the Tor network) or When Available for a balance between privacy and availability
  7. ☑ Enable Use random credentials (optional, but increases circuit isolation via the IsolateSOCKSAuth feature in Tor)
  8. Click Save & Update to save and reconnect
🛠️ Advanced network settings:

To access these: enable Developer tools from Settings → About, then go back to Network & Servers → Advanced network settings.

  • Transport isolation: Controls how TCP connections and Tor circuits are isolated.
    • App session: No isolation. All your profiles share the same app session and Tor circuit (higher performance, less privacy).
    • Chat profile (recommended): Gives each profile its own independent Tor circuit, preventing servers and network observers from seeing that traffic comes from the same device.
    • Servers (experimental BETA): Gives each server (contact/group) its own independent Tor circuit. ⚠️ Not recommended for large groups or many contacts because it may create heavy traffic and exceed TCP socket limits; also, creating many separate Tor circuits can be very slow.
  • Private routing: For protecting your IP address. Whonix users should use Unprotected (Tor already hides your IP).
  • Allow downgrade: Whonix users should use When IP hidden to ensure IP is always hidden while allowing connection to older servers.
🧅 Use .onion hosts options:
No: Never use .onion addresses
When Available (recommended for balance): Use .onion if available, otherwise use regular addresses
Required (maximum privacy): Enforce .onion only; will not connect to regular servers

📜 Portable Run Script (Optional - for local data storage)

This script makes SimpleX data portable in a folder next to the app. Note: After launching, manually enable SOCKS Proxy from inside the app according to the steps above to benefit from circuit isolation and .onion addresses.

cat > run-simplex.sh << 'EOF'
#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PORTABLE_DIR="${SCRIPT_DIR}/simplex-chat-data"
APP_DIR="${SCRIPT_DIR}/simplex"  # subfolder for binaries

mkdir -p "${APP_DIR}"

# 1️⃣ First: look for AppImage in subfolder or top level
APPIMAGE_PATH=$(ls "${APP_DIR}"/simplex*.AppImage 2>/dev/null | head -1)
if [[ -z "${APPIMAGE_PATH}" ]]; then
    APPIMAGE_PATH=$(ls "${SCRIPT_DIR}"/simplex*.AppImage 2>/dev/null | head -1)
fi

# 2️⃣ If not found, check for wget then download
if [[ -z "${APPIMAGE_PATH}" ]]; then
    echo "📥 SimpleX not found. Need to download."

    if ! command -v wget &> /dev/null; then
        echo "⚠️ wget not found. Installing now..."
        sudo apt update && sudo apt install -y wget
        echo "✅ wget installed"
    fi

    echo "📥 Downloading SimpleX..."
    wget -c "https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-x86_64.AppImage" \
         -O "${APP_DIR}/simplex-desktop-x86_64.AppImage"
    APPIMAGE_PATH="${APP_DIR}/simplex-desktop-x86_64.AppImage"
    echo "✅ Downloaded"
fi

# 3️⃣ Final check that file exists
if [[ ! -f "${APPIMAGE_PATH}" ]]; then
    echo "❌ Error: AppImage file not found after download."
    exit 1
fi

mkdir -p "${PORTABLE_DIR}"
chmod +x "${APPIMAGE_PATH}"

export XDG_CONFIG_HOME="${PORTABLE_DIR}/.config"
export XDG_DATA_HOME="${PORTABLE_DIR}/.local/share"
export XDG_CACHE_HOME="${PORTABLE_DIR}/.cache"
export HOME="${PORTABLE_DIR}"

echo "🚀 Launching SimpleX Chat..."
echo "💡 Reminder: Enable SOCKS Proxy from Settings → Network & Servers → 127.0.0.1:9050"
exec "${APPIMAGE_PATH}" "$@"
EOF
chmod +x run-simplex.sh
✅ Why SOCKS Proxy is the Best Solution?
  • Full Circuit Isolation: Gives each profile an independent Tor circuit separate from other apps, preventing identity correlation. With the "Chat profile" option, each profile gets its own circuit as well.
  • .onion Support: The Use .onion hosts option appears and works effectively, allowing you to restrict connections to the Tor network only (by choosing Required), thus avoiding malicious exit node attacks.
  • Full Stability: Works without any issues on standalone Whonix (Wayland) and Qubes OS (X11).
  • No Complex Setup: Just enable the option from inside the app, no additional configuration needed in Gateway or Workstation.

📊 Connection Methods Comparison in Whonix / Qubes OS

Aspect Transparent Proxy (TransPort) ⭐ SOCKS Proxy (127.0.0.1:9050)
Connection Mechanism Whonix transparently routes all connections through Tor App connects directly to SOCKS port (127.0.0.1:9050)
IP Concealment ✅ Guaranteed (shows Tor exit node IP to servers) ✅ Guaranteed
Circuit Isolation ⚠️ Not isolated (all apps share the same circuit) ✅ Isolated (gives the app independent circuits, can isolate per profile via "Chat profile" option)
.onion hosts option ❌ Option hidden in the interface ✅ Appears and works effectively
Required Setup None (works by default in Whonix) Only enable SOCKS proxy from settings
Stability on All Environments ✅ Excellent ✅ Excellent
💡 Conclusion: In Whonix, run SimpleX using the script above (or directly), then enable SOCKS Proxy from settings (127.0.0.1:9050). This gives you full circuit isolation + .onion addresses, the optimal setup for maximum privacy without any extra configuration in Gateway or Workstation. For better isolation, use the Transport isolation → Chat profile setting in advanced network settings.
⚠️ For Qubes-Whonix Users:

Do not use sudo directly. Replace it with:

qvm-run -u root anon-whonix '<command>'

Example: qvm-run -u root anon-whonix 'whonix_firewall'

Cwtch (⭐ Recommended - Peer-to-Peer Metadata Resistant)

Cwtch (pronounced /kʊtʃ/) is a decentralized, metadata-resistant messaging protocol built on Tor v3 Onion Services. Developed by the Open Privacy Research Society (Canada).

Features:
  • 🔒 True Peer-to-Peer (P2P) - direct connection without intermediary servers
  • 🧅 Built-in Tor - full IP concealment
  • 🎭 Multiple Identities (Profiles) - completely isolated personas
  • 📦 Fully Portable - runs from a single folder (USB)
  • 🔐 Perfect Forward Secrecy (PFS)
  • 🛡️ Metadata Resistant
  • 🚫 Plausible Deniability: Cwtch's authentication protocol allows complete denial of a conversation after it's closed. Either party can forge chat logs entirely, making it impossible to prove a "real" conversation occurred.
Limitations:
  • 🐢 Requires synchronous connection (both parties online) for private chats
  • 📱 Not available on iOS (Android & Desktop only)
  • 🔧 Requires special setup on Whonix/Qubes
  • 📊 Small user base
  • 🔍 Has not undergone independent security audit yet (per Whonix official page)

📁 Official Installation Structure

cwtch/
├── cwtch                    # launch script
├── lib/
│   ├── cwtch                # executable
│   └── libcwtch.so
├── Tor/
├── install.sh
├── install-home.sh
├── install-sys.sh
├── install-whonix.sh
└── cwtch-whonix.yml

💾 Portable Run Script

cat > run-cwtch-portable.sh << 'EOF'
#!/bin/sh
SCRIPT=$(realpath "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
cd "$SCRIPTPATH"

export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$SCRIPTPATH/lib"
export CWTCH_HOME="${SCRIPTPATH}/cwtch-data"
export CWTCH_RESTRICT_PORTS=true
export CWTCH_BIND_EXTERNAL_WHONIX=true

exec ./lib/cwtch
EOF
chmod +x run-cwtch-portable.sh
⚠️ Important Reminder for Whonix Users:

1. On Whonix-Gateway:

sudo onion-grater-add 40_cwtch

2. On Whonix-Workstation:

sudo mkdir -p /etc/whonix_firewall.d/
echo 'EXTERNAL_OPEN_PORTS+=" $(seq 15000 15378) "' | sudo tee -a /etc/whonix_firewall.d/50_user.conf

Then reload the firewall:

sudo whonix_firewall

3. Inside the Cwtch app (after first launch):

  • Click the Tor icon (onion) on the main interface
  • Choose Advanced Tor Configuration
  • Enter:
    • Custom SOCKS port: 9050
    • Custom Control port: 9051
  • Then click the Restart button

📌 Why? Without entering these ports, Cwtch tries to run its own built-in Tor inside the Workstation (which won't work). By entering them, you tell it to use the Gateway's Tor directly.

⚠️ Without these three steps, Cwtch will not be able to connect to the Tor network.

📥 Download Links

🧅 OnionShare (⭐ Secure & Anonymous File Sharing + Ephemeral Chat)

OnionShare is an open-source tool from the Freedom of the Press Foundation that allows secure and anonymous file sharing or receiving over the Tor network. Files travel directly from your device to the recipient's device without passing through any intermediary server. It also supports website hosting and anonymous chat.

✅ Why OnionShare is Secure?
  • 🔒 No intermediary server – your device is the server
  • 🧅 Tor network – encrypted connection through 3 layers
  • 🎛️ Full control – the link works only while the app is open
  • 📁 No size limits – can share very large files
  • 💬 Supports Anonymous Chat – no accounts, no logs
  • 🌐 Supports hosting temporary .onion websites
  • ⚠️ Does not support Plausible Deniability – per Whonix official page

📥 Receive Mode – For Journalists

  1. Open OnionShare and select "Receive Files"
  2. Choose a folder to save received files
  3. The app generates an .onion link
  4. Send the link to the source (via SimpleX or any secure means)

💬 Anonymous Chat Mode

  1. Select the "Chat" tab and click "Start chat server"
  2. Send the address and key to participants
  3. Once OnionShare is closed, the chat room disappears completely with no logs remaining

🔧 Running OnionShare on Whonix (Gateway + Workstation)

🔹 Step 1: On Whonix-Gateway

# Open terminal as root and run
sudo onion-grater-add 40_onionshare

# ⚠️ Critical step: Make sure to decompress the file if it's compressed
sudo gunzip /usr/local/etc/onion-grater-merger.d/40_onionshare.yml.gz

# To verify success
ls /usr/local/etc/onion-grater-merger.d/  # you should see 40_onionshare.yml without .gz

🔹 Step 2: On Whonix-Workstation

# Open required ports in the firewall
sudo mkdir -p /usr/local/etc/whonix_firewall.d
echo 'EXTERNAL_OPEN_PORTS+=" $(seq 17600 17659) "' | sudo tee -a /usr/local/etc/whonix_firewall.d/50_user.conf
sudo whonix_firewall

# Run OnionShare
onionshare

📊 OnionShare vs Signal vs Cloud Storage

Criterion🧅 OnionShare📱 Signal☁️ Google Drive
File storage location✅ Your device only🟡 Signal servers (temporarily)❌ Google servers
File size limit✅ Unlimited🟡 100MB✅ Very large
Data logging✅ None🟡 Minimal❌ Logs everything
Anonymous chat✅ Supported🟡 Messaging only❌ Not supported
Recommendation for journalism⭐ Ideal for large files🟡 Good for quick messaging❌ Not secure
What is XMPP?

XMPP is an open, decentralized instant messaging protocol. It supports strong end-to-end encryption via OMEMO and does not require a phone number to register. Choose a trusted server like conversations.im.

🔒 Privacy: Message content is fully encrypted. However, metadata protection is weak, as who talks to whom (social graph) can be monitored.

Installing Gajim on Whonix Workstation

Gajim requires no extra Gateway setup – it works directly through system Tor. Proxy settings must be configured manually.

sudo apt update
sudo apt install gajim gajim-httpupload -y

⚙️ Recommended Gajim Settings (from Whonix official page):

  • Logs: Edit → Accounts → Disable Save conversation logs
  • Privacy: Preferences → Advanced → Privacy → Uncheck all options
  • Proxy: Preferences → Advanced → global proxy → Tor ← then Manage → Enable Use proxy authentication leaving fields empty
  • Activity: Preferences → Status → Uncheck Away after and Not available after
  • Applications: Preferences → Advanced → applications → Custom → Clear Browser, Mail Client, and File Browser fields
Gajim - Portable Version (⚠️ Within the same system only)
⚠️ Portability Limitations: Gajim cannot be made fully portable across different systems because it depends on gnome-keyring (GNOME keyring) to manage encryption keys and certificates. The script below allows saving settings and conversations in a portable USB folder, but it will only work within the same system/user.
cat > run-gajim-portable.sh << 'EOF'
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PORTABLE_DIR="${SCRIPT_DIR}/gajim-data"
mkdir -p "$PORTABLE_DIR"

CONFIG_FILE="${PORTABLE_DIR}/config"
if [ ! -f "$CONFIG_FILE" ]; then
    echo "[global]" > "$CONFIG_FILE"
    echo "use_keyring = False" >> "$CONFIG_FILE"
fi

if ! command -v gajim &> /dev/null; then
    echo "❌ Gajim not installed"
    exit 1
fi

gajim -c "$PORTABLE_DIR" &
EOF
chmod +x run-gajim-portable.sh
What is Matrix? (⚠️ Has security concerns)

Matrix is an open, decentralized communication protocol based on a "server-to-server" (Federation) model. According to the Whonix official page, it has serious security concerns:

  • 🔴 Lack of forward secrecy in Element specifically – compromising any key reveals all past messages
  • 🔴 Wide metadata leakage
  • 🔴 libolm library vulnerabilities (later deprecated)
  • 🔴 Server admins could intercept newly encrypted messages by logging into the user's account
  • 🟡 Device verification is essential but often malfunctioning
Installing Element Desktop (using official extrepo)

Per Whonix recommendation, use extrepo to safely add the Element repository:

# Whonix recommended method
sudo extrepo enable element.io
sudo apt update && sudo apt full-upgrade
sudo apt install --no-install-recommends element-desktop
Element - Portable Version

This script makes Element portable, storing all settings, conversations, and encryption keys inside the Element-Data folder on the flash drive.

cat > run-element-portable.sh << 'EOF'
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PORTABLE_DATA_DIR="${SCRIPT_DIR}/Element-Data"
mkdir -p "${PORTABLE_DATA_DIR}"

if ! command -v element-desktop &> /dev/null; then
    echo "❌ Element not installed"
    exit 1
fi

element-desktop --profile-dir="${PORTABLE_DATA_DIR}" &
EOF
chmod +x run-element-portable.sh
Signal Desktop (❌ Not recommended for anonymity - Phone number required)
⚠️ Official Whonix Warning: Signal is not recommended for anonymous use in Whonix due to the mandatory phone number required for registration. This makes hiding your social graph from the server impossible. Use SimpleX Chat as a better alternative.
Method 1: APT Installation (in the Whonix Template)
🌐 Template-Specific Notice: To install Signal via APT, you must temporarily enable networking for the template (as it is blocked by default). After the installation is complete, disable networking again immediately.

This method installs Signal directly into the Whonix Template, making it available to all AppVMs derived from it.

# 0. Install required dependencies
sudo apt update > /dev/null 2>&1
sudo apt install wget gnupg -y > /dev/null 2>&1

# 1. Install the official signing key
wget -O- https://updates.signal.org/desktop/apt/keys.asc 2> /dev/null | gpg --dearmor > signal-desktop-keyring.gpg
cat signal-desktop-keyring.gpg | sudo tee /usr/share/keyrings/signal-desktop-keyring.gpg > /dev/null

# 2. Add the official repository to your sources list
wget -O signal-desktop.sources https://updates.signal.org/static/desktop/apt/signal-desktop.sources 2> /dev/null
cat signal-desktop.sources | sudo tee /etc/apt/sources.list.d/signal-desktop.sources > /dev/null

# 3. Update the database and install Signal
sudo apt update > /dev/null 2>&1
sudo apt install signal-desktop -y

✅ After installation, re-lock the network for the template. To run it in an AppVM: signal-desktop.

Method 2: AppImage (in the AppVM - Recommended)

This method does not touch the template at all and does not require enabling any network (the AppVM's network is already open via Tor). The AppImage file is downloaded and run directly inside the isolated AppVM.

cat > run-signal.sh << 'EOF'
#!/bin/bash
set -e
cd "$(dirname "$0")"
SCRIPT_DIR="$(pwd)"

# Search for the AppImage
APPIMAGE=$(find "$SCRIPT_DIR" -maxdepth 1 -iname "signal*.AppImage" -type f 2>/dev/null | head -1)

# If not found: install wget (if needed) then download the official stable release
if [ -z "$APPIMAGE" ]; then
    command -v wget &>/dev/null || { echo "⚠️ Installing wget..."; sudo apt update && sudo apt install -y wget; }
    echo "📥 Downloading Signal AppImage (stable release)..."
    wget -qc --show-progress -O "$SCRIPT_DIR/signal-desktop.AppImage" "https://updates.signal.org/desktop/signal-desktop.AppImage"
    APPIMAGE="$SCRIPT_DIR/signal-desktop.AppImage"
else
    APPIMAGE="$(basename "$APPIMAGE")"
fi

# Confirm existence + set execute permissions
[ -f "$SCRIPT_DIR/$APPIMAGE" ] || { echo "❌ AppImage not found"; exit 1; }
chmod +x "$SCRIPT_DIR/$APPIMAGE"

# Portable environment (all settings are saved inside the script's folder)
[ -d "$SCRIPT_DIR/Signal.AppImage.home" ] || "$SCRIPT_DIR/$APPIMAGE" --appimage-portable-home
[ -d "$SCRIPT_DIR/Signal.AppImage.config" ] || "$SCRIPT_DIR/$APPIMAGE" --appimage-portable-config

echo "🚀 Running Signal inside the AppVM..."
exec "$SCRIPT_DIR/$APPIMAGE" "$@"
EOF
chmod +x run-signal.sh
✅ Why this method is better in Qubes:
  • No need to modify the base template (keeps it clean).
  • Does not require enabling networking in the template, only the normal AppVM connection (via Tor).
  • Does not require sudo privileges (except for installing wget once inside the AppVM).
  • Copy the folder to any other AppVM and it will work immediately with its data.
(Optional) Portable Script for the APT Method in the AppVM

If you prefer the APT method in the template, you can use this script inside the AppVM to save data locally in a separate folder.

cat > run-signal-portable.sh << 'EOF'
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PORTABLE_DIR="${SCRIPT_DIR}/signal-data"

mkdir -p "$PORTABLE_DIR"

if ! command -v signal-desktop &> /dev/null; then
    echo "❌ Signal is not installed in the template. Run the APT commands first."
    exit 1
fi

echo "🚀 Running Signal with a portable environment inside the AppVM..."
echo "📁 Data is saved in: $PORTABLE_DIR"

signal-desktop --user-data-dir="$PORTABLE_DIR" --password-store="basic" &
SIGNAL_PID=$!

while kill -0 $SIGNAL_PID 2>/dev/null; do
    sleep 1
done

echo "✅ Signal has been closed."
exit 0
EOF
chmod +x run-signal-portable.sh
🛡️ Security Reminder: Signal may not connect properly over Tor in Whonix. If you encounter connection issues, use SimpleX Chat as the officially recommended alternative by Whonix.
Session (❌ Not officially recommended – known security issues)
⚠️ Whonix Official Warning: Session is not recommended for the following reasons:
  • 🔴 Single persistent identifier in the desktop app
  • 🔴 Encryption issues identified in 2025
  • 🔴 Lack of Perfect Forward Secrecy (PFS) – despite December 2025 update, official stance remains unchanged
  • 🔴 91 NPM vulnerabilities in the supply chain
  • 🔴 Does not prevent supply chain attacks

Requires no additional setup – works directly through the Oxen network.

cat > run-session.sh << 'EOF'
#!/bin/bash
set -e
cd "$(dirname "$0")"
SCRIPT_DIR="$(pwd)"

# Search for AppImage
APPIMAGE=$(find "$SCRIPT_DIR" -maxdepth 1 -iname "session*.AppImage" -type f 2>/dev/null | head -1)

# If not found: install wget (if needed) then download
if [ -z "$APPIMAGE" ]; then
    command -v wget &>/dev/null || { echo "⚠️ Installing wget..."; sudo apt update && sudo apt install -y wget; }
    echo "📥 Downloading Session..."
    wget -qc --show-progress -O "$SCRIPT_DIR/Session.AppImage" "https://getsession.org/linux"
    APPIMAGE="$SCRIPT_DIR/Session.AppImage"
else
    APPIMAGE="$(basename "$APPIMAGE")"
fi

# Confirm existence + executable permission
[ -f "$SCRIPT_DIR/$APPIMAGE" ] || { echo "❌ AppImage not found"; exit 1; }
chmod +x "$SCRIPT_DIR/$APPIMAGE"

# Portable environment (if needed)
[ -d "$SCRIPT_DIR/Session.AppImage.home" ] || "$SCRIPT_DIR/$APPIMAGE" --appimage-portable-home
[ -d "$SCRIPT_DIR/Session.AppImage.config" ] || "$SCRIPT_DIR/$APPIMAGE" --appimage-portable-config

echo "🚀 Launching Session..."
exec "$SCRIPT_DIR/$APPIMAGE" "$@"
EOF
chmod +x run-session.sh
Telegram (❌ Not secure – not recommended at all)
⚠️ Serious Security Warning: Telegram is not secure in high-privacy environments. We strongly recommend using SimpleX Chat instead.
cat > run-telegram.sh << 'EOF'
#!/bin/bash
set -e
cd "$(dirname "$0")"

TELEGRAM_DIR="./Telegram"
TELEGRAM_BIN="$TELEGRAM_DIR/Telegram"
PORTABLE_FLAG_DIR="$TELEGRAM_DIR/TelegramForcePortable"

# 1. Is Telegram already present?
if [[ ! -f "$TELEGRAM_BIN" ]]; then
    echo "ℹ️ Telegram not found, need to download."

    # 2. Is download tool wget available?
    if ! command -v wget &> /dev/null; then
        echo "⚠️ wget not found. Installing now..."
        sudo apt update
        sudo apt install -y wget
        echo "✅ wget installed."
    else
        echo "✅ wget already present."
    fi

    # 3. Download Telegram
    echo "📥 Downloading Telegram..."
    wget -c --show-progress -O telegram.tar.xz "https://telegram.org/dl/desktop/linux"
    mkdir -p "$TELEGRAM_DIR"
    tar -xf telegram.tar.xz -C "$TELEGRAM_DIR" --strip-components=1
    rm telegram.tar.xz
    echo "✅ Telegram installed."
else
    echo "✅ Telegram already present, no download needed."
fi

# 4. Create ForcePortable folder (required for portable mode)
mkdir -p "$PORTABLE_FLAG_DIR"

echo "🚀 Launching Telegram (portable mode)..."
exec "$TELEGRAM_BIN" "$@"
EOF

chmod +x run-telegram.sh
🧹 Metadata Removal – Before Sharing

Even with the strongest messaging apps, images and documents carry hidden data that can reveal you (GPS, camera type, author name, creation date).

mat2 (Best)

mat2 document.pdf

Built into Whonix/Tails – creates a clean copy

ExifTool

exiftool -all= -overwrite_original image.jpg

Most powerful – supports all formats

Dangerzone

dangerzone-cli file.pdf

For suspicious files – converts to safe images

Quick install: sudo apt install mat2 exiftool
Additional Tools: ExifCleaner (easy GUI) | Dangerzone (converts suspicious files to safe PDF) | PDF Redact Tools (specialized for PDFs)
Note: Metadata removal does not prevent watermarks or steganography techniques.
🆘 Troubleshooting – What to do if...
App fails to connect over TorEnsure you ran sudo onion-grater-add on the Gateway and decompressed .gz files. Try torsocks -i instead of direct run. Make sure Whonix-Gateway is running and online.
Extremely slow messagingNormal with Tor. Using torsocks -i to isolate the circuit may help. Avoid sending very large files all at once.
Keys disappear after restartEnsure app data is directed to the correct portable folder (cwtch-data/, simplex-chat-data/, etc.). Check folder permissions with ls -la.
AppImage does not runMake sure you ran chmod +x application.AppImage. Try running from terminal to see errors. Ensure fuse is installed: sudo apt install fuse -y.
Suspected compromiseClose the app immediately. Delete the entire portable folder. Start fresh with all new identifiers. Use torsocks -i for a completely new circuit.
"Permission denied" errorEnsure the flash drive is formatted with a filesystem that supports permissions (ext4). FAT32/NTFS systems do not support chmod and may cause issues.
📁 Unified Folder Structure (for USB Flash Drive)
USB/
├── PortableApps/
│   ├── SimpleXChat/
│   │   ├── run-simplex-chat.sh
│   │   ├── simplex-desktop-x86_64.AppImage
│   │   └── simplex-chat-data/
│   ├── Cwtch/
│   │   ├── run-cwtch-portable.sh
│   │   ├── lib/
│   │   │   └── cwtch
│   │   ├── Tor/
│   │   └── cwtch-data/
│   ├── Signal/                         ⚠️ Not secure for anonymity
│   │   ├── run-signal-portable.sh
│   │   └── signal-data/
│   ├── Session/                        ⚠️ Not secure
│   │   ├── run-session.sh
│   │   ├── Session.AppImage
│   │   ├── Session.AppImage.home/
│   │   └── Session.AppImage.config/
│   ├── Element/
│   │   ├── run-element-portable.sh
│   │   └── Element-Data/
│   ├── Gajim/                          ⚠️ Partially portable
│   │   ├── run-gajim-portable.sh
│   │   └── gajim-data/
│   └── Telegram/                       ⚠️ Not secure
│       ├── run-telegram.sh
│       └── Telegram/
│           ├── Telegram
│           └── TelegramForcePortable/
├── backups/                            📦 Backups
│   └── backup-YYYYMMDD.tar.gz
└── README.md
💾 Backup and restore of portable data

To protect your portable data, create regular backups:

📦 Create a backup:

# From the USB root folder
tar -czf backups/backup-$(date +%Y%m%d).tar.gz PortableApps/*/simplex-chat-data PortableApps/*/cwtch-data PortableApps/*/Element-Data

📥 Restore a backup:

# Extract the backup
tar -xzf backups/backup-YYYYMMDD.tar.gz
Note: Do not copy Gajim data while it is running, and remember that Dino cannot be run from the same database twice.
🧦 torsocks – run any application over Tor

torsocks forces any application to use the Tor network, even if it does not natively support proxies. No gateway configuration required.

🟢 Basic usage:

torsocks ./application.AppImage

🛡️ Circuit isolation (prevents session linking):

torsocks -i ./application.AppImage

✅ Verify that the application is using Tor:

# Make sure curl is installed
which curl || sudo apt install curl -y
torsocks curl https://check.torproject.org/api/ip
💡 Difference between torsocks and torsocks -i:
  • torsocks: shares the circuit with other applications – may link your activity
  • torsocks -i: opens a new circuit for each command – prevents linking completely
  • For sensitive applications, always use -i
✅ Summary of this tab: All scripts are ready to copy and use, compatible with the official Whonix recommendations. The primary recommendation: SimpleX Chat for anonymous messaging, Cwtch for maximum privacy with plausible deniability, OnionShare for temporary sharing. Pay special attention to the gateway and workstation configurations for Cwtch and OnionShare, and remember to decompress any .gz files on the gateway.

🚀 auto-script.sh

✔️ The helper script

How to run:
chmod +x auto-script.sh && ./auto-script.sh
#!/bin/bash
# ============================================================
# auto-script.sh - Qubes OS Security Hardening Script (2026 version)
# After review and verification from official sources
# ============================================================

# Get the real user and paths
REAL_USER=$(logname 2>/dev/null || echo "$SUDO_USER" 2>/dev/null || echo "$USER")
REAL_HOME="/home/$REAL_USER"
DESKTOP_PATH="$REAL_HOME/Desktop"

R='\033[0;31m'; G='\033[0;32m'; Y='\033[1;33m'; B='\033[0;34m'; N='\033[0m'
ok() { echo -e "${G}✓ $1${N}"; }
info() { echo -e "${B}ℹ $1${N}"; }
warn() { echo -e "${Y}⚠ $1${N}"; }
err() { echo -e "${R}✗ $1${N}"; }

pause() { echo; read -p "Press Enter to continue..."; }

BASE_TEMPLATE=$(qubes-prefs default_template) 


menu() {
    clear
    echo "======"
    echo "  🔒 Qubes OS + Kicksecure Security Hardening"
    echo "======"
    echo ""
    echo "1) Configure kernel parameters (dom0 + VMs)"
    echo "2) MAC Randomization + Disable IPv6"
    echo "3) Create Whonix gateways"
    echo "4) Install Kicksecure + set as default"
    echo "5) Create RAM Pool scripts"
    echo "6) Create Amnesic Tmpfs script"
    echo "7) Enable Live Mode"
    echo "8) 💀 Destruction tools (BusKill + Emergency)"
    echo "9) 🛡️ LUKS disk encryption hardening (PBKDF)"
    echo "10) Set up automatic disk mounting in Whonix"
    echo "11) Run automatic sections (1 to 8)"
    echo "0) Exit"
    echo ""
    read -p "Choose: " c
    case $c in
        1) kernel_opts_menu ;;
        2) mac_random ;;
        3) whonix_gws ;;
        4) kicksecure ;;
        5) ram_pool_scripts ;;
        6) amnesic_script ;;
        7) live_mode ;;
        8) emergency_script ;;
        9) luks_hardening ;;
        10) whonix_auto_mount ;;
        11) run_auto ;;
        0) exit 0 ;;
        *) menu ;;
    esac
}

# ===================================================================
# Section 1: Kernel Parameters Setup (corrected and documented version)
# ===================================================================

# ========
# 1. Kernel Parameters Setup Menu (3 options only)
# ========
kernel_opts_menu() {
    clear
    echo "======"
    echo "  ⚙️  Kernel Parameters Setup"
    echo "======"
    echo ""
    echo "1) Configure dom0 (GRUB + Secureblue sysctl)"
    echo "2) Configure all VMs (kernelopts)"
    echo "3) Restore GRUB from backup"
    echo "0) Back to main menu"
    echo ""
    read -p "Choose: " c
    case $c in
        1) setup_kernel_opts_dom0 ;;
        2) setup_kernel_opts_vms ;;
        3) rollback_grub ;;
        0) menu ;;
        *) kernel_opts_menu ;;
    esac
}

# ========
# 1a. Configure dom0 (GRUB + Secureblue sysctl)
# ========
setup_kernel_opts_dom0() {
    clear
    echo "=========================================="
    echo "  🔧 Configuring dom0 (GRUB + Secureblue sysctl)"
    echo "=========================================="
    
    # ------------- Part 1: GRUB -------------
    echo ""
    echo "📌 Part One: GRUB Parameter Configuration"
    echo ""
    
    if [ ! -f /etc/default/grub ]; then
        err "File /etc/default/grub does not exist!"
        pause
        return 1
    fi
    
    # Extract UUID using multiple methods
    CURRENT_UUID=$(grep -oP 'rd\.luks\.uuid=luks-\K[a-f0-9-]+' /etc/default/grub 2>/dev/null)
    
    if [ -z "$CURRENT_UUID" ]; then
        CURRENT_UUID=$(sudo cryptsetup luksUUID $(findmnt -n -o SOURCE /) 2>/dev/null)
    fi
    
    if [ -z "$CURRENT_UUID" ]; then
        err "Cannot extract UUID from current GRUB"
        echo ""
        read -p "Enter UUID manually (without luks-): " CURRENT_UUID
        [ -z "$CURRENT_UUID" ] && { err "No UUID entered"; pause; return 1; }
    fi
    
    ok "UUID: $CURRENT_UUID"
    
    # Create a backup
    BACKUP_FILE="/etc/default/grub.backup.$(date +%Y%m%d_%H%M%S)"
    sudo cp /etc/default/grub "$BACKUP_FILE"
    ok "Backup: $BACKUP_FILE"
    
    echo ""
    echo "📋 dom0 Parameters:"
    echo ""
    echo "┌─────────────────────────────────────────────────────────────┐"
    echo "│ Xen Parameters:                                             │"
    echo "│   bootscrub=1 xen_scrub_pages=1                            │"
    echo "│                                                             │"
    echo "│ Linux Parameters:                                           │"
    echo "│   init_on_free=1 init_on_alloc=1 page_poison=1              │"
    echo "│   lockdown=confidentiality module.sig_enforce=1             │"
    echo "│   slab_nomerge vsyscall=none kernel.kptr_restrict=2         │"
    echo "│   oops=panic debugfs=off quiet loglevel=0 spectre_v2=on     │"
    echo "│   page_alloc.shuffle=1 preempt=voluntary pti=on mds=full    │"
    echo "└─────────────────────────────────────────────────────────────┘"
    echo ""
    
    read -p "Proceed with applying GRUB parameters? (y/n): " confirm
    [[ "$confirm" != "y" && "$confirm" != "Y" ]] && { err "Cancelled"; pause; return 0; }
    
    # Create new GRUB file
    cat > /tmp/grub_new << 'INNER_EOF'
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=false
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.luks.uuid=UUID_PLACEHOLDER rd.lvm.lv=qubes_dom0/root rd.lvm.lv=qubes_dom0/swap plymouth.ignore-serial-consoles rhgb quiet init_on_free=1 init_on_alloc=1 page_poison=1 lockdown=confidentiality module.sig_enforce=1 slab_nomerge vsyscall=none kernel.kptr_restrict=2 oops=panic debugfs=off loglevel=0 spectre_v2=on page_alloc.shuffle=1 preempt=voluntary pti=on mds=full"
GRUB_DISABLE_RECOVERY="true"
GRUB_THEME="/boot/grub2/themes/qubes/theme.txt"
GRUB_CMDLINE_XEN_DEFAULT="console=none dom0_mem=min:1024M dom0_mem=max:8192M ucode=scan bootscrub=1 xen_scrub_pages=1 gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096"
GRUB_DISABLE_OS_PROBER="true"
GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX rd.qubes.hide_all_usb"
INNER_EOF

    sed -i "s/UUID_PLACEHOLDER/luks-$CURRENT_UUID/" /tmp/grub_new
    sudo cp /tmp/grub_new /etc/default/grub
    rm /tmp/grub_new
    
    info "Updating GRUB..."
    sudo grub2-mkconfig -o /boot/grub2/grub.cfg
    
    if [ $? -eq 0 ]; then
        ok "GRUB updated successfully"
    else
        err "GRUB update failed! Restoring backup..."
        sudo cp "$BACKUP_FILE" /etc/default/grub
        sudo grub2-mkconfig -o /boot/grub2/grub.cfg
        pause
        return 1
    fi
    
    # ------------- Part 2: Secureblue sysctl -------------
    echo ""
    echo "📌 Part Two: Secureblue sysctl Configuration (Additional Hardening Layer)"
    echo ""
    
    read -p "Do you want to apply Secureblue sysctl settings? (y/n): " confirm_sysctl
    if [[ "$confirm_sysctl" == "y" || "$confirm_sysctl" == "Y" ]]; then
        if [ -f /etc/sysctl.d/99-qubes-hardening.conf ]; then
            sudo cp /etc/sysctl.d/99-qubes-hardening.conf /etc/sysctl.d/99-qubes-hardening.conf.backup.$(date +%Y%m%d_%H%M%S)
            ok "sysctl backup created"
        fi
        
        sudo tee /etc/sysctl.d/99-qubes-hardening.conf << 'EOF'
# Secureblue Hardening for Qubes dom0
# Source: https://github.com/secureblue/secureblue

# Prevent memory dump via physical access (SysRq)
kernel.sysrq=0

# Restrict access to monitoring tools (prevents side channels)
kernel.perf_event_paranoid=3

# Hide kernel addresses (makes KASLR exploitation harder)
kernel.kptr_restrict=2

# Auto-reboot after 5 seconds on panic
kernel.panic=5

# Restrict console logging (reduces data leakage)
kernel.printk=3 3 3 3

# Prevent loading an alternate kernel (prevents persistence)
kernel.kexec_load_disabled=1

# Completely disable io_uring (prevents rootkits)
kernel.io_uring_disabled=2

# Protect regular files from symlink attacks
fs.protected_regular=2

# Protect FIFOs from privilege escalation
fs.protected_fifos=2

# Prevent unprivileged users from reading dmesg
kernel.dmesg_restrict=1

# Disable unprivileged eBPF
kernel.unprivileged_bpf_disabled=1

# Set maximum memory map count
vm.max_map_count=1048576

# Disable core dumps
fs.suid_dumpable=0
EOF
        sudo sysctl --system
        ok "Secureblue sysctl applied"
    else
        info "Skipped Secureblue sysctl"
    fi
    
    echo ""
    warn "⚠️ Changes will only take effect after reboot"
    echo ""
    echo "📋 To verify after reboot, use:"
    echo "   cat /proc/cmdline | grep -E 'init_on_free|pti'"
    echo "   sysctl kernel.sysrq kernel.perf_event_paranoid"
    echo ""
    
    read -p "Reboot now? (y/n): " reboot_choice
    [[ "$reboot_choice" == "y" || "$reboot_choice" == "Y" ]] && sudo reboot
    
    pause
}
# ========
# 1b. Add kernel parameters to all VMs (final version)
# ========
setup_kernel_opts_vms() {
    clear
    echo "=========================================="
    echo "  🔧 Configuring Kernel Parameters for all VMs"
    echo "=========================================="
    echo ""

    # Full list
    NEW_OPTS="xen_scrub_pages=1 init_on_free=1 init_on_alloc=1 page_poison=1 mitigations=auto random.trust_cpu=off random.trust_bootloader=off spec_store_bypass_disable=on tsx_async_abort=full mds=full,nosmt page_alloc.shuffle=1 randomize_kstack_offset=on l1tf=full,force ipv6.disable=1 slab_nomerge vsyscall=none retbleed=stuff spectre_v2_user=on kvm.nx_huge_pages=force"

    read -p "Apply parameters to all VMs? (y/n): " confirm
    [[ "$confirm" != "y" && "$confirm" != "Y" ]] && { info "Cancelled"; return; }

    info "Shutting down VMs..."
    sudo qvm-shutdown --all 2>/dev/null
    sleep 3

    local APPLIED_COUNT=0

    # Apply to all except dom0
    for vm in $(qvm-ls --raw-list 2>/dev/null); do
        vm_clean=$(echo "$vm" | tr -d '[:space:]')
        [ "$vm_clean" = "dom0" ] && continue

        if qvm-prefs "$vm_clean" kernelopts "$NEW_OPTS" 2>/dev/null; then
            echo "   ✅ $vm_clean: applied"
            ((APPLIED_COUNT++))
        else
            warn "   ❌ $vm_clean: failed"
        fi
    done

    echo ""
    ok "✅ Applied parameters to $APPLIED_COUNT VM(s)"

    # Clear gateways specifically
    echo ""
    info "🧹 Clearing kernelopts for Whonix gateways..."

    for gw in $(qvm-ls --raw-list 2>/dev/null | grep -E 'sys-whonix|whonix-gateway'); do
        gw_clean=$(echo "$gw" | tr -d '[:space:]')
        if qvm-prefs "$gw_clean" kernelopts "" 2>/dev/null; then
            echo "   🧹 $gw_clean: cleared (kernelopts=\"\")"
        else
            warn "   ❌ $gw_clean: failed to clear"
        fi
    done

    echo ""
    info "Restarting essential services..."
    for vm in sys-net sys-firewall sys-usb; do
        qvm-start "$vm" 2>/dev/null && echo "   🔄 $vm: started"
    done

    # Enable ephemeral_volatile
    echo ""
    echo "╔═══════════════════════════════════════════════════════════════╗"
    echo "║  💧 Enabling ephemeral_volatile                                ║"
    echo "╠═══════════════════════════════════════════════════════════════╣"
    echo "║  Enabling transient encryption for the volatile volume        ║"
    echo "╚═══════════════════════════════════════════════════════════════╝"
    echo ""

    info "Enabling ephemeral_volatile on pools..."
    for pool in vm-pool varlibqubes; do
        if qvm-pool ls 2>/dev/null | grep -q "$pool"; then
            qvm-pool set "$pool" -o ephemeral_volatile=True 2>/dev/null && \
                ok "Enabled ephemeral_volatile on $pool" || \
                warn "Failed to enable ephemeral_volatile on $pool"
        else
            warn "Pool $pool does not exist, skipping"
        fi
    done
    ok "Enabled ephemeral_volatile on $EPHEMERAL_COUNT VM(s)"

    echo ""
    ok "✅ VM kernel parameter setup completed successfully!"
    pause
}

# ========
# 1c. Restore GRUB from backup
# ========
rollback_grub() {
    clear
    echo "======"
    echo "  🔄 Restore GRUB from Backup"
    echo "======"
    echo ""
    
    echo "📁 Available backups:"
    ls -la /etc/default/grub.backup.* 2>/dev/null | head -5 || { err "No backups found"; pause; return 1; }
    echo ""
    
    read -p "Enter backup file name (or Enter for latest): " backup_name
    
    if [ -z "$backup_name" ]; then
        backup_name=$(ls -t /etc/default/grub.backup.* 2>/dev/null | head -1)
    else
        backup_name="/etc/default/$backup_name"
    fi
    
    [ ! -f "$backup_name" ] && { err "File not found"; pause; return 1; }
    
    sudo cp "$backup_name" /etc/default/grub
    ok "File restored"
    
    sudo grub2-mkconfig -o /boot/grub2/grub.cfg
    ok "GRUB updated"
    
    read -p "Reboot now? (y/n): " reboot_choice
    [[ "$reboot_choice" == "y" || "$reboot_choice" == "Y" ]] && sudo reboot
    pause
}



# ========
# 2. MAC Randomization + Disable IPv6 + Shutdown template
# ========
mac_random() {
    info "Setting up MAC Randomization: $BASE_TEMPLATE"
    
    info "Checking IPv6 settings for sys-net and sys-firewall..."
    
    for vm in sys-net sys-firewall; do
        if qvm-ls "$vm" >/dev/null 2>&1; then
            CURRENT_OPTS=$(qvm-prefs "$vm" kernelopts 2>/dev/null | xargs)
            
            if [[ "$CURRENT_OPTS" == *"ipv6.disable=1"* ]]; then
                warn "⚠️ $vm: IPv6 already disabled"
            else
                info "🔧 $vm: Disabling IPv6..."
                qvm-prefs "$vm" kernelopts "$CURRENT_OPTS ipv6.disable=1"
                ok "✅ IPv6 disabled for $vm"
            fi
        else
            warn "$vm does not exist, skipping"
        fi
    done
    
    if qvm-ls "$BASE_TEMPLATE" >/dev/null 2>&1; then
        info "🔧 Setting up MAC Randomization in $BASE_TEMPLATE..."
        qvm-run "$BASE_TEMPLATE" "sudo bash -c 'mkdir -p /usr/lib/NetworkManager/conf.d/ && cat > /usr/lib/NetworkManager/conf.d/31-randomize-mac.conf << EOF
[connection]
ethernet.cloned-mac-address=random
wifi.cloned-mac-address=random
wifi.scan-rand-mac-address=yes
EOF'"
        ok "✅ MAC Randomization configured in $BASE_TEMPLATE"
        
        info "Shutting down template $BASE_TEMPLATE..."
        qvm-shutdown "$BASE_TEMPLATE" 2>/dev/null || true
        ok "$BASE_TEMPLATE shut down"
    else
        warn "Template $BASE_TEMPLATE does not exist, skipping MAC setup"
    fi
    
    info "Note: You may need to restart VMs based on this template"
    pause
}


# ========
# 3. Create Whonix Gateways
# ========
whonix_gws() {
    for g in "sys-whonix-A:red" "sys-whonix-B:blue"; do
        n=${g%:*}; l=${g#*:}
        if ! qvm-ls | grep -q "$n"; then
            qvm-create --class AppVM --label "$l" --template whonix-gateway-18 "$n"
            qvm-prefs "$n" provides_network true
            ok "$n created"
        else
            info "$n already exists, skipping"
        fi
    done
    pause
}

# ===========
4. kicksecure + default-dvm
# ===========
kicksecure() {
    clear
    echo "=========================================="
    echo "  🔒 Install and Configure Kicksecure 18 for Qubes OS"
    echo "=========================================="
    echo ""
    echo "📌 Note: In Qubes OS, templates share the dom0 kernel."
    echo "   Only userspace hardening is applied inside the template."
    echo "   Kernel parameters must be set via kernelopts or GRUB."
    echo ""

    info "Checking for Kicksecure 18 template..."
    if qvm-ls | grep -q kicksecure-18; then
        ok "Kicksecure 18 template already exists"
        info "Skipping installation phase..."
    else
        info "Kicksecure 18 template not found, installing..."
        qvm-template --enablerepo qubes-templates-community install kicksecure-18
        if ! qvm-ls | grep -q kicksecure-18; then
            err "Failed to install kicksecure-18"
            pause
            return
        fi
        ok "Kicksecure-18 installed successfully"
    fi

    info "Installing addons..."
    sudo qubes-dom0-update --action=install -y qubes-core-admin-addon-kicksecure

    info "Restarting qubesd service..."
    sudo systemctl restart qubesd.service
    ok "qubesd.service restarted"

    info "Starting Kicksecure 18 template..."
    qvm-start kicksecure-18

    local MAX_WAIT=30
    local WAIT_COUNT=0
    while ! qvm-check --running kicksecure-18 2>/dev/null; do
        sleep 2
        WAIT_COUNT=$((WAIT_COUNT + 1))
        if [ $WAIT_COUNT -ge $MAX_WAIT ]; then
            warn "Template did not finish starting, proceeding with caution..."
            break
        fi
    done
    sleep 5

    # 1. Set timezone
    info "Setting timezone to UTC..."
    qvm-run kicksecure-18 "sudo timedatectl set-timezone Etc/UTC" 2>/dev/null || true
    ok "Timezone set"

    # 2. MAC Randomization
    info "Setting up MAC Randomization..."
    qvm-run kicksecure-18 "sudo bash -c 'mkdir -p /usr/lib/NetworkManager/conf.d/ && cat > /usr/lib/NetworkManager/conf.d/31-randomize-mac.conf << \"EOF\"
[connection]
ethernet.cloned-mac-address=random
wifi.cloned-mac-address=random
wifi.scan-rand-mac-address=yes
EOF'" 2>/dev/null || true
    ok "MAC Randomization configured"

    # 3. Disable unnecessary services
    info "Disabling unnecessary services..."
    qvm-run kicksecure-18 "sudo systemctl mask bluetooth.service cups.service avahi-daemon.service ModemManager.service 2>/dev/null || true" 2>/dev/null || true
    qvm-run kicksecure-18 "sudo systemctl disable --now NetworkManager-wait-online.service 2>/dev/null || true" 2>/dev/null || true
    ok "Unnecessary services disabled"

    # 4. Remove unnecessary packages
    info "Removing unnecessary packages..."
    qvm-run kicksecure-18 "sudo apt remove -y --purge thunderbird hexchat transmission-common 2>/dev/null || true" 2>/dev/null || true
    qvm-run kicksecure-18 "sudo apt autoremove -y 2>/dev/null || true" 2>/dev/null || true
    ok "Unnecessary packages removed"

    # 5. Shut down the template
    info "Shutting down Kicksecure 18 template..."
    qvm-shutdown --wait kicksecure-18 2>/dev/null || true
    ok "Kicksecure-18 shut down"

    # 6. Create a hardened DVM template (without rw False)
    echo ""
    echo "╔═══════════════════════════════════════════════════════════════╗"
    echo "║  🧬 Create Hardened DVM Template (Ephemeral Template)         ║"
    echo "╚═══════════════════════════════════════════════════════════════╝"
    echo ""
    read -p "Do you want to create a hardened DVM template from Kicksecure? (y/n): " create_dvm

    if [[ "$create_dvm" == "y" || "$create_dvm" == "Y" ]]; then
        info "Creating hardened DVM template..."
        if ! qvm-ls kicksecure-18-dvm >/dev/null 2>&1; then
            qvm-create --template kicksecure-18 --label red kicksecure-18-dvm
            ok "✓ kicksecure-18-dvm created"
        else
            info "kicksecure-18-dvm already exists"
        fi
        qvm-prefs kicksecure-18-dvm template_for_dispvms true
        # تم حذف: qvm-volume config kicksecure-18-dvm:root rw False
        qvm-prefs kicksecure-18-dvm kernelopts "init_on_free=1 init_on_alloc=1 page_poison=1 slab_nomerge"
        ok "✓ kicksecure-18-dvm set as a hardened DVM"

        read -p "Do you want to make it the default-dvm? (y/n): " make_default
        if [[ "$make_default" == "y" || "$make_default" == "Y" ]]; then
            qvm-prefs default-dvm template kicksecure-18-dvm
            ok "✓ default-dvm now uses kicksecure-18-dvm"
        fi
    fi

    # 7. Ask to make Kicksecure the default template (now includes sys-usb adjustment)
    echo ""
    echo "╔═══════════════════════════════════════════════════════════════╗"
    echo "║  🎯 Make Kicksecure 18 the default template for default-dvm  ║"
    echo "╚═══════════════════════════════════════════════════════════════╝"
    echo ""
    read -p "Make Kicksecure 18 the default for default-dvm? (y/n): " yn
    if [[ "$yn" == "y" || "$yn" == "Y" ]]; then
        BASE_TEMPLATE="kicksecure-18"
        if ! qvm-ls --raw-list | grep -q "^$BASE_TEMPLATE$"; then
            err "Template $BASE_TEMPLATE does not exist"
            pause
            return
        fi

        info "Shutting down affected VMs..."
        qvm-kill sys-* 2>/dev/null
        qvm-kill default-dvm 2>/dev/null

        info "Changing default-dvm template..."
        qvm-prefs default-dvm template "$BASE_TEMPLATE"
        ok "✓ default-dvm now runs from Kicksecure 18"

        echo ""
        info "Clearing kernelopts for sys-firewall and sys-net..."
        qvm-prefs sys-firewall kernelopts "" 2>/dev/null
        qvm-prefs sys-net kernelopts "" 2>/dev/null
        ok "✓ kernelopts cleared for relevant VMs"

        echo ""
        info "Adjusting memory settings for sys-net and sys-firewall..."
        qvm-prefs sys-net memory 800 2>/dev/null
        qvm-prefs sys-net maxmem 800 2>/dev/null
        qvm-prefs sys-firewall memory 800 2>/dev/null
        qvm-prefs sys-firewall maxmem 800 2>/dev/null
        ok "✓ Memory settings increased"

        # 🆕 Adjust sys-usb only if user agreed to make Kicksecure default
        if qvm-ls sys-usb >/dev/null 2>&1; then
            info "Adjusting sys-usb settings..."
            qvm-prefs sys-usb memory 800 2>/dev/null
            qvm-prefs sys-usb maxmem 800 2>/dev/null
            qvm-start sys-usb 2>/dev/null || true
            ok "✓ sys-usb memory increased to 800MB"
        fi

        echo ""
        info "Starting VMs..."
        qvm-start sys-net 2>/dev/null
        qvm-start sys-firewall 2>/dev/null

        sleep 5
        sudo systemctl restart qubesd.service
        ok "✓ Completed successfully"
    fi

    echo ""
    echo "╔═══════════════════════════════════════════════════════════════╗"
    echo "║                    ✅ Kicksecure Setup Complete!              ║"
    echo "╠═══════════════════════════════════════════════════════════════╣"
    echo "║  📋 What was applied:                                        ║"
    echo "║  • UTC timezone                                              ║"
    echo "║  • MAC Randomization                                         ║"
    echo "║  • Unnecessary services disabled                             ║"
    echo "║  • Unnecessary packages removed                              ║"
    echo "║                                                               ║"
    echo "║  ⚠️  Note: Kernel parameters are applied via:                 ║"
    echo "║     • dom0: GRUB (tab 0 or option 1 of the script)           ║"
    echo "║     • VMs: kernelopts (option 2 of the script)               ║"
    echo "╚═══════════════════════════════════════════════════════════════╝"
    echo ""
    pause
}
# ========
# 5. RAM Pool Scripts
# ========
ram_pool_scripts() {
    mkdir -p "$DESKTOP_PATH/ram-pool-scripts"
    echo 'org.gnome.DiskUtility.desktop
janondisttorbrowser.desktop
pcmanfm-qt.desktop
qterminal.desktop
anondist-torbrowser_update.desktop' | qvm-appmenus --set-default-whitelist - whonix-workstation-18

    qvm-appmenus --update whonix-workstation-18

    echo "✅ Disks (org.gnome.DiskUtility.desktop) set as default – make sure it's installed"

    # ========== setup-ram-pool.sh ==========
    cat > "$DESKTOP_PATH/ram-pool-scripts/setup-ram-pool.sh" << 'EOF'
#!/bin/bash
RAM_POOL_PATH="$HOME/ram_pool"
RAM_POOL_SIZE="4G"
sudo mkdir -p "$RAM_POOL_PATH"
sudo mount -t tmpfs -o size=$RAM_POOL_SIZE tmpfs "$RAM_POOL_PATH"
if ! qvm-pool ls | grep -q ram_pool; then
    qvm-pool add ram_pool file -o dir_path="$RAM_POOL_PATH"
fi
qvm-pool set ram_pool -o ephemeral_volatile=True
echo "✅ RAM pool ready with ephemeral_volatile enabled"
EOF

# ========== create-ram-vm.sh (single – no auto-mount) ==========
cat > "$DESKTOP_PATH/ram-pool-scripts/create-ram-vm.sh" << 'SCRIPT_EOF'
#!/bin/bash
# create-ram-vm.sh - Create whonix-work-ram
set -e

GATEWAY="sys-whonix"
WORKSTATION="whonix-work-ram"
TEMPLATE="whonix-workstation-18"
RAM_PATH="$HOME/ram_pool"

KERNEL_OPTS="xen_scrub_pages=1 init_on_free=1 init_on_alloc=1 page_poison=1 mitigations=auto random.trust_cpu=off random.trust_bootloader=off spec_store_bypass_disable=on tsx_async_abort=full mds=full,nosmt page_alloc.shuffle=1 randomize_kstack_offset=on l1tf=full,force ipv6.disable=1 slab_nomerge vsyscall=none retbleed=stuff spectre_v2_user=on kvm.nx_huge_pages=force"

# 1. RAM pool
if ! mount | grep -q "$RAM_PATH"; then
    sudo mkdir -p "$RAM_PATH"
    sudo mount -t tmpfs -o size=4G tmpfs "$RAM_PATH"
    qvm-pool add ram_pool file -o dir_path="$RAM_PATH" 2>/dev/null || true
    qvm-pool set ram_pool -o ephemeral_volatile=True 2>/dev/null || true
fi

# 2. Create VM
qvm-kill "$WORKSTATION" 2>/dev/null || true
qvm-remove --force "$WORKSTATION" 2>/dev/null || true
qvm-create -P ram_pool -t "$TEMPLATE" -l red "$WORKSTATION"
qvm-prefs "$WORKSTATION" kernelopts "$KERNEL_OPTS"
echo "✅ $WORKSTATION created"

# 3. Connect gateway (optional – ensure it exists)
qvm-prefs "$WORKSTATION" netvm "$GATEWAY" 2>/dev/null || echo "⚠️ Could not set gateway"
SCRIPT_EOF

chmod +x "$DESKTOP_PATH/ram-pool-scripts/create-ram-vm.sh"

# ========== mount-to-whonix.sh (attach + safe detach) ==========
cat > "$DESKTOP_PATH/ram-pool-scripts/mount-to-whonix.sh" << 'EOF'
#!/bin/bash
# mount-to-whonix.sh - Attach/detach disks in Whonix VMs
set -e

MOUNT_POINT="/home/user/usb"
DEFAULT_DISK="xvdi"

G='\033[0;32m'; Y='\033[1;33m'; R='\033[0;31m'; N='\033[0m'

find_vm() {
    mapfile -t VMS < <(qvm-ls --raw-list --running --template-source whonix-workstation-18 whonix-workstation-18-dvm | grep -v "^sys-whonix")
    [ ${#VMS[@]} -eq 0 ] && { echo -e "${R}❌ No running Whonix VMs${N}"; exit 1; }
    echo -e "${G}Available VMs:${N}"
    for i in "${!VMS[@]}"; do echo -e "  ${Y}[$((i+1))]${N} ${VMS[$i]}"; done
    while true; do
        read -p "Select number: " C
        [[ "$C" =~ ^[0-9]+$ && $C -ge 1 && $C -le ${#VMS[@]} ]] && { TARGET="${VMS[$((C-1))]}"; break; }
        echo -e "${R}Invalid${N}"
    done
}

echo -e "\n${G}🔌 Disk Management${N}"
echo -e "  [1] Attach and mount\n  [2] Safe detach (umount + detach)\n  [3] Show status\n  [4] Exit"
read -p "Choose: " OP

case $OP in
    1) # Attach
        find_vm
        qvm-run --pass-io -u root "$TARGET" "lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | head -20"
        read -p "Disk name [$DEFAULT_DISK]: " DISK; DISK=${DISK:-$DEFAULT_DISK}
        qvm-run --pass-io -u root "$TARGET" "test -b /dev/$DISK" || { echo -e "${R}❌ Not found${N}"; exit 1; }
        qvm-run --pass-io -u root "$TARGET" "mkdir -p $MOUNT_POINT && mount /dev/$DISK $MOUNT_POINT && echo '✅ Done'" || echo -e "${R}❌ Failed${N}"
        ;;
    2) # Safe detach
        find_vm
        echo -e "${Y}Devices mounted on $MOUNT_POINT:${N}"
        qvm-run --pass-io -u root "$TARGET" "mount | grep $MOUNT_POINT" 2>/dev/null || echo -e "${Y}Nothing mounted${N}"
        read -p "Proceed with full detach? (y/n): " CONFIRM
        [[ "$CONFIRM" =~ ^[Yy] ]] || exit 0

        # Unmount inside VM
        echo -e "${Y}Unmounting from VM...${N}"
        qvm-run --pass-io -u root "$TARGET" "sync && umount $MOUNT_POINT 2>/dev/null" && echo -e "${G}✅ Unmounted${N}" || echo -e "${Y}⚠️ Not mounted or unmount failed${N}"

        # Detach all devices from dom0
        echo -e "${Y}Detaching devices from dom0...${N}"
        qvm-block list | grep "$TARGET" | while read -r line; do
            DEVICE=$(echo "$line" | awk '{print $1}')
            echo -e "Detaching $DEVICE..."
            qvm-block detach "$TARGET" "$DEVICE" 2>/dev/null && echo -e "${G}✅ $DEVICE detached${N}" || echo -e "${R}❌ Failed to detach $DEVICE${N}"
        done

        echo -e "${G}🎉 Full detach completed. You can reconnect the device later and the monitor will auto-mount it.${N}"
        ;;
    3) # Show status
        find_vm
        echo -e "${Y}=== Device Status ===${N}"
        qvm-run --pass-io -u root "$TARGET" "lsblk -o NAME,SIZE,TYPE,MOUNTPOINT"
        echo -e "\n${Y}=== Mount Status ===${N}"
        qvm-run --pass-io -u root "$TARGET" "mountpoint -q $MOUNT_POINT 2>/dev/null && echo '✅ Mounted' || echo '⚠️ Not mounted'"
        echo -e "\n${Y}=== Attached Devices ===${N}"
        qvm-block list | grep "$TARGET" || echo "No devices attached"
        ;;
    4) echo -e "${G}👋${N}"; exit 0 ;;
    *) echo -e "${R}❌ Invalid${N}"; exit 1 ;;
esac
EOF

chmod +x "$DESKTOP_PATH/ram-pool-scripts/mount-to-whonix.sh"

    # ========== apply-cwtch-onionshare.sh ==========
    cat > "$DESKTOP_PATH/ram-pool-scripts/apply-cwtch-onionshare.sh" << 'EOF'
#!/bin/bash
# apply-cwtch-onionshare.sh - Configure Cwtch & OnionShare on a running Whonix VM
set -e

G='\033[0;32m'; Y='\033[1;33m'; R='\033[0;31m'; N='\033[0m'

echo -e "${G}🔧 Applying Cwtch & OnionShare settings${N}"

# 1. Find running Whonix VMs (excluding sys-whonix)
mapfile -t VMS < <(qvm-ls --running --raw-list | grep -i whonix | grep -v sys-whonix)

if [ ${#VMS[@]} -eq 0 ]; then
    echo -e "${R}❌ No running Whonix VMs. Start whonix-work-ram first.${N}"
    exit 1
fi

echo -e "${G}Available VMs:${N}"
for i in "${!VMS[@]}"; do
    echo -e "  ${Y}[$((i+1))]${N} ${VMS[$i]}"
done

# 2. Select VM
while true; do
    read -p "👉 Choose VM number to configure (1-${#VMS[@]}): " CHOICE
    if [[ "$CHOICE" =~ ^[0-9]+$ ]] && [ "$CHOICE" -ge 1 ] && [ "$CHOICE" -le "${#VMS[@]}" ]; then
        WORKSTATION="${VMS[$((CHOICE-1))]}"
        break
    else
        echo -e "${R}❌ Invalid choice.${N}"
    fi
done

GATEWAY=$(qvm-prefs "$WORKSTATION" netvm 2>/dev/null | cut -d'|' -f1 | tr -d '[:space:]')
if [ -z "$GATEWAY" ] || [ "$GATEWAY" = "None" ]; then
    echo -e "${R}❌ $WORKSTATION has no gateway set.${N}"
    exit 1
fi

echo -e "\n${Y}⚙️  Will apply settings to:${N}"
echo -e "  Workstation: ${G}$WORKSTATION${N}"
echo -e "  Gateway:     ${G}$GATEWAY${N}"
read -p "❓ Continue? (y/n): " CONFIRM
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
    echo -e "${R}Cancelled.${N}"
    exit 0
fi

# 3. Configure gateway (onion-grater)
echo -e "\n${Y}🔐 Adding onion-grater rules on $GATEWAY ...${N}"
qvm-run --pass-io -u root "$GATEWAY" "onion-grater-add 40_cwtch && onion-grater-add 40_onionshare && systemctl restart onion-grater" && \
    echo -e "${G}✅ onion-grater configured${N}" || echo -e "${R}❌ Failed to configure onion-grater${N}"

# 4. Configure workstation (open ports)
echo -e "\n${Y}📡 Opening ports on $WORKSTATION ...${N}"
qvm-run --pass-io -u root "$WORKSTATION" "mkdir -p /usr/local/etc/whonix_firewall.d && echo 'EXTERNAL_OPEN_PORTS+=\" \$(seq 15000 15378) \$(seq 17600 17659) \"' >> /usr/local/etc/whonix_firewall.d/50_user.conf && whonix_firewall" && \
    echo -e "${G}✅ Ports opened and firewall reloaded${N}" || echo -e "${R}❌ Failed to configure firewall${N}"

echo -e "\n${G}🎉 Cwtch & OnionShare configuration completed${N}"
EOF

    chmod +x "$DESKTOP_PATH/ram-pool-scripts/apply-cwtch-onionshare.sh"

# ========== create-multi-ram-vm.sh (multi – no auto-mount) ==========
cat > "$DESKTOP_PATH/ram-pool-scripts/create-multi-ram-vm.sh" << 'EOF'
#!/bin/bash

BASE_TEMPLATE="whonix-workstation-18"
VM_BASE="whonix-work-ram"
RAM_POOL_PATH="$HOME/ram_pool"
RAM_POOL_SIZE="4G"
SAVE_SCRIPT_PATH="$HOME/Desktop/qubes-auto-create.sh"

G='\033[0;32m'; Y='\033[1;33m'; R='\033[0;31m'; B='\033[0;34m'; N='\033[0m'

KERNEL_OPTS="xen_scrub_pages=1 init_on_free=1 init_on_alloc=1 page_poison=1 mitigations=auto random.trust_cpu=off random.trust_bootloader=off spec_store_bypass_disable=on tsx_async_abort=full mds=full,nosmt page_alloc.shuffle=1 randomize_kstack_offset=on l1tf=full,force ipv6.disable=1 slab_nomerge vsyscall=none retbleed=stuff spectre_v2_user=on kvm.nx_huge_pages=force"

echo -e "${G}🔧 Creating VMs in RAM pool...${N}"

# 1. Number of VMs
echo -e "${Y}How many VMs would you like to create?${N}"
read -p "Count: " VM_COUNT

if ! [[ "$VM_COUNT" =~ ^[0-9]+$ ]] || [ "$VM_COUNT" -lt 1 ]; then
    echo -e "${R}❌ Error: please enter a positive number${N}"
    exit 1
fi

# 2. Search for sys-whonix gateways
echo -e "${Y}🔍 Searching for available sys-whonix gateways...${N}"

mapfile -t GATEWAYS < <(qvm-ls --raw-data --fields NAME,CLASS 2>/dev/null | grep -E '^sys-whonix(-[0-9]+)?' | awk '{print $1}' | cut -d'|' -f1)

if [ ${#GATEWAYS[@]} -eq 0 ]; then
    mapfile -t GATEWAYS < <(qvm-ls 2>/dev/null | grep -E '^sys-whonix(-[0-9]+)?' | awk '{print $1}')
fi

if [ ${#GATEWAYS[@]} -eq 0 ]; then
    echo -e "${R}❌ Error: no sys-whonix gateways found${N}"
    exit 1
fi

echo -e "${G}✅ Gateways found:${N}"
for i in "${!GATEWAYS[@]}"; do
    CLEAN_GATEWAY=$(echo "${GATEWAYS[$i]}" | cut -d'|' -f1 | tr -d '[:space:]')
    GATEWAYS[$i]="$CLEAN_GATEWAY"
    echo -e "  ${Y}[$((i+1))]${N} ${GATEWAYS[$i]}"
done

# 3. Choose gateways
echo -e "\n${Y}Use different gateways for each VM? (y/n)${N}"
read -p "Choice: " USE_DIFFERENT_GATEWAYS

declare -a VM_GATEWAYS

if [[ "$USE_DIFFERENT_GATEWAYS" =~ ^[Yy]$ ]]; then
    echo -e "\n${Y}Select gateway number for each VM:${N}"
    for (( i=1; i<=VM_COUNT; i++ )); do
        while true; do
            read -p "VM $i – choose gateway (1-${#GATEWAYS[@]}): " GATEWAY_NUM
            if [[ "$GATEWAY_NUM" =~ ^[0-9]+$ ]] && [ "$GATEWAY_NUM" -ge 1 ] && [ "$GATEWAY_NUM" -le ${#GATEWAYS[@]} ]; then
                VM_GATEWAYS[$i]=${GATEWAYS[$((GATEWAY_NUM-1))]}
                echo -e "  ${G}✅ Selected: ${VM_GATEWAYS[$i]}${N}"
                break
            else
                echo -e "${R}❌ Invalid number${N}"
            fi
        done
    done
else
    echo -e "\n${Y}Choose gateway number for all VMs:${N}"
    while true; do
        read -p "Gateway number (1-${#GATEWAYS[@]}): " GATEWAY_NUM
        if [[ "$GATEWAY_NUM" =~ ^[0-9]+$ ]] && [ "$GATEWAY_NUM" -ge 1 ] && [ "$GATEWAY_NUM" -le ${#GATEWAYS[@]} ]; then
            SELECTED_GATEWAY=${GATEWAYS[$((GATEWAY_NUM-1))]}
            for (( i=1; i<=VM_COUNT; i++ )); do
                VM_GATEWAYS[$i]=$SELECTED_GATEWAY
            done
            echo -e "${G}✅ Selected: $SELECTED_GATEWAY${N}"
            break
        else
            echo -e "${R}❌ Invalid number${N}"
        fi
    done
fi

# 4. Confirmation
echo -e "\n${B}📋 VM summary:${N}"
for (( i=1; i<=VM_COUNT; i++ )); do
    echo -e "  ${VM_BASE}${i} → ${VM_GATEWAYS[$i]}"
done

echo -e "\n${Y}Proceed? (y/n)${N}"
read -p "Choice: " CONFIRM
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
    echo -e "${R}Cancelled${N}"
    exit 0
fi

# 5. Check template
if ! qvm-check --quiet "$BASE_TEMPLATE" 2>/dev/null; then
    echo -e "${R}❌ Template $BASE_TEMPLATE not found${N}"
    exit 1
fi

# 6. Remove old VMs
echo -e "${Y}🗑️  Removing old VMs...${N}"
for vm in $(qvm-ls --raw-data --fields NAME 2>/dev/null | grep "^${VM_BASE}[0-9]" | cut -d'|' -f1); do
    qvm-kill "$vm" 2>/dev/null
    qvm-remove --force "$vm" 2>/dev/null
done
echo -e "${G}✅ Removed${N}"

# 7. RAM pool
if ! mount | grep -q "$RAM_POOL_PATH"; then
    echo -e "${Y}📁 Creating RAM pool...${N}"
    sudo mkdir -p "$RAM_POOL_PATH"
    sudo mount -t tmpfs -o size=$RAM_POOL_SIZE tmpfs "$RAM_POOL_PATH"
    if ! qvm-pool ls | grep -q ram_pool; then
        qvm-pool add ram_pool file -o dir_path="$RAM_POOL_PATH"
        qvm-pool set ram_pool -o ephemeral_volatile=True 2>/dev/null
    fi
    echo -e "${G}✅ RAM pool ready${N}"
fi

# 8. Create VMs
declare -a CREATED_VMS

for (( i=1; i<=VM_COUNT; i++ )); do
    VM_NAME="${VM_BASE}${i}"
    GATEWAY="${VM_GATEWAYS[$i]}"
    CLEAN_GATEWAY=$(echo "$GATEWAY" | cut -d'|' -f1 | tr -d '[:space:]')

    echo ""
    echo -e "${Y}📦 Creating: $VM_NAME (gateway: $CLEAN_GATEWAY)${N}"

    if qvm-create -P ram_pool -t "$BASE_TEMPLATE" -l red "$VM_NAME" 2>/dev/null; then
        echo -e "  ${G}✅ Created${N}"
        CREATED_VMS+=("$VM_NAME")

        echo -e "  ${Y}⚙️  Kernel parameters...${N}"
        qvm-prefs "$VM_NAME" kernelopts "$KERNEL_OPTS" 2>/dev/null && echo -e "  ${G}✅ Done${N}" || echo -e "  ${Y}⚠️ Failed${N}"

        echo -e "  ${Y}🔌 Connecting gateway...${N}"
        if qvm-prefs "$VM_NAME" netvm "$CLEAN_GATEWAY" 2>/dev/null; then
            echo -e "  ${G}✅ Connected to $CLEAN_GATEWAY${N}"
        else
            echo -e "  ${R}❌ Connection failed${N}"
        fi

        qvm-features "$VM_NAME" no-monitor-layout 1 2>/dev/null || true

    else
        echo -e "  ${R}❌ Failed to create $VM_NAME${N}"
    fi
done

echo -e "\n${G}🎉 Created ${#CREATED_VMS[@]} VMs${N}"

# 9. Save script (without any auto-mount)
echo -e "\n${B}💾 Save configuration as script?${N}"
read -p "Choice (y/n): " SAVE_SCRIPT

if [[ "$SAVE_SCRIPT" =~ ^[Yy]$ ]]; then
    cat > "$SAVE_SCRIPT_PATH" << EOT
#!/bin/bash
# Saved script – create Whonix VMs in RAM
BASE_TEMPLATE="$BASE_TEMPLATE"
VM_BASE="$VM_BASE"
RAM_POOL_PATH="$RAM_POOL_PATH"
RAM_POOL_SIZE="$RAM_POOL_SIZE"
KERNEL_OPTS="$KERNEL_OPTS"

G='\\033[0;32m'
Y='\\033[1;33m'
R='\\033[0;31m'
N='\\033[0m'

echo -e "\${G}🚀 Starting saved creation...\${N}"

if ! qvm-check --quiet "\$BASE_TEMPLATE" 2>/dev/null; then
    echo -e "\${R}❌ Template not found\${N}"
    exit 1
fi

for vm in \$(qvm-ls --raw-data --fields NAME 2>/dev/null | grep "^\${VM_BASE}[0-9]" | cut -d'|' -f1); do
    qvm-kill "\$vm" 2>/dev/null
    qvm-remove --force "\$vm" 2>/dev/null
done

if ! mount | grep -q "\$RAM_POOL_PATH"; then
    sudo mkdir -p "\$RAM_POOL_PATH"
    sudo mount -t tmpfs -o size=\$RAM_POOL_SIZE tmpfs "\$RAM_POOL_PATH"
    qvm-pool add ram_pool file -o dir_path="\$RAM_POOL_PATH" 2>/dev/null || true
    qvm-pool set ram_pool -o ephemeral_volatile=True 2>/dev/null || true
fi

echo -e "\${Y}📦 Creating VMs...\${N}"
EOT

    for (( i=1; i<=VM_COUNT; i++ )); do
        cat >> "$SAVE_SCRIPT_PATH" << EOT

VM_NAME="${VM_BASE}${i}"
GATEWAY="${VM_GATEWAYS[$i]}"
echo "  Creating: \$VM_NAME (gateway: \$GATEWAY)"
if qvm-create -P ram_pool -t "\$BASE_TEMPLATE" -l red "\$VM_NAME" 2>/dev/null; then
    echo -e "  \${G}✅ Created\${N}"
    qvm-prefs "\$VM_NAME" kernelopts "\$KERNEL_OPTS" 2>/dev/null
    qvm-prefs "\$VM_NAME" netvm "\$GATEWAY" 2>/dev/null
    qvm-features "\$VM_NAME" no-monitor-layout 1 2>/dev/null
else
    echo -e "  \${R}❌ Creation failed\${N}"
fi
EOT
    done

    cat >> "$SAVE_SCRIPT_PATH" << EOT

echo -e "\n\${G}✅ VMs created without auto-mount\${N}"
EOT

    chmod +x "$SAVE_SCRIPT_PATH"
    echo -e "${G}✅ Saved to: $SAVE_SCRIPT_PATH${N}"
fi
EOF

chmod +x "$DESKTOP_PATH/ram-pool-scripts/create-multi-ram-vm.sh"

    # ========== cleanup-ram-pool.sh ==========
    cat > "$DESKTOP_PATH/ram-pool-scripts/cleanup-ram-pool.sh" << 'EOF'
#!/bin/bash
RAM_POOL_PATH="$HOME/ram_pool"
echo "🧹 Cleaning RAM Pool..."
for vm in $(qvm-ls --raw-list 2>/dev/null | grep -E "whonix-work-ram|ram-"); do
    qvm-kill "$vm" 2>/dev/null
    qvm-remove --force "$vm" 2>/dev/null
done
qvm-pool remove ram_pool 2>/dev/null
mount | grep -q "$RAM_POOL_PATH" && sudo umount "$RAM_POOL_PATH"
[ -d "$RAM_POOL_PATH" ] && sudo rm -rf "$RAM_POOL_PATH"
echo "✅ Cleanup complete"
free -h
EOF

    # ========== ram-pool-status.sh ==========
    cat > "$DESKTOP_PATH/ram-pool-scripts/ram-pool-status.sh" << 'EOF'
#!/bin/bash
echo "=== RAM Pool Status ==="
mount | grep ram_pool && df -h "$HOME/ram_pool"
echo "VMs in RAM Pool:"
qvm-pool volumes ram_pool 2>/dev/null | grep -oE '^[^:]+' | sort -u
echo "Memory usage:"
free -h
EOF

    chmod +x "$DESKTOP_PATH/ram-pool-scripts"/*.sh
    ok "7 scripts created in $DESKTOP_PATH/ram-pool-scripts/"
    pause
}

# ========
# 6. Amnesic Tmpfs Script
# ========
amnesic_script() {
    clear
    echo "╔═══════════════════════════════════════════════════════════╗"
    echo "║  🧠 Amnesic Tmpfs - RAM folders that disappear on shutdown ║"
    echo "╚═══════════════════════════════════════════════════════════╝"
    echo ""
    echo "1) Apply Amnesic Tmpfs to a VM"
    echo "2) Remove Amnesic Tmpfs from a VM"
    echo "0) Back"
    echo ""
    read -p "Choose: " CHOICE
    
    case $CHOICE in
        1) apply_amnesic ;;
        2) remove_amnesic ;;
        0) return ;;
        *) amnesic_script ;;
    esac
}

apply_amnesic() {
    clear
    echo "╔═══════════════════════════════════════════════════════════╗"
    echo "║  📌 Apply Amnesic Tmpfs                                   ║"
    echo "╚═══════════════════════════════════════════════════════════╝"
    echo ""
    
    SCRIPT_PATH="$DESKTOP_PATH/apply-amnesic.sh"
    
    cat > "$SCRIPT_PATH" << 'SCRIPTEOF'
#!/bin/bash
clear
echo "Apply Amnesic Tmpfs"
echo ""
echo "📋 Available VMs:"
echo "----------------------------------------"
qvm-ls --raw-list 2>/dev/null | grep -v dom0
echo "----------------------------------------"
echo ""
read -p "🔧 VM name: " TARGET_VM
[ -z "$TARGET_VM" ] && { echo "❌ No name"; read -p "Enter..."; exit 1; }
! qvm-ls "$TARGET_VM" >/dev/null 2>&1 && { echo "❌ Does not exist"; read -p "Enter..."; exit 1; }

echo ""
echo "🔧 Preparing: $TARGET_VM"
qvm-start "$TARGET_VM" 2>/dev/null
sleep 3

cat > /tmp/rc_local_content.txt << 'RCEOF'
#!/bin/bash
mkdir -p /var/log /var/cache /var/tmp /tmp /home/user/QubesIncoming
mount -t tmpfs tmpfs /var/log -o size=512M,mode=0755,nosuid,nodev
mount -t tmpfs tmpfs /var/cache -o size=512M,mode=0755,nosuid,nodev
mount -t tmpfs tmpfs /var/tmp -o size=256M,mode=1777,nosuid,nodev,noexec
mount -t tmpfs tmpfs /tmp -o size=512M,mode=1777,nosuid,nodev,noexec
mount -t tmpfs tmpfs /home/user -o size=2G,mode=0700,nosuid,nodev
cp -r /etc/skel/. /home/user/ 2>/dev/null
mkdir -p /home/user/QubesIncoming
chown -R user:user /home/user 2>/dev/null
exit 0
RCEOF

qvm-run --pass-io "$TARGET_VM" 'sudo tee /rw/config/rc.local' < /tmp/rc_local_content.txt 2>/dev/null
qvm-run "$TARGET_VM" "sudo chmod +x /rw/config/rc.local" 2>/dev/null
qvm-shutdown "$TARGET_VM" 2>/dev/null
rm -f /tmp/rc_local_content.txt

echo ""
echo "✅ Amnesic Tmpfs applied to: $TARGET_VM"
read -p "Enter..."
SCRIPTEOF

    chmod +x "$SCRIPT_PATH"
    bash "$SCRIPT_PATH"
    rm -f "$SCRIPT_PATH"
}

remove_amnesic() {
    clear
    echo "╔═══════════════════════════════════════════════════════════╗"
    echo "║  🗑️ Remove Amnesic Tmpfs                                  ║"
    echo "╚═══════════════════════════════════════════════════════════╝"
    echo ""
    
    SCRIPT_PATH="$DESKTOP_PATH/remove-amnesic.sh"
    
    cat > "$SCRIPT_PATH" << 'SCRIPTEOF'
#!/bin/bash
clear
echo "Remove Amnesic Tmpfs"
echo ""
echo "📋 Available VMs:"
echo "----------------------------------------"
qvm-ls --raw-list 2>/dev/null | grep -v dom0
echo "----------------------------------------"
echo ""
read -p "🔧 VM name: " TARGET_VM
[ -z "$TARGET_VM" ] && { echo "❌ No name"; read -p "Enter..."; exit 1; }
! qvm-ls "$TARGET_VM" >/dev/null 2>&1 && { echo "❌ Does not exist"; read -p "Enter..."; exit 1; }

echo ""
echo "🔧 Preparing: $TARGET_VM"
qvm-start "$TARGET_VM" 2>/dev/null
sleep 3

# Write just #!/bin/bash to rc.local
qvm-run "$TARGET_VM" "sudo bash -c 'echo \"#!/bin/bash\" > /rw/config/rc.local'" 2>/dev/null
qvm-run "$TARGET_VM" "sudo chmod +x /rw/config/rc.local" 2>/dev/null

qvm-shutdown "$TARGET_VM" 2>/dev/null

echo ""
echo "✅ Amnesic Tmpfs removed from: $TARGET_VM"
echo "📌 rc.local restored to default"
read -p "Enter..."
SCRIPTEOF

    chmod +x "$SCRIPT_PATH"
    bash "$SCRIPT_PATH"
    rm -f "$SCRIPT_PATH"
}
# ========
# 7. Live Mode
# ========
live_mode() {
    clear
    REAL_USER=$(logname 2>/dev/null || echo "$SUDO_USER" 2>/dev/null || echo "$USER")
    
    info "Real user: $REAL_USER"
    
    warn "This script will create live.sh and execute it with sudo privileges"
    warn "Execution may take several minutes"
    warn "🟢 Overlay-Live + zram"
    read -p "Proceed? (y/n): " confirm
    [[ "$confirm" != "y" && "$confirm" != "Y" ]] && return
    
    LIVE_SCRIPT="/home/$REAL_USER/Desktop/live.sh"
    info "Creating $LIVE_SCRIPT ..."
    
    cat > "$LIVE_SCRIPT" << 'EOOFF'
#!/bin/bash

# Qubes Dom0 Amnesic modes
# ⚠️ Make backup before running! Run as root: sudo ./amnesic.sh
echo "=== Qubes Dom0 Live Boot Setup ==="

#BOOT_UUID
BOOT_UUID=$(findmnt -n -o UUID /boot 2>/dev/null || echo "AUTO_BOOT_NOT_FOUND")
if [ "$BOOT_UUID" = "AUTO_BOOT_NOT_FOUND" ]; then
    BOOT_UUID=$(blkid -s UUID -o value -d $(findmnt -n -o SOURCE /boot 2>/dev/null))
fi

# LUKS_UUID
LUKS_DEVICE=$(blkid -t TYPE="crypto_LUKS" -o device 2>/dev/null | head -n1 || echo "")
if [ -n "$LUKS_DEVICE" ]; then
    LUKS_UUID=$(sudo cryptsetup luksUUID "$LUKS_DEVICE" 2>/dev/null)
else
    LUKS_UUID="AUTO_LUKS_NOT_FOUND"
fi

# Latest XEN_PATH 
XEN_PATH=$(ls /boot/xen*.gz 2>/dev/null | sort -V | tail -1 | xargs basename 2>/dev/null || echo "/xen-4.19.4.gz")

# Latest kernel/initramfs
LATEST_KERNEL=$(ls /boot/vmlinuz-*qubes*.x86_64 2>/dev/null | grep -E 'qubes\.fc[0-9]+' | sort -V | tail -1 | xargs basename)
LATEST_INITRAMFS=$(echo "/initramfs-${LATEST_KERNEL#vmlinuz-}.img")

# Max memory dom0
system_total_mb=$(xl info | grep total_memory | awk '{print $3}')

if [ -n "$system_total_mb" ] && [ "$system_total_mb" -gt 0 ] 2>/dev/null; then
    # 80% total_memory
    DOM0_MAX_MB=$((system_total_mb * 80 / 100))
    DOM0_MAX_GB=$((DOM0_MAX_MB / 1024))
    DOM0_MAX_RAM="dom0_mem=max:${DOM0_MAX_MB}M"
    DOM0_MAX_GBG="${DOM0_MAX_GB}G"
else
    DOM0_MAX_RAM="dom0_mem=max:10240M"
    DOM0_MAX_GB="10"
    DOM0_MAX_GBG="10G"
fi

# qubes_dom0-root
Qubes_Root=$(findmnt -n -o SOURCE /)

# /home/user
if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then
    USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
else
    USER_HOME="$HOME"
fi

if [ ! -d "$USER_HOME" ]; then
    echo "home dir '$USER_HOME' not found!" >&2
    exit 1
fi

# === dom0 resize ===

REQUIRED_FREE_GB=100
MIN_DOM0_SIZE_GB=40

get_dom0_size_gb() {
    local size_bytes
    size_bytes=$(df -B1 /dev/mapper/qubes_dom0-root 2>/dev/null | awk 'NR==2 {print $2}')
    if [[ -z "$size_bytes" ]]; then
        echo "Warning: failed to determine dom0 size" >&2
        return 1
    fi
    echo "$((size_bytes / 1024 / 1024 / 1024))"
    return 0
}

get_vg_free_gb() {
    local lv_size data_pct
    lv_size=$(lvs --noheadings --nosuffix --units b -o lv_size qubes_dom0/vm-pool 2>/dev/null | tr -dc '0-9')
    data_pct=$(lvs --noheadings -o data_percent qubes_dom0/vm-pool 2>/dev/null | tr ',' '.' | tr -dc '0-9.')

    if [[ -z "$lv_size" || -z "$data_pct" ]]; then
        echo "Warning: failed to determine free space in vm-pool" >&2
        return 1
    fi

    local free_gb
    free_gb=$(awk "BEGIN { printf \"%.0f\", ($lv_size * (100 - $data_pct) / 100) / 1024 / 1024 / 1024 }")
    echo "$free_gb"
    return 0
}

main() {
    if [[ "$EUID" -ne 0 ]]; then
        echo "Warning: This script should be run as root (sudo)" >&2
        # Changed from exit 1 to just warning
    fi

    local dom0_size_gb vg_free_gb
    dom0_size_gb=$(get_dom0_size_gb) || dom0_size_gb="unknown"
    vg_free_gb=$(get_vg_free_gb) || vg_free_gb="unknown"

    echo "Current dom0 size: ${dom0_size_gb} GB"
    echo "Free space in VM pool: ${vg_free_gb} GB"

    if [[ "$vg_free_gb" != "unknown" ]] && ((vg_free_gb < REQUIRED_FREE_GB)); then
        echo "Info: free space is less than ${REQUIRED_FREE_GB} GB (available: ${vg_free_gb} GB) - skipping resize"
        # Changed from exit 0 to continue
    elif [[ "$dom0_size_gb" != "unknown" ]] && ((dom0_size_gb >= MIN_DOM0_SIZE_GB)); then
        echo "Info: dom0 size is already ${dom0_size_gb} GB (>= ${MIN_DOM0_SIZE_GB} GB) - skipping resize"
        # Changed from exit 0 to continue
    else
        echo "Conditions met. Starting dom0 resize..."

        if lvresize --size 40G /dev/mapper/qubes_dom0-root; then
            resize2fs /dev/mapper/qubes_dom0-root
            lvresize -L +20G qubes_dom0/root-pool
            echo "Done. New dom0 size: $(get_dom0_size_gb) GB"
        else
            echo "Warning: dom0 resize failed - continuing with other commands"
        fi
    fi

    echo "--- dom0 resize completed ---"
}

main "$@"

# === swapoff ===
sudo sed -i '/\/dev\/mapper\/swap/!{/^[[:space:]]*#/!{/\/s/^/# /}}' /etc/fstab
sudo sed -i '/\/dev\/mapper\/swap/b; /[[:space:]]\+swap[[:space:]]\+/s/^/# /' /etc/fstab

# === harden autostart ===
mkdir -p "$USER_HOME/.config"
if [ ! -f "$USER_HOME/.config/harden.sh" ]; then
    cat > "$USER_HOME/.config/harden.sh" << 'EOF'
#!/bin/bash
sleep 1
if findmnt -n -o SOURCE / | grep -qE "(overlay|/dev/zram0)"; then
    notify-send --expire-time=20000 "Amnesic session is running" "dom0 mode: $(findmnt -n -o SOURCE /)" --icon=dialog-information
    sudo sysctl -w kernel.sysrq=0
    sudo sysctl -w kernel.perf_event_paranoid=3
    sudo sysctl -w kernel.kptr_restrict=2
    sudo sysctl -w kernel.panic=5
    sudo sysctl -w fs.protected_regular=2
    sudo sysctl -w fs.protected_fifos=2
    sudo sysctl -w kernel.printk="3 3 3 3"
    sudo sysctl -w kernel.kexec_load_disabled=1
    sudo sysctl -w kernel.io_uring_disabled=2
    sudo chattr +i /boot/grub2/grub.cfg
    sudo chattr +i /boot
else
    sudo chattr -i /boot/grub2/grub.cfg
    sudo chattr -i /boot
fi
EOF
    chmod 755 "$USER_HOME/.config/harden.sh"
    echo "Created harden.sh"
else
    echo "harden.sh already exists, skipping"
fi

# === autostart desktop entry ===
mkdir -p "$USER_HOME/.config/autostart"
if [ ! -f "$USER_HOME/.config/autostart/harden.desktop" ]; then
    cat > "$USER_HOME/.config/autostart/harden.desktop" << EOF
[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=harden
Comment=
Exec=$USER_HOME/.config/harden.sh
OnlyShowIn=XFCE;
RunHook=0
StartupNotify=false
Terminal=false
Hidden=false
EOF
    echo "Created harden.desktop"
else
    echo "harden.desktop already exists, skipping"
fi

# === Dracut module directories ===
DIR_OVERLAY_CRYPT=/usr/lib/dracut/modules.d/90overlay-crypt
DIR_RAMBOOT=/usr/lib/dracut/modules.d/90ramboot
DIR_OVERLAY=/usr/lib/dracut/modules.d/90overlayfs-root
DIR_RAMWIPE=/usr/lib/dracut/modules.d/40ram-wipe

for d in "$DIR_RAMBOOT" "$DIR_OVERLAY" "$DIR_RAMWIPE" "$DIR_OVERLAY_CRYPT"; do
    if [ ! -d "$d" ]; then
        mkdir -p "$d"
        echo "Created $(basename "$d")"
    else
        echo "$(basename "$d") already exists, skipping"
    fi
done

# === 90overlay-crypt/module-setup.sh ===
if [ ! -f "$DIR_OVERLAY_CRYPT/module-setup.sh" ]; then
    cat > "$DIR_OVERLAY_CRYPT/module-setup.sh" << 'EOF'
#!#!/bin/bash

check() {
    require_binaries cryptsetup || return 1
    require_binaries losetup || return 1
    require_binaries mkfs.ext4 || return 1
    return 0
}

depends() {
    return 0
}

installkernel() {
    hostonly='' instmods overlay 2>/dev/null || true
    hostonly='' instmods dm-crypt 2>/dev/null || true
}

install() {
    inst_multiple cryptsetup losetup mkfs.ext4 dd modprobe mount umount shred
    inst_hook pre-pivot 10 "$moddir/overlay-crypt.sh"
}
EOF
    chmod 755 "$DIR_OVERLAY_CRYPT/module-setup.sh"
    echo "Created 90overlay-crypt/module-setup.sh"
else
    echo "90overlay-crypt/module-setup.sh already exists, skipping"
fi

# === 90overlay-crypt/overlay-crypt.sh ===
if [ ! -f "$DIR_OVERLAY_CRYPT/overlay-crypt.sh" ]; then
    cat > "$DIR_OVERLAY_CRYPT/overlay-crypt.sh" << 'EOF'
#!/bin/bash

. /lib/dracut-lib.sh

if ! getargbool 0 cryptovl ; then
    return
fi

modprobe overlay 2>/dev/null || true
modprobe dm-crypt 2>/dev/null || true

#
mount -o remount,ro /sysroot 2>/dev/null || true

# 
mkdir -p /live/image
mount --bind /sysroot /live/image
umount /sysroot

# 
dd if=/dev/urandom bs=64 count=1 of=/dev/shm/overlay-key status=none
chmod 600 /dev/shm/overlay-key

# create 20 GB size (seek)
mkdir -p /var/lib
dd if=/dev/zero of=/var/lib/overlay-crypt.img bs=1M count=0 seek=20480 status=none

# 
losetup -f
LOOP_DEV=$(losetup -f --show /var/lib/overlay-crypt.img)

# 
cryptsetup luksFormat --type luks2 \
    --cipher aes-xts-plain64 --key-size 512 \
    --hash sha256 --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
    --batch-mode --key-file /dev/shm/overlay-key "$LOOP_DEV"

# 
cryptsetup open --type luks2 --key-file /dev/shm/overlay-key "$LOOP_DEV" overlaycrypt

# 
mkfs.ext4 -F -L "overlaycrypt" /dev/mapper/overlaycrypt

# 
mkdir -p /cow
mount -o noatime,nodiratime,nobarrier /dev/mapper/overlaycrypt /cow
mkdir -p /cow/work /cow/rw

# 
mount -t overlay -o noatime,nodiratime,volatile,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions,relatime overlay /sysroot

# 
mkdir -p /sysroot/live/cow /sysroot/live/image
mount --bind /cow/rw /sysroot/live/cow
mount --bind /live/image /sysroot/live/image

# 
umount /cow 2>/dev/null || true
umount /live/image 2>/dev/null || true

# 
shred -u /dev/shm/overlay-key 2>/dev/null || rm -f /dev/shm/overlay-key
EOF
    chmod 755 "$DIR_OVERLAY_CRYPT/overlay-crypt.sh"
    echo "Created 90overlay-crypt/overlay-crypt.sh"
else
    echo "90overlay-crypt/overlay-crypt.sh already exists, skipping"
fi

# === 90ramboot/module-setup.sh ===
if [ ! -f "$DIR_RAMBOOT/module-setup.sh" ]; then
    cat > "$DIR_RAMBOOT/module-setup.sh" << 'EOF'
#!/usr/bin/bash
check() {
    return 0
}
depends() {
    return 0
}
install() {
    inst_simple "$moddir/zram-mount.sh"
    inst_hook cleanup 00 "$moddir/zram-mount.sh"
}
EOF
    chmod 755 "$DIR_RAMBOOT/module-setup.sh"
    echo "Created 90ramboot/module-setup.sh"
else
    echo "90ramboot/module-setup.sh already exists, skipping"
fi

# === 90ramboot/zram-mount.sh ===
if [ ! -f "$DIR_RAMBOOT/zram-mount.sh" ]; then
    cat > "$DIR_RAMBOOT/zram-mount.sh" << EOF
#!/bin/sh

. /lib/dracut-lib.sh

if ! getargbool 0 rootzram ; then
    return
fi

mkdir -p /mnt
umount /sysroot
mount -o ro $Qubes_Root /mnt
modprobe zram
echo $DOM0_MAX_GBG > /sys/block/zram0/disksize
#/mnt/usr/sbin/mkfs.xfs -f -m reflink=0 /dev/zram0
/mnt/usr/sbin/mkfs.ext2 /dev/zram0
mount -o nodev,nosuid,noatime,nodiratime /dev/zram0 /sysroot
EXCLUDES=("dev" "proc" "sys" "tmp" "run" "mnt" "media" "lost+found" "var/log")

FIND_EXPR=()
for dir in "${EXCLUDES[@]}"; do
    FIND_EXPR+=(-name "$dir" -o)
done
unset 'FIND_EXPR[${#FIND_EXPR[@]}-1]'
find /mnt -mindepth 1 -maxdepth 1 ! \( "${FIND_EXPR[@]}" \) -exec cp -a {} /sysroot \;
for dir in "${EXCLUDES[@]}"; do
    mkdir -p "/sysroot/$dir"
done

# Disable swap
if [ -f /sysroot/etc/fstab ]; then
    sed -i '/[ \t]swap[ \t]/d' /sysroot/etc/fstab
fi

if [ -d /sysroot/etc/systemd/system ]; then
    ln -sf /dev/null /sysroot/etc/systemd/system/dev-mapper-swap.device
fi

umount /mnt
exit 0
EOF
    chmod 755 "$DIR_RAMBOOT/zram-mount.sh"
    echo "Created 90ramboot/zram-mount.sh"
else
    echo "90ramboot/zram-mount.sh already exists, skipping"
fi

# === 90overlayfs-root/module-setup.sh ===
if [ ! -f "$DIR_OVERLAY/module-setup.sh" ]; then
    cat > "$DIR_OVERLAY/module-setup.sh" << 'EOF'
#!/bin/bash

check() {
    [ -d /lib/modules/$kernel/kernel/fs/overlayfs ] || return 1
}

depends() {
    return 0
}

installkernel() {
    hostonly='' instmods overlay
}

install() {
    inst_hook pre-pivot 10 "$moddir/overlay-mount.sh"
}
EOF
    chmod 755 "$DIR_OVERLAY/module-setup.sh"
    echo "Created 90overlayfs-root/module-setup.sh"
else
    echo "90overlayfs-root/module-setup.sh already exists, skipping"
fi

# === 90overlayfs-root/overlay-mount.sh ===
if [ ! -f "$DIR_OVERLAY/overlay-mount.sh" ]; then
    cat > "$DIR_OVERLAY/overlay-mount.sh" << 'EOF'
#!/bin/sh
. /lib/dracut-lib.sh

if ! getargbool 0 rootovl ; then
    return
fi

modprobe overlay
mount -o remount,nolock,noatime $NEWROOT
mkdir -p /live/image
mount --bind $NEWROOT /live/image
umount $NEWROOT
mkdir /cow
mount -n -t tmpfs -o mode=0755,size=100%,nr_inodes=500k,noexec,nodev,nosuid,noatime,nodiratime tmpfs /cow
mkdir /cow/work /cow/rw
mount -t overlay -o noatime,nodiratime,volatile,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions,relatime overlay $NEWROOT
mkdir -p $NEWROOT/live/cow
mkdir -p $NEWROOT/live/image
mount --bind /cow/rw $NEWROOT/live/cow
umount /cow
mount --bind /live/image $NEWROOT/live/image
umount /live/image
umount $NEWROOT/live/cow
EOF
    chmod 755 "$DIR_OVERLAY/overlay-mount.sh"
    echo "Created 90overlayfs-root/overlay-mount.sh"
else
    echo "90overlayfs-root/overlay-mount.sh already exists, skipping"
fi

# === ramboot dracut.conf ===
if [ ! -f /etc/dracut.conf.d/ramboot.conf ]; then
    cat > /etc/dracut.conf.d/ramboot.conf << 'EOF'
add_drivers+=" zram "
add_dracutmodules+=" ramboot "
EOF
    echo "Created ramboot.conf"
else
    echo "ramboot.conf already exists, skipping"
fi

# === 40ram-wipe/module-setup.sh ===
if [ ! -f "$DIR_RAMWIPE/module-setup.sh" ]; then
    cat > "$DIR_RAMWIPE/module-setup.sh" << 'EOF'
#!/bin/bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh

## Copyright (C) 2023 - 2025 ENCRYPTED SUPPORT LLC 
## See the file COPYING for copying conditions.

check() {
   require_binaries sync || return 1
   require_binaries sleep || return 1
   require_binaries dmsetup || return 1
   return 0
}

depends() {
   return 0
}

install() {
   inst_simple "/usr/libexec/ram-wipe/ram-wipe-lib.sh" "/lib/ram-wipe-lib.sh"
   inst_multiple sync
   inst_multiple sleep
   inst_multiple dmsetup
   inst_hook shutdown 40 "$moddir/wipe-ram.sh"
   inst_hook cleanup 80 "$moddir/wipe-ram-needshutdown.sh"
}

installkernel() {
   return 0
}
EOF
    chmod +x "$DIR_RAMWIPE/module-setup.sh"
    echo "Created 40ram-wipe/module-setup.sh"
else
    echo "40ram-wipe/module-setup.sh already exists, skipping"
fi

# === 40ram-wipe/wipe-ram-needshutdown.sh ===
if [ ! -f "$DIR_RAMWIPE/wipe-ram-needshutdown.sh" ]; then
    cat > "$DIR_RAMWIPE/wipe-ram-needshutdown.sh" << 'EOF'
#!/bin/sh

## Copyright (C) 2023 - 2025 ENCRYPTED SUPPORT LLC 
## See the file COPYING for copying conditions.

type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh

. /lib/ram-wipe-lib.sh

ram_wipe_check_needshutdown() {
   #local kernel_wiperam_setting

   kernel_wiperam_setting="$(getarg wiperam)"

   if [ "$kernel_wiperam_setting" = "skip" ]; then
      force_echo "wipe-ram-needshutdown.sh: Skip, because wiperam=skip kernel parameter detected, OK."
      return 0
   fi

   true "wipe-ram-needshutdown.sh: Calling dracut function need_shutdown to drop back into initramfs at shutdown, OK."
   need_shutdown

   return 0
}

ram_wipe_check_needshutdown
EOF
    chmod +x "$DIR_RAMWIPE/wipe-ram-needshutdown.sh"
    echo "Created 40ram-wipe/wipe-ram-needshutdown.sh"
else
    echo "40ram-wipe/wipe-ram-needshutdown.sh already exists, skipping"
fi

# === 40ram-wipe/wipe-ram.sh ===
if [ ! -f "$DIR_RAMWIPE/wipe-ram.sh" ]; then
    cat > "$DIR_RAMWIPE/wipe-ram.sh" << 'EOF'
#!/bin/sh

## Copyright (C) 2023 - 2025 ENCRYPTED SUPPORT LLC 
## See the file COPYING for copying conditions.

## Credits:
## First version by @friedy10.
## https://github.com/friedy10/dracut/blob/master/modules.d/40sdmem/wipe.sh

. /lib/ram-wipe-lib.sh

drop_caches() {
   sync
   echo 3 > /proc/sys/vm/drop_caches
   sync
}

ram_wipe() {
   #local kernel_wiperam_setting

   kernel_wiperam_setting="$(getarg wiperam)"

   if [ "$kernel_wiperam_setting" = "skip" ]; then
      force_echo "wipe-ram.sh: Skip, because wiperam=skip kernel parameter detected, OK."
      return 0
   fi

   force_echo "wipe-ram.sh: RAM extraction attack defense... Starting RAM wipe pass during shutdown..."

   drop_caches

   force_echo "wipe-ram.sh: RAM wipe pass completed, OK."
}

ram_wipe
EOF
    chmod +x "$DIR_RAMWIPE/wipe-ram.sh"
    echo "Created 40ram-wipe/wipe-ram.sh"
else
    echo "40ram-wipe/wipe-ram.sh already exists, skipping"
fi

# === ram-wipe dracut.conf ===
if [ ! -f /etc/dracut.conf.d/30-ram-wipe.conf ]; then
    cat > /etc/dracut.conf.d/30-ram-wipe.conf << 'EOF'
add_dracutmodules+=" ram-wipe "
EOF
    echo "Created 30-ram-wipe.conf"
else
    echo "30-ram-wipe.conf already exists, skipping"
fi

# === ram-wipe-lib.sh ===
DIR_RAMWIPE_LIB=/usr/libexec/ram-wipe
if [ ! -d "$DIR_RAMWIPE_LIB" ]; then
    mkdir -p "$DIR_RAMWIPE_LIB"
fi

if [ ! -f "$DIR_RAMWIPE_LIB/ram-wipe-lib.sh" ]; then
    cat > "$DIR_RAMWIPE_LIB/ram-wipe-lib.sh" << 'EOF'
#!/bin/sh

## Copyright (C) 2023 - 2025 ENCRYPTED SUPPORT LLC 
## See the file COPYING for copying conditions.

if [ -z "$DRACUT_SYSTEMD" ]; then
    force_echo() {
        echo "<28>dracut INFO: $*" > /dev/kmsg
        echo "dracut INFO: $*" >&2
    }
else
    force_echo() {
        echo "INFO: $*" >&2
    }
fi
EOF
    chmod +x "$DIR_RAMWIPE_LIB/ram-wipe-lib.sh"
    echo "Created ram-wipe-lib.sh"
else
    echo "ram-wipe-lib.sh already exists, skipping"
fi

# Update INITRAMFS
dracut --verbose --force

# Create GRUB custom
echo "Creating GRUB custom ..."

cat > /etc/grub.d/40_custom << EOF
#!/usr/bin/sh
exec tail -n +3 \$0

menuentry 'Qubes Encrypted-Overlay Amnesic Mode' --class qubes --class gnu-linux --class gnu --class os --class xen \$menuentry_id_option 'xen-gnulinux-simple-/dev/mapper/qubes_dom0-root' {
	insmod part_gpt
	insmod ext2
	search --no-floppy --fs-uuid --set=root $BOOT_UUID
	echo 'Loading Xen ...'
	if [ "\$grub_platform" = "pc" -o "\$grub_platform" = "" ]; then
	    xen_rm_opts=
	else
	    xen_rm_opts="no-real-mode edd=off"
	fi
	insmod multiboot2
	multiboot2 /$XEN_PATH placeholder console=none dom0_mem=min:1024M $DOM0_MAX_RAM ucode=scan smt=off gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096 \${xen_rm_opts}
	echo 'Loading Linux $LATEST_KERNEL ...'
	module2 /$LATEST_KERNEL placeholder root=/dev/mapper/qubes_dom0-root ro rd.luks.uuid=$LUKS_UUID rd.lvm.lv=qubes_dom0/root rd.lvm.lv=qubes_dom0/swap plymouth.ignore-serial-consoles rhgb cryptovl quiet module.sig_enforce=1 bootscrub=on
	echo 'Loading initial ramdisk ...'
	insmod multiboot2
	module2 --nounzip $LATEST_INITRAMFS
}

menuentry 'Qubes Zram-Live Amnesic Mode' --class qubes --class gnu-linux --class gnu --class os --class xen \$menuentry_id_option 'xen-gnulinux-simple-/dev/mapper/qubes_dom0-root' {
	insmod part_gpt
	insmod ext2
	search --no-floppy --fs-uuid --set=root $BOOT_UUID
	echo 'Loading Xen ...'
	if [ "\$grub_platform" = "pc" -o "\$grub_platform" = "" ]; then
	    xen_rm_opts=
	else
	    xen_rm_opts="no-real-mode edd=off"
	fi
	insmod multiboot2
	multiboot2 /$XEN_PATH placeholder console=none dom0_mem=min:1024M $DOM0_MAX_RAM ucode=scan smt=off gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096 \${xen_rm_opts}
	echo 'Loading Linux $LATEST_KERNEL ...'
	module2 /$LATEST_KERNEL placeholder root=/dev/mapper/qubes_dom0-root ro rd.luks.uuid=$LUKS_UUID rd.lvm.lv=qubes_dom0/root rd.lvm.lv=qubes_dom0/swap plymouth.ignore-serial-consoles rhgb rootzram quiet module.sig_enforce=1 bootscrub=on
	echo 'Loading initial ramdisk ...'
	insmod multiboot2
	module2 --nounzip $LATEST_INITRAMFS
}
EOF

chmod 755  /etc/grub.d/40_custom

# Update GRUB
grub2-mkconfig -o /boot/grub2/grub.cfg

# Cleanup
dnf clean all
journalctl --vacuum-time=1d
rm -rf /var/cache/dnf/* 2>/dev/null || true


echo
echo "Done!"
echo "✓ ALL STEPS COMPLETED SUCCESSFULLY! Reboot Qubes OS, select one of the new GRUB options (Encrypted-Overlay or Zram-Live), and clone your appVMs to the varlibqubes pool to run in full amnesia mode."
EOOFF
    
    chmod +x "$LIVE_SCRIPT"
    ok "Created $LIVE_SCRIPT"
    
    info "Executing live.sh with sudo..."
    sudo bash "$LIVE_SCRIPT"
    
    info "Deleting $LIVE_SCRIPT..."
    rm -f "$LIVE_SCRIPT"
    ok "File deleted"
    
    pause
}

# ========
# 8. Emergency Scripts (BusKill)
# ========
emergency_script() {
    mkdir -p "$DESKTOP_PATH/emergency"
    
    # ========== Dead Man's Switch script ==========
    cat > "$DESKTOP_PATH/emergency/deadman-switch.sh" << 'EOF'
#!/bin/bash
# ⚠️ Dead Man's Switch - Delayed destruction using BusKill

echo "╔═══════════════════════════════════════════════════════════╗"
echo "║     💀 Dead Man's Switch - Delayed Destruction (BusKill) 💀  ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""

read -p "Timeout in minutes before destruction: " T
[ -z "$T" ] && T=5

read -p "Type YES for final confirmation: " C
[ "$C" != "YES" ] && { echo "Cancelled"; exit 1; }

# Print cancel command first
echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║  📌 Cancel command (save it now before continuing):       ║"
echo "║     sudo /usr/local/bin/cancel                            ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""

# Check for BusKill and install if not present
if [ ! -f "/etc/qubes-rpc/buskill.selfDestruct" ]; then
    echo "⚠️ BusKill not installed. Installing automatically..."
    
    sudo tee /etc/qubes-rpc/buskill.selfDestruct > /dev/null << 'BUSKILL'
#!/bin/bash
# 1. Delete Qubes folder from EFI
EFI_PART=$(findmnt -n -o SOURCE /boot/efi 2>/dev/null)
if [ -n "$EFI_PART" ]; then
    sudo mount $EFI_PART /mnt
    sudo rm -rf /mnt/EFI/qubes
    sudo umount /mnt
    echo "✅ Deleted Qubes folder from $EFI_PART"
else
    echo "⚠️ EFI partition not found"
fi
# Delete boot entry from NVRAM
if [ -d "/sys/firmware/efi" ]; then
    # Find Qubes entry and delete it
    sudo efibootmgr | grep -i qubes | while read -r line; do
        bootnum=
        sudo efibootmgr -b "" -B
        echo "✅ Deleted boot entry: Boot"
    done
fi

# 2. Destroy /boot partition
BOOT_PART=$(findmnt -n -o SOURCE /boot)
echo "💀 Starting destruction of $BOOT_PART"
sudo dd if=/dev/urandom of=$BOOT_PART bs=1M status=progress &

WHICH="/usr/bin/which"
sudo qvm-kill --all &
CRYPTSETUP=$($WHICH --skip-alias cryptsetup) || echo "ERROR: Unable to find cryptsetup"
LS=$($WHICH --skip-alias ls) || echo "ERROR: Unable to find ls"
CP=$($WHICH --skip-alias cp) || echo "ERROR: Unable to find cp"
MV=$($WHICH --skip-alias mv) || echo "ERROR: Unable to find mv"
LN=$($WHICH --skip-alias ln) || echo "ERROR: Unable to find ln"
MKDIR=$($WHICH --skip-alias mkdir) || echo "ERROR: Unable to find mkdir"
MOUNT=$($WHICH --skip-alias mount) || echo "ERROR: Unable to find mount"
CAT=$($WHICH --skip-alias cat) || echo "ERROR: Unable to find cat"
GREP=$($WHICH --skip-alias grep) || echo "ERROR: Unable to find grep"
ECHO=$($WHICH --skip-alias echo) || echo "ERROR: Unable to find echo"
AWK=$($WHICH --skip-alias awk) || echo "ERROR: Unable to find awk"
HEAD=$($WHICH --skip-alias head) || echo "ERROR: Unable to find head"
LSBLK=$($WHICH --skip-alias lsblk) || echo "ERROR: Unable to find lsblk"
OD=$($WHICH --skip-alias od) || echo "ERROR: Unable to find od"
SUDO=$($WHICH --skip-alias sudo) || echo "ERROR: Unable to find sudo"
CHMOD=$($WHICH --skip-alias chmod) || echo "ERROR: Unable to find chmod"
BASH=$($WHICH --skip-alias bash) || echo "ERROR: Unable to find bash"
NOHUP=$($WHICH --skip-alias nohup) || echo "ERROR: Unable to find nohup"
SLEEP=$($WHICH --skip-alias sleep) || echo "ERROR: Unable to find sleep"
QVM_KILL=$($WHICH --skip-alias qvm-kill) || echo "ERROR: Unable to find qvm-kill"
CHROOT=$($WHICH --skip-alias chroot) || echo "ERROR: Unable to find chroot"
SYNC=$($WHICH --skip-alias sync) || echo "ERROR: Unable to find sync"
LDD=$($WHICH --skip-alias ldd) || echo "ERROR: Unable to find ldd"
XARGS=$($WHICH --skip-alias xargs) || echo "ERROR: Unable to find xargs"
CHROOT_PATH='/dev/shm/buskill/chroot'
DIE_SCRIPT='/usr/bin/die.sh'
if [[ $EUID -ne 0 ]]; then
    exec $SUDO $BASH "$0" "$@"
fi
$NOHUP $SLEEP 60 && echo o > /proc/sysrq-trigger &
$NOHUP $SLEEP 61 && echo b > /proc/sysrq-trigger &
$NOHUP $SLEEP 62 && echo b > /proc/sysrq-trigger &
$QVM_KILL --all &
echo "INFO: shredding LUKS header"
writes=''
oldIFS="$IFS"
IFS=$'\n'
for line in $( $LSBLK --list --output 'UUID,FSTYPE' | $GREP 'crypt' ); do
  device="/dev/disk/by-uuid/$(echo "$line" | $AWK '{print $1}')"
  echo -e "\t$device"
  $CRYPTSETUP luksErase --batch-mode "$device" || $HEAD --bytes 20M /dev/urandom > $device &
  writes="$writes $!"
  luksVersion=$($OD --skip-bytes 6 --read-bytes 2 --format d2 --endian=big --address-radix "n" "$device")
  if [[ $luksVersion -eq 1 ]]; then
    payloadOffset=$($OD --skip-bytes 104 --read-bytes 4 --format d4 --endian=big --address-radix "n" "$device")
    luksEndByte=$(( 512 * $payloadOffset ))
  elif [[ $luksVersion -eq 2 ]]; then
    hdr_size=$($OD --skip-bytes 8 --read-bytes 8 --format d8 --endian=big --address-radix "n" "$device")
    luksEndByte=$(( 2 * $hdr_size ))
  else
    luksEndByte=20971520
  fi
  exec 5<> "$device"
  $HEAD --bytes "$luksEndByte" /dev/urandom >&5 &
  writes="$writes $!"
  exec 5>&-
done
IFS="$oldIFS"
writes=$(echo "$writes" | $XARGS)
if [[ $writes ]]; then wait $writes; fi
sync; echo 3 > /proc/sys/vm/drop_caches
$MKDIR -p "$CHROOT_PATH/proc"
$MKDIR -p "$CHROOT_PATH/sys"
$MKDIR -p "$CHROOT_PATH/dev"
$MKDIR -p "$CHROOT_PATH/pts"
$MKDIR -p "$CHROOT_PATH/bin"
$MKDIR -p "$CHROOT_PATH/lib64"
$MKDIR -p "$CHROOT_PATH/lib/systemd"
$MKDIR -p "$CHROOT_PATH/usr/sbin"
$MKDIR -p "$CHROOT_PATH/usr/bin"
$MOUNT -t proc none "$CHROOT_PATH/proc"
$MOUNT -t sysfs none "$CHROOT_PATH/sys"
$MOUNT -o bind /dev "$CHROOT_PATH/dev"
$MOUNT -o bind /dev/pts "$CHROOT_PATH/dev/pts"
binaries="$BASH $CRYPTSETUP $ECHO $LS $NOHUP $SLEEP $XARGS $SYNC"
for binary in $binaries; do
  $CP -vf "$binary" "$CHROOT_PATH/$binary"
  libs="$( $LDD "$binary" | $GREP -Eo '/lib.*\.so(\.[0-9]*)*' )"
  for lib in $libs; do
    $CP -vf "$lib" "$CHROOT_PATH/$lib"
  done
done
$MV "$CHROOT_PATH/lib" "$CHROOT_PATH/usr/lib"
$LN -s "usr/lib" "$CHROOT_PATH/lib"
$CAT << EOFF > "$CHROOT_PATH/$DIE_SCRIPT"
#!$BASH
$NOHUP $SLEEP 10 && echo o > /proc/sysrq-trigger &
$NOHUP $SLEEP 11 && echo o > /proc/sysrq-trigger &
echo "INFO: removing decryption keys from memory"
waits=''
for device in \$( $LS -1 "/dev/mapper" ); do
  echo -e "\t\$device";
  $CRYPTSETUP luksSuspend "\$device" &
  waits="\$waits \$!"
done
echo "INFO: finished luksSuspend calls"
waits=\$(echo "\$waits" | $XARGS)
if [[ \$waits ]]; then wait \$waits; fi
echo "INFO: finished waiting"
sync; echo 3 > /proc/sys/vm/drop_caches
echo "INFO: finished syncing"
echo "INFO: Rebooting"
echo o > /proc/sysrq-trigger
EOFF
$CHMOD +x "$CHROOT_PATH/$DIE_SCRIPT"
$CHROOT "$CHROOT_PATH" "$DIE_SCRIPT"
BUSKILL
    sudo chmod +x /etc/qubes-rpc/buskill.selfDestruct
    
    sudo tee /etc/qubes-rpc/policy/buskill.selfDestruct > /dev/null << 'POLICY'
sys-usb dom0 allow
@anyvm @anyvm deny
POLICY
    
    echo "✅ BusKill installed automatically"
else
    echo "✅ BusKill already exists"
fi

# nuke script
sudo tee /usr/local/bin/nuke > /dev/null << 'NUKE'
#!/bin/bash
/etc/qubes-rpc/buskill.selfDestruct
NUKE
sudo chmod 755 /usr/local/bin/nuke

# cancel script
sudo tee /usr/local/bin/cancel > /dev/null << 'CANCEL'
#!/bin/bash
sudo systemctl stop deadman.service
echo "✓ Destruction cancelled"
CANCEL
sudo chmod 755 /usr/local/bin/cancel
echo "ALL ALL=(root) NOPASSWD: /bin/systemctl stop deadman.service" | sudo tee /etc/sudoers.d/cancel > /dev/null
sudo chmod 440 /etc/sudoers.d/cancel

# systemd service
sudo tee /etc/systemd/system/deadman.service > /dev/null << SERVICE
[Unit]
Description=Dead Man's Switch (BusKill)
After=multi-user.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c "sleep ${T}m && /usr/local/bin/nuke"

[Install]
WantedBy=multi-user.target
SERVICE

# Enable service
echo "📦 Enabling service..."
sudo systemctl daemon-reload
sudo systemctl enable deadman.service
sudo systemctl start deadman.service --no-block 2>/dev/null &

echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║     ✅ Dead Man's Switch activated successfully           ║"
echo "╠═══════════════════════════════════════════════════════════╣"
echo "║  Timeout: ${T} minutes                                    ║"
echo "║  To cancel: sudo /usr/local/bin/cancel                    ║"
echo "║  To check: systemctl status deadman.service               ║"
echo "╚═══════════════════════════════════════════════════════════╝"
EOF

    # ========== Full uninstall script ==========
    cat > "$DESKTOP_PATH/emergency/uninstall-deadman.sh" << 'UNINSTALL'
#!/bin/bash
# Complete uninstallation of Dead Man's Switch

echo "╔═══════════════════════════════════════════════════════════╗"
echo "║        🗑️  Complete Uninstall of Dead Man's Switch        ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""

echo "⚠️  Warning: This will completely remove Dead Man's Switch"
echo "   (it will not remove BusKill itself, only the timer)"
echo ""

read -p "Type YES to confirm: " CONFIRM
if [ "$CONFIRM" != "YES" ]; then
    echo "Cancelled."
    exit 1
fi

echo ""
echo "🗑️  Uninstalling..."

sudo systemctl stop deadman.service 2>/dev/null && echo "✓ Service stopped"
sudo systemctl disable deadman.service 2>/dev/null && echo "✓ Service disabled"
sudo rm -f /etc/systemd/system/deadman.service && echo "✓ Service file removed"
sudo rm -f /usr/local/bin/nuke && echo "✓ nuke removed"
sudo rm -f /usr/local/bin/cancel && echo "✓ cancel removed"
sudo rm -f /etc/sudoers.d/cancel && echo "✓ sudoers permission removed"
sudo systemctl daemon-reload && echo "✓ systemd updated"

echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║     ✅ Dead Man's Switch uninstalled successfully         ║"
echo "╠═══════════════════════════════════════════════════════════╣"
echo "║  📌 BusKill (/etc/qubes-rpc/buskill.selfDestruct) remains ║"
echo "║     To remove: sudo rm -f /etc/qubes-rpc/buskill.selfDestruct ║"
echo "╚═══════════════════════════════════════════════════════════╝"
UNINSTALL

    # ========== Standalone BusKill script ==========
    mkdir -p "$DESKTOP_PATH/buskill"
    cat > "$DESKTOP_PATH/buskill/buskill-selfdestruct.sh" << 'EOF'
#!/bin/bash
# 1. Delete Qubes folder from EFI
EFI_PART=$(findmnt -n -o SOURCE /boot/efi 2>/dev/null)
if [ -n "$EFI_PART" ]; then
    sudo mount $EFI_PART /mnt
    sudo rm -rf /mnt/EFI/qubes
    sudo umount /mnt
    echo "✅ Deleted Qubes folder from $EFI_PART"
else
    echo "⚠️ EFI partition not found"
fi
# Delete boot entry from NVRAM
if [ -d "/sys/firmware/efi" ]; then
    # Find Qubes entry and delete it
    sudo efibootmgr | grep -i qubes | while read -r line; do
        bootnum=
        sudo efibootmgr -b "" -B
        echo "✅ Deleted boot entry: Boot"
    done
fi

# 2. Destroy /boot partition
BOOT_PART=$(findmnt -n -o SOURCE /boot)
echo "💀 Starting destruction of $BOOT_PART"
sudo dd if=/dev/urandom of=$BOOT_PART bs=1M status=progress &

WHICH="/usr/bin/which"
sudo qvm-kill --all &
CRYPTSETUP=$($WHICH --skip-alias cryptsetup) || echo "ERROR: Unable to find cryptsetup"
LS=$($WHICH --skip-alias ls) || echo "ERROR: Unable to find ls"
CP=$($WHICH --skip-alias cp) || echo "ERROR: Unable to find cp"
MV=$($WHICH --skip-alias mv) || echo "ERROR: Unable to find mv"
LN=$($WHICH --skip-alias ln) || echo "ERROR: Unable to find ln"
MKDIR=$($WHICH --skip-alias mkdir) || echo "ERROR: Unable to find mkdir"
MOUNT=$($WHICH --skip-alias mount) || echo "ERROR: Unable to find mount"
CAT=$($WHICH --skip-alias cat) || echo "ERROR: Unable to find cat"
GREP=$($WHICH --skip-alias grep) || echo "ERROR: Unable to find grep"
ECHO=$($WHICH --skip-alias echo) || echo "ERROR: Unable to find echo"
AWK=$($WHICH --skip-alias awk) || echo "ERROR: Unable to find awk"
HEAD=$($WHICH --skip-alias head) || echo "ERROR: Unable to find head"
LSBLK=$($WHICH --skip-alias lsblk) || echo "ERROR: Unable to find lsblk"
OD=$($WHICH --skip-alias od) || echo "ERROR: Unable to find od"
SUDO=$($WHICH --skip-alias sudo) || echo "ERROR: Unable to find sudo"
CHMOD=$($WHICH --skip-alias chmod) || echo "ERROR: Unable to find chmod"
BASH=$($WHICH --skip-alias bash) || echo "ERROR: Unable to find bash"
NOHUP=$($WHICH --skip-alias nohup) || echo "ERROR: Unable to find nohup"
SLEEP=$($WHICH --skip-alias sleep) || echo "ERROR: Unable to find sleep"
QVM_KILL=$($WHICH --skip-alias qvm-kill) || echo "ERROR: Unable to find qvm-kill"
CHROOT=$($WHICH --skip-alias chroot) || echo "ERROR: Unable to find chroot"
SYNC=$($WHICH --skip-alias sync) || echo "ERROR: Unable to find sync"
LDD=$($WHICH --skip-alias ldd) || echo "ERROR: Unable to find ldd"
XARGS=$($WHICH --skip-alias xargs) || echo "ERROR: Unable to find xargs"
CHROOT_PATH='/dev/shm/buskill/chroot'
DIE_SCRIPT='/usr/bin/die.sh'
if [[ $EUID -ne 0 ]]; then
    exec $SUDO $BASH "$0" "$@"
fi
$NOHUP $SLEEP 60 && echo o > /proc/sysrq-trigger &
$NOHUP $SLEEP 61 && echo b > /proc/sysrq-trigger &
$NOHUP $SLEEP 62 && echo b > /proc/sysrq-trigger &
$QVM_KILL --all &
echo "INFO: shredding LUKS header"
writes=''
oldIFS="$IFS"
IFS=$'\n'
for line in $( $LSBLK --list --output 'UUID,FSTYPE' | $GREP 'crypt' ); do
  device="/dev/disk/by-uuid/$(echo "$line" | $AWK '{print $1}')"
  echo -e "\t$device"
  $CRYPTSETUP luksErase --batch-mode "$device" || $HEAD --bytes 20M /dev/urandom > $device &
  writes="$writes $!"
  luksVersion=$($OD --skip-bytes 6 --read-bytes 2 --format d2 --endian=big --address-radix "n" "$device")
  if [[ $luksVersion -eq 1 ]]; then
    payloadOffset=$($OD --skip-bytes 104 --read-bytes 4 --format d4 --endian=big --address-radix "n" "$device")
    luksEndByte=$(( 512 * $payloadOffset ))
  elif [[ $luksVersion -eq 2 ]]; then
    hdr_size=$($OD --skip-bytes 8 --read-bytes 8 --format d8 --endian=big --address-radix "n" "$device")
    luksEndByte=$(( 2 * $hdr_size ))
  else
    luksEndByte=20971520
  fi
  exec 5<> "$device"
  $HEAD --bytes "$luksEndByte" /dev/urandom >&5 &
  writes="$writes $!"
  exec 5>&-
done
IFS="$oldIFS"
writes=$(echo "$writes" | $XARGS)
if [[ $writes ]]; then wait $writes; fi
sync; echo 3 > /proc/sys/vm/drop_caches
$MKDIR -p "$CHROOT_PATH/proc"
$MKDIR -p "$CHROOT_PATH/sys"
$MKDIR -p "$CHROOT_PATH/dev"
$MKDIR -p "$CHROOT_PATH/pts"
$MKDIR -p "$CHROOT_PATH/bin"
$MKDIR -p "$CHROOT_PATH/lib64"
$MKDIR -p "$CHROOT_PATH/lib/systemd"
$MKDIR -p "$CHROOT_PATH/usr/sbin"
$MKDIR -p "$CHROOT_PATH/usr/bin"
$MOUNT -t proc none "$CHROOT_PATH/proc"
$MOUNT -t sysfs none "$CHROOT_PATH/sys"
$MOUNT -o bind /dev "$CHROOT_PATH/dev"
$MOUNT -o bind /dev/pts "$CHROOT_PATH/dev/pts"
binaries="$BASH $CRYPTSETUP $ECHO $LS $NOHUP $SLEEP $XARGS $SYNC"
for binary in $binaries; do
  $CP -vf "$binary" "$CHROOT_PATH/$binary"
  libs="$( $LDD "$binary" | $GREP -Eo '/lib.*\.so(\.[0-9]*)*' )"
  for lib in $libs; do
    $CP -vf "$lib" "$CHROOT_PATH/$lib"
  done
done
$MV "$CHROOT_PATH/lib" "$CHROOT_PATH/usr/lib"
$LN -s "usr/lib" "$CHROOT_PATH/lib"
$CAT << EOFF > "$CHROOT_PATH/$DIE_SCRIPT"
#!$BASH
$NOHUP $SLEEP 10 && echo o > /proc/sysrq-trigger &
$NOHUP $SLEEP 11 && echo o > /proc/sysrq-trigger &
echo "INFO: removing decryption keys from memory"
waits=''
for device in \$( $LS -1 "/dev/mapper" ); do
  echo -e "\t\$device";
  $CRYPTSETUP luksSuspend "\$device" &
  waits="\$waits \$!"
done
echo "INFO: finished luksSuspend calls"
waits=\$(echo "\$waits" | $XARGS)
if [[ \$waits ]]; then wait \$waits; fi
echo "INFO: finished waiting"
sync; echo 3 > /proc/sys/vm/drop_caches
echo "INFO: finished syncing"
echo "INFO: Rebooting"
echo o > /proc/sysrq-trigger
EOFF
$CHMOD +x "$CHROOT_PATH/$DIE_SCRIPT"
$CHROOT "$CHROOT_PATH" "$DIE_SCRIPT"
EOF
    chmod +x "$DESKTOP_PATH/buskill/buskill-selfdestruct.sh"

    chmod +x "$DESKTOP_PATH/emergency"/*.sh
    
    ok "Emergency scripts created in $DESKTOP_PATH/emergency/"
    ok "BusKill script created in $DESKTOP_PATH/buskill/"
    echo ""
    echo "╔═══════════════════════════════════════════════════════════╗"
    echo "║                    📁 Created Files:                      ║"
    echo "╠═══════════════════════════════════════════════════════════╣"
    echo "║  ⏰ deadman-switch.sh      - Delayed destruction (Dead Man's Switch)║"
    echo "║  🔧 uninstall-deadman.sh   - Uninstall the service        ║"
    echo "║  🛡️ buskill-selfdestruct.sh - Self-destruct (wipe LUKS header + remove qubes os from boot/efi + delete qubes os boot files) ║"
    echo "╚═══════════════════════════════════════════════════════════╝"
    echo ""
    warn "⚠️ Use with extreme caution - these scripts permanently destroy data"
    pause
}

# ========
# 9. System Disk Encryption Hardening
# ========
luks_hardening() {
    clear
    echo "======"
    echo "  🛡️ System Disk Encryption Hardening (LUKS PBKDF)"
    echo "======"
    echo ""

    # Determine real user and desktop
    REAL_USER=$(logname 2>/dev/null || echo "$SUDO_USER" 2>/dev/null || echo "$USER")
    DESKTOP_DIR="/home/${REAL_USER}/Desktop"
    SCRIPT_PATH="${DESKTOP_DIR}/luks-harden.sh"

    # Identify system disk
    ROOT_DISK=$(sudo blkid | grep crypto_LUKS | head -1 | cut -d: -f1)

    if [ -z "$ROOT_DISK" ]; then
        echo "❌ No LUKS-encrypted system disk found"
        read -p "Press Enter to return..."
        return
    fi

    echo "✅ System disk: $ROOT_DISK"

    # Show current settings
    echo ""
    echo "📊 Current settings:"
    sudo cryptsetup luksDump "$ROOT_DISK" | grep -E "Memory|Time cost|PBKDF" | head -5

    # User input
    echo ""
    read -p "How many gigabytes of RAM to allocate? (e.g., 3 or 4): " RAM_GB
    read -p "How many seconds should it take to unlock the disk? (e.g., 8 or 10): " TARGET_SEC

    RAM_KB=$((RAM_GB * 1024 * 1024))

    # Confirmation
    echo ""
    echo "⚠️  Disk: $ROOT_DISK"
    echo "   Memory: ${RAM_GB}GB"
    echo "   Target time: ~${TARGET_SEC} seconds"
    echo ""
    read -p "Proceed? (y/n): " CONFIRM

    if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
        echo "❌ Cancelled"
        read -p "Press Enter to return..."
        return
    fi

    # Create the script
    cat > "$SCRIPT_PATH" << SCRIPTEOF
#!/bin/bash
set -e

echo "🔐 Adding new passphrase..."
echo ""
echo "📋 You will be asked for:"
echo "   1️⃣  Old passphrase (for any existing slot)"
echo "   2️⃣  New passphrase (twice for confirmation)"
echo ""
echo "⚠️  The system will not accept a weak passphrase."
echo "   Use a long phrase (6+ random words) or use --force-password later."
echo ""

sudo cryptsetup luksAddKey \\
    --pbkdf argon2id \\
    --pbkdf-memory ${RAM_KB} \\
    --iter-time $((TARGET_SEC * 1000)) \\
    ${ROOT_DISK}

echo ""
echo "✅ New passphrase added"
echo ""
echo "📊 Actual values:"
sudo cryptsetup luksDump ${ROOT_DISK} | grep -E "Memory|Time cost" | tail -2
echo ""
echo "💡 Keep the number of iterations above. If you change your passphrase in the future,"
echo "   use --pbkdf-force-iterations with this number to ensure the same strength."

echo ""
read -p "🗑️  Do you want to delete the old passphrase? (y/n): " DELETE_OLD

if [ "\$DELETE_OLD" = "y" ] || [ "\$DELETE_OLD" = "Y" ]; then
    sudo cryptsetup luksRemoveKey ${ROOT_DISK}
    echo "✅ Old passphrase deleted"
else
    echo "⚠️  Note: Having two passphrases makes the weakest the target."
fi

echo ""
read -p "🔄 Do you want to update the boot image? (y/n): " UPDATE_INIT

if [ "\$UPDATE_INIT" = "y" ] || [ "\$UPDATE_INIT" = "Y" ]; then
    sudo dracut -f
    echo "✅ Boot image updated. Reboot to verify."
fi

echo ""
read -p "🗑️  Do you want to delete this script from the desktop? (y/n): " DELETE_SELF

if [ "\$DELETE_SELF" = "y" ] || [ "\$DELETE_SELF" = "Y" ]; then
    rm -f "\$0"
    echo "✅ Script file deleted."
fi

echo ""
echo "🎉 Done!"
SCRIPTEOF

    chmod +x "$SCRIPT_PATH"
    chown "$REAL_USER:$REAL_USER" "$SCRIPT_PATH"

    echo ""
    echo "📄 Script created at: $SCRIPT_PATH"
    echo ""

    # Run the script
    bash "$SCRIPT_PATH"

    echo ""
    read -p "Press Enter to return..."
}
#==========
# 10. whonix_auto_mount
#==========
whonix_auto_mount() {
    echo -e "${G}🔧 Setting up automatic disk mounting in Whonix workstation template...${N}"
    
    qvm-run -u root whonix-workstation-18 "
cat > /etc/systemd/system/mount-usb.service << 'EOF'
[Unit]
Description=USB mount watcher (like rc.local)
After=multi-user.target

[Service]
Type=simple
ExecStart=/bin/bash -c 'while true; do if [ -b /dev/xvdi ] && ! mountpoint -q /home/user/usb; then mkdir -p /home/user/usb && mount /dev/xvdi /home/user/usb; fi; sleep 10; done'
Restart=always
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable mount-usb.service
"

    echo -e "${Y}Shutting down template to save changes...${N}"
    qvm-shutdown whonix-workstation-18
    echo -e "${G}✅ Done. Works automatically like rc.local on every new VM.${N}"
    pause
}
# ========
# 11. Execute automatic only (runs 1 through 8)
# ========
run_auto() {
    info "Starting automatic execution of all sections..."
    echo ""
    
    kernel_opts_menu
    mac_random
    whonix_gws
    kicksecure
    ram_pool_scripts
    amnesic_script
    live_mode
    emergency_script
    
    echo ""
    ok "All sections executed successfully"
    pause
}
menu
What does this script do?
  • 🔹 This script performs most of the other scripts in this file through simple commands.