Skip to content

Persist and surface decision evidence for rejected or removed community membership #240

Description

@stormer78

Surfaced by an external RAHP/DRARM assessment of the Cypress release
(sankarshanmukhopadhyay/rahp-toolkit#18, finding OVTC-RAHP-03). This issue
covers the client half only; the governance half (appeal, review,
restoration) belongs in VTC/VTI and is tracked separately.

Problem

When a VTC rejects a join request or removes an active member, the decision
evidence reaches us on the wire and we throw it away.

VTC returns a typed reject code plus a reason
(vtc-service/src/messaging.rs:974 in verifiable-trust-infrastructure). All
four inbound paths in openvtc-core/src/messaging.rs have that code and reason
in hand:

  • :308status: "rejected" on a join-status reply
  • :392VerdictEffect::Deny, which even logs code and reason
  • :473FORBIDDEN problem-report (invitation not accepted)
  • :564 — join-denial trust-task-error, which logs code and detail

Each one then calls record.reject(), and
CommunityRecord::reject() / remove() take no arguments
(openvtc-core/src/config/account.rs:441-454). They set a status and clear
acknowledged. Nothing else is persisted.

The net effect for the operator: a community flips to Rejected or Removed,
the actions-required badge lights up, they press a key to acknowledge it — and
the why exists only in a tracing line they will never see. There is no
record of who decided, on what basis, or when. Acknowledging an outcome you
cannot inspect is not meaningful acknowledgement.

Expired has the same shape but is client-generated, so it is less severe —
still worth carrying the timeout basis for consistency.

Proposed change

  1. Give reject() and remove() a decision-evidence argument and persist it on
    CommunityRecord:
    • deciding authority (the VTC DID that sent the decision)
    • decision code (typed) and human-readable reason
    • decision timestamp (theirs if present, ours as received otherwise)
    • the inbound path it arrived on, so the evidence is reconstructable
  2. Surface it wherever the terminal state is shown — the communities list detail
    view and the actions-required notice — not only in the acknowledge prompt.
  3. Keep it stable across the config round-trip (serde) so history survives a
    restart; inactive records are already retained for re-join
    (docs/design/multi-community-support.md R-S-1), and the evidence should be
    retained with them.
  4. Where no reason is supplied by the VTC, say so explicitly ("no reason given")
    rather than rendering an empty field — absence of a reason is itself
    information the operator should see.

Out of scope

Appeal/review endpoints, policy-version context, and correction/restoration
semantics after an overturned decision. Those need the governing layer to define
them first; this issue makes OpenVTC ready to surface them.

Acceptance

  • A rejected join and a removal each persist and display authority, code, reason
    and timestamp.
  • A decision arriving with no reason renders explicitly as such.
  • Round-trip test: evidence survives save/load.
  • No decision evidence is lost between the four inbound paths — a test per path.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions