pub struct VtcClient { /* private fields */ }Expand description
A client bound to one VTC’s API base, holding a bearer token once authenticated.
Implementations§
Source§impl VtcClient
impl VtcClient
Sourcepub async fn connect(
base_url: &str,
vtc_did: &str,
client_did: &str,
private_key_multibase: &str,
) -> Result<Self, VtcError>
pub async fn connect( base_url: &str, vtc_did: &str, client_did: &str, private_key_multibase: &str, ) -> Result<Self, VtcError>
Authenticate to the VTC as client_did (challenge-response, reusing the
VTA SDK’s audience-agnostic flow) and return a ready client.
base_url is the full API base including the mount (e.g.
https://vtc.example.com/v1); vtc_did is the community’s DID.
Sourcepub fn with_token(
base_url: &str,
vtc_did: &str,
token: impl Into<String>,
) -> Self
pub fn with_token( base_url: &str, vtc_did: &str, token: impl Into<String>, ) -> Self
Construct a client from an already-obtained bearer token (e.g. a token
minted out of band, or for testing). base_url includes the mount.
Sourcepub fn anonymous(base_url: &str, vtc_did: &str) -> Self
pub fn anonymous(base_url: &str, vtc_did: &str) -> Self
Construct a client with no bearer token, for the applicant side of the join ceremony.
submit_join authenticates with the document’s own
holder proof, so an applicant — who is by definition not yet a member and
has no token to get — needs exactly this. Every other method returns
VtcError::NotAuthenticated, which is the honest answer rather than a
401 from the server.
vtc_did still matters: it is the audience the submitted document is
addressed to, and the VTC rejects a document addressed elsewhere.
Sourcepub async fn list_members(
&self,
role: Option<&str>,
) -> Result<Vec<MemberRecord>, VtcError>
pub async fn list_members( &self, role: Option<&str>, ) -> Result<Vec<MemberRecord>, VtcError>
List every community member, optionally filtered by role, following the
cursor to completion. Requires an admin token. This is the fleet roster
when the community’s members are managed VTAs.
Sourcepub async fn list_join_requests(
&self,
status: Option<&str>,
) -> Result<Vec<JoinRequestSummary>, VtcError>
pub async fn list_join_requests( &self, status: Option<&str>, ) -> Result<Vec<JoinRequestSummary>, VtcError>
List join requests (the admin work queue), optionally filtered by
status (e.g. "pending"). Requires an admin token. For a fleet, these
are VTAs awaiting enrollment.
Sourcepub async fn approve_join(
&self,
request_id: &str,
) -> Result<DecideResult, VtcError>
pub async fn approve_join( &self, request_id: &str, ) -> Result<DecideResult, VtcError>
Approve a join request — admit the applicant and issue its membership credential (VMC). Requires an admin token. For a fleet, this enrolls a VTA that has applied to join.
Sourcepub async fn reject_join(
&self,
request_id: &str,
reason: Option<&str>,
) -> Result<DecideResult, VtcError>
pub async fn reject_join( &self, request_id: &str, reason: Option<&str>, ) -> Result<DecideResult, VtcError>
Reject a join request, optionally recording an operator rationale in the audit trail. Requires an admin token.
Sourcepub async fn remove_member(
&self,
did: &str,
reason: Option<&str>,
) -> Result<RemoveResult, VtcError>
pub async fn remove_member( &self, did: &str, reason: Option<&str>, ) -> Result<RemoveResult, VtcError>
Remove a member (offboarding). The VTC applies its removal disposition and
flips the member’s status-list revocation bit. reason is an optional
admin note. Requires an admin token. For a fleet, this decommissions a
managed VTA.
Sourcepub async fn update_member_extensions(
&self,
did: &str,
extensions: Value,
) -> Result<(), VtcError>
pub async fn update_member_extensions( &self, did: &str, extensions: Value, ) -> Result<(), VtcError>
Update a member’s community-defined extensions (opaque JSON) via
PATCH /members/{did}. A fleet manager records per-member operational
state here — e.g. the assigned fleet_index at enrollment, which the
roster then carries (see MemberRecord::extensions). Admin token.
Sourcepub async fn submit_join(
&self,
body: &JoinRequestSubmitBody,
applicant_did: &str,
private_key_multibase: &str,
) -> Result<VerdictResponse, VtcError>
pub async fn submit_join( &self, body: &JoinRequestSubmitBody, applicant_did: &str, private_key_multibase: &str, ) -> Result<VerdictResponse, VtcError>
Submit a join request (the applicant side): sign a
join-requests/submit/0.1 Trust Task with the applicant’s holder key and
post it to the document endpoint. Returns the community’s verdict —
auto-admit carries the issued VMC + role VEC inline, otherwise the
request is queued for an admin.
No bearer token. The document’s eddsa-jcs-2022 proof is the
authentication: the VTC takes the proof’s verificationMethod DID as the
applicant and requires the document issuer to match it
(vtc-service/src/trust_tasks/mod.rs::resolve_holder). So this is the
one method that works on a client built with neither
connect nor with_token — an
applicant is by definition not yet a member.
applicant_did must be a did:key (the server’s proof resolver accepts
no other method) whose seed is private_key_multibase. It is the DID
that becomes the member on admission, not whatever identity this client
may hold a token for — a fleet manager submitting on behalf of a VTA
signs with that VTA’s key.
The document is addressed to vtc_did (SPEC §4.8.2
audience binding), so a signed submit captured from one community cannot
be replayed into another.
§Why the key, and not just a body
This used to POST the VP-framed body to POST /join-requests, a route
that no longer exists — the holder-facing join verbs (submit/request,
manifest, status) were folded into the single Trust-Task document
endpoint, routed by document type. That fold moved the applicant’s
authentication from “a signature somewhere inside the body” to “a proof
over the whole document”, which is why this signature grew the key.
Sourcepub async fn list_policies(&self) -> Result<Vec<Value>, VtcError>
pub async fn list_policies(&self) -> Result<Vec<Value>, VtcError>
List the community’s policies (opaque JSON descriptors). Admin token.
Sourcepub async fn get_policy(&self, id: &str) -> Result<Value, VtcError>
pub async fn get_policy(&self, id: &str) -> Result<Value, VtcError>
Fetch one policy by id (opaque JSON, incl. the Rego source). Admin token.
Sourcepub async fn upload_policy(
&self,
purpose: &str,
rego_source: &str,
) -> Result<Value, VtcError>
pub async fn upload_policy( &self, purpose: &str, rego_source: &str, ) -> Result<Value, VtcError>
Upload a new Rego policy bundle for purpose ("join", "removal",
…). Returns the upload descriptor (id, sha256, version). Admin token.
Upload alone does not activate it — call activate_policy.