Trait CratestackEnvelope
pub trait CratestackEnvelope:
Clone
+ Send
+ Sync
+ 'static {
// Required methods
fn media_type(&self, shape: BodyShape) -> Option<&'static str>;
fn seal<'a>(
&'a self,
payload: Bytes,
bind: &'a Binding<'a>,
) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a;
fn open<'a>(
&'a self,
body: Bytes,
bind: &'a Binding<'a>,
ctx: &'a mut CratestackContext,
) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a;
fn stream_sealer(
&self,
bind: Binding<'static>,
) -> Option<Box<dyn StreamSealer>>;
fn stream_opener(
&self,
bind: Binding<'static>,
) -> Option<Box<dyn StreamOpener>>;
// Provided method
fn seal_value<'a, C, T>(
&'a self,
codec: &'a C,
value: &'a T,
bind: &'a Binding<'a>,
) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
where C: CratestackCodec,
T: Serialize + Sync + ?Sized { ... }
}Expand description
The signing seam between the codec and the HTTP body (ADR 0006 §1).
Replaces the sync, bytes-only open_request/seal_response trait,
which no router ever called. Signing needs a key, the request’s context
(the Binding) and async, so KMS- and HSM-backed signers can plug
in.
seal/open return impl Future + Send rather than the ADR sketch’s
BoxFuture, which heap-allocates on every call. That would put an
allocation on the unsigned path, and cratestack#1004 requires
NoEnvelope to allocate nothing. Object safety is
not lost, because the Clone supertrait already rules out dyn CratestackEnvelope: routers and clients are generic over E. As with
AuthProvider, an implementation may be written
as a plain async fn.
Verification failures must be CratestackError::Unauthorized with a
coarse message. The response must never reveal which check failed
(§10). A backend outage (the key resolver or the nonce store is
unreachable) is not a verification failure: it is
CratestackError::Internal, a 500, logged server-side (ADR 0006, P0
scoping decisions).
Required Methods§
fn media_type(&self, shape: BodyShape) -> Option<&'static str>
fn media_type(&self, shape: BodyShape) -> Option<&'static str>
The Content-Type of a sealed body of this shape. None means this
envelope adds no framing for shape, so the body keeps the
payload’s own media type (the codec’s CONTENT_TYPE, or
application/cbor-seq) and content negotiation is unchanged.
Contract: media_type(BodyShape::Stream) is None exactly when
stream_sealer and
stream_opener return None. A router may
therefore check this first and skip building the owned binding the
stream methods take.
fn seal<'a>(
&'a self,
payload: Bytes,
bind: &'a Binding<'a>,
) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
fn seal<'a>( &'a self, payload: Bytes, bind: &'a Binding<'a>, ) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
Wrap payload, the codec’s output byte for byte, for the peer that
will rebuild bind. It runs after codec.encode.
fn open<'a>(
&'a self,
body: Bytes,
bind: &'a Binding<'a>,
ctx: &'a mut CratestackContext,
) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
fn open<'a>( &'a self, body: Bytes, bind: &'a Binding<'a>, ctx: &'a mut CratestackContext, ) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
Verify body against bind, run the replay checks, and return the
payload for codec.decode. The result should be a zero-copy slice of
body, because the verifier checks exactly the bytes it received and
nothing is re-serialized (§1).
On success an envelope that authenticated a signer records it with
CratestackContext::record_verified_signer. The router wiring
(cratestack#1006; no router calls this trait yet) lifts that into the
rate-limit and idempotency principal, which is why open must run
before both of those layers (§12).
fn stream_sealer(&self, bind: Binding<'static>) -> Option<Box<dyn StreamSealer>>
fn stream_sealer(&self, bind: Binding<'static>) -> Option<Box<dyn StreamSealer>>
A sealer for one outgoing stream, or None when this envelope does
not sign streams (plain cbor-seq passes through). It takes
Binding<'static> because the sealer outlives the request borrow:
chain mode’s h₀ = SHA-256(external_aad) needs the binding (§6).
fn stream_opener(&self, bind: Binding<'static>) -> Option<Box<dyn StreamOpener>>
fn stream_opener(&self, bind: Binding<'static>) -> Option<Box<dyn StreamOpener>>
An opener for one incoming stream, or None when this envelope does
not verify streams. See stream_sealer.
Provided Methods§
fn seal_value<'a, C, T>(
&'a self,
codec: &'a C,
value: &'a T,
bind: &'a Binding<'a>,
) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
fn seal_value<'a, C, T>( &'a self, codec: &'a C, value: &'a T, bind: &'a Binding<'a>, ) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
Encode value with codec and seal the result for bind: the same
bytes as codec.encode followed by seal, which is
exactly what the default does.
It exists so that an envelope can encode in place (ADR 0006 §1;
maintainer decision on cratestack#1005, which kept that design over
accepting one copy of the payload): cratestack-cose overrides it to
reserve the payload’s bstr head inside its output buffer and let
CratestackCodec::encode_into write the payload right after it, so
the encoded payload is never held in a buffer of its own. seal
stays for callers that already hold the encoded bytes.
A provided method with a default body, added after cratestack#1004
merged the trait, so no implementation had to change. T: Sync
because the value is borrowed by the returned Send future.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".