Skip to content

refactor(core): typed credential registry replacing string-matched VC kinds (R19)#102

Merged
stormer78 merged 1 commit into
mainfrom
r19-typed-credential-registry
Jun 11, 2026
Merged

refactor(core): typed credential registry replacing string-matched VC kinds (R19)#102
stormer78 merged 1 commit into
mainfrom
r19-typed-credential-registry

Conversation

@stormer78

Copy link
Copy Markdown
Contributor

Problem

handle_credential_issue string-matched "MembershipCredential" /
"EndorsementCredential" and stored each into one of two hard-coded
Option<serde_json::Value> fields on CommunityRecord. Adding a credential
type meant touching dispatch, storage, and the UI — five places that could
drift out of sync (R19).

Fix

A single typed registry:

  • CredentialKind enum in openvtc-core/src/lib.rs, kept next to
    MessageType. It owns the vc_type (W3C VC type string), the persisted
    config_key, and activates_membership. CredentialKind::ALL is the one
    list dispatch / storage / UI iterate.
  • Storage: CommunityRecord.credentials: BTreeMap<CredentialKind, Value>
    replaces the two Option fields.
  • Dispatch: handle_credential_issue classifies via
    CredentialKind::from_credential and inserts — no per-kind branching.
  • UI: the My Credentials panel and the presence flags iterate the registry.

Adding a kind is now one variant + its match arms; nothing in dispatch or UI
changes.

Config round-trip (no migration)

A deserialize-only CommunityRecordShadow (#[serde(from = ...)]) folds
pre-R19 flat membership_credential / role_credential fields into the map,
with new-format keys winning. The shadow keys the map by String on purpose:
an unrecognised kind written by a newer build is dropped with a warning rather
than making the whole config unloadable. CredentialKind serializes as its
config_key string so it is a valid JSON object key.

Router

No change needed: every credential kind shares one
credential-exchange/issue message type and DIDComm route, so a new kind
can't drift the route list.

Tests

  • credential_kind_registry_is_self_consistentfrom_credential /
    from_config_key round-trip for every ALL kind; unknowns → None.
  • credential_issue_handles_every_registered_kind — dispatch stores + activates
    every registered kind generically (the acceptance criterion: a new kind
    touches the registry only).
  • legacy_flat_credential_fields_load_into_registry — old configs load.
  • typed_credentials_round_trip_and_tolerate_unknown — new format round-trips;
    unknown key dropped, not fatal.

Gate

cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings,
cargo test --workspace — all green. A code-reviewer pass on the diff found no
blockers (config round-trip, map-key serialization, and dispatch equivalence
all verified).

🤖 Generated with Claude Code

… kinds (R19)

Replace the hard-coded "MembershipCredential"/"EndorsementCredential"
string-matching in `handle_credential_issue` and the two
`Option<serde_json::Value>` fields on `CommunityRecord` with a single typed
registry: a `CredentialKind` enum (kept next to `MessageType` in core) keying
a `BTreeMap<CredentialKind, serde_json::Value> credentials`. Adding a
credential kind is now one variant + its match arms — dispatch, storage and
the My Credentials UI all iterate `CredentialKind::ALL` / match on `vc_type`,
so they pick it up without edits.

Config round-trip is preserved without a migration: a deserialize-only
`CommunityRecordShadow` (`#[serde(from = ...)]`) folds pre-R19 flat
`membership_credential`/`role_credential` fields into the map, new-format
keys winning. The shadow keys the map by `String` deliberately so an
unrecognised kind from a newer build is dropped with a warning rather than
making the whole config unloadable.

Behavior is equivalent on the substantive path (storage + membership
activation). The router needs no change: all credential kinds share one
`credential-exchange/issue` message type and DIDComm route, so a new kind
cannot drift the route list.

Tests: registry self-consistency; dispatch handles every registered kind
generically (acceptance criterion); legacy flat-field load; new-format
round-trip + unknown-key tolerance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
@stormer78 stormer78 requested a review from a team as a code owner June 11, 2026 13:44
@stormer78 stormer78 merged commit 4cb8152 into main Jun 11, 2026
11 of 13 checks passed
@stormer78 stormer78 deleted the r19-typed-credential-registry branch June 11, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant