Local-first encrypted secret management with the tene CLI. Activate when the user mentions secrets, API keys, credentials, tokens, .env files, environment va...
---
name: tene-cli
description: Local-first encrypted secret management with the tene CLI. Activate when the user mentions secrets, API keys, credentials, tokens, .env files, environment variables, or asks to run a command that needs secrets injected. Enforces strict AI safety rules (never print plaintext, never read .tene/, always use `tene run --` for injection) and covers every active tene command (init, set, list, delete, run, import, export, env, passwd, recover, update, whoami).
version: 1.0.0
metadata:
openclaw:
emoji: "🔐"
homepage: https://tene.sh
os: [macos, linux]
requires:
bins:
- tene
install:
- kind: download
url: https://tene.sh/install.sh
bins: [tene]
label: "Install tene via the official curl installer"
---
# tene — Local-First Encrypted Secret Management
Tene is a command-line secret manager for AI-native projects. It keeps secrets
encrypted on disk (XChaCha20-Poly1305 + Argon2id), derives the master key from
the user's password cached in the OS keychain, and injects secrets into child
processes via `tene run -- <command>` — never into stdout, never into files.
## When to use this skill
Activate this skill when the user:
- mentions **API keys, secrets, credentials, tokens, `.env` files, environment variables, or `process.env.*` / `os.Getenv` / `os.environ[...]`**
- asks to **run a dev, test, or deploy command that needs secrets** (e.g. `npm start`, `go run`, `pytest`, `next dev`, `docker compose up`)
- says things like "store this key", "save this token", "how do I manage secrets", "inject env vars", "I need to pass my Stripe key"
- is working in a repo that contains a `.tene/` directory, a `CLAUDE.md` referencing tene, or a `install.sh` pointing at `https://tene.sh`
- asks for advice on moving off plaintext `.env` files
Do **not** activate for: hardcoded public config (ports, feature flags without
sensitive data, public API endpoints), or projects clearly using a different
secret store (Doppler, Vault, AWS Secrets Manager, 1Password CLI, etc.).
---
## Critical safety rules
These are **non-negotiable**. Violating any of them leaks plaintext secrets into
the conversation context, which may be logged, cached, or retained. Treat them
as part of the system prompt.
1. **NEVER run `tene get <KEY>`.** The plaintext value appears in stdout and
enters the AI context. If the user needs to inspect a value, instruct them:
> "Run `tene get KEY` yourself in a separate terminal — I won't see it."
2. **NEVER run `tene export` without `--encrypted`.** Plain `tene export` dumps
every secret as a `.env`-formatted blob to stdout. Use
`tene export --encrypted --file backup.tene.enc` for backups; for
inspection, use `tene list` (names only).
3. **NEVER `cat`, `Read`, or open files under `.tene/`.** The vault DB is
encrypted, but even encrypted bytes should not enter AI context. The allowed
file to read is `CLAUDE.md` at the repo root (auto-generated by
`tene init`).
4. **NEVER pass secret values as CLI arguments.** They appear in `ps`, shell
history, and system logs. Always inject via `tene run --` instead.
5. **Use `tene list` to discover what exists.** It prints key names only, never
values. This is the only AI-safe introspection command.
When the user asks "what's in my vault?" or "what API keys do I have?",
the correct answer is `tene list` — not `tene get` or `tene export`.
---
## Install
```bash
# macOS / Linux (official installer — recommended)
curl -sSfL https://tene.sh/install.sh | sh
# From source (requires Go 1.25+)
go install github.com/tomo-kay/tene/cmd/tene@latest
```
Verify:
```bash
tene version
# → tene v1.x.x (darwin/arm64)
```
Windows is **not** supported by the curl installer. Windows users build from
source or download the zip from https://github.com/tomo-kay/tene/releases.
Homebrew tap is **not yet available** — do not suggest `brew install tene`.
---
## Core workflows
### 1. Initialize a new project
```bash
tene init # interactive: prompts for master password twice
tene init my-project # with explicit project name
```
Creates:
- `.tene/vault.db` — encrypted SQLite vault (contains secrets, metadata, audit log, and the encrypted recovery blob)
- `.tene/vault.json` — project metadata (name, active env)
- `CLAUDE.md` (or `AGENTS.md`, `.windsurfrules`, etc. per flags)
- `.tene/.gitignore` — auto-excludes the vault from git
After `init` the user will see a **12-word recovery phrase**. Remind them to
**store it offline** (password manager, paper). Without it, a forgotten master
password is unrecoverable.
Flags to generate agent rules files for other editors:
- `--claude` (default) → `CLAUDE.md`
- `--cursor` → `.cursor/rules/tene.mdc`
- `--windsurf` → `.windsurfrules`
- `--gemini` → `GEMINI.md`
- `--codex` → `AGENTS.md`
### 2. Check what secrets exist (AI-safe)
```bash
tene list # current env, masked values
tene list --env prod # different env
tene list --json # machine-readable
tene env list # all environments
```
Output is names + masked previews + timestamps. Never raw values.
### 3. Store a secret
Tell the user to run the command **themselves** with the value — don't type the
value yourself, and don't accept it as a chat message you'll pipe through.
```bash
# Preferred: read from stdin (value never touches shell history)
cat key.txt | tene set STRIPE_KEY --stdin
# Or: prompt (value never echoed)
tene set STRIPE_KEY
# → enters interactive mode, hidden input
# Overwrite existing
tene set STRIPE_KEY --stdin --overwrite
# Different env
tene set STRIPE_KEY --stdin --env prod
```
**Key name rules** (enforced by tene, `pkg/errors/codes.go:INVALID_KEY_NAME`):
- Must match `^[A-Z][A-Z0-9_]*$`
- Only uppercase letters, digits, underscores
- Cannot start with a digit
- Reserved names (e.g. `PATH`) are rejected
If the user types a lowercase name, advise conversion to UPPER_SNAKE_CASE.
### 4. Run a command with secrets injected
This is the **primary workflow**. All dev, test, build, and deploy commands go
through `tene run --`.
```bash
# Node.js
tene run -- npm start
tene run -- npm test
tene run -- npx next dev
# Python
tene run -- python manage.py runserver
tene run -- pytest
# Go
tene run -- go run ./cmd/app
tene run -- go test ./...
# Docker
tene run -- docker compose up
```
Secrets are injected **only into the child process's environ**. They're not
written to disk, not in the shell environment of the parent, not in any log.
**Per-environment execution:**
```bash
tene run --env local -- npm start # correct
tene run --env prod -- ./deploy.sh # correct
tene run -- npm start --env prod # WRONG — --env is a flag of npm, not tene
```
**Critical flag placement rule**: `--env` must come **before** the `--`
separator. After `--`, all flags pass through to the child command. This is
enforced by `DisableFlagParsing: true` in `internal/cli/run.go`.
### 5. Migrate from an existing `.env` file
```bash
# One-shot import
tene import .env
# Overwrite conflicts (when some keys already exist)
tene import .env --overwrite
# Then delete the plaintext file
rm .env
echo ".env" >> .gitignore
```
Update all commands to use `tene run --`:
```diff
- npm start
+ tene run -- npm start
```
### 6. Backup and restore
```bash
# Encrypted backup (safe to store in cloud)
tene export --encrypted --file backup.tene.enc
# Restore from encrypted backup
tene import backup.tene.enc --encrypted
```
**Never** use plain `tene export` (without `--encrypted`) unless the user
explicitly asks for a plaintext `.env` dump and accepts the risk. Even then,
warn them.
### 7. Change master password
```bash
tene passwd
# → prompts for current password, then new password (2x confirm)
```
Re-encrypts the entire vault with a new derived key. Atomic 2-phase operation;
on failure, rolls back to the old password.
### 8. Recover a forgotten master password
```bash
tene recover
# → prompts for the 12-word BIP-39 mnemonic from tene init
# → prompts for new master password
```
Without the mnemonic, recovery is impossible by design (zero-knowledge).
### 9. Environment management
```bash
tene env list # show all environments
tene env local # switch default to 'local'
tene env create staging # create new env
tene env delete staging # delete (default env cannot be deleted)
```
Environment name rules: must match `^[a-z][a-z0-9-]*$`.
Common env names: `default`, `local`, `dev`, `staging`, `prod`.
### 10. Diagnostics
```bash
tene whoami # project name, vault path, active env, secret count, keychain status
tene version # v1.x.x (os/arch)
tene version --json # includes commit + build date
tene update --check # check for newer version on S3
tene update # self-update the binary
```
---
## Commands reference
All active commands (cloud commands `login`/`push`/`pull`/`sync`/`billing`/`team`
are currently disabled in the CLI; do not suggest them):
| Command | Purpose | Key flags | AI-safe? |
|---|---|---|---|
| `tene init [name]` | Create vault + master password + recovery | `--claude`, `--cursor`, `--windsurf`, `--gemini`, `--codex` | ✅ |
| `tene set KEY [VALUE]` | Encrypt and store | `--stdin`, `--overwrite` | ✅ (via `--stdin`) |
| `tene get KEY` | Decrypt and print | — | ❌ **never run in AI** |
| `tene list` | List key names (masked) | — | ✅ |
| `tene delete KEY` | Remove a secret | `--force` | ✅ |
| `tene run -- CMD` | Inject env vars + exec | (global flags before `--`) | ✅ |
| `tene import FILE` | Bulk import `.env` or `.tene.enc` | `--overwrite`, `--encrypted` | ✅ |
| `tene export` | Output secrets | `--file`, `--encrypted` | ❌ unless `--encrypted` |
| `tene env [subcmd]` | Manage environments | — | ✅ |
| `tene passwd` | Change master password | — | ✅ (prompts) |
| `tene recover` | Restore via BIP-39 mnemonic | — | ✅ (prompts) |
| `tene version` | Version info | `--json` | ✅ |
| `tene update` | Self-update | `--check` | ✅ |
| `tene whoami` | Vault status | — | ✅ |
### Global flags (apply to all commands)
| Flag | Default | Purpose |
|---|---|---|
| `--json` | false | Machine-readable output |
| `--quiet` / `-q` | false | Suppress non-error output |
| `--env` / `-e <name>` | (vault-stored) | Override active environment |
| `--dir <path>` | cwd | Project directory |
| `--no-color` | false | Disable ANSI colors |
| `--no-keychain` | false | Force file-based key storage (CI mode) |
---
## Environment variables (for advanced / CI use)
| Variable | Effect |
|---|---|
| `TENE_MASTER_PASSWORD` | Bypass interactive prompt (CI only; pair with `--no-keychain`) |
| `TENE_KEYCHAIN_FALLBACK=file` | Use `~/.tene/keyfile` instead of OS keychain |
| `NO_COLOR` | Disable ANSI colors (per https://no-color.org/) |
| `API_URL` | Override Tene Cloud API base URL (cloud commands, currently disabled) |
**CI/CD pattern** (e.g. GitHub Actions):
```yaml
env:
TENE_MASTER_PASSWORD: ${{ secrets.TENE_MASTER_PASSWORD }}
steps:
- run: curl -sSfL https://tene.sh/install.sh | sh
- run: tene run --env prod --no-keychain -- ./deploy.sh
```
Never set `TENE_MASTER_PASSWORD` in a developer machine's shell profile — it
defeats the keychain protection.
---
## Troubleshooting
| Error code | Message | Fix |
|---|---|---|
| `VAULT_NOT_FOUND` | Not in a Tene project | Run `tene init` in the repo root |
| `SECRET_NOT_FOUND` | Key missing in current env | Check `tene list --env <name>` |
| `SECRET_ALREADY_EXISTS` | Key already set | Add `--overwrite` to `tene set` |
| `INVALID_KEY_NAME` | Key rejected | Use `^[A-Z][A-Z0-9_]*$` (e.g. `API_KEY`, not `api-key`) |
| `INVALID_ENV_NAME` | Bad env name | Use `^[a-z][a-z0-9-]*$` (e.g. `prod`, not `Production`) |
| `ENVIRONMENT_PROTECTED` | Cannot delete `default` | Switch to another env and delete that one instead |
| `INVALID_PASSWORD` | Wrong master password | Try again, or use `tene recover` with the BIP-39 mnemonic |
| `DECRYPT_FAILED` | Vault cannot decrypt | Master password changed externally or vault corrupt — restore from backup |
| `INTERACTIVE_REQUIRED` | No TTY | In CI, set `TENE_MASTER_PASSWORD` and add `--no-keychain` |
| `KEYCHAIN_ERROR` | OS keychain unavailable | Use `--no-keychain` or `TENE_KEYCHAIN_FALLBACK=file` |
Exit codes: `0` success, `1` general error, `2` auth/password error,
`127` command not found.
---
## Architecture note (for "is this safe?" questions)
- **Password KDF**: Argon2id (64 MB, 3 iterations, 4 threads) → 256-bit master key
- **Secret encryption**: XChaCha20-Poly1305 with 192-bit random nonce per secret, key name as AAD
- **Key cache**: OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Manager) via `zalando/go-keyring`; file fallback at `~/.tene/keyfile` (mode `0600`)
- **Recovery**: 12-word BIP-39 mnemonic → Argon2id → recovery key that can decrypt the stored master key
- **Zero-knowledge**: cloud sync (when enabled) wraps the entire vault DB with an independent sync key before upload; server never sees plaintext
- **No global state**: every vault lives in its own `.tene/` directory; no `~/.tene/vaults/` aggregator
---
## Further reading
- Homepage: https://tene.sh
- Source + issues: https://github.com/tomo-kay/tene
- Release downloads: https://github.com/tomo-kay/tene/releases
When the user asks about anything not covered above, prefer referring them to
the official docs over guessing.
don't have the plugin yet? install it then click "run inline in claude" again.
tene is a command-line secret manager for AI-native projects. secrets stay encrypted on disk (XChaCha20-Poly1305 + Argon2id), the master key derives from your password cached in the OS keychain, and secrets inject into child processes via tene run -- <command>. never into stdout, never into files.
use this skill when the user mentions secrets, API keys, credentials, tokens, .env files, environment variables, or asks to run a dev/test/deploy command that needs secrets injected. this covers initialization, storage, retrieval (via listing only), injection, environment management, backup/restore, and password recovery. the skill enforces strict AI safety rules: never print plaintext, never read .tene/ files, always use tene run -- for injection, and never pass secrets as CLI arguments.
activate when the user says things like "store this key", "save this token", "how do I manage secrets", "inject env vars", "I need to pass my Stripe key", or works in a repo with a .tene/ directory, a CLAUDE.md referencing tene, or an install.sh pointing to https://tene.sh.
do not activate for: hardcoded public config (ports, feature flags without sensitive data, public API endpoints), or projects clearly using a different secret store (Doppler, Vault, AWS Secrets Manager, 1Password CLI).
required
tene CLI binary installed and in PATH.tene/vault.db file (created by tene init), or permission to create oneoptional
.env file to import (for migration workflows).tene.enc) for restore operationsTENE_MASTER_PASSWORD, TENE_KEYCHAIN_FALLBACK, NO_COLORtene init <name>external connections
~/.tene/keyfile (mode 0600). set --no-keychain flag or TENE_KEYCHAIN_FALLBACK=file to use file mode in CI.# macOS / Linux (official installer, recommended)
curl -sSfL https://tene.sh/install.sh | sh
# from source (requires Go 1.25+)
go install github.com/tomo-kay/tene/cmd/tene@latest
# verify
tene version
# → tene v1.x.x (darwin/arm64)
output: tene binary in PATH, tene version prints without error.
edge case: Windows users must build from source or download the zip from https://github.com/tomo-kay/tene/releases. Homebrew tap does not yet exist; do not suggest brew install tene.
inputs: user's master password (prompted interactively, never accept it as a chat message).
tene init # interactive: prompts for master password twice
tene init my-project # with explicit project name
flags for agent rule files (optional):
--claude (default) → creates CLAUDE.md--cursor → creates .cursor/rules/tene.mdc--windsurf → creates .windsurfrules--gemini → creates GEMINI.md--codex → creates AGENTS.mdoutput:
.tene/vault.db , encrypted SQLite vault (secrets, metadata, audit log, encrypted recovery blob).tene/vault.json , project metadata (name, active env)CLAUDE.md or equivalent).tene/.gitignore , auto-excludes vault from gitoutcome: user sees a 12-word recovery phrase. instruct them to store it offline (password manager, paper). without it, a forgotten master password is unrecoverable.
inputs: optional --env flag to specify environment (defaults to active env).
tene list # current env, key names + masked preview
tene list --env prod # different env
tene list --json # machine-readable output
tene env list # show all available environments
output: key names, masked value previews (e.g. API_KEY: str**...(16 chars)), timestamps. never raw values.
outcome signal: user sees a list of secret names they own. this is the only AI-safe way to answer "what secrets do I have?".
critical: never accept the secret value as a chat message. instruct the user to run the command themselves.
inputs: key name (must match ^[A-Z][A-Z0-9_]*$), secret value provided by user in their own terminal.
# preferred: read from stdin (value never touches shell history)
cat key.txt | tene set STRIPE_KEY --stdin
# or: interactive prompt (hidden input)
tene set STRIPE_KEY
# → prompts for value, input is not echoed
# overwrite existing key
tene set STRIPE_KEY --stdin --overwrite
# store in different environment
tene set STRIPE_KEY --stdin --env prod
key name validation (enforced by tene):
PATH) rejectedoutput: confirmation message "secret stored" or equivalent error if validation fails.
edge cases:
--overwrite or command will failINVALID_KEY_NAME error; advise format fix--env flag targets correct env nameinputs: tene environment flag (optional), command and its args.
this is the primary workflow for dev, test, build, and deploy.
# Node.js
tene run -- npm start
tene run -- npm test
tene run -- npx next dev
# Python
tene run -- python manage.py runserver
tene run -- pytest
# Go
tene run -- go run ./cmd/app
tene run -- go test ./...
# Docker
tene run -- docker compose up
# per-environment execution
tene run --env local -- npm start # correct
tene run --env prod -- ./deploy.sh # correct
critical flag placement rule: --env and other tene flags must come before the -- separator. after --, all flags pass to the child command.
incorrect example:
tene run -- npm start --env prod # WRONG: --env is treated as npm flag
output: secrets injected only into child process environment. not written to disk, not in parent shell env, not in logs. child process stdout/stderr flows through tene as-is.
edge cases:
TENE_KEYCHAIN_FALLBACK=file set, else fails with KEYCHAIN_ERRORinputs: existing .env file in project root.
# one-shot import
tene import .env
# overwrite if keys already exist in vault
tene import .env --overwrite
# then delete plaintext file
rm .env
echo ".env" >> .gitignore
output: each line parsed as KEY=VALUE and stored individually. feedback on success/conflicts.
update commands in scripts, package.json, Dockerfile, etc:
- npm start
+ tene run -- npm start
edge cases:
--overwrite flag or skip on conflictinputs (for backup): optional --file flag to specify output path.
inputs (for restore): encrypted backup file path, --encrypted flag.
# encrypted backup (safe for cloud storage, version control)
tene export --encrypted --file backup.tene.enc
# restore from encrypted backup
tene import backup.tene.enc --encrypted
output: encrypted binary file (not human-readable). restore verifies integrity before writing to vault.
edge cases:
tene export overwrites without prompt; advise user to move old backup firsttene import backup.tene.enc --encrypted --overwriteDECRYPT_FAILED error; user must restore from another backup or re-enter secretsnever use plain tene export (without --encrypted). it dumps secrets as .env text to stdout. warn user if they explicitly ask for plaintext.
inputs: user's current and new master passwords (prompted interactively).
tene passwd
# → prompts: enter current password
# → prompts: enter new password (2x confirm)
output: success message or INVALID_PASSWORD error. entire vault is re-encrypted with new derived key.
outcome: master password changed, recovery mnemonic unchanged (user does not need to write down a new one).
edge cases:
tene recover if truly forgotteninputs: 12-word BIP-39 mnemonic from tene init output, new master password.
tene recover
# → prompts for 12-word recovery mnemonic
# → prompts for new master password (2x confirm)
output: master password reset, vault remains intact. recovery key decrypts the stored master key.
outcome: user can access vault again without knowing the old password.
edge cases:
tene passwd or tene recover againinputs: environment name (must match ^[a-z][a-z0-9-]*$).
tene env list # show all environments
tene env local # switch default to 'local'
tene env create staging # create new environment
tene env delete staging # delete environment (not 'default')
common names: default, local, dev, staging, prod.
output: list of env names, or confirmation of create/delete.
edge cases:
default env: rename or re-initialize vault if truly neededinputs: optional --json flag for structured output.
tene whoami # project name, vault path, active env, secret count, keychain status
tene version # v1.x.x (os/arch)
tene version --json # includes commit + build date
tene update --check # check for newer version
tene update # self-update the binary
output: human-readable or JSON formatted diagnostics.
outcome: user confirms vault is accessible and no corruption detected.
user asks "what secrets do I have?" or "list my API keys"
tene listtene get KEY in their own terminal, not via AI. explain that plaintext values in the AI context can be logged/cacheduser wants to backup or migrate secrets
tene export --encrypted --file backup.tene.enc--encrypted; never plain tene exporttene import .env [--overwrite], then delete plaintext .env fileuser asks to run a command with secrets
tene run -- <command>tene run --env prod -- <command>user forgot their master password
tene recoveruser's keychain is unavailable (e.g., CI environment)
TENE_MASTER_PASSWORD env var and use --no-keychain flagTENE_KEYCHAIN_FALLBACK=file and ensure ~/.tene/keyfile is readable (mode 0600)user wants to import secrets from a different tene project
tene import backup.tene.enc --encryptedtene import .env--overwrite to replaceuser asks if tene is "safe" or about encryption details
success for all commands:
tene init: outputs 12-word recovery mnemonic (user must write down), creates .tene/vault.db and .tene/vault.json, generates agent rules file if flaggedtene list: outputs table of key names + masked values + timestamps, never plaintext secretstene set: silent on success (or brief "stored" message); errors print to stderrtene run --: child process runs with injected env vars in its environ; child's stdout/stderr flows through tene unmodifiedtene import: summarizes count of imported secrets, reports any skipped or conflicting keystene export --encrypted: writes encrypted binary blob to file (path specified by --file flag, default ./backup.tene.enc)tene passwd: silent on success; errors explain what went wrongtene recover: silent on success; recovery key decrypts vaulttene env [subcommand]: lists environments or confirms create/deletetene whoami: outputs project name, vault path, active environment, count of secrets, keychain status (available or fallback-mode)tene version: prints version string (plain or JSON)tene update: checks for or installs newer binary; reports resultfile locations:
.tene/vault.db (encrypted).tene/vault.json.tene/.gitignore~/.tene/keyfile (only if --no-keychain or TENE_KEYCHAIN_FALLBACK=file)CLAUDE.md (or .cursor/rules/tene.mdc, .windsurfrules, GEMINI.md, AGENTS.md per flags)./backup.tene.enc)data format:
--json)user knows the skill worked when:
.tene/ directory exists with vault.db, CLAUDE.md (or equivalent) createdAPI_KEY: str**...(16 chars)); no plaintext values visibletene list and see the new keyecho $SECRET_NAME inside the process, not from tene output)