# pki — design
> **Class:** `design` · **Function axis:** `f4` (AIRGAP4). States only the shape we ship; the *why*
> (signature-not-clock trust, Vault as a seam) is in [`pki-reasoning.md`](pki-reasoning.md).
## The three sources
`pub enum PkiSource` (`pki.rs:28`):
| variant | meaning | status |
|---|---|---|
| `SelfGenerated` (`pki.rs:32`) | a real per-machine ed25519 X.509 CA + leaf, pure Rust, offline | implemented (feature `pki`, default-on) |
| `ExistingVault(String)` (`pki.rs:30`) | issue leaves from an existing Vault PKI mount | seam (feature `vault`) |
| `TempVault` (`pki.rs:34`) | spin a throwaway `vault server -dev`, issue, tear down | seam (feature `vault`) |
`pki::provision(src, vault) -> ()` (`pki.rs:524`) materialises PKI per source: `SelfGenerated`
drives the real engine (errors without the `pki` feature, `pki.rs:537`); the Vault variants need a
`VaultClient` and error honestly if none is supplied.
## SelfGenerated — the real machine CA (feature `pki`)
`pub struct MachineCa` (`pki.rs:72`) — an ed25519 signing key + a self-signed X.509 CA cert.
| method | signature | what it does |
|---|---|---|
| `generate` | `generate(hostname) -> MachineCa` (`pki.rs:87`) | fresh CA from the OS CSPRNG, CN `<hostname>.x14.local`, 10-year window |
| `from_seed` | `from_seed(hostname, [u8;32]) -> MachineCa` (`pki.rs:99`) | deterministic CA from a seed (reproducible tests; still a real keypair) |
| `ca_cert_pem` | `ca_cert_pem() -> String` (`pki.rs:154`) | the self-signed CA cert PEM — the trust root |
| `issue_leaf` | `issue_leaf(service_cn, sans) -> (LeafCert, String)` (`pki.rs:163`) | mint a real ServerAuth X.509 leaf + its PKCS#8 private-key PEM |
| `verifying_key` / `common_name` | (`pki.rs:143`,`:148`) | CA public key / CN |
`pub struct LeafCert { subject_cn, sans, issuer_cn, not_before, not_after, cert_pem, cert_der }`
(`pki.rs:41`) — a self-contained, offline-verifiable leaf that loads into rustls.
`pki::verify_leaf(cert, ca_verifying_key) -> bool` (`pki.rs:253`) parses the leaf DER and verifies
its signature against the CA's ed25519 key — real crypto (wrong CA / tampered DER → false).
## Vault sources (feature `vault`)
`pub trait VaultClient { issue(cn, sans) -> VaultIssuedCert }` (`pki.rs:306`) is the seam.
- `HttpVaultClient` — token-auth client for an existing Vault; `new(addr, token, role)`, builder
`mount`/`ttl`; `issue` does a real `POST /v1/<mount>/issue/<role>` **through the shared
`vault_http::VaultHttp` transport** (reuse-law L5, the ONE token-authenticated `POST
{addr}/v1/{path}` also used by `openbao.rs`); this client keeps only the error-surface + parse
response handling, `parse_issue_response` being the pure parser.
- `TempVaultClient` (`pki.rs:434`) — `spawn(role)` (`pki.rs:443`) starts `vault server -dev`,
configures the PKI mount, issues, and tears down on `Drop` (`pki.rs:508`); honest error if the
`vault` binary is absent.
- `VaultIssuedCert` (`pki.rs:277`) — the issued chain (cert/key/issuing_ca PEM + serial), kept
verbatim (RSA/ECDSA) and distinct from the ed25519 `LeafCert`.
## Contract (inject-assert tests)
`self_generated_leaf_is_real_x509_and_loads_into_rustls` (`pki.rs:573`),
`from_seed_is_deterministic` (`pki.rs:652`),
`multiple_sans_are_all_present_in_leaf` (`pki.rs:682`),
`tampered_leaf_cert_is_rejected` (`pki.rs:728`),
`vault_sources_require_a_client` (`pki.rs:642`).