The configuration layers system provides hierarchical config resolution, allowing settings to be overridden at different levels—from project-specific to machine-specific to user defaults—without modifying core files.
# Initialize machine-specific config
blackdot config init machine my-laptop
# Set a machine-specific value
blackdot config set machine vault.backend 1password
# View where a setting comes from
blackdot config show vault.backend
# See merged config from all layers
blackdot config mergedSettings resolve from highest to lowest priority:
flowchart TB
subgraph Resolution["Configuration Resolution"]
direction TB
Q[/"blackdot config get vault.backend"/]
Q --> E{{"1. Environment<br/>BLACKDOT_VAULT_BACKEND"}}
E -->|not set| P{{"2. Project<br/>.blackdot.json"}}
P -->|not set| M{{"3. Machine<br/>~/.config/blackdot/machine.json"}}
M -->|not set| U{{"4. User<br/>~/.config/blackdot/config.json"}}
U -->|not set| D{{"5. Default<br/>Built-in"}}
E -->|"found"| R1[/"Return value"/]
P -->|"found"| R2[/"Return value"/]
M -->|"found"| R3[/"Return value"/]
U -->|"found"| R4[/"Return value"/]
D --> R5[/"Return default"/]
end
style E fill:#ff6b6b,color:#fff
style P fill:#feca57,color:#000
style M fill:#48dbfb,color:#000
style U fill:#1dd1a1,color:#000
style D fill:#c8d6e5,color:#000
Priority order: Environment → Project → Machine → User → Default
| Priority | Layer | Location | Scope |
|---|---|---|---|
| 1 (highest) | Session | BLACKDOT_* env vars |
Current shell only |
| 2 | Project | .blackdot.json in repo |
This project |
| 3 | Machine | ~/.config/blackdot/machine.json |
This computer |
| 4 | User | ~/.config/blackdot/config.json |
All machines |
| 5 (lowest) | Default | Built into CLI | Fallback |
| Layer | Location | Git Tracked | Purpose |
|---|---|---|---|
| Session | Environment | N/A | Temporary overrides |
| Project | .blackdot.json (project root) |
Yes | Project-specific settings |
| Machine | ~/.config/blackdot/machine.json |
No | Machine-specific settings |
| User | ~/.config/blackdot/config.json |
No | User preferences |
| Defaults | internal/config/config.go |
Yes | Built-in defaults |
Get a configuration value with layered resolution.
blackdot config get vault.backend
# Output: bitwarden
blackdot config get shell.theme "default"
# Output: default (if not set)Set a configuration value in a specific layer.
# Set in user config
blackdot config set user vault.backend bitwarden
# Set in machine config
blackdot config set machine vault.backend 1password
# Set in project config (current directory)
blackdot config set project features.vault falseShow the value from all layers to understand where settings come from.
blackdot config show vault.backendOutput:
Configuration layers for: vault.backend
env: (not set)
project: (not set)
machine: 1password
user: bitwarden
resolved: 1password
Show all layer locations and their status.
blackdot config listOutput:
Configuration Layers
═══════════════════════════════════════════════════════════════
Layer Locations:
───────────────────────────────────────────────────────────────
env: BLACKDOT_* environment variables
project: .blackdot.json (not found in current directory)
machine: ~/.config/blackdot/machine.json ✓
user: ~/.config/blackdot/config.json ✓
Priority: env > project > machine > user > default
Show the merged configuration from all layers.
blackdot config mergedInitialize a configuration layer.
# Initialize machine config
blackdot config init machine work-macbook
# Initialize project config in current directory
blackdot config init projectOpen a config file in your editor.
# Edit user config (default)
blackdot config edit
# Edit machine config
blackdot config edit machine
# Edit project config
blackdot config edit projectMachine-specific settings that don't travel between computers:
{
"version": 1,
"machine_id": "work-macbook-2024",
"vault": {
"backend": "1password"
},
"paths": {
"workspace": "~/Code",
"blackdot_dir": "~/Code/blackdot"
},
"features": {
"claude_integration": false
},
"packages": {
"tier": "full",
"extras": ["docker", "kubernetes-cli"]
},
"shell": {
"theme": "work",
"prompt_context": "work"
}
}Project-specific settings that travel with the repository:
{
"version": 1,
"features": {
"vault": false,
"templates": true
},
"shell": {
"auto_activate_venv": true,
"node_version_file": ".nvmrc"
},
"hooks": {
"directory_change": [
{ "command": "source .env", "if_exists": ".env" }
]
},
"aliases": {
"dev": "npm run dev",
"test": "npm test"
}
}Default preferences for all machines:
{
"version": 3,
"vault": {
"backend": "bitwarden",
"auto_sync": false
},
"features": {
"vault": true,
"workspace_symlink": true
},
"setup": {
"completed": ["symlinks", "packages"]
}
}Configure different vault backends for different machines:
# On work laptop
blackdot config init machine work-macbook
blackdot config set machine vault.backend 1password
blackdot config set machine features.claude_integration false
# On personal desktop
blackdot config init machine home-desktop
blackdot config set machine vault.backend bitwarden
blackdot config set machine packages.tier fullSet up auto-activation and aliases for a specific project:
cd ~/projects/webapp
blackdot config init project
# The .blackdot.json file is created
# Edit to add project-specific aliases and settingsOverride settings for the current session only:
# Override vault backend for this session
export BLACKDOT_VAULT_BACKEND=pass
blackdot vault pull # Uses pass instead of configured backend
# Override feature for testing
export BLACKDOT_FEATURES_VAULT=false
blackdot doctorUse environment variables to configure behavior in CI:
# In CI pipeline
export BLACKDOT_VAULT_BACKEND=none
export BLACKDOT_FEATURES_TEMPLATES=false
./install.shAny configuration key can be overridden via environment variable:
| Config Key | Environment Variable |
|---|---|
vault.backend |
BLACKDOT_VAULT_BACKEND |
features.vault |
BLACKDOT_FEATURES_VAULT |
shell.theme |
BLACKDOT_SHELL_THEME |
packages.tier |
BLACKDOT_PACKAGES_TIER |
Pattern: config.key.name → BLACKDOT_CONFIG_KEY_NAME
The blackdot system distinguishes between state (what happened) and configuration (what should happen):
| Data Type | Example | Layer-Aware | Why |
|---|---|---|---|
| Setup state | setup.completed[] |
No | Machine reality doesn't vary |
| Preferences | vault.backend |
Yes | Can differ by machine |
| Feature state | features.vault |
Yes | Can be overridden per-project |
State management (setup wizard state in internal/cli/setup.go) always uses direct access to track what has happened on this specific machine. Configuration layers handle preferences that can vary.
The Feature Registry uses configuration layers to resolve feature state:
# Default: claude_integration disabled
# User config: features.claude_integration = true
# Machine config: features.claude_integration = false (work laptop)
# Result: claude_integration is DISABLED on this machine
blackdot features status claude_integration
# Shows: disabled (via machine config)| Setting Type | Recommended Layer | Reason |
|---|---|---|
| Default preferences | User | Applies everywhere |
| Work-specific settings | Machine | Stays on work machine |
| Project dependencies | Project | Travels with code |
| Temporary testing | Environment | Session-only |
- Project configs are git-tracked – Don't put secrets in
.blackdot.json - Machine configs are local – Safe for machine-specific settings
- Environment vars are transient – Good for CI/CD overrides
- Secrets – Use the Vault System for secrets
When a setting isn't what you expect:
# See all layers for a setting
blackdot config show vault.backend
# Check environment
env | grep BLACKDOT_
# See merged result
blackdot config merged | jq '.vault'# Check layer resolution
blackdot config show <key>
# A higher-priority layer may be overriding# Project config must be named .blackdot.json
# Must be in current directory or parent
ls -la .blackdot.json# Initialize machine config first
blackdot config init machine $(hostname -s)- Feature Registry - Feature enable/disable system
- State Management - Setup wizard state tracking
- CLI Reference - All blackdot commands