TazPod v2.0: Surrendering to Root and the RAM Revolution#
In the world of DevOps and Security Engineering, there is a fine line between a secure architecture and an unusable one. With TazPod v1.0, I had built what looked on paper like a masterpiece of isolation: a “Ghost Mode” that leveraged Linux Namespaces and LUKS devices to make secrets invisible even to concurrent processes in the same container.
Today, with the release of v2.0, I am officially documenting the failure of that approach and the complete rewrite of the system’s core. This is the chronicle of how operational stability won over theoretical paranoia, and how I learned to stop fighting against the root user.
1. The Collapse of “Ghost Mode”: A Post-Mortem#
The ambition of v1.0 was high: use unshare --mount to create a private mount space within the container, where a LUKS volume (vault.img) would be decrypted. The idea was that upon exiting the shell, the namespace would collapse and the secrets would vanish.
Loop Device Instability#
The first sign of structural failure appeared during intensive development sessions. The Linux kernel manages loop devices (files mounted as disks) as global resources. Inside a Docker container—which is already an isolated environment and often “privileged” in a precarious way to allow these operations—managing locks on device mappers proved disastrous.
The error Failed to create loop device or Device or resource busy became a constant. Often, a container that didn’t terminate cleanly left the vault.img file “hanging” on a ghost loop device on the host. This required machine reboots or surgical interventions with losetup -d that broke the workflow.
The Data Loss Event#
The breaking point was a filesystem corruption event. LUKS and ext4 do not like being terminated abruptly. On two separate occasions, a container crash left the encrypted volume in an inconsistent state (“dirty bit”), making a remount impossible.
I lost data. And among those data, I lost precious sessions of Mnemosyne (my AI’s long-term memory), which I had imprudently decided to save inside the vault for “maximum security.” This event forced me to reconsider the entire strategy: a security system that makes data inaccessible to its legitimate owner is a failed system.
2. Surrendering to Root: Threat Analysis#
While struggling to stabilize mount points, I had to face an uncomfortable truth regarding the threat model.
“Ghost Mode” protected secrets from other unprivileged processes. But TazPod runs as a --privileged container to perform mounts. Anyone with root access to the container (or the host) can simply use nsenter to enter the “secret” namespace or perform a RAM dump.
The Isolation Paradox#
I spent weeks building a house of cards with unshare and mount --make-private, only to realize I was protecting secrets from… myself. An attacker capable of compromising the host would have had access to everything anyway.
I therefore decided to change my approach: accept that Root sees everything. Instead of trying to hide data from an omnipotent user via kernel isolation, I decided to reduce the time window and physical surface area where data exists in the clear.
3. v2.0 Architecture: The RAM Vault (tmpfs + AES-GCM)#
The new architecture completely eliminates the dependency on cryptsetup, dm-crypt, and loop devices. We shifted security from the block level (kernel) to the application level (Go) and volatile level (RAM).
Storage: The vault.tar.aes Format#
Instead of an encrypted ext4 filesystem, data at rest is now a simple compressed and encrypted TAR archive.
For encryption, I chose AES-256-GCM (Galois/Counter Mode).
- Why GCM? Unlike CBC (Cipher Block Chaining) mode, GCM offers authenticated encryption. This means the file is not only unreadable but also protected from tampering. If a bit of the encrypted file on disk is corrupted or altered, the decryption phase fails immediately with an authentication error, protecting the integrity of the secrets.
- Key Derivation: I use PBKDF2 with a random salt generated at each save to derive the AES key from the user passphrase.
Runtime: The Volatility of tmpfs#
When the user launches tazpod unlock, the CLI does not touch the disk.
- Mount: A 64MB
tmpfsvolume (RAM Disk) is mounted at/home/tazpod/secrets.// Internal code for volatile mount func mountRAM() { cmd := exec.Command("sudo", "mount", "-t", "tmpfs", "-o", "size=64M,mode=0700,uid=1000,gid=1000", "tmpfs", MountPath) cmd.Run() } - Decrypt & Extract: The
vault.tar.aesfile is read into memory, decrypted on-the-fly, and the resulting TAR stream is unpacked directly into the RAM mount point. - Zero Trace: No temporary files are ever written to the host’s physical disk.
Lifecycle: Pull, Save, Lock#
Persistence management has been completely overhauled to adapt to the ephemeral nature of RAM.
tazpod pull: Downloads secrets from Infisical, writes them to RAM, and immediately triggers an Auto-Save.- Auto-Save: The CLI recursively reads the RAM content, creates a new TAR in memory, encrypts it, and atomically overwrites the
vault.tar.aesfile on disk. tazpod lock(or exit): The final command is brutal and effective:umount /home/tazpod/secrets. Data vanishes instantly. No need for secure overwrites (shred), because the bits never touched the magnetic platters or NAND cells.
4. Developer Experience: Resolving Friction#
Beyond security, v1.0 suffered from usability issues that slowed down my daily workflow.
The Name Collision Problem#
Initially, the container name was hardcoded (tazpod-lab). This prevented working on two projects simultaneously (e.g., tazlab-k8s and blog-src).
I introduced dynamic initialization logic in tazpod init.
// Generating a unique identifier for the project
cwd, _ := os.Getwd()
folderName := filepath.Base(cwd)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
randomSuffix := fmt.Sprintf("%04d", r.Intn(10000))
containerName := fmt.Sprintf("tazpod-%s-%s", folderName, randomSuffix)Now, each project folder has its dedicated container (e.g., tazpod-backend-8492), isolated from others, with its own vault and configuration.
Hot Reloading: Developing the CLI within the CLI#
Developing TazPod using TazPod presented an “Inception” challenge. How to test the new version of the CLI without having to rebuild the entire Docker image (which takes minutes) for every change?
I implemented a Hot Reload workflow:
- Compile the Go binary on the host (
task build). - Copy the binary to
~/.local/bin(for host use). - Inject it directly into the active container:
docker cp bin/tazpod tazpod-lab:/home/tazpod/.local/bin/tazpod
This reduced the feedback cycle from 4 minutes to 3 seconds, allowing me to iterate quickly on encryption and mount logic.
5. Mnemosyne: Memory Outside the Vault#
One of the hardest lessons from v1.0 was the loss of AI sessions. For Mnemosyne, persistence is more important than absolute secrecy. Chats with Gemini contain architectural context, not passwords.
In v2.0, I decided to decouple the AI memory from the secrets vault.
During the setupBindAuth phase, the CLI creates a strategic symlink:
- Host: Logs reside in
/workspace/.tazpod/.gemini(on the host disk, persistent). - Container: Linked to
~/.gemini.
This ensures that even if I destroy the vault or reset the container, the project’s “consciousness” survives. Secrets (API tokens to talk to Gemini) remain in the RAM Vault, but memories are saved on standard disk.
Conclusions: Simplicity is a Security Feature#
TazPod v2.0 is, paradoxically, technologically less advanced than v1.0. It doesn’t use esoteric kernel features, nor does it manipulate network or mount namespaces in creative ways. It’s just an encrypted file and a RAM disk.
However, it is infinitely more robust.
- It doesn’t break if Proxmox has a high load.
- It doesn’t corrupt data if the container crashes.
- It is portable to any Linux system without requiring specific kernel modules for encryption.
I’ve learned that in DevOps, complexity is often technical debt disguised as “best practice.” Reducing the attack surface meant, in this case, reducing the complexity of the architecture. Now my secrets live in a digital soap bubble (RAM): ephemeral, fragile if touched, but perfectly isolated as long as it exists.
The next step? Bringing this philosophy of “resilient simplicity” to the heart of the Kubernetes cluster, where Mnemosyne will find its definitive home.
Technical Chronicle by Taz - Systems Engineering and Zero-Trust Infrastructure.


