Bus-Envelope Integrity (HMAC)
Stratara supports an opt-in HMAC signature on every CommandEnvelope + EventBundle that travels on the bus. When enabled, consumers verify the signature before deserializing the body; tampered envelopes are rejected.
When to turn this on
- Multi-tenant brokers — if your bus connects multiple apps with different trust levels (e.g. a shared RabbitMQ cluster).
- Defense-in-depth for compliance regimes where "message provenance" needs an auditable verifier.
You don't need this for single-app, single-host scenarios — the network + broker auth already gates who can publish.
Wire it
services.AddBusEnvelopeIntegrity(options =>
{
options.Mode = BusEnvelopeIntegrityMode.Strict;
options.SharedKey = builder.Configuration["BusIntegrity:SharedKey"];
});
Three modes:
| Mode | Producer behaviour | Consumer behaviour |
|---|---|---|
Off (default) |
No signature added | No verification — accepts any envelope |
Permissive |
Always signs | Verifies if a signature is present; accepts unsigned (for rolling deployments) |
Strict |
Always signs | Rejects unsigned envelopes + envelopes with invalid signatures |
Roll-out pattern: deploy producers with Permissive first, wait for the entire fleet to be running the signed version, then flip consumers to Strict.
What's signed
Identity-only — the signature covers:
- CommandEnvelope:
CommandTypeName + "|" + SessionContextJson(the routing identity + the actor/subject context). - EventBundle:
SessionContextJson(the actor/subject context for the originating command).
The payload body is NOT signed. Payload tamper-resistance comes from the [EncryptData]-AAD binding instead (see Encrypt Sensitive Data) — the AAD encodes the tenant, so a tampered envelope addressed to a different tenant fails decryption.
This is intentional: keeping the signature scope small means the signing cost stays constant + the verifier doesn't have to deserialize the body to check the signature.
The signer interface
public interface IBusEnvelopeSigner
{
string Sign(BusEnvelopeCanonical canonical);
bool Verify(BusEnvelopeCanonical canonical, string signature);
}
Default impl: HmacBusEnvelopeSigner — HMAC-SHA-256 over the canonical projection. Constant-time compare via CryptographicOperations.FixedTimeEquals. Length-check happens before the compare (v3.0.13+ — protects against ArgumentException from missized attacker-supplied signatures).
Startup probe
BusEnvelopeIntegrityStartupProbe (v3.0.13+) warns at host-start if Mode != Off but no signer is registered, or if Mode == Off and IsProduction() returns true. Production hosts should default to at least Permissive.
Configuration
{
"BusIntegrity": {
"Mode": "Strict",
"SharedKey": "base64-encoded 32-byte HMAC key"
}
}
Rotate the key by shipping the new key to all participants first, then redeploying. There's no built-in rolling-key support — the shared key is a single value at any point in time.