Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
trust-tasks-rs
Reference Rust library for the Trust Tasks framework.
Trust Tasks are self-contained, transport-agnostic, JSON-based descriptions of
the verifiable work that happens between two parties — a KYC handoff, a consent
grant, an access-control change. This crate provides the framework-level
document type and a TransportHandler trait that lets concrete transports
(REST, DIDComm, message queues, ...) plug their identity, integrity, and
freshness semantics into a single validation pipeline.
The framework specification this crate implements is SPEC.md.
What's in here
| Module | Purpose | SPEC.md section |
|---|---|---|
TrustTask<P> |
The framework document envelope | §4.2 |
TypeUri |
Parsed https://trusttasks.org/spec/<slug>/<MAJOR.MINOR> + #request/#response variant |
§4.4, §6.1 |
Proof |
W3C Data Integrity proof attachment | §4.7 |
ErrorPayload, StandardCode, TrustTaskCode |
The trust-task-error payload + standard codes + extension codes |
§8.2, §8.3, §8.5 |
trust_task_error_type_uri() |
The one definition of the trust-task-error version this library emits |
§8.1 |
ReplayGuard, InMemoryReplayGuard |
Duplicate-execution record: absorbs a bit-for-bit retry, rejects a reused id with idConflict |
§7.2 item 11, §8.4 |
FreshnessPolicy |
Acceptance window over issuedAt / expiresAt — the bound that makes the replay record droppable |
§4.2, §7.2 |
RejectReason, ErrorResponse |
Typed rejection conditions + TrustTask<ErrorPayload> alias, both ?-propagatable |
§7.2, §8 |
TransportHandler |
Trait for transport bindings: derive party identity, prepare outbound, cross-check inbound | §4.8.1, §9.2 |
handlers::NoopHandler |
Transport contributes nothing; in-band members are authoritative | reference impl |
handlers::InMemoryHandler |
Simulated transport with configured local+peer VIDs | reference impl |
Payload, TrustTask::for_payload |
Ties a Rust struct to its Type URI; auto-fills type on construction |
trait |
Dispatcher<R> |
Type-URI → handler routing for consumers that implement N specs | open-set match |
AsyncDispatcher<Ctx, R> |
The same routing for async handlers, carrying a request-scoped context |
open-set match |
specs::<slug>::<version> |
Generated per-spec payload types (one module per registry entry) | generated |
validate feature |
Runtime JSON Schema validation against the embedded payload.schema.json |
opt-in |
Quick start
use ;
use ;
let doc: = from_str?;
assert_eq!;
assert_eq!;
# Ok::
Plugging in a transport
The TransportHandler trait encodes the §4.8.1 precedence rule: in-band
issuer / recipient values are authoritative; transport-derived identity is
used to fill in absent members or to cross-check present ones — never to
override them.
use ;
let handler = new
.with_local
.with_peer;
let resolved: ResolvedParties = handler.resolve_parties?;
// resolved.issuer / resolved.recipient now hold the values the consumer
// MUST apply for every subsequent framework rule that references a party.
A REST or DIDComm binding implements the same trait — populating
derive_parties from the peer certificate, the DIDComm envelope's verified
sender, or whatever the transport authenticates — and the rest of the
validation pipeline stays unchanged.
Routing to a handler
A consumer that implements several specs registers one handler per Type URI
rather than writing an if doc.type_uri == … chain. Dispatcher<R> is the
synchronous form; AsyncDispatcher<Ctx, R> is the same routing for handlers
that need to await and for the request-scoped context they need to do it:
use ;
let dispatcher = new
.
.;
// `dispatch_or_reject` returns the §8.1-routed `trust-task-error` document
// for every routing-time failure, so the caller emits one or the other.
match dispatcher.dispatch_or_reject.await
Both dispatchers downcast Value → P once, and both distinguish
unsupportedType (slug not registered) from unsupportedVersion (slug
registered at a different MAJOR.MINOR, SPEC §5.2 / §8.3) — the answer a
match on the whole URI string cannot produce. AsyncDispatcher additionally
applies TrustTask::enforce_spec_policy to request documents after the
downcast, which is where §7.2 items 5b / 7A / 8 (recipient REQUIRED, proof
REQUIRED, audience binding) become checkable at all.
Request → response → error
The framework's request/response model (SPEC §4.4.1) and trust-task-error
response (SPEC §8) are first-class. The recommended consumer-side pipeline is
[consume_inbound], which runs the SPEC §7.2 checks (item 2 and items 4–8, plus
the freshness bound and item 11's duplicate-execution record) and hands the
accepted document plus the resolved parties to your handler:
use ;
// The guard *is* the duplicate-execution record — one per consumer, held for
// the process's lifetime. Back it with a shared store if you run replicas.
static GUARD: = new;
let outcome = consume_inbound.await;
match outcome
Payload::IS_PROOF_REQUIRED (codegen-emitted from each spec's
proofRequirement.requirement: REQUIRED front-matter) is enforced
authoritatively; ProofPolicy makes the proof-handling tradeoff explicit at
the call site rather than implicit in an Option. On the receiving side,
ErrorPayload::should_retry_at(now) applies §8.4 retry semantics in one call,
and effective_code() collapses an unrecognized extension code to
StandardCode::TaskFailed per §8.5.
For a runnable producer/consumer loop using the framework primitives directly
(no consume_inbound), see examples/loopback.rs:
Per-spec payload types
Every spec under ../specs/<slug>/<version>/payload.schema.json has a
corresponding Rust module under src/specs/, produced by the
sibling trust-tasks-codegen crate. Each module
exposes:
- A
Payloadstruct (the request payload) with animpl Payloadpinning the request Type URI. - A
Responsestruct (when the spec defines a success response, SPEC §4.4.1) with a secondimpl Payloadcarrying the#responsefragment. - Any shared
$defstypes — for example,AclEntryfor the ACL specs.
use ;
let req = for_payload;
assert_eq!;
Regenerate when a payload.schema.json changes:
The output is committed (no OUT_DIR magic), so PRs that change a schema
should include the regenerated src/specs/ diff. CI can enforce this with a
git diff --exit-code src/specs/ after running the generator.
The framework-defined trust-task-error spec is the one exception — its
payload is modelled by hand in ErrorPayload because the framework needs the
richer TrustTaskCode enum (standard codes + namespaced extension codes) the
codegen can't produce.
Cargo features
| Feature | What it enables |
|---|---|
| (default) | all-specs — the framework crate plus every generated spec family, which is what the crate has always shipped |
all-specs |
Every top-level spec family |
| one per family | acl, audit, auth, chat, config, confirm, consent, credential-exchange, device, did-management, git-trust, governance, keys, messaging, policy, provision, push, registry, sync, task-consent, vault, vrc, vta, vtc, webvh, witness — the specs::<family> module tree and nothing else |
validate |
Runtime JSON Schema validation. Pulls in jsonschema and exposes a validate module + ValidatedPayload impls for every generated request payload. Belt-and-suspenders over serde's structural decoding — catches pattern, minItems, and additionalProperties constraints that the typed structs can't always encode. |
Compiling only the families you use
specs/ is 344 generated modules and ~15 MB of source. Depending on the crate
with default features compiles all of it. If you use three tasks, say so:
[]
= { = "0.13", = false, = ["vault"] }
Measured on one machine with dependencies cached and CARGO_INCREMENTAL=0:
22 s for the default feature set, 4.0 s for vault alone, 1.2 s
for acl alone.
The framework surface — TrustTask, the §7.2 consume pipeline, the transport
traits, ErrorPayload, discovery — is always compiled, along with the five
framework-reserved slugs of SPEC §6.1 (trust-task-control,
trust-task-discovery, trust-task-next-step, trust-task-ok,
trust-ceremony-receipt) that it depends on. Only the task families are
selectable, and each is self-contained: no family's types reference another's,
so any subset compiles.
schema_index::schema_for returns None for a Type URI whose family you did
not select. That is what None already means — "this build knows no spec for
it" — but if you dispatch on Type URIs across the whole registry, take
all-specs.
Status
The crate version is semver over this library's own API, deliberately
decoupled from the SPEC.md framework version — the two move for different
reasons, and one number cannot answer both questions. A document's framework
version is carried by its specification's targetFrameworkVersion declaration
(SPEC §7.3 item 3).
The framework spec is itself a Working Draft; this crate is a reference
implementation maintained alongside it. Breaking changes are expected until the
framework reaches candidate. See CHANGELOG.md for what has
landed and for the versioning rules.
License
Apache-2.0. See ../SOURCE_CODE.md for the source-code
licensing terms of this repository.