npm package: @homeflare/hfs
Description: Encrypted secret vault CLI for Cloudflare Workers
Latest version: 0.28.0

# hfs - Encrypted Secret Vault CLI

CLI for managing your encrypted secret vault on Cloudflare Workers.

[![GitHub Package](https://img.shields.io/badge/GitHub-Package-181717?logo=github&logoColor=white)](https://github.com/FlarelyLegal/worker-secrets/packages)
[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![Commander.js](https://img.shields.io/badge/Commander.js-red)](https://github.com/tj/commander.js)
[![Node.js](https://img.shields.io/badge/Node.js-20+-339933?logo=node.js&logoColor=white)](https://nodejs.org/)

[![skill: add-command](https://img.shields.io/badge/skill-add--command-5D5CDE)](../.agents/skills/add-command/SKILL.md)
[![skill: deploy](https://img.shields.io/badge/skill-deploy-5D5CDE)](../.agents/skills/deploy/SKILL.md)

## Install

```bash
# Homebrew (macOS/Linux)
brew tap homeflare/tap && brew install homeflare

# npm
npm install -g @homeflare/hfs
```

Installs both `homeflare secrets` (umbrella CLI) and `hfs` (shortcut alias).

Requires [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) for interactive login.

## Getting started

```bash
hfs config set --url https://secrets.yourcompany.com   # point to your vault
hfs login                                               # authenticate (opens browser)
hfs keygen --register                                   # generate e2e encryption key
```

An admin gives you the vault URL. If auto-provisioning is enabled, your account is created on first login.

## Auth

Two modes, no fallback. They never mix.

**Human** - Set the vault URL with `hfs config set --url`, then `hfs login` opens a browser via cloudflared. Tap your YubiKey or use your IdP. Short-lived JWT stored locally.

**Machine** - Set `HFS_URL`, `HFS_CLIENT_ID`, `HFS_CLIENT_SECRET` env vars. Must correspond to a registered service token. Tokens registered with `--secret` authenticate via hashed client secret and work on all endpoints, not just Access-protected paths.

## Commands

### Secrets
```
hfs get <key>              Get a decrypted secret
hfs get <key> -q           Print only the value (pipe-friendly)
hfs get <key> -j           Output as JSON
hfs set <key> <value>      Store a secret
hfs set <key> --from-file  Read value from a file
hfs set <key> -t <tags>    Set with comma-separated tags
hfs set <key> --expires <date>  Set with expiry (ISO 8601 date or duration)
hfs set <key> --ttl <dur>  Set with relative expiry (e.g. 90d, 12h, 2w)
hfs get <key> --resolve    Resolve ${SECRET} references in the value
hfs env --resolve <keys>   Resolve references in exported values
hfs expiring               List secrets expiring within 7 days (shows EXPIRES column)
hfs expiring --within 30d  Custom time window
hfs rm <key>               Delete a secret (confirms first)
hfs ls                     List all secret keys
hfs versions <key>         List version history
hfs restore <key> <id>     Restore a previous version
hfs env <key> [key...]     Output as KEY=value (dashes → underscores)
hfs env -e <key> [key...]  Same with export prefix
hfs export                 Export all as JSON (includes tags)
hfs import <file>          Import from JSON
hfs cp <src> <dest>        Copy a secret to a new key
hfs cp <src> <dest> -m     Move (copy + delete source, confirms first)
hfs diff <key> <id>        Compare current value with a version
hfs template <file>        Render {{SECRET_KEY}} placeholders from vault
hfs template <file> -o out Write rendered output to file
hfs template <file> --resolve  Also resolve ${SECRET} references in rendered output
```

### End-to-end encryption
```
hfs keygen                 Generate age identity + bind ZT device (one-time setup)
hfs pubkey                 Show your age public key (share with teammates)
hfs set <key> <value> --e2e          Encrypt for all eligible team members (RBAC)
hfs set <key> <value> --private      Encrypt for only yourself (not shared)
hfs set <key> <value> --recipients <file>  Encrypt for explicit recipient list
hfs rewrap <key>               Re-encrypt for current eligible recipients
hfs rewrap --all               Re-encrypt all e2e secrets (after user changes)
hfs get <key>              Auto-decrypts e2e secrets with your identity
hfs get <key> --raw        Show age ciphertext (what the server stores)
hfs config set --e2e-identity <path>  Use a custom identity file
```

The server never sees plaintext for `--e2e` secrets. Uses [age](https://age-encryption.org/) encryption.

### Auth & config
```
hfs login                  Authenticate via cloudflared
hfs logout                 Clear stored session
hfs whoami                 Show auth method, role, and scopes
hfs whoami -j              Output as JSON
hfs health                 Check vault connectivity (no auth)
hfs config set --url <url> Set vault URL
hfs config show            Show config + auth status
hfs config clear           Clear all config
```

### Users (admin only)
```
hfs user ls                List all users
hfs user add <email> -r <role> [-n <name>]  Add or update a user
hfs user rm <email>        Remove a user
hfs user disable <email>   Disable without deleting
hfs user enable <email>    Re-enable a disabled user
hfs user role <email> <role>  Change a user's role
```

### Roles (admin only)
```
hfs role ls                List all roles with scopes and allowed_tags
hfs role set <name> <scopes> [-d <desc>] [--allowed-tags <tags>]  Create or update a role
hfs role rm <name>         Delete a role (must have no users)
hfs role policy ls <role>                      List policies for a role
hfs role policy add <role> <scopes> [-t tags]  Add a policy rule
hfs role policy rm <role> <id>                 Remove a policy rule by ID
```

### Service tokens (interactive only)
```
hfs token register <id> -n <name> [-s <scopes>] [-r <role>] [--secret 
] [--age-key <k>] [-d <desc>]
hfs token revoke <id>      Revoke a token
hfs token ls               List tokens with last-used times
```

### Feature flags
```
hfs flag ls                List all flags
hfs flag get <key>         Get a flag value (-q for value only, -j for JSON)
hfs flag set <key> <value> Set a flag (auto-detects type)
hfs flag rm <key>          Delete a flag
```

Auto-type detection: `"true"`/`"false"` become boolean, numeric strings become number, valid JSON objects become json, everything else is string. Flags are plaintext KV, not encrypted like secrets.

### Profiles
```
hfs profile ls              List available profiles (tags) with secret counts
hfs profile show <tag>      List secrets in a profile
hfs profile env <tag>       Export profile secrets as shell variables (KEY=value)
hfs profile env <tag> -e    Same with export prefix
hfs profile env <tag> --resolve  Resolve ${SECRET} references in exported values
hfs profile diff <a> <b>    Compare two profiles side by side
```

Profiles are virtual groupings based on tags. Any tag you apply to secrets becomes a profile automatically.

### Admin
```
hfs audit [-n 100] [-j]    View audit log
hfs audit --action get     Filter by action
hfs audit --identity <email> Filter by identity
hfs audit --key <key>      Filter by secret key
hfs audit --from/--to <date> Filter by date range
hfs audit consumers <key>  Show who accessed a secret (identity, agent, count)
hfs audit-verify [-n 1000] Verify audit log hash chain integrity
hfs re-encrypt             Migrate legacy secrets to envelope encryption
hfs rotate-key --stdin     Re-wrap all DEKs with a new master key (reads from stdin)
hfs rotate-key <new-key>   Same, but key on command line (leaks to shell history)
hfs deploy                 Deploy the Worker to Cloudflare
hfs completion bash|zsh    Generate shell completions
```

## Scopes

`read`, `write`, `delete`, or `*` (default). Combine: `read,write`

## Examples

```bash
# Load secrets into shell
eval $(hfs env -e API_KEY DB_PASSWORD)

# Pipe into tools
hfs get github-token -q | gh auth login --with-token

# Store a cert from file
hfs set tls-cert --from-file ./cert.pem -d "Prod TLS"

# Backup and restore
hfs export > backup.json
hfs import backup.json --overwrite

# Register a read-only CI token
hfs token register abc.access -n ci-pipeline -s read --secret "your-client-secret"

# Audit
hfs audit -n 50
```

## Shell integration

Load secrets into your shell on startup. Add to `~/.bashrc` or `~/.zshrc`:

```bash
# Load secrets from vault (requires hfs login session)
if command -v hfs &>/dev/null && hfs health &>/dev/null 2>&1; then
  eval $(hfs env -e CLOUDFLARE_API_KEY GITHUB_TOKEN 2>/dev/null)
fi
```

This silently skips if `hfs` isn't installed, the vault is unreachable, or the session is expired. Secrets are fetched fresh on each shell startup.

For CI/CD, use service token env vars instead:

```bash
export HFS_URL=https://vault.example.com
export HFS_CLIENT_ID=abc123.access
export HFS_CLIENT_SECRET=your-secret
eval $(hfs env -e DEPLOY_KEY DB_PASSWORD)
```

## Backup and restore

```bash
# Export all secrets (decrypted JSON)
hfs export > vault-backup.json

# Restore to same or different vault
hfs import vault-backup.json --overwrite
```

The export file contains decrypted values. Store it securely and delete after use. If you change the `ENCRYPTION_KEY`, existing secrets become unreadable. Export first, rotate the key, then re-import.

## Security

RBAC with custom roles, no credentials stored on disk (URL + short-lived JWT only), hard errors on expired auth, and every operation audit-logged.

See [Encryption Architecture](../docs/encryption.md) for detailed diagrams and [Threat Model](../SECURITY.md) for the hardening guide.