# rxv — secret store for remote execution workflows

rxv manages age-encrypted secrets with kernel keyring-backed identity.
Secrets are files on disk. The decryption key lives in kernel memory only —
never on disk, auto-evicts after timeout.

## Commands

rxv unlock              # Load age identity into keyring (prompts via provider)
rxv lock                # Revoke identity from keyring
rxv status              # Check if unlocked (JSON)
rxv list [HOST]         # List available secrets (JSON)
rxv get HOST/NAME       # Decrypt secret to stdout
rxv set HOST/NAME       # Encrypt stdin as new secret

## Typical agent workflow

# Compose with rx:
rxv get host1/db_password | rx start host1 tee /run/secrets/db_pass

# Check what's available:
rxv list host1
# → {"type":"list","entries":["host1/db_password","host1/api_key"]}

# Read a secret:
rxv get host1/api_key
# → plaintext value on stdout

## Store layout

Secrets live as .age files:

  ~/.local/share/rxv/
    host1/
      db_password.age
      api_key.age
    host2/
      tls_cert.age

File path = secret identity. No manifest, no database.

## Security model

- Age identity loaded into Linux kernel keyring (`keyctl`)
- Key never touches disk — lives in kernel memory only
- Auto-evicts after configured timeout (default 8h)
- Provider (1Password, stdin, etc.) supplies key on `rxv unlock`
- `rxv set` only needs the public key — no unlock required to store secrets
- Closing the terminal session revokes access

## Configuration

~/.config/rxv/config.toml

  keyctl_name = "rxv-identity"    # keyring key name
  timeout = 28800                 # seconds before kernel evicts key

  [provider]
  type = "op"                     # "op" or "stdin"
  ref = "op://vault/item/field"   # 1Password secret reference

  [store]
  path = "~/.local/share/rxv"
  public_key = "age1..."          # for encrypting (rxv set)

## JSON responses

### status
{"type":"status","unlocked":true,"key_id":"12345","description":"..."}
{"type":"status","unlocked":false}

### list
{"type":"list","entries":["host1/db_password","host1/api_key"]}

### error
{"type":"error","message":"not unlocked — run `rxv unlock` first"}

## Error recovery

- "not unlocked": Run `rxv unlock` — provider will prompt for auth.
- "already unlocked": Identity is loaded. Use `rxv lock` to revoke and re-unlock.
- "secret not found": Check path with `rxv list`. Format: host/name (no .age suffix).
- "age decrypt failed": Identity may have expired. Run `rxv unlock` again.
- "provider did not return a valid age identity": Provider returned unexpected data.
  Check the provider reference in config.

## Dependencies

- age (encryption/decryption)
- keyctl (kernel keyring management)
- Provider CLI (op for 1Password, etc.)
