This document describes the in-memory staging daemon architecture, stash commands, security features, and configuration options.
suve uses an in-memory daemon process to store staged changes. This provides:
- Fast access: No disk I/O for staging operations
- Persistent state: Survives CLI invocations within the same session
- Security: Memory protection prevents sensitive data from being swapped to disk
- Automatic lifecycle: Daemon starts when needed and stops when empty
┌─────────────────┐ Unix Socket ┌─────────────────┐
│ CLI Process │ ◄──────────────────► │ Daemon Process │
│ (client) │ JSON messages │ (background) │
└─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ In-Memory │
│ State (mlock) │
└─────────────────┘
| Component | Location | Description |
|---|---|---|
| Client | internal/staging/store/agent/internal/client/ |
Connects to daemon via socket |
| Server | internal/staging/store/agent/internal/server/ |
Handles requests, manages state |
| Daemon Runner | internal/staging/store/agent/daemon/runner.go |
Main daemon process loop |
| Launcher | internal/staging/store/agent/daemon/launcher.go |
Starts/stops daemon from CLI |
| Protocol | internal/staging/store/agent/internal/protocol/ |
IPC message format, socket paths |
- Auto-Start: When you run a write command (e.g.,
suve stage param add), the daemon starts automatically if not running - Active: Daemon stores staged changes in memory, responds to CLI requests
- Auto-Shutdown: When all staged changes are cleared (via
apply,reset, orstash push), the daemon shuts down automatically
Write commands (auto-start daemon):
add,edit,delete- Stage changes for creation/modification/deletiontag,untag- Stage tag changesreset <name>#version- Stage a historical version (fetches from AWS)stash pop- Restore changes from file to agent
Read commands (no auto-start, returns "nothing staged" if daemon not running):
status- Show staged changesdiff- Show diff between staged and AWSapply- Apply staged changes to AWS (read first, then write to AWS)reset --all- Unstage all changesreset <name>- Unstage specific entry (no version = just remove from staging)stash push- Save staged changes to file
File-only commands (no daemon interaction):
stash show- Preview stashed file contentsstash drop- Delete stash file
The daemon shuts down automatically when the staging area becomes empty:
| Action | Shutdown Message |
|---|---|
apply completes |
"All changes applied" |
reset --all completes |
"All changes unstaged" |
stash push completes |
"State saved to file" |
The daemon communicates via Unix sockets. Socket location depends on the platform:
| Platform | Socket Path |
|---|---|
| Linux | $XDG_RUNTIME_DIR/suve/{accountID}/{region}/agent.sock |
| Linux (fallback) | /tmp/suve-{uid}/{accountID}/{region}/agent.sock |
| macOS | $TMPDIR/suve/{accountID}/{region}/agent.sock |
| Windows | $LOCALAPPDATA/suve/{accountID}/{region}/agent.sock |
Socket directories are created with mode 0700 for security.
Stash commands allow you to save staged changes to a file for later restoration, similar to git stash.
| Command | Description |
|---|---|
suve stage stash |
Save staged changes to file (alias for stash push) |
suve stage stash push |
Save staged changes from memory to file |
suve stage stash pop |
Restore staged changes from file (deletes file) |
suve stage stash pop --keep |
Restore staged changes from file (keeps file) |
suve stage stash show |
Preview stashed changes without restoring |
suve stage stash drop |
Delete stash file without restoring |
stash push
Agent Memory ──────────────────► File (~/.suve/.../stage.json)
▲ │
│ │
│ stash pop │
└─────────────────────────────────────┘
Save staged changes to file:
# Save and clear from memory (default)
suve stage stash
# Save but keep in memory
suve stage stash push --keep
# Save with encryption (prompts for passphrase)
# Passphrase is prompted interactively in a TTY
# Save with passphrase from stdin (for scripts)
echo "my-passphrase" | suve stage stash push --passphrase-stdinRestore staged changes:
# Restore and delete file
suve stage stash pop
# Restore but keep file
suve stage stash pop --keep
# Overwrite existing memory (no prompt)
suve stage stash pop --overwrite
# Merge with existing memory
suve stage stash pop --merge
# Decrypt with passphrase from stdin
echo "my-passphrase" | suve stage stash pop --passphrase-stdinPreview and delete:
# Preview stashed changes
suve stage stash show
suve stage stash show -v # Verbose mode
# Delete stash file
suve stage stash drop
suve stage stash drop --yes # Skip confirmationYou can stash changes for a specific service:
# Stash only param changes
suve stage param stash
# Stash only secret changes
suve stage secret stash
# Pop only param changes
suve stage param stash popWhen using service-specific stash:
stash pushsaves only that service's changes (other services remain in memory)stash poprestores only that service's changes (other services in file are preserved)
The stash file is stored at ~/.suve/{accountID}/{region}/stage.json:
Unencrypted:
{"version":1,"entries":{...},"tags":{...}}Encrypted: Binary format with SUVE_ENC header, salt, and AES-GCM ciphertext.
When restoring stashed changes with stash pop:
| Scenario | Default Behavior | Options |
|---|---|---|
| Agent memory is empty | Restore directly | N/A |
| Agent has changes | Prompt for action | --overwrite (replace), --merge (combine) |
| File has conflicts | User chooses | Interactive prompt in TTY |
When using --merge:
- File changes are combined with existing memory changes
- For duplicate keys, file values take precedence (newer wins)
The daemon verifies that connecting clients are running as the same user:
| Platform | Mechanism | Description |
|---|---|---|
| Linux | SO_PEERCRED |
Socket option returns peer UID via GetsockoptUcred |
| macOS | LOCAL_PEERCRED |
GetsockoptXucred returns peer UID from xucred structure |
| Windows | Socket ACLs | No peer credentials available; relies on socket file permissions |
This prevents unauthorized processes from accessing your staged secrets.
Note (Windows): Windows AF_UNIX sockets do not support peer credential verification. Security relies on socket file ACLs and directory permissions (
0700).
Sensitive data in daemon memory is protected using the memguard library:
- mlock: Prevents memory from being swapped to disk
- Guard pages: Detects buffer overflows/underflows
- Secure destruction: Uses
memguard.WipeBytes()for cryptographically secure memory zeroing
The daemon prevents sensitive data from leaking via crash dumps:
| Platform | Mechanism | Description |
|---|---|---|
| Linux | prctl(PR_SET_DUMPABLE, 0) |
Disables core dump generation |
| macOS | setrlimit(RLIMIT_CORE, 0) |
Sets core dump size limit to zero |
| Windows | SetErrorMode(SEM_*) |
Disables Windows Error Reporting and minidump generation |
This ensures that even if the daemon crashes, no sensitive data is written to disk.
Stash files can be encrypted with a passphrase:
- Key derivation: Argon2id (memory-hard, resistant to GPU attacks)
- Encryption: AES-256-GCM (authenticated encryption)
- Format: Magic header + version + salt + ciphertext
┌──────────┬─────────┬──────────────┬─────────────────┐
│ SUVE_ENC │ Version │ Salt (32B) │ AES-GCM Payload │
│ (8 bytes)│ (1 byte)│ │ (variable) │
└──────────┴─────────┴──────────────┴─────────────────┘
-
Always use encryption when stashing sensitive data:
suve stage stash # Will prompt for passphrase in TTY -
Clear stashed data when no longer needed:
suve stage stash drop
-
Use
--passphrase-stdinfor automation (avoid shell history):read -s PASS && echo "$PASS" | suve stage stash --passphrase-stdin
-
Socket permissions are automatically set to
0700(owner only)
| Variable | Description | Default |
|---|---|---|
SUVE_DAEMON_MANUAL_MODE |
Set to 1 to disable auto-start and auto-shutdown |
Not set (auto mode) |
By default, the daemon starts and stops automatically. Enable manual mode for:
- Debugging daemon issues
- Keeping daemon running across sessions
- CI/CD environments where you want explicit control
# Enable manual mode
export SUVE_DAEMON_MANUAL_MODE=1
# Start daemon manually
suve stage agent start
# ... perform staging operations ...
# Stop daemon manually
suve stage agent stop| Command | Description |
|---|---|
suve stage agent start |
Start the daemon manually |
suve stage agent stop |
Stop the daemon (warning: unsaved changes are lost) |
| Item | Path |
|---|---|
| Stash file | ~/.suve/{accountID}/{region}/stage.json |
| Socket | Platform-specific (see Socket Paths) |
-
Check if another daemon is running:
suve stage agent stop suve stage agent start
-
Check socket permissions:
ls -la $TMPDIR/suve/ # macOS ls -la $XDG_RUNTIME_DIR/suve/ # Linux
-
Enable manual mode for debugging:
export SUVE_DAEMON_MANUAL_MODE=1 suve stage agent start
If the daemon stopped unexpectedly:
- Check if changes were auto-stashed (unlikely unless you used
stash push) - Staged changes in memory are lost when daemon stops
- Use
suve stage stash pushbefore closing your session to persist changes
- Wrong passphrase: Try again with the correct passphrase
- Corrupted file: The file may be damaged; use
stash dropand re-stage - Automation: Use
--passphrase-stdinfor scripts
For developers working on the staging system:
-
CLAUDE.md files: Each sub-package has its own documentation
internal/staging/CLAUDE.md- Core staging domaininternal/staging/store/CLAUDE.md- Store interfacesinternal/staging/store/agent/CLAUDE.md- Agent storeinternal/staging/store/agent/daemon/CLAUDE.md- Daemon runner/launcherinternal/staging/store/file/CLAUDE.md- File store with encryption
-
State transitions: See Staging State Transitions
-
Testing: E2E tests for daemon IPC are in
e2e/staging_daemon_test.go