# nornir-vault — guide
> **Class:** `guide` · How to drive `nornir-vault`. Sibling: [`vault-design.md`](vault-design.md).
## Create, store, lock
```rust
use nornir_vault::{Vault, PinProvider, SecretEntry, SecretString};
let mut v = Vault::create("~/.korp/korp.vault", &PinProvider::new("4913"))?;
v.set("spark", SecretEntry::SparkToken(SecretString::new(token)))?;
v.set("mtls", SecretEntry::MtlsClientIdentity {
cert_pem: SecretString::new(cert),
key_pem: SecretString::new(key),
})?;
v.lock(); // drops the DEK + all decrypted entries (zeroized)
```
`create` returns the vault **unlocked** and writes the encrypted header (0600).
`set` re-seals under the DEK and persists.
## Open and unlock
```rust
let mut v = Vault::open(path)?; // locked: only the header is in memory
v.unlock(&PinProvider::new(pin))?; // decrypts DEK + every entry into RAM
match v.get("spark")? { // Err(Locked) if still locked
Some(SecretEntry::SparkToken(t)) => use_token(t.expose()),
_ => {}
}
```
A wrong PIN returns `VaultError::Unlock { remaining }` and burns one attempt
(persisted). Once the budget is spent, unlock returns `VaultError::LockedOut` —
even for the correct PIN. A successful unlock resets the counter.
## OS keyring (feature `os-keyring`)
```rust
# #[cfg(feature = "os-keyring")]
let p = nornir_vault::OsKeyringProvider::new("korp-vault", "kek");
let mut v = Vault::create(path, &p)?; // stores a random KEK in the OS keyring
// later: Vault::open(path)?.unlock(&p)? // fetches it back — no PIN prompt
```
The consumer must enable a `keyring` backend for the platform (KDE Plasma thin
client → `keyring/sync-secret-service` or the pure-Rust `keyring/async-secret-service`).
## Showing state in a UI (the safe view)
```rust
let s = v.state(); // VaultState — NO secret bytes
serde_json::to_string(&s)?; // safe to embed in state_json
```
`VaultState` carries `locked`, `locked_out`, `method`, `attempts_remaining`, and
each entry's `name` + `kind`. **Never** put a `SecretEntry`/`SecretString` in a
`state_json`, a log, or a `Debug` dump — the types refuse to serialize, and their
`Debug` is redacted, but do not defeat that with `.expose()`.
## Rules of thumb
* Read secrets only after `unlock`; call `lock()` as soon as you are done.
* Keep `SecretString::expose()` borrows short-lived; do not copy into a `String`.
* The correct-but-locked-out case is intentional: reset means a fresh vault (or an
operator reset), not relaunching the process.