Kimen is a local-first secrets tool.
Apps can keep reading normal env vars and files. No need for complex config libraries and precedence hierarchies. Kimen prepares the environment before the app starts.
brew install flakstad/kimen/kimenOr:
Download the latest release binaries for your platform from:
https://github.com/flakstad/kimen/releases/latest
Or:
make installOr:
make build
./dist/kimen versionInitialize a vault:
kimen vault initAdd a database URL. prod.database_url is the key in the vault, not the value.
Kimen prompts for the value in the terminal. It is not echoed, and it does not
go through shell history, an env var, or a repo file.
kimen secret set prod.database_urlExpose it to one command as a normal environment variable:
kimen run --env DATABASE_URL=prod.database_url -- ./your-appThe app reads DATABASE_URL. It does not need to know about Kimen.
For repeated commands, commit a profile to the repo.
Example: .kimen/profiles/prod.kmap
env DATABASE_URL=prod.database_url
env SERVICE_API_TOKEN=prod.service_api_token
env PORT=const:5050
file credentials.json=prod.gcp_credentials_json
envpath GOOGLE_APPLICATION_CREDENTIALS=credentials.json
The names on the right are keys in the vault, except const:5050, which is a
literal value.
Run the profile:
kimen run --profile prod -- ./your-appKimen reads .kimen/profiles/prod.kmap, loads the referenced vault values, writes
any requested files into a temporary runtime directory, sets env vars, starts the
command, and cleans up afterwards.
envpath writes a file and sets the environment variable to the path of that
file. That is useful for tools that expect something like
GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json.
Some deploy tools want an envfile instead of a wrapped command:
kimen envfile --profile prod --out .env.runtimeThe envfile is generated from the vault and profile. It does not need to live in the repo.
Unlock once for a short local window:
kimen session start --ttl 15mThen scripts and tools running as the same user can use the vault without prompting again until the TTL expires.
Lock it explicitly:
kimen session lock- Secrets are encrypted at rest in a local vault.
- Projections can produce env vars, files, envfiles, or one scoped command execution.
- Profiles are map files, usually committed under
.kimen/profiles/. - Values can come from
secret:<name>, bare secret names,const:<literal>, orexec:<command...>.
Profile lookup order:
$KIMEN_PROFILE_DIR/<name>.kmap./.kimen/profiles/<name>.kmap$XDG_CONFIG_HOME/kimen/profiles/<name>.kmap$HOME/.config/kimen/profiles/<name>.kmap
The CLI is the primary API.
Vault and config:
kimen versionkimen config path|showkimen config vault set|show|clearkimen config unlock set|show|clearkimen vault init|info|path|rekeykimen session start|status|lock|stop
Secrets:
kimen secret set <name> [--stdin|--value <text>]kimen secret listkimen secret get <name> --unsafe-stdoutkimen secret rm <name>kimen secret mv <old-name> <new-name>
Projection and planning:
kimen runkimen renderkimen envfilekimen plankimen map lintkimen doctorkimen project run|render|plan
Bundles, remotes, and sync:
kimen bundle keygen|recipient|seal|openkimen remote add|get|set|list|rmkimen synckimen sync init|preflight|status|conflicts|changes|push|pull|resolve|reset-baseline|unlock|restore
Other:
kimen init ci-pr-safety|ci-deploy|ci-sync-gate
Common conventions:
- vault precedence:
--vault>KIMEN_VAULT>config.vault.path> default user config location - passphrase sources:
KIMEN_PASSPHRASE,--passphrase-cmd,--passphrase-stdin, active local session, configured unlock method, then TTY prompt - machine-readable mode:
--json
kimen session provides a short-lived, local unlock window for trusted same-user processes. It is useful when you want tools, scripts, or AI agents running as your user to use Kimen for a bounded period without receiving or re-entering the vault passphrase.
kimen session start --ttl 15m
kimen session status
kimen session lock
kimen session stopsession start verifies the passphrase against the selected vault and stores it only in a background daemon's memory. The daemon listens on a local Unix socket and keeps an idle TTL. Each successful reuse refreshes the expiry.
Commands that open the same vault reuse the session when no explicit passphrase source was provided:
kimen session start --ttl 15m
cd /some/other/project
kimen secret list --vault ~/.config/kimen/vault.db
kimen envfile --env API_KEY=api_key --out .env.runtime
kimen session lockSession reuse is scoped by the canonical vault path, not by the current directory. If a command resolves to a different vault path, Kimen falls back to the normal passphrase sources.
Passphrase source precedence is:
KIMEN_PASSPHRASE--passphrase-cmd--passphrase-stdin- active local session for the same vault
- configured unlock method
- TTY prompt
session status reports whether the daemon is stopped, locked, or unlocked:
kimen session status
kimen session status --jsonsession lock forgets the in-memory passphrase but leaves the daemon running. session stop forgets the passphrase and shuts down the daemon.
The session socket is local to the user and stored under $KIMEN_SESSION_DIR, $XDG_RUNTIME_DIR/kimen, or the user cache directory with strict local permissions. Treat an active session as a deliberate grant: any local process running as your OS user can use Kimen commands against the unlocked vault until the session is locked, stopped, or expires. This is not a sandbox boundary between processes running as the same user.
Kimen is designed to be scriptable.
Rules:
- success JSON is emitted on
stdout - standard error envelopes are emitted on
stderr map lint --json,doctor --json, andsync preflight --jsonemit reports onstdout
Standard error envelope:
{
"ok": false,
"error": "human-readable message",
"exit_code": 23,
"reason": "machine_readable_reason"
}General rule:
- success payloads that include
okalso includeexit_code: 0 config showis a raw JSON config dump, not a standard envelope
Important success shapes:
secret --json:{"ok":true,"action":"set|list|get|rm|mv","exit_code":0,...}vault --json:{"ok":true,"action":"vault_init|vault_info|vault_path|vault_rekey","exit_code":0,...}bundle --json:{"ok":true,"action":"bundle_keygen|bundle_recipient|bundle_seal|bundle_open","exit_code":0,...}remote --json:{"ok":true,"action":"remote_add|remote_get|remote_set|remote_list|remote_rm","exit_code":0,...}sync --json: orchestration report onstdoutsync preflight --json: preflight report onstdout
Important reasons you can branch on include:
secret_not_foundsecret_existsvault_not_foundwrong_passphraseinvalid_vault_fileremote_not_foundremote_existsremote_lock_presentremote_changedno_local_baseline
Important typed exits:
0: success20: map lint failure31: sync conflict / reconciliation required32: sync precondition failure
Team Sync is optional. Runtime remains local even when sync is configured.
Shipped surface:
fsandgitremotes- orchestration-first
kimen sync - explicit operator subcommands for status, conflicts, pull/push, resolve, unlock, and baseline reset
- ciphertext-only transport via
vault.age - strict CI gating with
kimen sync preflight --strict --json
Recommended default operating model:
- one writer pushes
- others pull
Useful commands:
kimen sync preflight --remote team --strict --json
kimen sync conflicts --remote team --strict --json
kimen sync push --remote team
kimen sync pull --remote team --reconcileThe intended CI pattern is:
- keep a ciphertext bundle such as
vault.age - open it locally inside the job with
kimen bundle open - run
doctor,envfile, orproject runagainst the job-local vault
The repo includes starter workflow templates in .github/workflows/:
kimen-pr-safety-template.ymlkimen-deploy-template.ymlkimen-sync-gate-template.yml
You can scaffold them with:
kimen init ci-pr-safety
kimen init ci-deploy
kimen init ci-sync-gateEnable the pre-commit hook:
git config core.hooksPath .githooks
chmod +x .githooks/pre-commitRun tests:
go test ./...Run sync E2E smoke tests:
make sync-e2e
make sync-e2e-git