TazPod: Vault Lifecycle Detail
Level 3 (Detail) — Complete vault lifecycle: unlock, lock, save, push, pull.
Concept
The vault is an encrypted tar.gz archive (vault.tar.aes) stored under .tazpod/vault/. It holds all operator secrets (AWS credentials, SSH keys, API tokens) and is only decrypted into a tmpfs RAM mount during active sessions.
Path Constants
File: internal/vault/vault.go (lines 17-25)
| Constant | Path | Purpose |
|---|---|---|
VaultDir | /workspace/.tazpod/vault | Encrypted archive directory |
VaultFile | VaultDir + /vault.tar.aes | Encrypted vault file |
MountPath | /home/tazpod/secrets | RAM mount point (tmpfs) |
AwsLocalHome | /home/tazpod/.aws | AWS credential home |
AwsVaultDir | MountPath + /.aws | AWS creds inside RAM |
PassCache | MountPath + /.vault_pass | Cached passphrase file |
Commands
tazpod unlock
File: cmd/tazpod/vault_cmd.go — unlock() (lines 13-25)
- If
MountPathalready mounted → reuse cached passphrase,SetupIdentity(),setupBindAuth(), return - Mount 64MB tmpfs at
MountPath - If
VaultFileexists:- Read encrypted file
- Prompt for passphrase (3 attempts,
term.ReadPassword) crypto.Decrypt()→vault.Untar()intoMountPath- Cache passphrase to
PassCache
- If
VaultFiledoesn’t exist → new vault: prompt passphrase, cache it SetupIdentity()— create/workspace/.tazpod/subdirssetupBindAuth()— bind-mountMountPath/.aws→~/.aws
tazpod lock
File: cmd/tazpod/vault_cmd.go — lock() (lines 27-34)
- Unmount
~/.aws(AWS bridge) vault.Lock()— unmount tmpfs atMountPath- All secrets in RAM are destroyed
Auto-Lock on Last Shell Exit (v0.3.22)
The final fix for vault auto-lock uses marker files, not process counting. Each interactive shell creates a marker in /tmp/.tazpod-shells/$$; on EXIT it removes its own marker, cleans stale markers for dead PIDs, and locks the vault only when no markers remain.
- first shell exit of N>1 → other markers still exist → no lock
- last shell exit → no markers remain →
tazpod lock - dead shells leave stale markers, but those are removed on the next shell start by checking
/proc/<pid>andcomm == bash
This replaced the earlier experimental fixes:
- v0.3.20:
pgrep-based counting + unconditionalexecInContainer("tazpod lock") - v0.3.21: removed unconditional host-triggered lock
- v0.3.22: marker files under
/tmp/.tazpod-shells/— final verified solution
TD-027 documents the full regression history: deletion of the old unlock→exit shell behavior in commit 7f8e719, host-side lock bug introduced in a9652b3, and multiple failed intermediate counting strategies before the final marker-file approach.
tazpod save
File: cmd/tazpod/vault_cmd.go — save() (lines 36-39)
vault.Save("")ininternal/vault/vault.go- If
MountPathnot mounted → print warning “Vault is not mounted” but returns success (TD-022) - Load cached passphrase (or prompt if missing)
TarDir(MountPath)→ tar.gz bytescrypto.Encrypt()with passphrase- Write to
.tazpod/vault/vault.tar.aes
Returns SHA256 content hash (of the plaintext before encryption) for skip detection.
TD-022: save prints a success message even when the RAM vault is not mounted.
tazpod pull vault
File: cmd/tazpod/sync.go — pullVault() (lines 90-102)
loadVaultAWSCredentials()— read AWS creds from RAM if mounted- Resolves vault file path via
vaultFilePath()— tries/workspace/.tazpod/(container) thencwd/.tazpod/(host) s3.DownloadFile("tazpod/vault/vault.tar.aes", vaultFile)
--index N: tazpod pull vault --index 1 downloads the penultimate history copy from tazpod/vault/history/. Index 0 (default) = latest.
tazpod push vault
File: cmd/tazpod/sync.go — pushVault() (lines 104-117)
- Calls
pushVaultInternal(contentHash) - Resolves vault path via
vaultFilePath()— same dual-path logic - Hash skip:
HeadObjecton latest to comparecontent-sha256metadata → if unchanged, skip all - Upload history copy to
tazpod/vault/history/vault-<TIMESTAMP>.tar.aeswithcontent-sha256metadata - Upload latest to
tazpod/vault/vault.tar.aeswithcontent-sha256metadata (overwrite) - Prune:
ListObjects→ keeps max N copies (default 50),DeleteObjectsoldest
History/retention only on manual push — the sync daemon does not create history copies.
AWS Enclave Bridge
On unlock, the vault’s .aws directory is bind-mounted to ~/.aws:
/home/tazpod/secrets/.aws → /home/tazpod/.aws (mount --bind)
This makes AWS credentials available to all tools (aws, s3, SDKs) while keeping them inside the RAM enclave. On lock, the bind mount is unmounted with sudo umount -l.
Identity Setup
SetupIdentity() creates these persistent directories under /workspace/.tazpod/:
.pi— Pi agent config.omp— OMP state.gemini— Gemini CLI sessions.claude— Claude CLI state.aws— AWS SSO cache (persistent across container recreations).opencode— OpenCode config + data + state
See Also
- Parent hub: tazpod
- Sibling details: Smart Entry Detail, Crypto Detail, S3 Detail, Config Detail
- Topic: Vault Security
- Debt: TD-022 (save silent failure, push/pull cwd-sensitive), TD-021 (bootstrap anchor gap)