Files
amethyst/quartz/tools/cordn-vector-gen
Claude 5527bea451 feat(cordn): verify the coordinator wire against cordn's own contracts
Everything cordn-side was verified against ts-mls, the MLS library their client
happens to use. That covers the crypto layer and says nothing about the layer
above it: the eleven coordinator tools, their argument names, which fields are
optional, and the `cordn1…` group ref. That layer rested on our reading of the
spec.

`quartz/tools/cordn-vector-gen` closes it from their side. It generates
`resources/cordn/coordinator-contracts.json` from `@cordn/core` (MIT) -- the
package their reference coordinator and client both import -- by handing each
payload we send to their zod schema, so a field we named wrong fails at
generation time. Each method also carries a `rejects` set their schema must
refuse, because a positive only means something if the schema is strict where
we assume it is. Group refs round-trip through their bech32 codec.

- CoordinatorContractVectorTest (15): drives CoordinatorClient through the real
  ContextVM transport, asserts the arguments the coordinator sees equal those
  vectors, then replays their result shapes back through our parser.
- CordnGroupRefVectorTest (5): both directions, including the uppercase form
  (their `decodeGroupRef` accepts it though their `isGroupRef` screen does not)
  and a non-ASCII gid, which §4.1 requires to survive byte for byte.
- CordnFixtureCoordinator now records each call's arguments and can serve a
  scripted result, which is what lets the same fixture do both directions.

Mutation-checked, since 20 tests passing on the first run means nothing on its
own: sending `after = 0` on a first-ever fetch instead of omitting it, and
flipping the group-ref TLV emission to ascending order, each killed exactly one
test. The TLV mutation was caught only by the encode-direction test -- decoders
accept any order by spec, so a decode-only suite would have shipped that
divergence.

Also settles §4.1 (credential encoding) by implementing both rather than
waiting on an agreement neither ecosystem has an incentive to reach. Marmot
writes a pubkey as 32 raw bytes, cordn as 64 ASCII hex bytes; they are mutually
exclusive by length, so no leaf can be misread as the other profile's.
BothCredentialProfilesTest runs both groups on one engine and pins the
`memberIdentityHex` hex-of-hex trap that would otherwise drop cordn members
from a member list.

Two findings recorded in the plan:

- Tier B is blocked on licensing, not tooling. `packages/coordinator`,
  `packages/server` and `packages/test-utils` carry no LICENSE file and no
  `license` field, and neither does the repo root -- so the reference
  coordinator, and the ghcr image built from it, are unlicensed. Only
  `packages/core` and `packages/cli` are MIT. The plan's claim that
  "everything under Cordn-msg is MIT" was wrong and is corrected.
- Their current `kp_publish` schema rejects the legacy `keyPackageBase64`
  field, so our fallback is for parsing old publication events only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012BfD4txdnsaPRXmNXbup9n
2026-09-18 22:41:37 +00:00
..

cordn-vector-gen

Emits quartz/src/commonTest/resources/cordn/coordinator-contracts.json from @cordn/core — the package cordn's own reference coordinator and client both import. Consumed by CoordinatorContractVectorTest and CordnGroupRefVectorTest.

Why this exists

Our ts-mls fixtures (resources/tsmls/) cover the crypto layer: KeyPackages, Welcomes, exporters, sealed payloads. They say nothing about the layer above — the eleven coordinator tools, their argument names, which fields are optional, and the cordn1… group ref. That layer had been verified only against our reading of the spec, which is exactly the kind of agreement that holds right up until someone runs a real coordinator.

So this generator goes the other way: it takes the payloads we put on the wire and hands each one to their zod schema. A field we named wrong fails at generation time. Each method also carries rejects — payloads their schema must refuse — because a positive result only means something if the schema is strict where we assume it is.

Group refs are round-tripped through their bech32 codec, and the vectors pin both directions: we must decode what they encode, and encode byte-identically. Only the second half catches TLV ordering, since the spec lets decoders accept any order.

Regenerating

cd quartz/tools/cordn-vector-gen
npm install
node generate.mjs > ../../src/commonTest/resources/cordn/coordinator-contracts.json

Output is deterministic — fixed actors, fixed timestamps, no randomness — so a regeneration that changes the file means cordn changed something. Commit the result with the reason.

Licensing

@cordn/core and @cordn/cli are MIT and each ship a LICENSE file; this generator is a dev-only dependency on the first and ships in nothing.

The rest of the Cordn-msg/cordn repository — packages/coordinator, packages/server, packages/test-utils, and the ghcr.io/cordn-msg/cordn image built from them — carries no license file and no license field, so default copyright applies. That is why Tier B (a live coordinator round-trip) is not wired up here and why these vectors are generated from the two licensed packages instead. See quartz/plans/2026-09-17-cordn-interop.md §7.