OpenClaw-first secrets workflow
Secrets you can run, not paste.
dotkc stores secrets in an encrypted vault file (ideal for iCloud Drive sync) and injects them into
commands when you need them. It's built for personal OpenClaw/local-agent setups where you want secure, repeatable
env injection without sprinkling secrets across .env files, shell history, or CI logs.
This mode is for tools. dotkc can emit an OpenClaw-friendly JSON envelope via --openclaw.
Use it to discover the CLI surface area and to drive dotkc run safely.
# initialize vault (creates iCloud Drive vault + local key file)
dotkc init
# store secrets in the vault (interactive hidden prompt)
dotkc set vercel acme-app-dev CLERK_SECRET_KEY
# inspect (redacted by default)
dotkc run vercel:acme-app-dev
# run your app with env injected
dotkc run vercel:acme-app-dev -- pnpm dev
Inspect mode prints redacted values by default. Use --unsafe-values only on a trusted machine.
# discover commands as JSON
dotkc --help --openclaw
# structured status
dotkc status --openclaw
# inspect allowlisted secrets (redacted)
dotkc run --spec-file ./dotkc.spec --openclaw
Agents should prefer allowlists (--spec-file) and redacted inspect output.
Why dotkc?
The pain
.envfiles drift and leak (repos, backups, screenshots).- Copy/paste secrets into terminals ends up in history.
- Agents (like OpenClaw) need secrets frequently, but you don't want them in chat.
The idea
- Encrypt secrets into a vault file.
- Sync the vault via iCloud Drive.
- Inject them into commands at runtime.
Designed for OpenClaw
- iCloud Drive sync is reliable and easy to reason about.
- The vault is encrypted; iCloud only sees ciphertext.
- A per-machine key file enables unattended OpenClaw runs.
How it works
dotkc stores secrets using a simple 3-dimension naming model: service + category + KEY.
Storage model
Secrets are stored in an encrypted vault file using: service + category + KEY
- service: the SaaS or system (e.g.
vercel,github) - category: project/environment (e.g.
acme-app-dev) - KEY: env var name (e.g.
CLERK_SECRET_KEY)
Tip: avoid : in category names to keep parsing unambiguous.
Two modes: run vs inspect
- Run:
dotkc run <spec> -- <cmd>injects env into the child process. - Inspect:
dotkc run <spec>prints redacted values so you can sanity check quickly.
Want full values? Use --unsafe-values (and accept the logging risk).
dotkc run to inject secrets into tools (Next.js, REST services, CLIs, etc.).
Compare (OpenClaw-focused)
The #1 question: will secrets end up in model context?
dotkc
- Positioning: personal, local-first, env-injection focused.
- Pricing: free / OSS (you bring iCloud Drive sync).
- Model-context risk: low by default (inspect is redacted; prefer allowlist +
runinjection). - On-disk: synced vault is ciphertext; key is per-machine.
- Recovery: backups + sync-conflict refuse overwrite.
1Password CLI (op)
- Positioning: password manager ecosystem + sharing/governance.
- Pricing: paid subscription (per user/month; varies by plan).
- Model-context risk: depends on usage. If an agent runs
op readand prints output, secrets can land in the model transcript. - On-disk: managed by 1Password app/agent (not a simple vault file you control).
- Recovery: strong account recovery + sharing controls (best for teams).
Cloud secret managers
- Positioning: production IAM + rotation + audit.
- Pricing: pay-per-secret / API usage (plus KMS/requests depending on provider).
- Model-context risk: depends on the integration path (agents can still leak secrets if they print values).
- On-disk: not local-first; requires cloud access and policy wiring.
- Recovery: enterprise-grade controls; heavy for personal local dev.
Quickstart
Two-machine setup (A = laptop, B = OpenClaw host). Copy/paste friendly.
Step 1 — Install on both machines
# machine A + machine B
npm i -g dotkc
Step 2 — Initialize on machine A
# machine A
dotkc init
dotkc status
This creates the vault (synced) + a local key file (NOT synced).
Step 3 — Install the key on machine B (SSH host)
# machine A → machine B
cat ~/.dotkc/key | ssh user@machine-b 'dotkc key install'
# verify
ssh user@machine-b 'dotkc status'
# if something is wrong
ssh user@machine-b 'dotkc doctor --openclaw'
Step 4 — Add or import secrets (machine A)
# interactive (hidden prompt)
dotkc set vercel acme-app-dev CLERK_SECRET_KEY
# or import from dotenv (picker)
dotkc import vercel acme-app-dev .env
Step 5 — Run with injected env
dotkc run vercel:acme-app-dev -- pnpm dev
Use cases
Concrete recipes (why it exists + how to use it).
Run commands without leaking secrets
Avoid exporting env vars into your interactive shell.
dotkc run vercel:acme-app-dev -- pnpm dev
Inspect safely / export only when needed
# inspect (redacted)
dotkc run vercel:acme-app-dev
# export dotenv lines (redacted)
dotkc export vercel:acme-app-dev
# full values only when you must
# dotkc export vercel:acme-app-dev --unsafe-values
Backups + sync conflicts (P0 safety)
dotkc backs up before overwriting and refuses to overwrite on sync conflicts.
# defaults
DOTKC_BACKUP_KEEP=3
# disable backups
DOTKC_BACKUP_KEEP=0
# store backups elsewhere
DOTKC_BACKUP_DIR=/path/to/dir
Find / reorganize secrets fast
search shows names only (no values). copy/move operate on a whole
service:category and do copy/move the values inside that category.
# find keys by substring (no values printed)
dotkc search clerk
# duplicate an environment (dev → prod): copies all keys + values in that category
dotkc copy vercel:acme-app-dev vercel:acme-app-prod
# rename/move a category: moves keys + values
dotkc move vercel:acme-app-prod fly.io:acme-app-prod
OpenClaw integration (L1)
Goal: let OpenClaw use secrets without printing them. The safest pattern is: keep a repo-committed allowlist
(no values) and always run commands via dotkc run.
# 1) commit allowlist (NO values)
# ./dotkc.spec
fly.io:acme-app-dev
vercel:acme-app-dev:CLERK_SECRET_KEY
# 2) on the OpenClaw host, inspect allowlist resolution (redacted JSON)
dotkc run --spec-file ./dotkc.spec --openclaw
# 3) run real commands with injected env (values never printed)
dotkc run --spec-file ./dotkc.spec -- pnpm dev
Tip: keep the vault synced (ciphertext), but keep ~/.dotkc/key local and protected. Install it on machine B via
dotkc key install (stdin-only).
Want typed tools instead of shelling out? See the OpenClaw plugin: dotkc-openclaw.hczhang.com
Case study: manage BRAVE_API_KEY for OpenClaw web search
OpenClaw web_search uses the BRAVE_API_KEY environment variable. Keep it out of
openclaw.json by managing it in dotkc.
Option A (recommended): inject env when starting/restarting the Gateway
# 1) store key (interactive hidden prompt)
dotkc set brave web BRAVE_API_KEY
# 2) allowlist (NO values)
# ./dotkc.spec
brave:web:BRAVE_API_KEY
# 3) two common startup styles
# (a) manual / ad-hoc restart
# key never printed; only injected into the gateway process env
dotkc run --spec-file ./dotkc.spec -- openclaw gateway restart
# (b) service-managed (launchd/systemd): run a wrapper as the service command
# start-openclaw.sh:
# exec dotkc run --spec-file ./dotkc.spec -- openclaw gateway start
Note: avoid generating plaintext .env / env files on disk. Prefer Option A.
OpenClaw tool policy recommendations
The biggest OpenClaw footgun is letting an agent print secrets. Guardrails:
- Prefer inspect redaction: use
dotkc run ... --openclawfor checks (never--unsafe-values). - Prefer spec files: agents should use
--spec-file, not ad-hoc secret names in prompts. - Minimize shell blast radius: restrict exec approvals/allowlists to the commands you actually need (e.g.
pnpm dev,npm test). - Keep transcripts clean: do not run
dotkc getin agent workflows (it prints raw values). If you use the dotkc OpenClaw plugin, it enforcesDOTKC_NO_LEAK=1for dotkc subprocesses by default.
# structured discovery for agents
dotkc --help --openclaw
dotkc status --openclaw
dotkc doctor --openclaw
Why a vault vs .env?
- Encrypted at rest (AES‑256‑GCM). The synced file is ciphertext.
- Less accidental leakage (repos, zip files, backups, screenshots, shell history).
- Sync via iCloud Drive works well for personal laptop ↔ Mac mini setups.
- Better agent hygiene: OpenClaw can use secrets without you pasting them into chat.
FAQ
Is inspect mode safe?
It's safer than printing raw values: dotkc prints redacted values + length by default. Still, be mindful of terminal logs, screen recording, and shared machines.
How do I print the full values?
Use dotkc run --unsafe-values <spec> (inspect mode) or
dotkc export <spec> --unsafe-values to export dotenv lines.
Does dotkc create backups?
Yes. dotkc creates backups before overwriting the vault and refuses to write if backup fails.
Configure with DOTKC_BACKUP_KEEP and DOTKC_BACKUP_DIR.
What does dotkc store exactly?
Secrets are stored in an encrypted vault file. The per-machine local key file is required to decrypt.
OpenClaw / agent guide (JSON-first)
This mode is designed for OpenClaw to parse directly.
{
"tool": "dotkc",
"purpose": "Local-first secrets vault + env injection designed for OpenClaw automation",
"agentMode": {
"switches": [
"--openclaw",
"--format openclaw"
],
"envelope": {
"format": "openclaw",
"command": "string",
"ok": "boolean",
"code": "number",
"data": "any",
"warnings": "string[]",
"errors": "string[]"
}
},
"discovery": [
{
"cmd": "dotkc --help --openclaw",
"expect": "data.commands + data.usageText"
},
{
"cmd": "dotkc --version --openclaw",
"expect": "data.version"
}
],
"paths": {
"vault": "DOTKC_VAULT_PATH (default: iCloud Drive dotkc.vault)",
"key": "DOTKC_VAULT_KEY_PATH (default: ~/.dotkc/key)"
},
"safety": {
"backups": {
"DOTKC_BACKUP_KEEP": "3 (0 disables)",
"DOTKC_BACKUP_DIR": "optional"
},
"syncConflicts": "dotkc refuses overwrite if vault changed on disk; retry"
},
"commands": [
{
"name": "init",
"usage": "dotkc init [--vault <path>] [--key <path>]",
"desc": "Initialize vault + local key (prompts before overwriting)."
},
{
"name": "status",
"usage": "dotkc status [--vault <path>] [--key <path>]",
"desc": "Print JSON status (paths + canDecrypt)."
},
{
"name": "doctor",
"usage": "dotkc doctor [--vault <path>] [--key <path>] [--json]",
"desc": "Run diagnostics and suggest fixes."
},
{
"name": "key install",
"usage": "cat ~/.dotkc/key | dotkc key install [--key <path>] [--force]",
"desc": "Install key from stdin (refuses overwrite unless --force)."
},
{
"name": "set",
"usage": "dotkc set <service> <category> <KEY> [value|-]",
"desc": "Set a secret (prompt hidden if value omitted)."
},
{
"name": "get",
"usage": "dotkc get <service> <category> <KEY>",
"desc": "Print secret value to stdout."
},
{
"name": "del",
"usage": "dotkc del <service> <category> <KEY>",
"desc": "Delete a secret."
},
{
"name": "list",
"usage": "dotkc list <service> [category]",
"desc": "List categories or keys."
},
{
"name": "search",
"usage": "dotkc search <query> [--json]",
"desc": "Search keys by substring (no values)."
},
{
"name": "export",
"usage": "dotkc export <spec>[,<spec>...] [--unsafe-values]",
"desc": "Export dotenv lines (redacted by default)."
},
{
"name": "copy",
"usage": "dotkc copy <srcService>:<srcCategory> <dstService>:<dstCategory> [--force]",
"desc": "Copy a category."
},
{
"name": "move",
"usage": "dotkc move <srcService>:<srcCategory> <dstService>:<dstCategory> [--force]",
"desc": "Move a category."
},
{
"name": "import",
"usage": "dotkc import <service> <category> [dotenv_file]",
"desc": "Interactive import from .env."
},
{
"name": "run",
"usage": "dotkc run [options] <spec>[,<spec>...] [-- <cmd> ...]",
"desc": "Inspect (redacted) or execute with injected env."
}
],
"workflows": {
"health": {
"steps": [
"dotkc status --openclaw",
"dotkc doctor --openclaw"
]
},
"allowlist": {
"specFile": "./dotkc.spec",
"specFormat": [
"one spec per line",
"supports comments with #",
"wildcard: <service>:<category>",
"exact: <service>:<category>:<KEY>"
],
"inspect": "dotkc run --spec-file ./dotkc.spec --openclaw",
"run": "dotkc run --spec-file ./dotkc.spec -- <cmd>"
},
"manage": {
"examples": [
"dotkc set vercel acme-app-dev CLERK_SECRET_KEY",
"dotkc import vercel acme-app-dev .env",
"dotkc search clerk --json",
"dotkc export vercel:acme-app-dev",
"dotkc copy vercel:acme-app-dev vercel:acme-app-prod"
]
}
},
"notes": [
"Never print full values unless explicitly needed (--unsafe-values).",
"Anyone with vault + key can decrypt; protect ~/.dotkc/key."
]
}
Commands are listed in commands[] inside the JSON above.