dotkc v0.3.10 Encrypted vault secrets for OpenClaw

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.

How secrets flow
Encrypt vault + local key iCloud Drive Sync encrypted dotkc run OpenClaw inject env Secrets stay encrypted in a vault. Never in repos, .env files, or chat.
Vault Encrypted vault file (synced)
Key Local key file (per-machine)
Run OpenClaw uses vault to inject env

Why dotkc?

The pain

  • .env files 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).

OpenClaw pattern: manage secrets on your laptop → sync an encrypted vault via iCloud Drive → OpenClaw host uses 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 + run injection).
  • 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 read and 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.
OpenClaw reality check: OpenClaw configuration/auth files are typically plaintext JSON on disk (with tight permissions). If you want encryption-at-rest + agent-friendly injection, keep secrets out of config and use a dedicated workflow.
Reference: OpenClaw issue “Support for encrypted API keys / secrets management” (#7916).

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 ... --openclaw for 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 get in agent workflows (it prints raw values). If you use the dotkc OpenClaw plugin, it enforces DOTKC_NO_LEAK=1 for 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.

openclaw.json
{
  "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.