# pki — reasoning
> **Class:** `reasoning`. Why the PKI layer is shaped this way. The descriptive shape is in
> [`pki-design.md`](pki-design.md).
## Why trust by signature, not by clock
An airgapped target has no reliable time source — no NTP, often a drifting or reset RTC. Standard
X.509 practice (short validity windows, expiry-driven rotation) assumes a trustworthy clock, which
we do not have. So the design leans on **signature verification** as the trust primitive:
`verify_leaf` checks the leaf's signature against the CA's ed25519 key, and validity windows are
made deliberately wide (10 years for the CA). We reject short expiries as a revocation mechanism
here because a clock-skewed target would spuriously reject valid certs — the failure mode is worse
than the risk it guards against in a sealed environment.
## Why SelfGenerated is the default and real, not a stub
The lean, offline core must be able to stand up a working TLS identity with **no external service
and no network** — otherwise "airgap" is a lie the moment a service needs a cert. So
`SelfGenerated` is a *real* ed25519 X.509 CA (via rcgen with the ring backend, pure Rust, no
C/cmake) whose leaves actually load into a rustls `ServerConfig`. The earlier hand-rolled cert it
replaced could not load into rustls — a cert that does not parse is not a cert. Making it real and
default is what lets the offline path be honest.
## Why Vault is a seam, not the default
Many nordisk deployments *do* have a Vault, and issuing from a shared CA is the right thing when
one exists. But pulling a Vault HTTP client (reqwest) into the always-on core would fatten the
embedded client that runs on the target device and couple its build to a network stack it may
never use. So Vault lives behind the `vault` feature as a `VaultClient` trait with two impls
(`HttpVaultClient` against a real Vault, `TempVaultClient` for tests). `provision` errors clearly
when a Vault source is named without a client — an honest "you asked for Vault but gave me none"
rather than a silent fallback that would mask a misconfiguration.
## Why ring, not aws-lc-rs
The rcgen and x509-parser deps use the `ring` crypto backend rather than `aws_lc_rs`. ring keeps
the whole PKI path pure-Rust with **no C/cmake build step**, matching the workspace's
rustls-via-ring posture — so the airgap core cross-compiles cleanly and builds on a headless
target without a C toolchain. aws-lc-rs would drag a C build into the lean core.