# HOW-TO: Configure and Run Encryption Services for typedialog

## Overview

This guide walks through setting up **Age** (local file-based encryption) and **RustyVault** (HTTP-based encryption service) to test the typedialog encryption pipeline end-to-end.

**Service Matrix:**

| Backend | Type | Setup Complexity | Network | Requires |
 | --------- | ------ | ------------------ | --------- | ---------- |
| **Age** | Local file-based | Trivial | None | age CLI tool |
| **RustyVault** | HTTP vault server | Moderate | localhost:8200 | Docker or manual build |
| **SOPS** | External tool | Complex | Varies | sops CLI + backends |

This guide covers Age (trivial) and RustyVault (moderate). SOPS is skipped for now.

---

## Part 1: Age Backend (Local File Encryption)

### What is Age

Age is a simple, modern encryption tool using X25519 keys. Perfect for development because:

- No daemon/service required
- Keys stored as plaintext files
- Single binary

### Installation

**macOS (via Homebrew):**

```bash
brew install age
```text

**Linux (Ubuntu/Debian):**

```bash
sudo apt-get install age
```text

**Manual (any OS):**

```bash
# Download from https://github.com/FiloSottile/age/releases
# Extract and add to PATH
tar xzf age-v1.1.1-linux-amd64.tar.gz
sudo mv age/age /usr/local/bin/
sudo mv age/age-keygen /usr/local/bin/
```text

**Verify installation:**

```bash
age --version
# age v1.1.1
```text

### Generate Age Key Pair

Age uses a single private key file that contains both public and private components. The public key is derived from the private key.

**Generate keys for testing:**

```bash
# Create a test directory
mkdir -p ~/.age

# Generate private key
age-keygen -o ~/.age/key.txt

# Output will show
# Public key: age1...xxx (save this, shown in file)
# Written to /home/user/.age/key.txt
```text

**Verify key generation:**

```bash
# Check private key exists
cat ~/.age/key.txt
# Output: AGE-SECRET-KEY-1XXXX...

# Extract public key (age CLI does this automatically)
grep "^public key:" ~/.age/key.txt | cut -d' ' -f3
```text

### Test Age Encryption Locally

**Create a test plaintext file:**

```bash
echo "This is a secret message" > test_message.txt
```text

**Encrypt with age:**

```bash
# Get public key from private key
PUBLIC_KEY=$(grep "^public key:" ~/.age/key.txt | cut -d' ' -f3)

# Encrypt
age -r "$PUBLIC_KEY" test_message.txt > test_message.age

# Verify ciphertext is unreadable
cat test_message.age
# Output: AGE-ENCRYPTION-V1...binary...
```text

**Decrypt with age:**

```bash
# Decrypt (will prompt for passphrase if key is encrypted)
age -d -i ~/.age/key.txt test_message.age

# Output: This is a secret message
```text

### Configure typedialog to Use Age

**Environment variables:**

```bash
export AGE_KEY_FILE="$HOME/.age/key.txt"
```text

**CLI flags:**

```bash
# Redact mode (no encryption needed)
typedialog form examples/08-encryption/simple-login.toml --redact --format json

# Encrypt mode (requires Age backend)
typedialog form examples/08-encryption/simple-login.toml --encrypt --backend age --key-file ~/.age/key.txt --format json
```text

See `examples/08-encryption/README.md` for more example forms and test cases.

**TOML form configuration:**

```toml
[[fields]]
name = "password"
type = "password"
prompt = "Enter password"
sensitive = true
encryption_backend = "age"

[fields.encryption_config]
key = "~/.age/key.txt"
```text

---

## Part 2: RustyVault Backend (HTTP Service)

### What is RustyVault

RustyVault is a Rust implementation of HashiCorp Vault's Transit API:

- HTTP-based encryption/decryption service
- Suitable for production environments
- API-compatible with Vault Transit secrets engine

### Installation & Setup

**Option A: Docker (Recommended for testing)**

RustyVault provides official Docker images. Check availability:

```bash
# Search Docker Hub
docker search rustyvault

# Or build from source
git clone https://github.com/Tongsuo-Project/RustyVault.git
cd RustyVault
docker build -t rustyvault:latest .
```text

**Option B: Manual Build (if Docker not available)**

```bash
# Prerequisites: Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Clone and build
git clone https://github.com/Tongsuo-Project/RustyVault.git
cd RustyVault
cargo build --release

# Binary at: target/release/rustyvault
```text

### Run RustyVault Service

**Using Docker (single command):**

```bash
docker run -d \
  --name rustyvault \
  -p 8200:8200 \
  -e RUSTYVAULT_LOG_LEVEL=info \
  rustyvault:latest

# Verify it started
docker logs rustyvault | head -20
```text

**Using local binary:**

```bash
# Create config directory
mkdir -p ~/.rustyvault
cd ~/.rustyvault

# Create minimal config (rustyvault.toml)
cat > config.toml <<'EOF'
[server]
address = "127.0.0.1:8200"
tls_disable = true

[backend]
type = "inmem"  # In-memory storage (ephemeral)
EOF

# Run service
~/RustyVault/target/release/rustyvault server -c config.toml
```text

**Verify service is running:**

```bash
# In another terminal
curl -s http://localhost:8200/v1/sys/health | jq .
# Should return health status JSON
```text

### Configure RustyVault for Encryption

**Initialize RustyVault (first time only):**

```bash
# Generate initial token
VAULT_INIT=$(curl -s -X POST http://localhost:8200/v1/sys/init \
  -d '{"secret_shares": 1, "secret_threshold": 1}' | jq -r .keys[0])

# Unseal vault
curl -s -X PUT http://localhost:8200/v1/sys/unseal \
  -d "{\"key\": \"$VAULT_INIT\"}" > /dev/null

# Save root token
ROOT_TOKEN=$(curl -s -X POST http://localhost:8200/v1/sys/unseal \
  -d "{\"key\": \"$VAULT_INIT\"}" | jq -r .auth.client_token)

export VAULT_TOKEN="$ROOT_TOKEN"
```text

**Enable Transit secrets engine:**

```bash
curl -s -X POST http://localhost:8200/v1/sys/mounts/transit \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d '{"type": "transit"}' | jq .
```text

**Create encryption key:**

```bash
curl -s -X POST http://localhost:8200/v1/transit/keys/typedialog-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d '{}' | jq .

# Verify key created
curl -s http://localhost:8200/v1/transit/keys/typedialog-key \
  -H "X-Vault-Token: $VAULT_TOKEN" | jq .
```text

### Test RustyVault Encryption

**Encrypt data via HTTP:**

```bash
# Plaintext (base64 encoded)
PLAINTEXT=$(echo -n "my-secret-password" | base64)

curl -s -X POST http://localhost:8200/v1/transit/encrypt/typedialog-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d "{\"plaintext\": \"$PLAINTEXT\"}" | jq .data.ciphertext
```text

**Decrypt data via HTTP:**

```bash
# From encryption output above
CIPHERTEXT="vault:v1:..."

curl -s -X POST http://localhost:8200/v1/transit/decrypt/typedialog-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d "{\"ciphertext\": \"$CIPHERTEXT\"}" | jq -r .data.plaintext | base64 -d
```text

### Configure typedialog to Use RustyVault

**Environment variables:**

```bash
export VAULT_ADDR="http://localhost:8200"
export VAULT_TOKEN="s.xxxx..." # Token from above
```text

**CLI flags:**

```bash
typedialog form examples/08-encryption/credentials.toml \
  --encrypt \
  --backend rustyvault \
  --vault-addr http://localhost:8200 \
  --vault-token "s.xxxx..." \
  --vault-key-path "transit/keys/typedialog-key" \
  --format json
```text

This form includes field-level RustyVault configuration in the `vault_token` field.

**TOML form configuration:**

```toml
[[fields]]
name = "password"
type = "password"
prompt = "Enter password"
sensitive = true
encryption_backend = "rustyvault"

[fields.encryption_config]
vault_addr = "http://localhost:8200"
vault_token = "s.xxxx..."
key_path = "transit/keys/typedialog-key"
```text

---

## Part 3: Complete Integration Test Workflow

### Script: Setup Everything

Create `scripts/encryption-test-setup.sh`:

```bash
#!/usr/bin/env bash
set -e

echo "=== typedialog Encryption Services Setup ==="

# Age Setup
echo "1. Setting up Age..."
if ! command -v age &> /dev/null; then
  echo "   ✗ age not installed. Run: brew install age"
  exit 1
fi

mkdir -p ~/.age
if [ ! -f ~/.age/key.txt ]; then
  echo "   → Generating Age keys..."
  age-keygen -o ~/.age/key.txt
fi
export AGE_KEY_FILE="$HOME/.age/key.txt"
echo "   ✓ Age configured at: $AGE_KEY_FILE"

# RustyVault Setup (Docker)
echo ""
echo "2. Setting up RustyVault (Docker)..."
if ! command -v docker &> /dev/null; then
  echo "   ⚠ Docker not installed, skipping RustyVault"
  echo "   → Install Docker or skip RustyVault tests"
else
  if ! docker ps | grep -q rustyvault; then
    echo "   → Starting RustyVault container..."
    docker run -d \
      --name rustyvault \
      -p 8200:8200 \
      -e RUSTYVAULT_LOG_LEVEL=info \
      rustyvault:latest
    sleep 2
  fi

  # Initialize vault
  echo "   → Initializing RustyVault..."
  VAULT_INIT=$(curl -s -X POST http://localhost:8200/v1/sys/init \
    -d '{"secret_shares": 1, "secret_threshold": 1}' | jq -r .keys[0])

  curl -s -X PUT http://localhost:8200/v1/sys/unseal \
    -d "{\"key\": \"$VAULT_INIT\"}" > /dev/null

  # Get root token
  RESPONSE=$(curl -s -X GET http://localhost:8200/v1/sys/unseal \
    -H "X-Vault-Token: $VAULT_INIT")
  export VAULT_TOKEN=$(echo "$RESPONSE" | jq -r .auth.client_token // "root")
  export VAULT_ADDR="http://localhost:8200"

  # Enable transit
  curl -s -X POST http://localhost:8200/v1/sys/mounts/transit \
    -H "X-Vault-Token: $VAULT_TOKEN" \
    -d '{"type": "transit"}' > /dev/null 2>&1 || true

  # Create key
  curl -s -X POST http://localhost:8200/v1/transit/keys/typedialog-key \
    -H "X-Vault-Token: $VAULT_TOKEN" \
    -d '{}' > /dev/null 2>&1 || true

  echo "   ✓ RustyVault running at: http://localhost:8200"
  echo "   ✓ Token: $VAULT_TOKEN"
fi

echo ""
echo "=== Setup Complete ==="
echo ""
echo "Test Age encryption:"
echo "  typedialog form test.toml --encrypt --backend age --key-file ~/.age/key.txt"
echo ""
echo "Test RustyVault encryption:"
echo "  export VAULT_ADDR='http://localhost:8200'"
echo "  export VAULT_TOKEN='$VAULT_TOKEN'"
echo "  typedialog form test.toml --encrypt --backend rustyvault --vault-key-path 'transit/keys/typedialog-key'"
```text

**Make executable and run:**

```bash
chmod +x scripts/encryption-test-setup.sh
./scripts/encryption-test-setup.sh
```text

### Test Case 1: Age Redaction (No Service Required)

**Option A: Use pre-built example (Recommended)**

```bash
typedialog form examples/08-encryption/simple-login.toml --redact --format json

# Expected output
# {"username": "alice", "password": "[REDACTED]"}
```text

**Option B: Create form manually**

```bash
# Create test form
cat > test_redaction.toml <<'EOF'
name = "test_form"
display_mode = "complete"

[[fields]]
name = "username"
type = "text"
prompt = "Username"

[[fields]]
name = "password"
type = "password"
prompt = "Password"
sensitive = true
EOF

# Test redaction (requires no service)
typedialog form test_redaction.toml --redact --format json
```text

### Test Case 2: Age Encryption (Service Not Required, Key File Required)

**Option A: Use pre-built example (Recommended)**

```bash
# Prerequisites: Age key generated (from setup script)
./scripts/encryption-test-setup.sh

# Test with simple form
typedialog form examples/08-encryption/simple-login.toml \
  --encrypt --backend age --key-file ~/.age/key.txt --format json

# Expected output: password field contains age ciphertext
# {"username": "alice", "password": "age1muz6ah54ew9am7mzmy0m4w5..."}

# Or test with full credentials form
typedialog form examples/08-encryption/credentials.toml \
  --encrypt --backend age --key-file ~/.age/key.txt --format json
```text

**Option B: Create form manually**

```bash
# Generate Age key if not exists
mkdir -p ~/.age
if [ ! -f ~/.age/key.txt ]; then
  age-keygen -o ~/.age/key.txt
fi

# Create test form
cat > test_age_encrypt.toml <<'EOF'
name = "test_form"
display_mode = "complete"

[[fields]]
name = "username"
type = "text"
prompt = "Username"

[[fields]]
name = "password"
type = "password"
prompt = "Password"
sensitive = true
encryption_backend = "age"

[fields.encryption_config]
key = "~/.age/key.txt"
EOF

# Test encryption (requires Age key file)
typedialog form test_age_encrypt.toml --encrypt --backend age --key-file ~/.age/key.txt --format json
```text

### Test Case 3: RustyVault Encryption (Service Required)

**Prerequisites: RustyVault running**

```bash
# Start RustyVault and setup (requires Docker)
./scripts/encryption-test-setup.sh

# Verify service is healthy
curl http://localhost:8200/v1/sys/health | jq .
```text

**Option A: Use pre-built example (Recommended)**

```bash
# Export Vault credentials
export VAULT_ADDR="http://localhost:8200"
export VAULT_TOKEN="root"

# Test with simple form
typedialog form examples/08-encryption/simple-login.toml \
  --encrypt --backend rustyvault \
  --vault-key-path "transit/keys/typedialog-key" \
  --format json

# Expected output: password field contains vault ciphertext
# {"username": "alice", "password": "vault:v1:K8..."}

# Or test with full credentials form (demonstrates field-level config)
typedialog form examples/08-encryption/credentials.toml \
  --encrypt --backend rustyvault \
  --vault-key-path "transit/keys/typedialog-key" \
  --format json
```text

**Option B: Create form manually**

```bash
cat > test_vault_encrypt.toml <<'EOF'
name = "test_form"
display_mode = "complete"

[[fields]]
name = "username"
type = "text"
prompt = "Username"

[[fields]]
name = "password"
type = "password"
prompt = "Password"
sensitive = true
encryption_backend = "rustyvault"

[fields.encryption_config]
vault_addr = "http://localhost:8200"
key_path = "transit/keys/typedialog-key"
EOF

# Test encryption with RustyVault
export VAULT_TOKEN="s.xxxx" # From setup output
export VAULT_ADDR="http://localhost:8200"

typedialog form test_vault_encrypt.toml \
  --encrypt \
  --backend rustyvault \
  --vault-addr http://localhost:8200 \
  --vault-token "$VAULT_TOKEN" \
  --vault-key-path "transit/keys/typedialog-key" \
  --format json

# Expected output: password field contains vault ciphertext
# {"username": "alice", "password": "vault:v1:..."}
```text

---

## Part 4: Run Actual Integration Tests

### Test Case: Age Roundtrip (Encrypt → Decrypt)

Once Age is set up, these test scenarios validate the pipeline:

**Scenario 1: Redaction works (no encryption service)**

```bash
cargo test --test nickel_integration test_encryption_roundtrip_with_redaction -- --nocapture

# Expected: PASS - redacts sensitive fields
```text

**Scenario 2: Metadata mapping works**

```bash
cargo test --test nickel_integration test_encryption_metadata_to_field_definition -- --nocapture

# Expected: PASS - EncryptionMetadata maps to FieldDefinition
```text

**Scenario 3: Auto-detection of password fields**

```bash
cargo test --test nickel_integration test_encryption_auto_detection_from_field_type -- --nocapture

# Expected: PASS - Password fields auto-marked as sensitive
```text

### Run All Encryption Tests

```bash
cargo test --test nickel_integration test_encryption -- --nocapture
```text

**Current status:**

- ✅ 5 tests passing (redaction, metadata mapping)
- ⏳ 0 tests for actual Age encryption roundtrip (not yet implemented)
- ⏳ 0 tests for RustyVault integration (backend not implemented)

---

## Part 5: Troubleshooting

### Age Issues

**Problem: `age: command not found`**

```bash
# Install age
brew install age  # macOS
sudo apt install age  # Linux
```text

**Problem: Permission denied on ~/.age/key.txt**

```bash
chmod 600 ~/.age/key.txt
```text

**Problem: Invalid key format**

```bash
# Regenerate keys
rm ~/.age/key.txt
age-keygen -o ~/.age/key.txt
```text

### RustyVault Issues

**Problem: Docker container won't start**

```bash
# Check logs
docker logs rustyvault

# Remove and restart
docker rm -f rustyvault
docker run -d --name rustyvault -p 8200:8200 rustyvault:latest
```text

**Problem: Vault initialization fails**

```bash
# Check if vault is responding
curl -s http://localhost:8200/v1/sys/health

# If not, restart container
docker restart rustyvault
```text

**Problem: Transit API not working**

```bash
# Verify token
echo $VAULT_TOKEN

# Check auth
curl -s http://localhost:8200/v1/sys/mounts \
  -H "X-Vault-Token: $VAULT_TOKEN"
```text

**Problem: Can't connect from typedialog**

```bash
# Verify network
curl -s http://localhost:8200/v1/sys/health | jq .

# Check environment variables
echo $VAULT_ADDR
echo $VAULT_TOKEN

# Test encryption endpoint
curl -s -X POST http://localhost:8200/v1/transit/encrypt/typedialog-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d '{"plaintext": "dGVzdA=="}' | jq .
```text

---

## Part 6: Next Steps

Once services are running, implement:

1. **test_age_encrypt_roundtrip** - Encrypt with Age, decrypt, verify plaintext
2. **test_rustyvault_encrypt_roundtrip** - Encrypt with RustyVault, decrypt, verify
3. **test_cli_encrypt_age** - Run `typedialog form --encrypt --backend age`, verify output is ciphertext
4. **test_cli_encrypt_rustyvault** - Run `typedialog form --encrypt --backend rustyvault`, verify output is ciphertext
5. **Integration test script** - Single script that tests all pipelines end-to-end

---

## References

- **Age**: <https://github.com/FiloSottile/age>
- **RustyVault**: <https://github.com/Tongsuo-Project/RustyVault>
- **HashiCorp Vault Transit**: <https://www.vaultproject.io/api-docs/secret/transit>
