dsec - Domain Secrets Manager

Overview

dsec is the primary CLI tool for managing Age-encrypted secrets across multiple domains. It uses opaque domain identifiers (d000, d001, d002…​) to separate personal infrastructure from client/organization secrets.

Location: ~/.secrets/bin/dsec

Architecture Overview

dsec Architecture
Figure 1. dsec Architecture

Domain Model

Domain Structure Purpose

d000

Nested (dev/network, prod/app)

Personal Infrastructure

d001+

Nested or Flat (dev, staging, prod)

Client/Organization work

Categories (Nested Tiers)

Category Contents

app

API keys, OAuth, webhooks, SaaS tokens

infra

Servers, VMs, containers, hypervisor

network

ISE, firewalls, switches, routers, DNS

security

SIEM, vulnerability scanners, threat intel

identity

Azure AD, Okta, LDAP, SSO, MFA

db

PostgreSQL, Redis, MongoDB

cloud

AWS, Azure, GCP, Cloudflare

storage

NAS, S3, backup credentials

registry

Crates.io, PyPI, NPM, Docker Hub

cicd

GitHub Actions, GitLab CI, Jenkins

identifiers

Test device IDs, endpoint MACs, group IDs

Commands

Core Commands

Command Description

dsec list

List all domains and their environments

dsec init <id> <name>

Initialize new domain (e.g., dsec init d001 "Client")

dsec add <id> <tier> <file>

Add/update env file for domain tier

dsec load <id> <tier> [dir]

Decrypt to directory or stdout

dsec unload [dir]

Securely remove decrypted .env (shred -n 10)

dsec show <id> <tier>

Display decrypted env to stdout (for review)

dsec source <id> <tier>

Output export statements for eval

dsec unsource

Output unset statements to clear loaded secrets

dsec edit <id> <tier>

Edit encrypted env file

dsec registry

View/edit domain registry

Utility Commands (v2.0.0+)

Command Description

dsec status

Show overall secrets status (env loaded, domains, last access)

dsec verify

Verify all encrypted files can be decrypted

dsec tree

Display domain/environment tree structure

dsec count

Count secrets across domains

dsec backup [dest]

Backup entire secrets directory to destination

dsec version

Show dsec version and codename

dsec completions [shell]

Generate shell completions (bash/zsh/fish)

Security Commands

Command Description

dsec shell-init [shell]

Output shell wrapper functions (zsh/bash/fish)

dsec set-passphrase

Enable passphrase protection

dsec remove-passphrase

Disable passphrase protection

For maximum security, use shell wrappers instead of raw eval "$(dsec source …​)".

Secrets Lifecycle

Secrets Lifecycle
Figure 2. Secrets Lifecycle - Load, Use, Clear

First-Time Setup

# Add to your shell config
dsec shell-init >> ~/.zshrc       # or ~/.bashrc
source ~/.zshrc

Wrapper Commands

Wrapper Example Description

dsource

dsource d000 dev/network

Load secrets (recommended)

dsunsource

dsunsource

Clear ALL loaded secrets

ds

ds d000 dev/network

Short alias for dsource

dsu

dsu

Short alias for dsunsource

Usage

# Load network secrets (RECOMMENDED)
dsource d000 dev/network

# Load ALL dev secrets
dsource d000 dev

# Clear secrets when done
dsunsource

Domain Access Control

This feature enforces strict boundaries around client/third-party credentials to prevent unauthorized access or accidental disclosure.

Access Control Model

Domain Access Control
Figure 3. Domain Access Control - Home vs Client Permissions

The Simple Rule

Domain What It Is What You Can Access

d000

Your personal infrastructure

EVERYTHING (dev, staging, prod, network, app, etc.)

d001, d002, …​

Client/work domains

All standard tiers (lab, dev, staging, prod)

Why This Exists

Protected credentials are subject to NDAs, contracts, and federal law. The domain access control system:

  • Separates personal and client credential namespaces

  • Logs all access attempts for audit purposes

  • Can be restricted to specific tiers when needed (e.g., DSEC_ALLOWED_TIERS="lab")

Attempting to access a tier not in the allowed list triggers a legal warning and is logged.

Restricting Access

To lock down client domains to specific tiers (e.g., lab-only for a shared machine):

# Restrict client domains to lab tier only
export DSEC_ALLOWED_TIERS="lab"

# Or disable domain locking entirely
export DSEC_DOMAIN_LOCK=false

Quick Reference

# These ALWAYS work (d000 = your stuff)
dsec show d000 dev/network        # ✓
dsec show d000 prod/app           # ✓
dsec edit d000 staging/identity   # ✓

# These work for client domains (all standard tiers)
dsec show d001 dev/network        # ✓
dsec show d001 staging            # ✓
dsec show d001 prod               # ✓
dsec show d001 lab                # ✓

# If DSEC_ALLOWED_TIERS has been restricted to "lab":
dsec show d001 dev                # ✗ BLOCKED
dsec show d001 prod               # ✗ BLOCKED
DSEC_DOMAIN_LOCK=false dsec show d001 prod   # ✓ (override)

Configuration

These environment variables control the behavior:

Variable Default Purpose

DSEC_HOME_DOMAIN

d000

Your home domain (full access always)

DSEC_ALLOWED_TIERS

lab dev staging prod

Allowed tiers for client domains (space-separated)

DSEC_DOMAIN_LOCK

true

Enable/disable the restriction

Audit Trail

All blocked access attempts are logged:

# View security blocks
grep SECURITY ~/.secrets/.metadata/audit.log

Example log entry:

[2026-01-22T02:10:30Z] user@host - SECURITY: Blocked d001/prod - tier 'prod' not in allowed list (lab)

Security Modes

dsec operates in two security modes:

Mode Behavior When to Use

strict (default)

Requires wrapper functions, blocks raw $(dsec source …​)

Always (production)

permissive

Allows eval "$(dsec source …​)" without wrapper

Legacy scripts, debugging

Why Strict Mode?

Running $(dsec source d000 dev) without eval would print all your secrets to the terminal. Strict mode prevents this by requiring the DSEC_EVAL_VERIFIED=true flag that only the wrapper sets.

Bypass Options

# Temporary permissive mode for a session
export DSEC_SECURITY_MODE=permissive

# Bypass passphrase for single command
DSEC_REQUIRE_PASSPHRASE=false eval "$(dsec source d000 dev)"

Passphrase Protection

Add an extra authentication layer before any decrypt operation:

# Enable passphrase (prompts you to set one)
dsec set-passphrase

# Disable passphrase
dsec remove-passphrase

Protection Against

  • Someone with shell access to your machine

  • Malicious scripts trying to read secrets

  • Accidental secret exposure

Bypass for Automation

export DSEC_REQUIRE_PASSPHRASE=false

Directory Structure

~/.secrets/environments/domains/
├── .registry.age           # Encrypted domain→name mapping
├── DOMAIN_INVENTORY.yaml.age  # Schema documentation
├── d000/                    # Personal Infrastructure
│   ├── dev/
│   │   ├── app.env.age        # API keys, OAuth, SaaS
│   │   ├── network.env.age    # ISE, firewalls, switches
│   │   ├── identity.env.age   # Azure AD, Okta, LDAP
│   │   ├── storage.env.age    # NAS, backup
│   │   ├── identifiers.env.age # Test IDs
│   │   └── ...                # infra, security, db, cloud, registry, cicd
│   ├── staging/
│   │   └── (same structure)
│   └── prod/
│       └── (same structure)
├── d001/                    # Client 1
│   ├── dev/
│   │   └── network.env.age
│   ├── staging/
│   │   └── network.env.age
│   └── prod/
│       └── network.env.age
└── d002/                    # Client 2
    └── ...

Examples

Initialize New Client

dsec init d002 "XYZ Corporation"

Add Secrets from Template

# Copy template to RAM
cp ~/.secrets/templates/network.env.example /dev/shm/temp.env

# Edit with your values
vim /dev/shm/temp.env

# Add to domain (encrypts automatically)
dsec add d001 prod/network /dev/shm/temp.env
# Say 'y' to shred source file

Load Secrets to Shell

# Using wrappers (RECOMMENDED)
dsource d000 dev/network
dsource d001 prod

# Legacy syntax (requires permissive mode)
eval "$(dsec source d000 dev/network)"

Clear Secrets

# Using wrapper
dsunsource

# Legacy syntax
eval "$(dsec unsource)"

Edit Secrets

# Personal infrastructure
dsec edit d000 dev/network
dsec edit d000 prod/app

# Client domains
dsec edit d001 dev
dsec edit d001 prod

Load to Project Directory

# Creates .env file in directory
dsec load d001 prod ~/projects/client/

# Clean up when done
dsec unload ~/projects/client/

Multi-Shell Support

dsec automatically detects your shell and outputs appropriate syntax:

Shell Source Syntax Unsource Syntax

zsh (primary)

export VAR=value

unset VAR

fish

set -gx VAR value

set -e VAR

bash

export VAR=value

unset VAR

Override Detection

DSEC_SHELL_TYPE=fish dsec source d000 dev/network

Security Features

Feature Implementation

Temp files

/dev/shm (RAM-based, never on disk)

Cleanup

shred -n 10 (10 overwrite passes)

Backups

Automatic .backup-YYYYMMDD-HHMMSS before edit

Change detection

SHA256 hash comparison

Audit logging

~/.secrets/.metadata/audit.log

Eval protection

Blocks direct TTY output without eval

Passphrase

Optional authentication before decrypt

Env File Format

Files use rest.nvim compatible format with @ prefix:

@VARIABLE_NAME = value
@ANOTHER_VAR = another value

dsec automatically strips the @ when sourcing to shell.

Troubleshooting

Issue Cause Solution

bad pattern

Unquoted glob chars in secrets

Use double quotes: "$(dsec source …​)"

DSEC_EVAL_VERIFIED required

Strict mode active

Use dsource wrapper or set permissive mode

Passphrase prompt

Passphrase protection enabled

Enter passphrase or set DSEC_REQUIRE_PASSPHRASE=false

Variables empty after load

Wrong shell detected

Override with DSEC_SHELL_TYPE=zsh

Secrets visible

Used show instead of source

Use dsec source or dsource wrapper

Templates

Templates are located in ~/.secrets/templates/ and provide starting points for new secrets.

Network Template

The network.env.example template includes placeholders for:

Section Services

Cisco ISE

ERS API, OpenAPI, MnT, pxGrid, DataConnect, mTLS

Switches

Cisco IOS-XE (3560-CX, 3850, 9300, 9500)

WLC

Cisco 9800 RESTCONF API and SSH

Firewalls

pfSense, Palo Alto, Cisco FMC/FTD

IPAM/Network Mgmt

Infoblox WAPI, Cisco DNA Center, Cisco Prime Infrastructure

netapi CLI aliases

DNAC_HOST, PRIME_HOST, FMC_USER, INFOBLOX_HOST

Using Templates

# Copy template to RAM
cp ~/.secrets/templates/network.env.example /dev/shm/temp.env

# Edit with your values
vim /dev/shm/temp.env

# Add to domain (auto-encrypts and shreds)
dsec add d001 dev/network /dev/shm/temp.env

netapi CLI Integration

Network secrets include aliases that map to what netapi CLI expects:

# These are set automatically when you load network secrets
DNAC_HOST={{DNAC_IP}}
PRIME_HOST={{PRIME_IP}}
FMC_USER={{FMC_API_USER}}
INFOBLOX_HOST={{INFOBLOX_GM_IP}}

After loading secrets, netapi commands work directly:

dsource d000 dev/network
netapi dnac get-devices
netapi prime get-alarms
netapi fmc get-policies
netapi infoblox get-networks