# DOMAIN_RULES.md — encryption_service.rs

## 1. Tier Assignments

| Function / Method | Tier | Reason |
|-------------------|------|--------|
| `Aes256CtrEncryption::encrypt_content()` | 1 | Primary API — silent failure exposes plaintext to logs/network |
| `Aes256CtrEncryption::decrypt_content()` | 1 | Primary API — silent failure returns garbage, corrupts downstream data |
| `Aes256CtrEncryption::decrypt_json_field()` | 1 | Primary API — silent failure masks missing/malformed fields |
| `Aes256CtrEncryption::encrypt_to_json_field()` | 1 | Primary API — silent failure embeds wrong value in config |
| `Aes256CtrEncryption::derive_256bit_key_from_password()` | 3 | Private helper — deterministic pure transform, no failure modes |

## 2. Error Handling Strategy

- **Error type:** `anyhow::Result<T>` — rich `.context()` chains without module-specific enum
- **No custom error types:** All errors are either:
  - `env::VarError` (wrapped via `.context()`) for missing `ENCRYPTION_KEY`
  - `base64::DecodeError` for invalid token format
  - `utf8::Utf8Error` for corrupted plaintext
  - `anyhow::Error` for missing JSON path segments or type mismatches

## 3. Error Classes

| Condition | Returned as | Raised when |
|-----------|-------------|-------------|
| Missing `ENCRYPTION_KEY` | `Err(anyhow)` | `env::var("ENCRYPTION_KEY")` fails |
| Invalid base64 | `Err(anyhow)` | Token is not valid base64 |
| Ciphertext too short | `Err(anyhow)` | Decoded data < 16 bytes (no room for IV) |
| Invalid UTF-8 | `Err(anyhow)` | Decrypted bytes are not valid UTF-8 |
| Missing JSON path | `Err(anyhow)` | `json_path` segment not found in tree |
| Non-string leaf | `Err(anyhow)` | Path resolves to non-string JSON value |
| Empty `json_path` | `Err(anyhow)` | Empty string passed to JSON field helpers |

No custom error structs. `anyhow` is sufficient for utility-module propagation.

## 4. Retry Policy

| Function | Retries | Max Attempts | Retryable Errors | Delay |
|----------|---------|--------------|------------------|-------|
| All functions | No | — | — | — |

No retry. AES-CTR is a pure in-memory transform — operations either succeed
immediately or fail with a logic error (bad key, bad base64, bad UTF-8).
There is no transient failure mode. Retry would mask bugs without recovery benefit.

## 5. Domain-Specific Test Scenarios

### Cross-language compatibility
- Encrypt in Rust, decrypt in Python must produce original plaintext — token format must be byte-for-byte identical
- Same plaintext + same key must produce identical token (deterministic IV)

### Key derivation
- Same passphrase must always produce identical 32-byte key
- Empty passphrase must produce valid 32-byte key (not panic)

### JSON field traversal
- Deeply nested paths (`a.b.c.d.e`) must work without recursion depth limits
- Missing intermediate path segment must fail with clear message
- Leaf that is not a string must fail with clear message (not base64 decode error)

### Edge cases
- Empty plaintext must produce valid round-trippable token
- Unicode (multi-byte UTF-8) must survive encryption/decryption unchanged
- Very long plaintext must work (no arbitrary length limits)
- Different keys must produce different ciphertexts (no key collision)

### encrypt_to_json_field
- Deeply nested path (e.g., "user.credentials.password") must create nested JSON structure
- Single-segment path (no dots) must place token at top level of returned Value
- Empty path must return error — not produce Value with empty-string key
- Result must be a serde_json::Value (not String) to prevent accidental logging

(End of file — total 73 lines)
