detached 5c255ed

docs(wiki): add comprehensive SOPS secrets guide

Add dedicated guide covering SOPS installation with age/GPG,
creating encrypted secrets files, Gordon configuration,
environment file integration, container setup, team workflows
with key management, and troubleshooting common issues.
commit
5c255ed8715448513bce0ac34cead4ad2e605d31
author
brice · · 2026-01-16 06:39 UTC
committer
brice · · 2026-01-16 06:44 UTC
parent
dd108b6
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +430 -1
M README.md +1 -1
@@ -44,7 +44,7 @@ Full documentation is available at **[gordon.bnema.dev](https://gordon.bnema.dev
4444
4545 ```bash
4646 # Install Gordon
47-curl -fsSL https://gordon.bnema.dev/install | sh
47+curl -fsSL https://gordon.bnema.dev/install.sh | sh
4848 # Or: curl -fsSL https://raw.githubusercontent.com/bnema/gordon/main/install.sh | sh
4949
5050 # Start Gordon (generates config on first run)
@@ -44,7 +44,7 @@ Full documentation is available at **[gordon.bnema.dev](https://gordon.bnema.dev
44 44
45 ```bash45 ```bash
46 # Install Gordon46 # Install Gordon
47-curl -fsSL https://gordon.bnema.dev/install | sh47+curl -fsSL https://gordon.bnema.dev/install.sh | sh
48 # Or: curl -fsSL https://raw.githubusercontent.com/bnema/gordon/main/install.sh | sh48 # Or: curl -fsSL https://raw.githubusercontent.com/bnema/gordon/main/install.sh | sh
49 49
50 # Start Gordon (generates config on first run)50 # Start Gordon (generates config on first run)
A wiki/guides/secrets-sops.md +429 -0
@@ -0,0 +1,429 @@
1+# Using SOPS for Secrets
2+
3+Set up Mozilla SOPS as Gordon's secrets backend for encrypted file-based secret storage.
4+
5+## What You'll Learn
6+
7+- Installing SOPS and age (or GPG)
8+- Creating encrypted secrets files
9+- Configuring Gordon to use SOPS
10+- Using secrets in environment files
11+- Team workflows and key rotation
12+
13+## Prerequisites
14+
15+- Linux or macOS system
16+- Basic understanding of encryption concepts
17+
18+## Why SOPS?
19+
20+SOPS (Secrets OPerationS) encrypts specific values in YAML/JSON files while keeping keys readable:
21+
22+- **Git-friendly**: Encrypted files can be version controlled
23+- **Multiple backends**: age, GPG, AWS KMS, GCP KMS, Azure Key Vault
24+- **Selective encryption**: Only values are encrypted, keys remain visible
25+- **Team support**: Multiple recipients can decrypt the same file
26+
27+## Installation
28+
29+### SOPS
30+
31+```bash
32+# Ubuntu/Debian
33+curl -LO https://github.com/getsops/sops/releases/download/v3.9.0/sops-v3.9.0.linux.amd64
34+sudo mv sops-v3.9.0.linux.amd64 /usr/local/bin/sops
35+sudo chmod +x /usr/local/bin/sops
36+
37+# macOS
38+brew install sops
39+
40+# Arch Linux
41+sudo pacman -S sops
42+```
43+
44+### age (Recommended)
45+
46+age is simpler than GPG and recommended for new setups:
47+
48+```bash
49+# Ubuntu/Debian
50+sudo apt install age
51+
52+# macOS
53+brew install age
54+
55+# Arch Linux
56+sudo pacman -S age
57+```
58+
59+### GPG (Alternative)
60+
61+```bash
62+# Ubuntu/Debian
63+sudo apt install gnupg
64+
65+# macOS
66+brew install gnupg
67+```
68+
69+## Setup with age
70+
71+### 1. Generate an age Key
72+
73+```bash
74+# Generate key pair
75+age-keygen -o ~/.config/sops/age/keys.txt
76+
77+# Output shows your public key:
78+# Public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
79+```
80+
81+Note your public key - you'll need it for encryption.
82+
83+### 2. Configure SOPS
84+
85+Create `.sops.yaml` in your project or home directory:
86+
87+```yaml
88+creation_rules:
89+ - age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
90+```
91+
92+Or for multiple recipients:
93+
94+```yaml
95+creation_rules:
96+ - age: >-
97+ age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
98+ age1yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
99+```
100+
101+### 3. Set age Key Location
102+
103+```bash
104+# Add to ~/.bashrc or ~/.zshrc
105+export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
106+```
107+
108+## Setup with GPG
109+
110+### 1. Generate or Use Existing GPG Key
111+
112+```bash
113+# Generate new key
114+gpg --gen-key
115+
116+# List keys to get fingerprint
117+gpg --list-keys
118+```
119+
120+### 2. Configure SOPS
121+
122+```yaml
123+creation_rules:
124+ - pgp: ABCD1234EFGH5678... # Your GPG fingerprint
125+```
126+
127+## Creating Encrypted Secrets
128+
129+### Create secrets.yaml
130+
131+```bash
132+# Create and encrypt in one step
133+sops edit secrets.yaml
134+```
135+
136+This opens your editor. Add secrets in YAML format:
137+
138+```yaml
139+registry:
140+ password_hash: "$2y$10$..."
141+ token_secret: "your-random-32-char-string-here"
142+database:
143+ password: "db-password-here"
144+api:
145+ key: "api-key-here"
146+```
147+
148+Save and exit. SOPS encrypts the values automatically.
149+
150+### View Encrypted File
151+
152+```bash
153+cat secrets.yaml
154+```
155+
156+Output shows encrypted values:
157+
158+```yaml
159+registry:
160+ password_hash: ENC[AES256_GCM,data:...,iv:...,tag:...]
161+ token_secret: ENC[AES256_GCM,data:...,iv:...,tag:...]
162+database:
163+ password: ENC[AES256_GCM,data:...,iv:...,tag:...]
164+api:
165+ key: ENC[AES256_GCM,data:...,iv:...,tag:...]
166+sops:
167+ age:
168+ - recipient: age1xxx...
169+ enc: |
170+ -----BEGIN AGE ENCRYPTED FILE-----
171+ ...
172+ -----END AGE ENCRYPTED FILE-----
173+ lastmodified: "2024-01-15T12:00:00Z"
174+ version: 3.9.0
175+```
176+
177+### Decrypt and View
178+
179+```bash
180+# View entire file
181+sops decrypt secrets.yaml
182+
183+# Extract specific value
184+sops decrypt --extract '["registry"]["token_secret"]' secrets.yaml
185+```
186+
187+### Edit Existing File
188+
189+```bash
190+sops edit secrets.yaml
191+```
192+
193+## Gordon Configuration
194+
195+### Enable SOPS Backend
196+
197+```toml
198+[secrets]
199+backend = "sops"
200+
201+[registry_auth]
202+enabled = true
203+type = "token"
204+username = "admin"
205+password_hash = "secrets.yaml:registry.password_hash"
206+token_secret = "secrets.yaml:registry.token_secret"
207+```
208+
209+The path format is `file:key.path` where:
210+- `file` is the SOPS-encrypted file path
211+- `key.path` is dot-notation to the value
212+
213+### Using Secrets in Environment Files
214+
215+Reference SOPS secrets in your app's environment files:
216+
217+```bash
218+# ~/.gordon/env/app_mydomain_com.env
219+DATABASE_URL=postgresql://user:${sops:secrets.yaml:database.password}@postgres:5432/app
220+API_KEY=${sops:secrets.yaml:api.key}
221+```
222+
223+## File Organization
224+
225+Recommended structure:
226+
227+```
228+project/
229+├── .sops.yaml # SOPS configuration
230+├── secrets.yaml # Encrypted secrets (commit this)
231+├── secrets.dev.yaml # Dev environment secrets
232+├── secrets.prod.yaml # Production secrets
233+└── gordon.toml # Gordon config (references secrets)
234+```
235+
236+### Environment-Specific Rules
237+
238+```yaml
239+# .sops.yaml
240+creation_rules:
241+ # Dev secrets - developer keys
242+ - path_regex: \.dev\.yaml$
243+ age: age1devkey...
244+
245+ # Prod secrets - production keys only
246+ - path_regex: \.prod\.yaml$
247+ age: age1prodkey...
248+
249+ # Default
250+ - age: age1defaultkey...
251+```
252+
253+## Running in Containers
254+
255+### Custom Dockerfile
256+
257+```dockerfile
258+FROM ghcr.io/bnema/gordon:latest
259+
260+USER root
261+RUN apk add --no-cache sops age
262+USER gordon
263+```
264+
265+### Mount Secrets and Keys
266+
267+```bash
268+docker run -d \
269+ --name gordon \
270+ -p 80:8080 \
271+ -p 5000:5000 \
272+ -v /var/run/docker.sock:/var/run/docker.sock \
273+ -v gordon-data:/data \
274+ -v $(pwd)/secrets.yaml:/app/secrets.yaml:ro \
275+ -v $(pwd)/.sops.yaml:/app/.sops.yaml:ro \
276+ -v ~/.config/sops/age/keys.txt:/home/gordon/.config/sops/age/keys.txt:ro \
277+ -e SOPS_AGE_KEY_FILE=/home/gordon/.config/sops/age/keys.txt \
278+ -v $(pwd)/gordon.toml:/etc/gordon/gordon.toml:ro \
279+ gordon-with-sops
280+```
281+
282+### Docker Compose
283+
284+```yaml
285+services:
286+ gordon:
287+ build:
288+ context: .
289+ dockerfile: Dockerfile.gordon
290+ container_name: gordon
291+ restart: unless-stopped
292+ ports:
293+ - "80:8080"
294+ - "5000:5000"
295+ volumes:
296+ - /var/run/docker.sock:/var/run/docker.sock
297+ - gordon-data:/data
298+ - ./gordon.toml:/etc/gordon/gordon.toml:ro
299+ - ./secrets.yaml:/app/secrets.yaml:ro
300+ - ./.sops.yaml:/app/.sops.yaml:ro
301+ - ~/.config/sops/age/keys.txt:/home/gordon/.config/sops/age/keys.txt:ro
302+ environment:
303+ - SOPS_AGE_KEY_FILE=/home/gordon/.config/sops/age/keys.txt
304+ - GORDON_SECRETS_BACKEND=sops
305+
306+volumes:
307+ gordon-data:
308+```
309+
310+## Team Workflows
311+
312+### Adding Team Members
313+
314+1. Get their age public key
315+2. Add to `.sops.yaml`:
316+
317+```yaml
318+creation_rules:
319+ - age: >-
320+ age1yourkey...,
321+ age1teammemberkey...
322+```
323+
324+3. Re-encrypt existing files:
325+
326+```bash
327+sops updatekeys secrets.yaml
328+```
329+
330+### Key Rotation
331+
332+1. Generate new key
333+2. Update `.sops.yaml`
334+3. Re-encrypt all files:
335+
336+```bash
337+sops updatekeys secrets.yaml
338+sops updatekeys secrets.prod.yaml
339+```
340+
341+### Git Workflow
342+
343+Encrypted files are safe to commit:
344+
345+```bash
346+git add .sops.yaml secrets.yaml
347+git commit -m "Add encrypted secrets"
348+git push
349+```
350+
351+Team members with valid keys can decrypt after pulling.
352+
353+## Troubleshooting
354+
355+### "failed to get the data key"
356+
357+Your key isn't in the recipients list or key file is missing:
358+
359+```bash
360+# Check if age key is set
361+echo $SOPS_AGE_KEY_FILE
362+cat $SOPS_AGE_KEY_FILE
363+
364+# Check recipients in encrypted file
365+grep -A5 "age:" secrets.yaml
366+```
367+
368+### "sops: command not found"
369+
370+Install SOPS:
371+
372+```bash
373+# Check if installed
374+which sops
375+
376+# Install if missing (see Installation section)
377+```
378+
379+### "could not decrypt data key"
380+
381+Wrong key or corrupted file:
382+
383+```bash
384+# Verify your public key matches a recipient
385+age-keygen -y ~/.config/sops/age/keys.txt
386+# Compare output to recipients in .sops.yaml
387+```
388+
389+### "no matching creation_rules"
390+
391+Create or fix `.sops.yaml`:
392+
393+```bash
394+# Check if .sops.yaml exists
395+cat .sops.yaml
396+
397+# Ensure path_regex matches your file
398+```
399+
400+### Permission Denied in Container
401+
402+Ensure key file is readable:
403+
404+```bash
405+docker exec gordon cat /home/gordon/.config/sops/age/keys.txt
406+```
407+
408+Check mount permissions:
409+
410+```bash
411+ls -la ~/.config/sops/age/keys.txt
412+# Should be readable by container user
413+```
414+
415+## Security Best Practices
416+
417+1. **Never commit unencrypted secrets**: Always use `sops edit`, never edit decrypted files directly
418+2. **Protect age/GPG keys**: Store private keys securely, never commit them
419+3. **Use separate keys per environment**: Production keys should be isolated
420+4. **Rotate keys regularly**: Update keys and re-encrypt periodically
421+5. **Audit access**: Track who has decryption keys for each environment
422+6. **Use .sops.yaml**: Enforce encryption rules via configuration
423+
424+## Related
425+
426+- [Secrets Configuration](/docs/config/secrets.md)
427+- [Registry Authentication](/docs/config/registry-auth.md)
428+- [Running in Containers](/wiki/guides/running-in-container.md)
429+- [Using Pass for Secrets](./secrets-pass.md)
@@ -0,0 +1,429 @@
1+# Using SOPS for Secrets
2+
3+Set up Mozilla SOPS as Gordon's secrets backend for encrypted file-based secret storage.
4+
5+## What You'll Learn
6+
7+- Installing SOPS and age (or GPG)
8+- Creating encrypted secrets files
9+- Configuring Gordon to use SOPS
10+- Using secrets in environment files
11+- Team workflows and key rotation
12+
13+## Prerequisites
14+
15+- Linux or macOS system
16+- Basic understanding of encryption concepts
17+
18+## Why SOPS?
19+
20+SOPS (Secrets OPerationS) encrypts specific values in YAML/JSON files while keeping keys readable:
21+
22+- **Git-friendly**: Encrypted files can be version controlled
23+- **Multiple backends**: age, GPG, AWS KMS, GCP KMS, Azure Key Vault
24+- **Selective encryption**: Only values are encrypted, keys remain visible
25+- **Team support**: Multiple recipients can decrypt the same file
26+
27+## Installation
28+
29+### SOPS
30+
31+```bash
32+# Ubuntu/Debian
33+curl -LO https://github.com/getsops/sops/releases/download/v3.9.0/sops-v3.9.0.linux.amd64
34+sudo mv sops-v3.9.0.linux.amd64 /usr/local/bin/sops
35+sudo chmod +x /usr/local/bin/sops
36+
37+# macOS
38+brew install sops
39+
40+# Arch Linux
41+sudo pacman -S sops
42+```
43+
44+### age (Recommended)
45+
46+age is simpler than GPG and recommended for new setups:
47+
48+```bash
49+# Ubuntu/Debian
50+sudo apt install age
51+
52+# macOS
53+brew install age
54+
55+# Arch Linux
56+sudo pacman -S age
57+```
58+
59+### GPG (Alternative)
60+
61+```bash
62+# Ubuntu/Debian
63+sudo apt install gnupg
64+
65+# macOS
66+brew install gnupg
67+```
68+
69+## Setup with age
70+
71+### 1. Generate an age Key
72+
73+```bash
74+# Generate key pair
75+age-keygen -o ~/.config/sops/age/keys.txt
76+
77+# Output shows your public key:
78+# Public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
79+```
80+
81+Note your public key - you'll need it for encryption.
82+
83+### 2. Configure SOPS
84+
85+Create `.sops.yaml` in your project or home directory:
86+
87+```yaml
88+creation_rules:
89+ - age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
90+```
91+
92+Or for multiple recipients:
93+
94+```yaml
95+creation_rules:
96+ - age: >-
97+ age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
98+ age1yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
99+```
100+
101+### 3. Set age Key Location
102+
103+```bash
104+# Add to ~/.bashrc or ~/.zshrc
105+export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
106+```
107+
108+## Setup with GPG
109+
110+### 1. Generate or Use Existing GPG Key
111+
112+```bash
113+# Generate new key
114+gpg --gen-key
115+
116+# List keys to get fingerprint
117+gpg --list-keys
118+```
119+
120+### 2. Configure SOPS
121+
122+```yaml
123+creation_rules:
124+ - pgp: ABCD1234EFGH5678... # Your GPG fingerprint
125+```
126+
127+## Creating Encrypted Secrets
128+
129+### Create secrets.yaml
130+
131+```bash
132+# Create and encrypt in one step
133+sops edit secrets.yaml
134+```
135+
136+This opens your editor. Add secrets in YAML format:
137+
138+```yaml
139+registry:
140+ password_hash: "$2y$10$..."
141+ token_secret: "your-random-32-char-string-here"
142+database:
143+ password: "db-password-here"
144+api:
145+ key: "api-key-here"
146+```
147+
148+Save and exit. SOPS encrypts the values automatically.
149+
150+### View Encrypted File
151+
152+```bash
153+cat secrets.yaml
154+```
155+
156+Output shows encrypted values:
157+
158+```yaml
159+registry:
160+ password_hash: ENC[AES256_GCM,data:...,iv:...,tag:...]
161+ token_secret: ENC[AES256_GCM,data:...,iv:...,tag:...]
162+database:
163+ password: ENC[AES256_GCM,data:...,iv:...,tag:...]
164+api:
165+ key: ENC[AES256_GCM,data:...,iv:...,tag:...]
166+sops:
167+ age:
168+ - recipient: age1xxx...
169+ enc: |
170+ -----BEGIN AGE ENCRYPTED FILE-----
171+ ...
172+ -----END AGE ENCRYPTED FILE-----
173+ lastmodified: "2024-01-15T12:00:00Z"
174+ version: 3.9.0
175+```
176+
177+### Decrypt and View
178+
179+```bash
180+# View entire file
181+sops decrypt secrets.yaml
182+
183+# Extract specific value
184+sops decrypt --extract '["registry"]["token_secret"]' secrets.yaml
185+```
186+
187+### Edit Existing File
188+
189+```bash
190+sops edit secrets.yaml
191+```
192+
193+## Gordon Configuration
194+
195+### Enable SOPS Backend
196+
197+```toml
198+[secrets]
199+backend = "sops"
200+
201+[registry_auth]
202+enabled = true
203+type = "token"
204+username = "admin"
205+password_hash = "secrets.yaml:registry.password_hash"
206+token_secret = "secrets.yaml:registry.token_secret"
207+```
208+
209+The path format is `file:key.path` where:
210+- `file` is the SOPS-encrypted file path
211+- `key.path` is dot-notation to the value
212+
213+### Using Secrets in Environment Files
214+
215+Reference SOPS secrets in your app's environment files:
216+
217+```bash
218+# ~/.gordon/env/app_mydomain_com.env
219+DATABASE_URL=postgresql://user:${sops:secrets.yaml:database.password}@postgres:5432/app
220+API_KEY=${sops:secrets.yaml:api.key}
221+```
222+
223+## File Organization
224+
225+Recommended structure:
226+
227+```
228+project/
229+├── .sops.yaml # SOPS configuration
230+├── secrets.yaml # Encrypted secrets (commit this)
231+├── secrets.dev.yaml # Dev environment secrets
232+├── secrets.prod.yaml # Production secrets
233+└── gordon.toml # Gordon config (references secrets)
234+```
235+
236+### Environment-Specific Rules
237+
238+```yaml
239+# .sops.yaml
240+creation_rules:
241+ # Dev secrets - developer keys
242+ - path_regex: \.dev\.yaml$
243+ age: age1devkey...
244+
245+ # Prod secrets - production keys only
246+ - path_regex: \.prod\.yaml$
247+ age: age1prodkey...
248+
249+ # Default
250+ - age: age1defaultkey...
251+```
252+
253+## Running in Containers
254+
255+### Custom Dockerfile
256+
257+```dockerfile
258+FROM ghcr.io/bnema/gordon:latest
259+
260+USER root
261+RUN apk add --no-cache sops age
262+USER gordon
263+```
264+
265+### Mount Secrets and Keys
266+
267+```bash
268+docker run -d \
269+ --name gordon \
270+ -p 80:8080 \
271+ -p 5000:5000 \
272+ -v /var/run/docker.sock:/var/run/docker.sock \
273+ -v gordon-data:/data \
274+ -v $(pwd)/secrets.yaml:/app/secrets.yaml:ro \
275+ -v $(pwd)/.sops.yaml:/app/.sops.yaml:ro \
276+ -v ~/.config/sops/age/keys.txt:/home/gordon/.config/sops/age/keys.txt:ro \
277+ -e SOPS_AGE_KEY_FILE=/home/gordon/.config/sops/age/keys.txt \
278+ -v $(pwd)/gordon.toml:/etc/gordon/gordon.toml:ro \
279+ gordon-with-sops
280+```
281+
282+### Docker Compose
283+
284+```yaml
285+services:
286+ gordon:
287+ build:
288+ context: .
289+ dockerfile: Dockerfile.gordon
290+ container_name: gordon
291+ restart: unless-stopped
292+ ports:
293+ - "80:8080"
294+ - "5000:5000"
295+ volumes:
296+ - /var/run/docker.sock:/var/run/docker.sock
297+ - gordon-data:/data
298+ - ./gordon.toml:/etc/gordon/gordon.toml:ro
299+ - ./secrets.yaml:/app/secrets.yaml:ro
300+ - ./.sops.yaml:/app/.sops.yaml:ro
301+ - ~/.config/sops/age/keys.txt:/home/gordon/.config/sops/age/keys.txt:ro
302+ environment:
303+ - SOPS_AGE_KEY_FILE=/home/gordon/.config/sops/age/keys.txt
304+ - GORDON_SECRETS_BACKEND=sops
305+
306+volumes:
307+ gordon-data:
308+```
309+
310+## Team Workflows
311+
312+### Adding Team Members
313+
314+1. Get their age public key
315+2. Add to `.sops.yaml`:
316+
317+```yaml
318+creation_rules:
319+ - age: >-
320+ age1yourkey...,
321+ age1teammemberkey...
322+```
323+
324+3. Re-encrypt existing files:
325+
326+```bash
327+sops updatekeys secrets.yaml
328+```
329+
330+### Key Rotation
331+
332+1. Generate new key
333+2. Update `.sops.yaml`
334+3. Re-encrypt all files:
335+
336+```bash
337+sops updatekeys secrets.yaml
338+sops updatekeys secrets.prod.yaml
339+```
340+
341+### Git Workflow
342+
343+Encrypted files are safe to commit:
344+
345+```bash
346+git add .sops.yaml secrets.yaml
347+git commit -m "Add encrypted secrets"
348+git push
349+```
350+
351+Team members with valid keys can decrypt after pulling.
352+
353+## Troubleshooting
354+
355+### "failed to get the data key"
356+
357+Your key isn't in the recipients list or key file is missing:
358+
359+```bash
360+# Check if age key is set
361+echo $SOPS_AGE_KEY_FILE
362+cat $SOPS_AGE_KEY_FILE
363+
364+# Check recipients in encrypted file
365+grep -A5 "age:" secrets.yaml
366+```
367+
368+### "sops: command not found"
369+
370+Install SOPS:
371+
372+```bash
373+# Check if installed
374+which sops
375+
376+# Install if missing (see Installation section)
377+```
378+
379+### "could not decrypt data key"
380+
381+Wrong key or corrupted file:
382+
383+```bash
384+# Verify your public key matches a recipient
385+age-keygen -y ~/.config/sops/age/keys.txt
386+# Compare output to recipients in .sops.yaml
387+```
388+
389+### "no matching creation_rules"
390+
391+Create or fix `.sops.yaml`:
392+
393+```bash
394+# Check if .sops.yaml exists
395+cat .sops.yaml
396+
397+# Ensure path_regex matches your file
398+```
399+
400+### Permission Denied in Container
401+
402+Ensure key file is readable:
403+
404+```bash
405+docker exec gordon cat /home/gordon/.config/sops/age/keys.txt
406+```
407+
408+Check mount permissions:
409+
410+```bash
411+ls -la ~/.config/sops/age/keys.txt
412+# Should be readable by container user
413+```
414+
415+## Security Best Practices
416+
417+1. **Never commit unencrypted secrets**: Always use `sops edit`, never edit decrypted files directly
418+2. **Protect age/GPG keys**: Store private keys securely, never commit them
419+3. **Use separate keys per environment**: Production keys should be isolated
420+4. **Rotate keys regularly**: Update keys and re-encrypt periodically
421+5. **Audit access**: Track who has decryption keys for each environment
422+6. **Use .sops.yaml**: Enforce encryption rules via configuration
423+
424+## Related
425+
426+- [Secrets Configuration](/docs/config/secrets.md)
427+- [Registry Authentication](/docs/config/registry-auth.md)
428+- [Running in Containers](/wiki/guides/running-in-container.md)
429+- [Using Pass for Secrets](./secrets-pass.md)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close