# v0.7.0 — Layer 1 Built-in Fetchers

**Release date:** 2026-05-20
**Phase:** 0.7.0 — `EnvFetch` + `FileFetch` + `KeychainFetch` + `TpmFetch`
**Status:** development (pre-1.0; public API subject to change without notice)

---

## What is `key-vault`?

`key-vault` is an enterprise-grade in-memory key management vault for Rust.
See [README](../../README.md) for the 1.0 design and
[`docs/SECURITY.md`](../SECURITY.md) for the per-layer architecture.

After this release, **7 of the 9 layers are complete**:

| Layer | Status after 0.7.0 |
|-------|--------------------|
| **Layer 1 — built-in fetchers (4/4)** | **✅ complete** |
| Layer 2 — page locking | ✅ |
| Layer 3 — fragmentation (4/4 strategies) | ✅ |
| Layer 4 — decoy bytes (3 strategies) | ✅ |
| Layer 5 — codex (4/4 implementations) | ✅ |
| Layer 6 — constant-time handle equality | ✅ |
| Layer 7 — zero-on-drop | ✅ |
| Layer 8 — monitor impls | planned for 0.8 |
| Layer 9 — audit logging | planned for 0.8 |

---

## What's in 0.7.0

### `EnvFetch` — process environment variable (feature `fetcher-env`)

Reads key bytes from a named environment variable. Error messages
include the variable **name** for diagnostics but **never the value**.

```rust,no_run
use key_vault::{EnvFetch, FetchContext, KeyFetch};

# fn main() -> Result<(), key_vault::Error> {
let fetcher = EnvFetch::new("MY_APP_KEY");
let raw = fetcher.fetch(&FetchContext::new("primary"))?;
# let _ = raw;
# Ok(())
# }
```

**Threat profile:** lowest-security built-in. Anything in the process
environment is readable by `/proc/<pid>/environ`, debuggers, and
crash-dump tooling. Use for development and container deployments
(Kubernetes Secrets → env, AWS Lambda env, systemd `EnvironmentFile=`).

### `FileFetch` — file on disk with permission gate (feature `fetcher-file`)

Reads key bytes from a file. **Strict Unix permission checking by
default**: any mode bit in `0o077` rejects the read with a clear error.
Windows trusts NTFS ACLs.

```rust,no_run
use key_vault::{FetchContext, FileFetch, KeyFetch};

# fn main() -> Result<(), key_vault::Error> {
let fetcher = FileFetch::new("/etc/myapp/key.bin");
let raw = fetcher.fetch(&FetchContext::new("primary"))?;
# let _ = raw;
# Ok(())
# }
```

`FileFetch::allow_loose_perms()` disables the check for test fixtures.
Error messages include the path and the I/O error **kind** only — the
file contents and OS error string are never logged.

**Threat profile:** higher than `EnvFetch` (POSIX permissions confine
access to one user account). Lower than `KeychainFetch` (bytes live on
disk in cleartext and persist across reboots). Pair with OS-level disk
encryption (LUKS / FileVault / BitLocker) for encryption-at-rest.

AEAD-encrypted file format is on the post-1.0 backlog.

### `KeychainFetch` — OS native credential store (feature `fetcher-keychain`)

Cross-platform via the `keyring` crate:

- **macOS** — Keychain Services
- **Windows** — Credential Manager
- **Linux** — Secret Service (gnome-keyring, KWallet)

```rust,no_run
use key_vault::{FetchContext, KeyFetch, KeychainFetch};

# fn main() -> Result<(), key_vault::Error> {
let fetcher = KeychainFetch::new("my-app", "primary-key");
let raw = fetcher.fetch(&FetchContext::new("primary"))?;
# let _ = raw;
# Ok(())
# }
```

`keyring::Error` variants are mapped to short, discriminant-only
strings (`"no such entry"`, `"platform-specific keyring failure"`,
etc.) so platform-specific error details — which on some platforms
include OS error numbers near sensitive paths — don't leak.

**Threat profile:** highest-security general-purpose backend short of
dedicated hardware. Recommended whenever the OS has the relevant
service available.

### `TpmFetch` — detection-only (feature `fetcher-tpm`)

```rust
use key_vault::{FetchContext, KeyFetch, TpmFetch};

let err = TpmFetch.fetch(&FetchContext::new("k")).unwrap_err();
assert!(format!("{err}").contains("TPM"));
```

In 1.0, TPM **detection** is shipped (via `detect_tee_capabilities()`).
TPM **acquisition** requires deep integration with `tss-esapi` and is
on the 1.x roadmap. `TpmFetch::fetch` always returns a documented
`Error::Acquisition` so it can be wired into composite fetcher chains
today and inherit the 1.x upgrade automatically.

### Composite fetcher pattern

The four built-ins compose freely — implement a "first-success" wrapper
over a `Vec<Arc<dyn KeyFetch>>` to get TPM → keychain → file → env
fallback semantics:

```rust
use std::sync::Arc;
use std::borrow::Cow;
use key_vault::{Error, FetchContext, KeyFetch, RawKey, Result};

struct FirstSuccess(Vec<Arc<dyn KeyFetch>>);

impl KeyFetch for FirstSuccess {
    fn fetch(&self, ctx: &FetchContext) -> Result<RawKey> {
        let mut last_err = None;
        for f in &self.0 {
            match f.fetch(ctx) {
                Ok(raw) => return Ok(raw),
                Err(e) => last_err = Some(e),
            }
        }
        Err(last_err.unwrap_or(Error::Acquisition {
            source: Cow::Borrowed("composite"),
            reason: "no fetchers configured".into(),
        }))
    }
    fn describe(&self) -> Cow<'_, str> { Cow::Borrowed("composite") }
}
```

### New / changed public surface

| Item                | Status                              |
|---------------------|-------------------------------------|
| `EnvFetch`          | **new** (feature `fetcher-env`)     |
| `FileFetch`         | **new** (feature `fetcher-file`)    |
| `KeychainFetch`     | **new** (feature `fetcher-keychain`) |
| `TpmFetch`          | **new** (feature `fetcher-tpm`)     |
| Crate-root re-exports | extended for all four fetchers (feature-gated) |
| `clippy.toml`       | `doc-valid-idents` extended with `FileVault`, `BitLocker`, `KWallet`, etc. |

### Test surface (relative to 0.6.0)

|                   | 0.6.0 | 0.7.0 |
|-------------------|-------|-------|
| Unit tests        |   99  |  111  |
| Integration tests |   15  |   15  |
| Doc tests         |   13  |   17  |
| **Total**         | **127**| **143** |

New tests cover:

- `EnvFetch` happy path, missing variable, value-redaction guarantee,
  `describe()`.
- `FileFetch` read, missing-file error path, Unix `0o600` accept,
  Unix `0o644` rejection, `describe()`.
- `KeychainFetch` construction + describe; live keychain test gated on
  `KEY_VAULT_KEYCHAIN_TEST=1`.
- `TpmFetch` detection-only error contract + describe.

---

## Breaking changes

Purely additive on top of 0.6.0:

- All 0.6.0 types and methods are preserved.
- All four new fetcher types are behind individual Cargo features off
  by default — existing builds are unaffected.

---

## Verification

Verified locally on both stable and pinned MSRV 1.85:

```text
cargo {fmt, clippy, test, doc} --all-features      clean
143 tests pass; -D warnings on clippy + RUSTDOCFLAGS
```

No new `unsafe` blocks in this release. The `env::set_var` / `env::remove_var`
calls in `EnvFetch` tests are unsafe under Rust 1.85+ and carry `SAFETY:`
comments per the `clippy::undocumented_unsafe_blocks` lint.

---

## Documentation sync

Per the project's doc-sync rule:

- `README.md` — "Current status" marks Layer 1 **complete**;
  Quick-start dep `0.6` → `0.7`; pluggable-fetchers section lists all
  four with feature flags.
- `docs/API.md` — full public-API reference (added in this release; lists
  every type with examples).
- `docs/release/` — release notes moved out of the (now-gitignored)
  `.dev/` tree into the public `docs/release/` directory.
- `CHANGELOG.md` — `[0.7.0]` section.

---

## What's next — Phase 0.8.0

**Layers 8 + 9 — `SecurityMonitor` implementations + audit logging.**

Exit criteria for 0.8:

- `NoMonitor` / `LogMonitor` / `MetricsMonitor` / `WebhookMonitor` /
  `CompositeMonitor`
- Threshold detection (N failures in M seconds → alert; → lockout)
- Anomalous-access detection
- `AuditEvent` emission through `log-io` or `tracing`
- Feature-gated behind `monitor` / `audit` Cargo features
- Integration tests for the full monitor + audit flow

Estimated effort: 4–5 days.

---

## Installation

```toml
[dependencies]
key-vault = "0.7"
```

To enable specific fetchers:

```toml
[dependencies]
key-vault = { version = "0.7", features = ["fetcher-env", "fetcher-file"] }
```

Or enable all built-in fetchers at once:

```toml
[dependencies]
key-vault = { version = "0.7", features = ["fetcher-all"] }
```

Minimum supported Rust version: **1.85** (edition 2024).

---

## Documentation

- [`README.md`](../../README.md) — overview + Current status table
- [`docs/API.md`](../API.md) — full public-API reference
- [`docs/SECURITY.md`](../SECURITY.md) — 9-layer architecture
- [`docs/TRANSFORMATION.md`](../TRANSFORMATION.md) — visual walkthrough
- [`CHANGELOG.md`](../../CHANGELOG.md) — full change log

---

## Diff

Full diff: [`v0.6.0...v0.7.0`](https://github.com/jamesgober/key-vault/compare/v0.6.0...v0.7.0)

---

<sub>key-vault v0.7.0 — Copyright (c) 2026 James Gober. Apache-2.0 OR MIT.</sub>
