Struct Binding
pub struct Binding<'a> {
pub audience: Cow<'a, str>,
pub method: Cow<'a, str>,
pub route: Cow<'a, str>,
pub path_params: PathParams<'a>,
pub query: Option<Cow<'a, str>>,
pub contract_sha: [u8; 32],
pub payload_media_type: Cow<'a, str>,
pub bound_headers: BoundHeaders<'a>,
pub response: Option<ResponseBinding>,
}Expand description
Everything that goes into a sealed body’s COSE external_aad (ADR 0006
§4). It is never sent: the router and the client each rebuild it
from context they already hold, so the binding costs 0 bytes on the
wire. A body sealed for one (audience, method, route, path_params, query, op contract) fails to open under any other, which defeats
cross-service and cross-endpoint replay; a response is bound to its
request (the digest and how it was computed) and its status, which
defeats response swapping; a signed REST response is bound to the
resource it answers, not only to the route’s shape; and a client whose
wire shape for the op differs from the server’s fails closed on
contract_sha (binding version 2: per op, not per schema).
This type carries the inputs only. Encoding them into the AAD’s CBOR
array, including the leading binding-version field (currently 2, the
escape hatch ADR 0006 Q5 reserves), belongs to cratestack-cose
(cratestack#1005). Core has no CBOR dependency (the CBOR backend lives
in cratestack-codec-cbor), and every crate depends on core, so it
should not gain one here. Keeping the byte layout in one crate also
keeps it to one encoder that every client shares (§11).
A request binding leaves response as None; a
response binding sets it. The response half is one Option, not one per
field, so a half-built response binding cannot be expressed.
Deviation from the ADR 0006 §1 sketch, which uses &'a str,
&'a [&'a str] and &'a [u8; 32]: the string fields are Cow,
path_params is the Cow-like PathParams, and contract_sha is held
by value. The unary path still borrows everything and allocates nothing.
The stream path can also produce the Binding<'static> that
CratestackEnvelope::stream_sealer
takes, through Binding::into_owned. A sealer outlives the request
borrow it was created from, so it cannot hold &'a fields. Copying 32
bytes is cheaper than a second lifetime parameter.
Fields§
§audience: Cow<'a, str>The logical identifier of the service the message is addressed to,
e.g. "payments": a configured name that the sender and the
receiver agree on, never the Host header (a proxy rewrites it, and
the sender does not control it). A server builds its bindings with
its own inbound audience; a client, with the audience of the service
it calls. The AAD encodes it right after the binding version.
Two attacks need it, both of which a shared schema and route would
otherwise let through (maintainer decision on cratestack#1005,
2026-09-24): a request sealed for service A replayed against service
B, and a COSE_Mac0 request that service A sent out, reflected back to
A itself (the MAC key is symmetric, so A cannot tell its own message
from its peer’s by the key). Both fail once A’s inbound audience
differs from the audience it seals outbound requests for, so a name
a service uses in both directions (a shared "internal", say)
quietly gives up the reflection protection. An empty audience binds
nothing at all, and cratestack-cose refuses it as local misuse (a
500), when sealing and when opening.
Added after cratestack#1004 merged, which made this a breaking change to a public-field struct. That is acceptable pre-1.0 with no downstream user yet.
method: Cow<'a, str>HTTP method, e.g. "POST".
route: Cow<'a, str>The RPC op_id (e.g. model.Payment.create), or the REST route
template (e.g. /accounts/{id}). The template alone names the
route’s shape, not the resource; the matched values are bound
separately in path_params. Never the raw URL
path: gateways rewrite prefixes, and a binding that changes under
Router::nest fails every legitimate request (the same fact broke
descriptor lookup in cratestack#877).
path_params: PathParams<'a>REST: the matched path parameter values, in the order the route
template declares them, as the router decoded them. RPC: empty
(PathParams::EMPTY), because the op_id and the signed body
(for a response, the request digest) already bind it. Without these,
a signed response to GET /accounts/1 would verify as the answer to
GET /accounts/2 (ADR 0006 §4, amended 2026-09-24). The AAD encodes
them right after route.
query: Option<Cow<'a, str>>The request’s canonicalised query string (the same canonical form
cratestack-auth’s canonical_query produces), or None when the
request has none.
contract_sha: [u8; 32]The digest of the op this message calls, as raw bytes:
cratestack_core::op_contract_digest, the SHA-256 over the op’s
wire closure (its input and output shapes and every model, type and
enum reachable from them, without policies, indexes, SQL, validators
or @server_only fields; cratestack#1123). It moves only when this
op’s wire shape does, so a server-only schema edit, or an edit to
another op, leaves every other op’s signed messages valid.
Generated code bakes the digests in as OP_CONTRACTS; a signed
/rpc/batch binds the whole-contract client_contract_digest
instead (BATCH_CONTRACT_KEY). Binding version 2 replaced the
whole-IR schema_sha of version 1; the AAD encodes it in the same
position.
payload_media_type: Cow<'a, str>Media type of the payload inside the envelope, e.g.
"application/cbor". It is bound here instead of being sent in a
COSE header (label 3 would cost bytes on every message; ADR 0006 §2),
so a verifier cannot be tricked into decoding the payload as
something else.
On a request binding it is the request payload’s type; on a
response binding (response is Some) it is the
response payload’s own type, which need not be the request’s
(form in, JSON out). Both default to application/cbor
(DEFAULT_PAYLOAD_MEDIA_TYPE), and
the Cratestack-Payload-Type / Cratestack-Payload-Accept headers
(see PAYLOAD_TYPE_HEADER) tell the
verifier which string to rebuild the AAD with. The AAD layout and the
binding version are unchanged.
bound_headers: BoundHeaders<'a>The request’s Idempotency-Key and If-Match, exactly as sent (see
BoundHeaders); BoundHeaders::NONE for a request with
neither. A response binding repeats its request’s, so a response is
also bound to the key it was stored and replayed under. Added by the
maintainer’s decision S1 on cratestack#1006 (2026-09-26), before
binding version 1 was frozen; the AAD encodes it right after
payload_media_type.
response: Option<ResponseBinding>Responses only: the request answered and the status (see
ResponseBinding). The request digest is SHA-256 over the request’s
COSE bytes when the request was signed; when it was not (a bodiless
GET, say), SHA-256 over the 16-byte Cratestack-Nonce the client
sent followed by the payload, so that a signed response answers
exactly one request and a cached one cannot be replayed for the next
GET of the same URL (maintainer decision on cratestack#1005). The
digest’s RequestKind records which of the two
it is. cratestack-cose computes both.
Implementations§
§impl Binding<'_>
impl Binding<'_>
pub fn into_owned(self) -> Binding<'static>
pub fn into_owned(self) -> Binding<'static>
Detach from the borrowed request data, for a stream sealer or opener
that outlives the request. This allocates for every field that is
still borrowed. A caller whose fields are already 'static (an
op_id from generated code, a codec’s CONTENT_TYPE) can build a
Binding<'static> directly with Cow::Borrowed and skip the copy.
Trait Implementations§
impl<'a> Eq for Binding<'a>
impl<'a> StructuralPartialEq for Binding<'a>
Auto Trait Implementations§
impl<'a> Freeze for Binding<'a>
impl<'a> RefUnwindSafe for Binding<'a>
impl<'a> Send for Binding<'a>
impl<'a> Sync for Binding<'a>
impl<'a> Unpin for Binding<'a>
impl<'a> UnsafeUnpin for Binding<'a>
impl<'a> UnwindSafe for Binding<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<'src, T> IntoMaybe<'src, T> for Twhere
T: 'src,
impl<'src, T> IntoMaybe<'src, T> for Twhere
T: 'src,
impl<T> MaybeSend for T
impl<T> MaybeSendSync for T
impl<T> OrderedSeq<'_, T> for Twhere
T: Clone,
§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling [Attribute] value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi [Quirk] value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the [Condition] value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
§impl<'p, T> Seq<'p, T> for Twhere
T: Clone,
impl<'p, T> Seq<'p, T> for Twhere
T: Clone,
§type Iter<'a> = Once<&'a T>
where
T: 'a
type Iter<'a> = Once<&'a T> where T: 'a
§fn contains(&self, val: &T) -> boolwhere
T: PartialEq,
fn contains(&self, val: &T) -> boolwhere
T: PartialEq,
§fn to_maybe_ref<'b>(item: <T as Seq<'p, T>>::Item<'b>) -> Maybe<T, &'p T>where
'p: 'b,
fn to_maybe_ref<'b>(item: <T as Seq<'p, T>>::Item<'b>) -> Maybe<T, &'p T>where
'p: 'b,
MaybeRef].