-
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 v0.1.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.
| 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 |
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
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 staging/platform/app --template laravel --forcekeyseal edit <logical-name>
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 |
edit has no command-specific flags. It simply delegates to sops <file> with stdin/stdout/stderr attached to the current terminal.
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 SOPS exits with a non-zero code, edit returns that error wrapped with context.
keyseal edit production/platform/appkeyseal render <logical-name...> [flags]
Decrypts one or more secrets, merges their values maps, and writes the result to a file or stdout.
At least one logical name is required. Multiple names are allowed - later names win on key conflicts.
| 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, merges their values, and injects them as environment variables into a subprocess. The -- separator is required.
At least one logical name before -- and at least one command token after --.
exec has no command-specific flags.
- Decrypts and merges the named 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)
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.
| 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 version [flags]
Prints build metadata.
| Flag | Default | Description |
|---|---|---|
--short |
false | Print only the version string |
Without flags:
keyseal v0.1.0 (abc1234)
tag: v0.1.0
commit: abc1234
built: 2026-04-19T13:10:00Z
With --short:
v0.1.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