SIGN IN SIGN UP

feat(rustd): the provider resolves through two strategies, one interface

The lease pass took `{posture, provider, model}` as given. This resolves
them — and the key that rides with them.

`resolveActiveProvider` follows a tenant's selection into a vault and
decrypts a key across five Zig modules and about 1,180 lines. Two of
those functions do the same three things — open a row, read its JSON,
own the result — and each writes that part out again with its own
`errdefer` ladder and its own `secureZero`.

Here the difference is a `Resolution`: a value that knows WHICH row
carries its key and what that row's body means. The shared half runs
between the trait's two methods, so it is written once. Both strategies
are synchronous and pure, so the whole fork is provable with no
Postgres, no vault and no key — 35 datastore-free tests, none of which
opens a connection.

What does not come across:

- `ip_literal.zig`'s 249 lines of hand-rolled IPv4/IPv6 parsers and
  octet constants. `std::net::IpAddr` parses and `is_loopback` /
  `is_private` / `is_link_local` / `is_multicast` classify. Four ranges
  std does not name are four masked comparisons, each named. A literal
  a hand-rolled parser fails to PARSE is a literal it reports as safe,
  which is the wrong place to be re-deriving anything.
- The `committed` flag and the `defer if (!committed) …deinit()` the
  billing pass threads through itself. That is a hand-rolled move, and
  the flag is what a move already means; `SecretString` zeroes on drop
  wherever it ends up. The key is a PRIVATE field with no `Deref` and
  no owned getter, so Invariant 3 is a thing the compiler enforces
  rather than a thing each future call site remembers.
- `crypto_store.zig`'s decrypt tally, its workspace-wide reads, and its
  per-row degradation. The tally proves LIBRARY reads never decrypt;
  resolution decrypts by definition, so it has nothing to assert here.

Two findings worth the reader's attention:

`serde_json` fills a struct from a JSON ARRAY positionally, so
`["anthropic","sk-live"]` parsed as a credential with a provider and a
key and passed every shape check after it. `loadJson` refuses a
non-object at the top; this now does too, structurally and in one pass.

`SecretEndpointInvalid` is absent from `resolveTenant`'s permanent list
and falls through its `else`, so a stored endpoint that fails the SSRF
guard is retried forever. Copied rather than corrected — the two
daemons must write the same rows during the cutover — and pinned by
`an_ssrf_refusal_is_ported_as_transient` so flipping it cannot be
silent. Flagged for Indy.

`error/mod.rs` reached 460 lines with every gate green, exactly as the
last handoff warned. Split at the seam `detail.rs` already names: that
module is what we SAY, `classify.rs` is what we DECIDE, and the enum is
what happened. All three tables are exhaustive, so a new kind fails the
build until someone has answered all three questions about it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
K
Kishore Kumar committed
ffad7c998a57476cbd376c608e404912091a150b
Parent: 840e8cc