Skip to content
Snigdha OSDecentralized

Encrypted Persistence Architecture

LUKS2 storage volumes, Argon2id key derivation, and selective bind mounts.

9 min readUpdated: 2026-11-12

Snigdha Persistence: Subsystem Architecture Specification

Snigdha OS Decentralized — Phase 4 Engineering Architecture
SPDX-License-Identifier: GPL-3.0-or-later


1. Architectural Philosophy and Mission

Snigdha OS Decentralized operates as a strictly amnesic live operating system by default. Volatile root overlays (tmpfs), kernel memory scrubbing (CONFIG_INIT_ON_FREE_DEFAULT_ON=y), storage automount elimination, swap masking, and emergency shutdown media watchdogs collectively guarantee that all session traces vanish on power-off or reboot.

Snigdha Persistence introduces an encrypted, selectively activated persistent storage subsystem that satisfies two seemingly contradictory requirements:

  1. Preserving Amnesia as the Operating System Default: The operating system root filesystem, package databases, system logs, hardware identifiers, browser caches, temporary files, and unselected user configuration remain completely volatile and ephemeral.
  2. Deterministic Selective Durability: Users may opt to retain discrete, explicitly approved data sets (such as personal documents, GnuPG keyrings, or SSH configurations) across reboots inside an authenticated LUKS2 encrypted volume on the verified USB boot medium.

Persistent storage is never automatically unlocked, never automatically mounted, and never bound to internal machine firmware (TPM/FIDO2 in v1). Storage activation requires explicit operator authentication and passphrase presentation in every live session.


2. Component Architecture Overview

The persistence architecture consists of five coordinated tiers:

+-----------------------------------------------------------------------------------+
|                              Desktop Tier (i3wm / X11)                            |
|  - Rofi Control Menu (snigdha-persistence-menu: Super+Shift+P)                    |
|  - Polybar Status Module (snigdha-polybar-persistence)                            |
|  - Thunar Persistent Bookmark (~/Persistent)                                      |
|  - Polkit Authentication Agent (polkit-gnome)                                     |
+-----------------------------------------------------------------------------------+
                                        | (UNIX Socket IPC / D-Bus)
+-----------------------------------------------------------------------------------+
|                     Privileged Daemon Tier (snigdha-persistenced)                |
|  - State Engine (DISABLED, LOCKED, UNLOCKED, ACTIVE, ERROR)                       |
|  - Device Ancestry Verifier (Boot USB matching, internal disk isolation)          |
|  - Cryptographic Coordinator (LUKS2 / Argon2id / dm-crypt)                        |
|  - Mount & Teardown Controller (bind-mount management, safe unmounts)              |
|  - Polkit Authorization Enforcement (org.snigdha.persistence.*)                   |
+-----------------------------------------------------------------------------------+
                                        |
+-----------------------------------------------------------------------------------+
|                         Feature-Based Persistence Engine                          |
|  - Versioned Manifest (/run/snigdha/persistence/metadata/manifest.json)           |
|  - Feature Adapters:                                                              |
|      * persistent-folder -> ~/Persistent (mode 0700, uid 1000)                   |
|      * gnupg             -> ~/.gnupg      (mode 0700, uid 1000, volatile sockets) |
|      * ssh               -> ~/.ssh        (mode 0700, uid 1000, agent volatile)  |
+-----------------------------------------------------------------------------------+
                                        |
+-----------------------------------------------------------------------------------+
|                        Filesystem Tier (ext4 / e2fsprogs)                         |
|  - Mount Point: /run/snigdha/persistence (tmpfs-backed mount parent)              |
|  - Mount Options: nodev, nosuid, noatime                                          |
|  - Pre-mount Integrity Check: e2fsck -p -f                                        |
|  - Volume Label: SNIGDHA_PERSIST                                                  |
+-----------------------------------------------------------------------------------+
                                        |
+-----------------------------------------------------------------------------------+
|                     Cryptographic Storage Tier (LUKS2 / dm-crypt)                 |
|  - Format: LUKS2 (UUID, Subsystem 'snigdha', Label 'SNIGDHA_PERSIST')             |
|  - PBKDF: Argon2id (cross-hardware compatibility bounds: max memory 1 GiB)       |
|  - Cipher: aes-xts-plain64, 512-bit key, SHA-512                                  |
|  - Target Partition: Verified partition on physical boot USB medium               |
+-----------------------------------------------------------------------------------+

3. Storage Discovery & Ancestry Tracing

To prevent accidental modification or unauthorized persistence targets, DeviceInventory enforces a multi-point device ancestry verification algorithm:

  1. Boot Medium Identification: The live boot device is identified by querying the block device backing /run/archiso/bootmnt in /proc/mounts or /run/snigdha/boot-device. Its whole-disk parent device is extracted via sysfs /sys/class/block/<dev>/partition (e.g., sdb1 -> parent sdb).
  2. USB Bus Validation: The parent whole disk is verified to reside on a physical USB bus by traversing sysfs /sys/block/<disk>/device and auditing TRAN == "usb".
  3. Internal Disk Rejection: Any partition or whole disk whose bus transport is SATA, NVMe, ATA, IDE, or SCSI without removable attributes (RM=1 + USB) is unconditionally rejected.
  4. Boot Partition Rejection: The active boot partition (e.g. /dev/sdb1 carrying ArchISO EROFS / squashfs images) is permanently disqualified from formatting or persistent mounting.
  5. Runtime System Partition Rejection: Any device mounted as /, /run/archiso/cowspace, or /run/archiso/airootfs is rejected.
  6. Swap Partition Rejection: Any partition present in /proc/swaps or formatted with fstype == "swap" is rejected.
  7. Dedication to Boot USB: Only partitions residing on the same physical boot USB device are accepted in v1. Candidate partitions with label SNIGDHA_PERSIST are prioritized.

4. Cryptographic Storage & Argon2id Derivation

Persistent volumes are formatted exclusively as LUKS2 containers:

  • Format: LUKS2 (specification compliance, header metadata resilience).
  • PBKDF: Argon2id. Argon2id combines Argon2d (data-dependent memory access resisting GPU cracking) and Argon2i (data-independent memory access resisting side-channel timing attacks).
  • Cipher: aes-xts-plain64, 512-bit key length (256-bit encryption + 256-bit tweak).
  • Hash: SHA-512.
  • Cross-Hardware Memory Compatibility: In cryptsetup, Argon2id benchmarks memory usage based on available RAM at format time. On a 32GB workstation, default benchmarking might select >2GB of RAM cost. To guarantee that persistent storage created on high-memory hardware can be safely unlocked on 4GB RAM laptops without triggering Linux Out-Of-Memory (OOM) killer aborts, Snigdha Persistence bounds the PBKDF memory cost: --pbkdf-memory 1048576 (1 GiB maximum memory cost) and --iter-time 2000 (2.0s target unlock duration).
  • Passphrase Handling: Passphrases are passed to cryptsetup strictly via standard input pipes or protected file descriptors (--key-file -). Passphrases are never passed as command-line arguments (argv), never stored in environment variables, never written to temporary files, and never logged to journald. Mutable memory buffers in Python are zeroed (zero_buffer) immediately after invocation.

5. Filesystem Layout & Metadata Architecture

After unlocking, the dm-crypt mapper /dev/mapper/snigdha-persist is checked with e2fsck -p -f and mounted at /run/snigdha/persistence with nodev, nosuid, noatime.

The internal layout of the persistent filesystem is structured and versioned:

/run/snigdha/persistence/
├── metadata/
│   ├── manifest.json       <-- Versioned persistence manifest (schema 1.0.0)
│   ├── schema-version      <-- Schema version indicator ("1.0.0")
│   └── migration-state     <-- Atomic migration state tracker ("ready")
├── features/
│   ├── persistent-folder/  <-- Payload for ~/Persistent (mode 0700, uid 1000)
│   ├── gnupg/              <-- Payload for ~/.gnupg      (mode 0700, uid 1000)
│   └── ssh/                <-- Payload for ~/.ssh        (mode 0700, uid 1000)
├── backups/                <-- User recovery backups & LUKS header snapshots
└── README.txt              <-- Plaintext security advisory for offline inspectors

The Persistence Manifest (metadata/manifest.json)

The manifest governs which features are permitted to mount. It is strictly validated against an authoritative schema and allowlist:

  • schema_version: Must match 1.0.0.
  • os_distribution: Snigdha OS Decentralized.
  • enabled_features: Array of allowlisted feature IDs (persistent-folder, gnupg, ssh, thunderbird-profile).
  • Any unknown feature ID or path outside the internal feature registry is rejected with InvalidFeatureError. Untrusted paths in the manifest are never passed directly to mount.

6. Feature-Based Persistence Engine

Snigdha Persistence rejects blanket home-directory or /etc persistence. Blanket persistence risks cross-session state contamination, application fingerprinting, and forensic token accumulation.

Each supported feature is encapsulated in a dedicated FeatureAdapter:

Feature 1: Persistent Folder (persistent-folder)

  • Mount Target: /home/snigdha/Persistent
  • Source Payload: /run/snigdha/persistence/features/persistent-folder
  • Ownership & Mode: snigdha:snigdha (1000:1000), 0700.
  • Locked Behavior: An empty directory containing .PERSISTENCE_LOCKED advisory.
  • Unlocked Behavior: Bind-mounted to persistent storage payload. Any documents saved here survive reboot.

Feature 2: GnuPG Keys & Configuration (gnupg)

  • Mount Target: /home/snigdha/.gnupg
  • Source Payload: /run/snigdha/persistence/features/gnupg
  • Ownership & Mode: snigdha:snigdha (1000:1000), 0700 (files 0600).
  • Socket Isolation: Runtime daemon sockets (S.gpg-agent, S.dirmngr) reside in volatile tmpfs under /run/user/1000/gnupg/ and are never written to disk.
  • Status: Disabled by default. Explicit user activation required.

Feature 3: SSH Keys & Configuration (ssh)

  • Mount Target: /home/snigdha/.ssh
  • Source Payload: /run/snigdha/persistence/features/ssh
  • Ownership & Mode: snigdha:snigdha (1000:1000), 0700 (private keys 0600, public keys 0644).
  • Socket Isolation: SSH_AUTH_SOCK is managed dynamically by ssh-agent in /run/user/1000/ and is never persisted.
  • Status: Disabled by default. Explicit user activation required.

7. Shutdown & Teardown Architecture

Proper synchronization and unmounting prior to volatile root overlay destruction is critical:

[User requests Poweroff/Reboot]
                |
                v
[systemd target: final.target]
                |
                v
[snigdha-persistence-teardown.service]  <-- ORDERED STRICTLY BEFORE AMNESIA SHUTDOWN
  1. Deactivate feature bind-mounts (~/.ssh, ~/.gnupg, ~/Persistent)
  2. Flush buffers (sync)
  3. Unmount /run/snigdha/persistence
  4. Flush buffers (sync)
  5. Close dm-crypt mapper: cryptsetup close snigdha-persist
  6. Verify mapper device removal
                |
                v
[snigdha-amnesia-shutdown.service]
  1. Flush filesystem buffers (sync)
  2. Free unreferenced pages (drop_caches -> init_on_free)
  3. Arm /run/initramfs/shutdown pivot
                |
                v
[systemd pivot to initramfs /run/initramfs/shutdown]
  1. Unmount /oldroot recursively
  2. Detach loop devices
  3. Final sync & poweroff -f / reboot -f

8. Emergency Media Removal Watchdog Coordination

The boot medium watchdog daemon (snigdha-media-watchdog) runs with memory locked in RAM (mlockall) and listens for kernel block-device removal uevents via NETLINK_KOBJECT_UEVENT.

Because the persistent partition resides on the boot USB medium, if the USB drive is unplugged while storage is unlocked:

  1. The kernel emits a remove uevent on parent disk sdb and partition sdb2.
  2. snigdha-media-watchdog matches the parent device and immediately triggers an emergency shutdown sequence (systemctl poweroff -ff or sysrq/reboot), preventing unwritten dirty buffers from corrupting the media.

9. Application Sandboxing Boundary Integration (Phase 5)

Snigdha Persistence integrates directly with the Phase 5 Snigdha Isolation subsystem (snigdha-sandbox):

  1. Default Locked State Masking: When persistence is locked, every sandboxed application receives an empty, inaccessible tmpfs over ~/Persistent. The application cannot read, write, or detect persistent contents.
  2. Selective Feature Binding: When persistence is unlocked by the user, only applications declaring "persistent_storage": "selective" in their policy (e.g. Thunar file manager) are granted bind-mount access to authorized persistent folders. Applications declaring "persistent_storage": "denied" remain masked.
  3. Hardware & Mapper Concealment: Raw storage nodes (/dev/sd*, /dev/nvme*) and device-mapper containers (/dev/mapper/*) are denied by AppArmor and excluded from the sandbox's minimal /dev tree. Sandboxed applications cannot interact with cryptsetup or invoke persistence management operations.

10. Snigdha Security Center Integration (Phase 7)

Snigdha Persistence exposes container state and lifecycle operations over the D-Bus system bus via interface org.snigdha.Persistence1 (/org/snigdha/Persistence1):

  • Status Telemetry: GetStatus() reports container detection, lock/unlock status, mapper device, mount point, disk usage, and active feature bindings to Snigdha Security Center.
  • Zero Passphrase Caching: When unlocking via Security Center, the passphrase is sent directly across the system D-Bus connection to snigdha-persistenced, used by cryptsetup, and immediately wiped from memory. The UI never retains passphrases.
  • Polkit Authorization: Mutating calls (Unlock(), Lock(), SetFeatureEnabled()) require authorization under Polkit action org.snigdha.persistence.manage.