-
Notifications
You must be signed in to change notification settings - Fork 0
Command Reference
All commands read keyseal.yaml from the current working directory (except init, which creates it). Cobra's SilenceUsage and SilenceErrors are set - usage is not printed on errors, and errors are handled by main() directly.
Prints a single line with the version and short commit hash, then exits.
keyseal --version
# keyseal v1.0.0 (abc1234)This is a root-level flag, not a subcommand. For more detail, use keyseal version.
keyseal init [flags]
Initializes a Keyseal repository in the current directory.
Creates keyseal.yaml and .sops.yaml, then creates the default directory scaffold:
production/platform/
production/infra/
production/tenants/
staging/platform/
staging/infra/
staging/tenants/
| Flag | Default | Description |
|---|---|---|
--profile string |
default |
Profile name written into the generated keyseal.yaml
|
--force |
false | Overwrite existing files; continue even if directories already exist |
--dry-run |
false | Print planned actions without writing anything |
Without --force, init fails if keyseal.yaml or .sops.yaml already exists, or if any of the six directories already exist. Use --force to re-initialize or update the scaffold.
--dry-run prints the profile name and a list of directories and files that would be created, then returns without writing.
The generated .sops.yaml contains placeholder age recipients (age1REPLACE_ME, age1RECOVERY_REPLACE_ME). These must be replaced with real age public keys before any encryption will work. Doctor will flag the placeholders until they are replaced.
keyseal init
keyseal init --profile production
keyseal init --force
keyseal init --dry-runkeyseal add <logical-name> [flags]
Creates a new encrypted secret file for the given logical name.
The starter document is written to a secure temporary file (mode 0600) and passed to sops encrypt --filename-override <target>. Only the ciphertext is written to the final path. The temp file is removed on exit.
If sops.age_key_file is configured in keyseal.yaml, Keyseal passes that path through automatically unless SOPS_AGE_KEY_FILE is already set in the shell.
| Argument | Description |
|---|---|
logical-name |
The logical name of the secret (e.g. production/platform/app) |
| Flag | Default | Description |
|---|---|---|
--kind string |
env |
Document kind. Only env is supported; any other value returns an error |
--template string |
(empty) | Built-in template to use for the starter values |
--force |
false | Overwrite an existing file |
--commit |
false | Stage the new secret file and create a Git commit |
-m, --message string |
(empty) | Git commit message. Implies --commit
|
--no-commit |
false | Suppress commit even when git.auto_commit is enabled |
If --template is not set, the starter document contains a single EXAMPLE_KEY: REPLACE_ME entry.
Available templates: laravel, stripe, mail, mysql-app. See Templates for the keys each template scaffolds.
- Validates the logical name format
- Loads
keyseal.yamland resolves the target file path - Builds the starter document from the template (or default single-key starter)
- Validates the document against the config's validation rules
- Calls
sops encryptvia the configured binary - Writes the ciphertext atomically to the target path
- Optionally stages only the new secret file and commits it
If a SOPS creation rule does not match the target path, SOPS will error out and no file is written.
- Passing a logical name that ends in
.yamlor includes.enc.- Keyseal rejects this - Running
addbefore replacing placeholder recipients in.sops.yaml- SOPS will error - Using a
--templatename that doesn't exist - returnsunknown template "<name>"
keyseal add production/platform/app
keyseal add production/platform/mail --template mail
keyseal add production/platform/app -m "Add production app secret"
keyseal add staging/platform/app --template laravel --forcekeyseal edit <logical-name> [flags]
Opens the encrypted file in SOPS's interactive editor. SOPS decrypts the file to a temp buffer, opens it in your $EDITOR (or its own default editor), and re-encrypts on save.
| Argument | Description |
|---|---|
logical-name |
The logical name of the secret to edit |
| Flag | Default | Description |
|---|---|---|
--commit |
false | Stage the edited secret file and create a Git commit if the file changed |
-m, --message string |
(empty) | Git commit message. Implies --commit
|
--no-commit |
false | Suppress commit even when git.auto_commit is enabled |
The command resolves the logical name to a file path, then runs sops <path>. Your terminal is attached to the sops process directly - Keyseal does not intercept or buffer the interaction.
If the target file already exists but is empty or whitespace-only, Keyseal first bootstraps it with the standard encrypted starter document so SOPS has valid content to edit.
If sops.age_key_file is configured in keyseal.yaml, Keyseal passes that path through automatically unless SOPS_AGE_KEY_FILE is already set in the shell.
If commit behavior is enabled, Keyseal compares the encrypted file before and after SOPS exits. It only stages and commits the file if the contents actually changed.
If SOPS exits with a non-zero code, edit returns that error wrapped with context.
keyseal edit production/platform/app
keyseal edit production/platform/app --commit
keyseal edit production/platform/app -m "Update app secret"keyseal updatekeys [logical-name...] [flags]
Runs sops updatekeys for Keyseal-managed encrypted files so their recipients match the current .sops.yaml creation rules.
This command syncs recipients only. It does not rotate secret values and does not rotate the SOPS data encryption key.
| Argument | Description |
|---|---|
logical-name... |
One or more secrets to update. Omit only when using --all
|
| Flag | Default | Description |
|---|---|---|
--all |
false | Discover every encrypted file under repository.root
|
--yes |
false | Pass -y to sops updatekeys for non-interactive confirmation |
--commit |
false | Stage changed encrypted files and create a Git commit if the full batch succeeds |
-m, --message string |
(empty) | Git commit message. Implies --commit
|
Before changing files, Keyseal validates keyseal.yaml, repository.root, .sops.yaml, SOPS recipient readiness, placeholder recipients, and the configured SOPS binary. This command requires the external SOPS CLI.
For each target file:
- empty or whitespace-only placeholder files are skipped
- missing files fail
- non-empty plaintext files at encrypted paths fail
- encrypted files run through
sops updatekeys
The command continues after per-file failures, prints a final summary, and exits non-zero if any file failed. If commit behavior is enabled, Keyseal commits only when the whole batch succeeded and at least one file changed.
Unlike add, edit, and rollback, updatekeys does not honor git.auto_commit; commits are explicit only.
keyseal updatekeys production/platform/app
keyseal updatekeys production/platform/app staging/platform/app --yes
keyseal updatekeys --all --yes
keyseal updatekeys --all --yes -m "Sync SOPS recipients"keyseal status [logical-name]
Shows Git status for Keyseal-managed files only.
That includes:
- encrypted secret files that match the configured encrypted extension
keyseal.yaml.sops.yaml
The output uses Git's short two-column status format (XY path) so staged vs unstaged changes remain visible without showing unrelated repo noise.
If you pass a logical name, the output is narrowed to that single encrypted secret file.
keyseal status
keyseal status production/platform/app
keyseal status --jsonkeyseal diff <logical-name>
Resolves the logical name to an encrypted file path, then runs git diff -- <file>.
keyseal diff production/platform/appkeyseal history <logical-name>
Resolves the logical name to an encrypted file path, then runs file-scoped Git history for that file.
| Flag | Default | Description |
|---|---|---|
--oneline |
false | Show compact one-line Git history |
keyseal history production/platform/app
keyseal history production/platform/app --onelinekeyseal commit [flags]
Stages current Keyseal-managed changes and creates a Git commit.
It stages only:
- encrypted secret files
keyseal.yaml.sops.yaml
It does not run git add ..
| Flag | Default | Description |
|---|---|---|
-m, --message string |
generated | Commit message. If omitted, Keyseal uses a blunt count-based default |
If no Keyseal-managed files are changed, the command fails with nothing to commit.
keyseal commit
keyseal commit -m "Rotate Stripe webhook secret"keyseal rollback <logical-name> --to <commit> [flags]
Restores the encrypted file for the logical name from the specified Git commit.
Rollback is Git-based file restore. Keyseal does not reconstruct decrypted state manually.
| Flag | Default | Description |
|---|---|---|
--to string |
(required) | Git commit to restore from |
--dry-run |
false | Show the target rollback without changing the working tree |
--commit |
false | Stage the restored file and create a Git commit |
-m, --message string |
generated | Git commit message. Implies --commit
|
--no-commit |
false | Suppress commit even when git.auto_commit is enabled |
- If the target file has local changes, rollback fails
- If the specified commit does not contain the file, rollback fails
-
--dry-runprints the intended action without modifying the working tree, even if the target file currently has local changes
keyseal rollback production/platform/app --to abc1234
keyseal rollback production/platform/app --to abc1234 --dry-run
keyseal rollback production/platform/app --to abc1234 -m "Rollback app secret"keyseal render <logical-name...> [flags]
Decrypts one or more secrets with the official SOPS Go decrypt library, merges their values maps, and writes the result to a file or stdout. This command does not require external sops or age binaries; it requires age private key material.
If sops.age_key_file is configured in keyseal.yaml, Keyseal passes that path to the decrypt library unless SOPS_AGE_KEY_FILE is already set in the shell.
At least one logical name is required. Multiple names are allowed - later names win on key conflicts.
Empty or whitespace-only .enc.yaml files are treated as uninitialized placeholders and skipped. If every requested file is an empty placeholder, render fails with a clear error instead of producing empty output.
| Flag | Default | Description |
|---|---|---|
--format string |
defaults.output_format |
Output format: dotenv, json, or yaml
|
--out string |
(none) | Write output to this file path |
--stdout |
false | Write output to stdout |
--mode string |
defaults.file_mode |
File mode for --out (4-digit octal, e.g. 0600) |
--force |
false | Allow unsafe file modes; overwrite existing output file |
--out and --stdout are mutually exclusive. One is required.
dotenv: KEY="VALUE"\n per entry, sorted alphabetically. Escape sequences applied: \ → \\, newline → \n, " → \".
json: JSON object with string keys and values, indented with 2 spaces. Keys are sorted alphabetically by Go's json.MarshalIndent.
yaml: YAML mapping with all values tagged !!str (explicit string type). Keys are sorted alphabetically.
When writing to a file, the mode is checked for group or world bits. If the mode is not owner-only (e.g. 0644) and --force is not set, render refuses with:
mode 0644 is not owner-only; use --force to override
When multiple logical names are provided, their values maps are merged in order. If staging/platform/app has APP_ENV: staging and production/platform/app has APP_ENV: production, then:
keyseal render staging/platform/app production/platform/app --stdout
# APP_ENV will be "production" (last wins)# dotenv to file
keyseal render production/platform/app --out /run/secrets/app.env
# json to stdout
keyseal render production/platform/app --stdout --format json
# merge two secrets, yaml output
keyseal render production/platform/app production/platform/mail \
--out /run/secrets/combined.yaml --format yaml
# allow unsafe mode
keyseal render production/platform/app --out ./app.env --mode 0644 --forcekeyseal exec <logical-name...> -- <command...>
Decrypts one or more secrets with the official SOPS Go decrypt library, merges their values, and injects them as environment variables into a subprocess. The -- separator is required. This command does not require external sops or age binaries; it requires age private key material.
If sops.age_key_file is configured in keyseal.yaml, Keyseal passes that path to the decrypt library unless SOPS_AGE_KEY_FILE is already set in the shell.
At least one logical name before -- and at least one command token after --.
exec has no command-specific flags.
- Classifies the named files first: empty or whitespace-only placeholders are skipped, non-empty files without SOPS metadata fail immediately as plaintext-at-encrypted-path errors
- Decrypts and merges the remaining usable secrets
- Merges secret values with the current environment (
os.Environ()): secrets override any matching keys in the parent environment; all other parent environment variables are inherited - Runs the command with the merged environment, with stdin/stdout/stderr attached to the current terminal
- If the subprocess exits non-zero,
execexits with the same code (no error message is printed)
If every requested file is an empty placeholder, exec fails with a clear error instead of launching the child process with no secret values.
keyseal exec production/platform/app -- php artisan migrate
keyseal exec production/platform/app production/platform/mail -- ./start.sh
keyseal exec staging/platform/app -- env | grep APP_- Forgetting the
--separator: Keyseal cannot distinguish between logical names and command arguments without it - Expecting
execto write a file: it does not; secrets exist only in the subprocess's environment
keyseal doctor [flags]
Runs health checks against the repository and reports findings. See Doctor for detailed coverage of each check.
Doctor validates config, SOPS availability, age availability warnings, .sops.yaml readiness, repository layout, plaintext mistakes, placeholder files, and decrypted document shape.
| Flag | Default | Description |
|---|---|---|
--json |
false | Emit structured JSON instead of human-readable text |
-
0: all checks passed or produced only warnings -
1: one or more checks failed
keyseal doctor
keyseal doctor --json
keyseal doctor --json | jq '.checks[] | select(.status == "fail")'keyseal verify [flags]
Runs the same non-mutating repository checks as doctor, but treats every warning as a failure. Decrypt validation uses the official SOPS Go decrypt library, so read-only CI/deploy runners need age key material but not external sops or age binaries.
| Flag | Default | Description |
|---|---|---|
--json |
false | Emit structured JSON instead of concise text |
-
0: all checks passed with no warnings or failures -
1: one or more checks warned or failed
keyseal verify
keyseal verify --jsonkeyseal version [flags]
Prints build metadata.
| Flag | Default | Description |
|---|---|---|
--short |
false | Print only the version string |
Without flags:
keyseal v1.0.0 (abc1234)
tag: v1.0.0
commit: abc1234
built: 2026-04-19T13:10:00Z
With --short:
v1.0.0
For builds without a Git tag, Version is "dev". For builds without a known commit, the commit is "unknown" and is omitted from the one-line format.
keyseal version
keyseal version --shortGetting Started
Reference
Operations
Development