pub trait Payload: Serialize + DeserializeOwned {
const TYPE_URI: &'static str;
const IS_BEARER: bool = false;
const IS_PROOF_REQUIRED: bool = false;
const IS_RECIPIENT_REQUIRED: bool = false;
const IS_ISSUED_AT_REQUIRED: bool = false;
const PAYLOAD_SCHEMA: Option<&'static str> = None;
// Provided methods
fn type_uri() -> TypeUri { ... }
fn extended_code(local: impl Into<String>) -> TrustTaskCode { ... }
fn family_code(namespace: &str, local: impl Into<String>) -> TrustTaskCode { ... }
}Expand description
Required Associated Constants§
Provided Associated Constants§
Sourceconst IS_BEARER: bool = false
const IS_BEARER: bool = false
Whether the originating Trust Task specification is a bearer specification per SPEC.md §4.8.3 — that is, opts out of the §4.8.2 audience-binding rule.
Defaults to false (non-bearer). The codegen emits an explicit
const IS_BEARER: bool = true; override only when the spec’s front
matter declares bearer: true.
Consumers consult this constant via
crate::TrustTask::enforce_audience_binding to apply SPEC.md §7.2
item 8 without consulting the registry at runtime.
The codegen emits this constant on both the request Payload
impl and the response Response impl (when the spec defines
one). The audience-binding check fires on request-side documents
only, so the constant on the response impl is informational —
downstream tooling that walks generated modules generically can
read it without special-casing variants.
Sourceconst IS_PROOF_REQUIRED: bool = false
const IS_PROOF_REQUIRED: bool = false
Whether the originating Trust Task specification obliges a consumer
to reject a document that arrives without a proof, per SPEC.md §7.3
item 8 (proofRequirement.requirement == "REQUIRED").
Defaults to false (i.e. OPTIONAL or RECOMMENDED — the consumer
is free to accept a proofless document). The codegen emits an explicit
const IS_PROOF_REQUIRED: bool = true; override only when the spec’s
front matter declares proofRequirement.requirement: REQUIRED.
Consumers consult this constant via crate::consume_inbound to
apply SPEC.md §7.2 item 7 authoritatively per-spec, rather than as a
consumer-wide policy toggle.
Like IS_BEARER, this constant is emitted on
both the request Payload impl and the response Response impl.
consume_inbound consults it on the request side; a producer
consuming a response would do the same check against the response
impl if its trust posture requires it.
Sourceconst IS_RECIPIENT_REQUIRED: bool = false
const IS_RECIPIENT_REQUIRED: bool = false
Whether the originating Trust Task specification obliges a consumer
to reject a document that arrives without an in-band recipient, per
SPEC.md §7.2 item 5 and §7.3 item 5 (the party filling the recipient
member is declared REQUIRED).
Defaults to false. The codegen emits an explicit
const IS_RECIPIENT_REQUIRED: bool = true; override only when the
spec’s front matter declares the relevant party (the one carrying
member: recipient) as requirement: REQUIRED. Because a response
document swaps the parties, the Response impl’s value tracks the
issuer party’s requirement instead.
When true, a document whose in-band recipient is absent is rejected
with malformedRequest — the audience must be carried in-band (not
merely transport-derived) so the document is self-contained (§4.8).
Consumers consult this via crate::consume_inbound.
Sourceconst IS_ISSUED_AT_REQUIRED: bool = false
const IS_ISSUED_AT_REQUIRED: bool = false
Whether the originating Trust Task specification obliges a consumer
to reject a document that arrives without an issuedAt, per SPEC.md
§7.3 item 17 (issuedAtRequirement declared REQUIRED).
Item 17 raises the framework’s §4.2 SHOULD to a MUST for the
documents of the specification that declares it, and obliges every
specification defining a consequential Trust Task (§2) to declare it.
The reason is §7.2 item 11: duplicate-execution protection is
implementable only over a bounded window, and a document carrying
neither expiresAt nor issuedAt cannot be placed in one — so a
consumer would have to retain its record forever or refuse to execute.
Defaults to false, so a hand-written Payload impl (the crate’s own
trust-task-error, or any downstream one) keeps compiling unchanged.
The codegen emits an explicit
const IS_ISSUED_AT_REQUIRED: bool = true; override only when the
spec’s front matter declares it, exactly as it does for
IS_PROOF_REQUIRED.
§Not the same thing as crate::FreshnessPolicy::require_issued_at
That field is the consumer’s own posture, chosen at the call site
and applied to every document it sees. This constant is the
specification’s requirement, published in the registry and true of
the type regardless of which consumer holds it. A consumer running the
permissive FreshnessPolicy::default
still rejects a document of a spec that declares this, because the
obligation is not the consumer’s to relax.
When true, a document with no issuedAt is rejected with
malformedRequest — §8.3 defines no dedicated code, and expired
would misdescribe a document that was never acceptable. It is the same
code §7.2 item 13 already uses for the freshness rejections.
Like IS_BEARER, this constant is emitted on both
the request Payload impl and the response Response impl.
Sourceconst PAYLOAD_SCHEMA: Option<&'static str> = None
const PAYLOAD_SCHEMA: Option<&'static str> = None
Raw text of the payload.schema.json describing values of this type,
or None where this build has no schema for it.
This is the artifact SPEC.md §7.2 item 2 is performed against. It is
emitted unconditionally — it is a &'static str and pulls in no
dependency; only evaluating it needs a JSON Schema implementation,
which the caller supplies (see crate::PayloadPolicy).
Most of item 2 has already happened by the time you hold a
Payload. Deserializing into these generated types enforces
required members, member types, additionalProperties: false, and the
string constraints typify expresses as validating newtypes (pattern,
minLength). What survives deserialization is what typify cannot
express — minProperties, minItems on an optional array,
conditional subschemas — and that residue is what a schema check
against this constant still catches.
None for hand-modelled payloads outside the codegen’s reach
(trust-task-error, whose shape is carried by the Rust type system
instead). A policy that validates treats None as nothing to check;
it is not a silent failure, because the type it deserialized into is
itself the constraint.
Provided Methods§
Sourcefn type_uri() -> TypeUri
fn type_uri() -> TypeUri
Parsed form of TYPE_URI.
The default implementation calls str::parse and panics on a
malformed value — which can only happen if a Payload impl supplies
an invalid TYPE_URI, i.e. a static-string bug worth surfacing
loudly.
Sourcefn extended_code(local: impl Into<String>) -> TrustTaskCode
fn extended_code(local: impl Into<String>) -> TrustTaskCode
Build an extended TrustTaskCode under this payload’s slug, per
SPEC.md §8.5.
Equivalent to writing:
TrustTaskCode::new_extended("acl/change-role", "last_authority_protected").unwrap()but sources the slug from TYPE_URI so the slug
literal cannot drift away from the type’s identity. The §8.5
namespace rule (“the slug of the spec being processed”) is then
enforced by construction.
local is validated against spec.meta.schema.json’s
errorCodes[].code grammar (the part after the colon: a lowercase
letter, then letters of either case, digits, or underscores).
Both casings are accepted so that framework 0.2 lowerCamelCase
locals (documentRevoked) and frozen framework 0.1 snake_case
locals (document_revoked) parse under one rule; SPEC §4.10 item 4
SHOULDs lowerCamelCase for new specifications. Panics on
an invalid local — this method is for static call-site usage;
callers handling runtime input should use
TrustTaskCode::new_extended and propagate the Result.
Also panics under the same condition as
type_uri: when TYPE_URI
is not a valid Type URI, i.e. a static-string bug.
Sourcefn family_code(namespace: &str, local: impl Into<String>) -> TrustTaskCode
fn family_code(namespace: &str, local: impl Into<String>) -> TrustTaskCode
Build an extended TrustTaskCode under a family namespace, per
SPEC.md §8.5 rule 2.
A family namespace is a proper path prefix of this payload’s slug, used
for a condition whose meaning is defined once across a family rather
than per specification — did-management:unknownDomain on
did-management/did/delete, say, where every member of the family can
reject a request naming a domain the consumer does not host and the
rejection means the same thing in each.
// On a `did-management/did/delete` handler:
let code = Payload::family_code("did-management", "unknownDomain");
assert_eq!(code.to_string(), "did-management:unknownDomain");Use extended_code for a code the specification
defines for itself; that is the common case. Reach for this only when
the code is genuinely shared, because a family namespace claims the
condition means the same thing across every sibling.
namespace is checked against the slug derived from
TYPE_URI rather than taken on trust, so the §8.5
prefix rule holds by construction and a hand-written namespace cannot
drift away from the type’s identity — the same guarantee
extended_code provides for the own-slug case.
Panics when namespace is neither the slug nor a proper path prefix of
it, or when local fails the errorCodes[].code grammar. Like
extended_code this method is for static
call-site usage; callers handling runtime input should use
TrustTaskCode::new_extended and propagate the Result.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".