0013. Crate and workspace structure
Context
Vord is Rust (ADR-0002) and has not yet written a line of it. Before the walking
skeleton, the workspace shape must be fixed enough that trait seams exist from
day one — they are cheap to define and expensive to retrofit — while crate
boundaries stay coarse and split later, because premature fine-grained crates buy
nothing but Cargo plumbing and visibility friction. This is precisely the tension
Wyrd settled in its ADR-0016 (monorepo, evolving crate structure, the keystone
trait crate, xtask for CI logic), and Vord adopts the same shape — it is the
sibling project's proven answer, and consistency between the two lowers the cost of
moving between them.
The seams Vord must reserve are dictated by the prior ADRs: KeyCustody (ADR-0003),
Authorizer (ADR-0006), and the CredentialVerifier/Vör contract whose whole
point is that the core is absent from its signature (ADR-0010). Several more fall
out of this plan and must exist as seams before the concretes behind them are
built.
Decision
-
A single Cargo workspace (monorepo),
vord-*crates, starting coarse. The keystone rule (Wyrd ADR-0016): implementations and consumers depend on the trait crate, never on concrete backends; only the top-level binary crate wires concretes together.Cargo.lockis committed (Vord is an application, not a library). CI logic lives in anxtaskcrate (cargo xtask ci), not in YAML or a Makefile, so it runs identically on a laptop and in CI — a claim scoped to the cargo-native gates (fmt, clippy, build, test, conformance, cargo-deny); the GitHub-side gates (DCO, CodeQL, secret scanning, OSS-Fuzz, ADR-immutability) are platform servicesxtaskneither runs nor replaces (ADR-0028 §6). -
Trait seams fixed from day one, even where the M0 concrete behind them is trivial:
KeyCustody(ADR-0003) — signing-key custody; embedded keystore in dev, KMS/HSM in prod.CredentialVerifier(Vör, ADR-0010) — the verification seam with no core handle.Authorizer(Forseti, ADR-0006) — the policy-decision seam.CredentialStore/ directory (ADR-0011) — principal and credential records on Wyrd's metadata tier.SessionStore(ADR-0016) — the eventually-consistent SSO session.StatusListPublisher(ADR-0005) — the revocation status-list projection.EventSink(ADR-0022) — the identity-event emit point (projections built later; the seam reserved now).
-
The M0 crate set (coarse; split per the Wyrd ADR-0016 evolution rule as boundaries firm up):
Crate Role vord-format(+specs/)The token/credential format encode/decode against the normative spec (ADR-0003); dependency-light, spec-first. vord-traitsThe keystone — every trait above, definitions only. Consumers/impls depend here. vord-protogRPC/protobuf message shapes for the admin and internal surfaces (transport deferred; shapes defined as needed). vord-coreModgud issuance, the OIDC flow logic, the Vör verifier, the embedded backends; combined now, split later — the Vör split is not optional: vord-vor(or equivalent) must be its own crate by the M4 release, because that is when the "no core dependency" claim graduates from trait-signature discipline to a crate-graph fact (see consequences), and when per-plane binaries (ADR-0024) need a verifier crate that never links the core client.vord-serverThe binary; the only crate that knows concrete backends and wires them. Per-plane binary crates (a vord-edgethat links only the verifier side) are reserved: the deployment topology (ADR-0024) runs Vör and Modgud as separate node types, and an edge deployable that carries issuance/core-client code would weaken the blast-radius story (ADR-0027 §2) from "no capability" to "no credentials".testkitThe DST harness (abstract time/clock, seed-reproducible runner, fault injection) — first-class, not a helper (ADR-0002, Wyrd ADR-0009). xtaskCodegen, conformance-vector runs, cargo xtask ci. -
Vord-only accommodations stay in Vord's crates (ADR-0001 rule 4). Anything that would otherwise be pushed into Wyrd to serve Vord lives here instead, behind Vord's own traits that consume Wyrd's client surface.
Consequences
- The dependency graph makes the constitution visible — in two stages, stated
honestly: while Vör and Modgud share
vord-core(M0–M3), the "no core handle" guarantee is enforced at the trait signature (theCredentialVerifierinputs carry no core client, ADR-0010) and asserted structurally in tests, but the crate does link the core client for issuance's sake. It becomes a crate-graph, compile-time fact at the Vör split (M4 gate, decision 3): from then on the verifier crate cannot import what it must not read, andcargoitself is the reviewer. - The single-binary dev profile is dependency-free (embedded keystore, embedded authz, in-memory everything behind the seams), exactly as Wyrd's redb/in-memory profile is, satisfying the scale-range goal at the small end.
- Swapping a production backend (KMS, SpiceDB/OpenFGA, the Wyrd metadata core) is a
vord-servercomposition change, not a refactor — the pluggability that the ADR-0008 selection criteria assume and that lets the deferred engine picks stay cheap. - Extension is composition, never dynamic loading: operators add backends and
authentication methods by building
vord-serverwith additional crates (rung 3 of the extension ladder, ADR-0029; the method contract is ADR-0015 decision 5) — so the executing code of any deployment is enumerable from itsCargo.lock, which is the auditability property a runtime plugin loader would forfeit. - Crate boundaries will move as the design firms up; that is expected and governed by the same evolution rule Wyrd uses, not a re-decision each time.