oth_rvault 0.3.0

Partial Ansible Vault encoder and decoder
Documentation
[![crates.io](https://img.shields.io/crates/v/oth_rvault.svg)](https://crates.io/crates/oth_rvault)
[![Docker Hub](https://img.shields.io/badge/docker-okieoth%2Frvault-blue?logo=docker)](https://hub.docker.com/r/okieoth/rvault)

# rvault

A tool to encrypt or decrypt JSON or YAML files that contain partial Ansible vault encrypted values.

This is a Rust port from here: https://github.com/OkieOth/partial-vaults/tree/main

## Overview

rvault allows you to work with files that have a mix of plain text and Ansible Vault encrypted values. Unlike standard Ansible Vault which encrypts entire files, this tool lets you:

- Encrypt specific values within JSON or YAML files
- Decrypt specific values while leaving others encrypted
- Process nested structures with selective encryption
- Interactively edit encrypted values in-place
- Copy decrypted values to the clipboard with auto-clear

## Install

```bash
# install from crates.io
cargo install oth_rvault

# use
rvault --help
```

## Usage

rvault has four subcommands: `encrypt`, `decrypt`, `edit`, and `clip`.

### Password resolution

All subcommands need a vault password. It is resolved in this priority order:

1. `--password <PASSWORD>` — pass the password directly on the command line
2. `--pfile <FILE>` — read the password from a file
3. `MY_RVAULT_PWD` environment variable — path to a password file

```bash
# inline password
rvault decrypt -i secrets.yaml --stdout --password mypassword

# password from file
rvault decrypt -i secrets.yaml --stdout --pfile ~/.vault_pass

# password from environment (recommended for scripts)
export MY_RVAULT_PWD=~/.vault_pass
rvault decrypt -i secrets.yaml --stdout
```

### Output target

For `encrypt`, `decrypt`, and `edit`, exactly one output option must be given:

| Flag | Behaviour |
|------|-----------|
| `-o <FILE>` | Write result to a new file |
| `--overwrite` | Replace the input file in-place |
| `--stdout` | Print result to standard output |

---

### encrypt

Encrypt scalar values inside a JSON or YAML file using Ansible Vault AES256.

```
rvault encrypt -i <INPUT> (-o <OUTPUT> | --overwrite | --stdout)
               --password <PASSWORD>
               [-k <KEY>]...
               [--level <DEPTH>]
               [--ansible]
```

**Encrypt all values** in a file:

```bash
rvault encrypt -i example.json -o encrypted.json --password secret
```

**Encrypt specific keys** using dot-notation paths:

```bash
# single key
rvault encrypt -i example.json -o encrypted.json --password secret -k first.a

# multiple keys (repeat the flag or use comma-separation)
rvault encrypt -i example.json -o encrypted.json --password secret \
    -k first.a -k first.z

rvault encrypt -i example.json -o encrypted.json --password secret \
    -k first.a,first.z
```

**Encrypt by nesting depth** with `--level` (cannot be combined with `--key`):

```bash
# encrypt all values at the top level (depth 1)
rvault encrypt -i example.json -o encrypted.json --password secret --level 1

# encrypt all values two levels deep
rvault encrypt -i example.yaml --overwrite --password secret --level 2
```

**Overwrite the file in-place:**

```bash
rvault encrypt -i secrets.yaml --overwrite --password secret
```

**Print to stdout** (useful for piping):

```bash
rvault encrypt -i example.json --stdout --password secret
```

---

### decrypt

Decrypt some or all encrypted values in a partially-encrypted file.

```
rvault decrypt -i <INPUT> (-o <OUTPUT> | --overwrite | --stdout)
               --password <PASSWORD>
               [-k <KEY>]...
               [--value-only]
               [--interactive]
```

**Decrypt all values:**

```bash
rvault decrypt -i encrypted.json -o plain.json --password secret
```

**Decrypt specific keys:**

```bash
rvault decrypt -i encrypted.json --stdout --password secret -k second.a.v
```

**Value-only mode** (`--value-only` / `-v`) — output only the selected key(s) as a minimal nested document (requires at least one `--key` or `--interactive`):

```bash
# prints just the nested sub-document for the selected keys
rvault decrypt -i encrypted.json --stdout --password secret \
    -k second.a.v --value-only
```

**Interactive (fuzzy) mode** — opens an interactive prompt to select which encrypted keys to decrypt:

```bash
rvault decrypt -i encrypted.json --stdout --password secret --interactive

# combine with --value-only to output only the selected values
rvault decrypt -i encrypted.json --stdout --password secret \
    --interactive --value-only
```

---

### edit

Decrypt a value, open it in `$EDITOR`, then re-encrypt and save.

```
rvault edit -i <INPUT> (-o <OUTPUT> | --overwrite | --stdout)
            --password <PASSWORD>
            [-k <KEY>]...
            [-a | --interactive]
```

**Edit a specific key:**

```bash
rvault edit -i secrets.yaml --overwrite --password secret -k database.password
```

**Edit multiple keys:**

```bash
rvault edit -i secrets.yaml --overwrite --password secret \
    -k database.password -k api.token
```

**Fuzzy-select which key to edit** (default when no `--key` is given):

```bash
rvault edit -i secrets.yaml --overwrite --password secret
```

**Edit all encrypted values without selection** (`-a`):

```bash
rvault edit -i secrets.yaml --overwrite --password secret -a
```

---

### clip

Fuzzy-search encrypted values and copy the decrypted result to the clipboard. The clipboard is automatically cleared after a timeout.

```
rvault clip -i <INPUT> --password <PASSWORD>
            [--timeout <SECONDS>]
            [--pfile <FILE>]
```

```bash
# default: clear clipboard after 30 seconds
rvault clip -i secrets.yaml --password secret

# keep clipboard for 60 seconds
rvault clip -i secrets.yaml --password secret --timeout 60

# never clear the clipboard automatically
rvault clip -i secrets.yaml --password secret --timeout 0
```

---

## File format

rvault works with plain JSON or YAML files. Encrypted values are stored inline as Ansible Vault strings:

```yaml
# example.yaml — mixed plain/encrypted YAML
first:
    z: last
    a: first
    m: middle
second:
    a:
        v: true   # plain boolean
        w: 14     # plain integer
        x: "$ANSIBLE_VAULT;1.1;AES256\n..."  # encrypted string
```

```json
{
  "first": {
    "z": "last",
    "a": "$ANSIBLE_VAULT;1.1;AES256\n...",
    "m": "middle"
  }
}
```

The original Rust type (bool, integer, float, string) is preserved across an encrypt→decrypt round-trip.

---

## Docker

```bash
# decrypt a file
docker run --rm -u $(id -u):$(id -g) \
    -v /path/to/input:/input \
    -v /path/to/output:/output \
    docker.io/okieoth/rvault:latest decrypt \
     -i /input/secrets.yaml -o /output/decrypted.yaml \
     --password mypassword

# encrypt a file
docker run --rm -u $(id -u):$(id -g) \
    -v /path/to/input:/input \
    -v /path/to/output:/output \
    docker.io/okieoth/rvault:latest encrypt \
     -i /input/plain.yaml -o /output/secrets.yaml \
     --password mypassword
```

---

## Build

```bash
# build binary
cargo build

# run tests
cargo test

# build docker image
make docker-build
```

---

# Additional

* Password for `resources/tests/partial.yaml`: `aa`
* Password for `resources/tests/partial_encrypted_example.json`: `test999`
* Password for `resources/tests/partial_encrypted_example.yaml`: `test999`