Envelope Validation
Strata includes built-in envelope validation that checks the structure of every message before it is processed. This catches malformed requests, bad field types, and missing required fields at the transport layer -- before your operation handlers or middleware ever see the message.
Validation is enabled by default and has near-zero performance overhead.
Why Validate Envelopes
- Runtime type safety. TypeScript types disappear at runtime. A service can receive a payload that is
nullor an array instead of the expected object. Envelope validation catches these at the boundary. - Better error messages. Invalid requests get a clear
failedresponse explaining what's wrong, instead of an opaque crash somewhere in your handler. - Cross-implementation compatibility. If you have Strata services written in different languages or different versions, validation ensures they all conform to the same wire format.
- Bug prevention. Catches structural errors during development and testing before they reach production.
Performance
Validation uses hand-rolled checks with simple JavaScript primitives (typeof, equality comparisons, Array.isArray()). No schema libraries, no runtime compilation.
Benchmark results (Apple M1 Max, Node.js v20.x, 100,000 iterations):
| Method | Time per Operation | Overhead |
|---|---|---|
| No validation | 1.09 us/op | baseline |
| Hand-rolled validation | 1.09 us/op | 0% |
| Zod schema validation | 43.17 us/op | 3,860% |
The validation checks are so fast they're lost in the noise of JSON.parse() itself. At 10,000 requests per second, hand-rolled validation adds less than 0.1% additional CPU time compared to no validation at all.
| Scenario (10K req/sec) | CPU Time per Second | % of CPU Core |
|---|---|---|
| No validation | ~11ms | ~1.1% |
| Hand-rolled validation | ~11ms | ~1.1% |
| Zod library | ~455ms | ~45.5% |
Configuration
Validation is configured per-backend using the validateEnvelopes option.
Enabled (Default)
const service = new StrataService({
service: { serviceGroup: 'MyService' },
backend: {
type: 'redis-streams',
redis: { host: 'localhost', port: 6379 },
// validateEnvelopes defaults to true -- no need to set it
},
});Disabled
const service = new StrataService({
service: { serviceGroup: 'TrustedService' },
backend: {
type: 'redis-streams',
redis: { host: 'localhost', port: 6379 },
validateEnvelopes: false,
},
});WARNING
Disabling validation removes a safety net for negligible performance gain. Only do this if you fully control every service on the bus and have verified envelope correctness through other means.
What Gets Validated
Validation checks the structure and types of message envelopes. It does not inspect payload contents -- that's your handler's responsibility.
Request Envelopes
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | Must be non-empty |
messageType | 'request' | yes | Literal value |
context | string | yes | Must be non-empty |
operation | string | yes | Must be non-empty |
timestamp | string | yes | Must be non-empty |
payload | object | yes | Not null, not an array |
metadata | object | yes | Not null, not an array |
responseQueue | string | no | |
timeout | number | no | |
auth | string | no | |
client | string | no | |
priorRequest | string | no | |
requestChain | string[] | no | Must be an array if present |
Response Envelopes
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | Must be non-empty |
messageType | 'response' | yes | Literal value |
context | string | yes | Must be non-empty |
operation | string | yes | Must be non-empty |
timestamp | string | yes | Must be non-empty |
payload | object | yes | Not null, not an array |
status | 'succeeded' | 'failed' | 'pending' | yes | |
messages | ResponseMessage[] | yes | Each message is validated |
service | string | yes |
Each ResponseMessage in the messages array must have:
| Field | Type | Required | Notes |
|---|---|---|---|
severity | 'error' | 'warning' | 'info' | 'debug' | yes | |
message | string | yes | |
code | string | no | |
type | string | no | |
stack | string | no | |
details | object | no | Not an array if present |
Post Envelopes
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | Must be non-empty |
messageType | 'post' | yes | Literal value |
context | string | yes | Must be non-empty |
operation | string | yes | Must be non-empty |
timestamp | string | yes | Must be non-empty |
payload | object | yes | Not null, not an array |
metadata | object | yes | Not null, not an array |
auth | string | no | |
client | string | no | |
priorRequest | string | no | |
requestChain | string[] | no | Must be an array if present |
Service Commands
All commands require id (non-empty string) and command (string). Additional fields vary by command:
| Command | Additional Required Fields |
|---|---|
info | responseChannel (string) |
concurrency | payload.concurrency (number) |
shutdown | None -- payload is optional (graceful: boolean, exitCode: number) |
toobusy | payload (object, all fields optional: maxLag, interval, smoothingFactorOnRise, smoothingFactorOnFall -- all numbers) |
Command Responses
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | Must be non-empty |
payload | object | no | Not an array if present |
What Is NOT Validated
Payload contents are not inspected. Validation only checks that payload is a non-null, non-array object. Your handlers and middleware are responsible for validating the actual contents:
ctx.registerOperation('update', async (request) =>
{
// Envelope validation guarantees payload is an object.
// You must validate its contents:
if(!request.payload.userId || typeof request.payload.userId !== 'string')
{
throw new Error('userId is required and must be a string');
}
// Process the request...
});Error Handling
What happens when validation fails depends on the message type:
Invalid Requests (Service Side)
- The backend validates the envelope structure.
- If invalid: extract minimal fields (
id,responseQueue,context,operation) if possible. - Send a
failedresponse with message: "Envelope validation failed: The request envelope structure is invalid." - If
responseQueuecan't be extracted, log a critical error and drop the message. - The
incomingRequestevent is not emitted -- your handlers never see the request.
Invalid Responses (Client Side)
- The backend validates the envelope structure.
- If invalid: log a warning and drop the message.
- The
incomingResponseevent is not emitted.
Invalid Posts
- The backend validates the envelope structure.
- If invalid: log a warning and drop the message.
Invalid Commands
- The backend validates the command structure.
- If invalid: log a warning and drop the message.
When to Disable Validation
Almost never. The performance cost is effectively zero, and the safety benefits are real. That said, there are narrow cases where disabling makes sense:
Disable when:
- All services are written and controlled by your team
- All services use the same Strata version
- Services communicate on a fully trusted network
- You have verified envelope correctness through other means (comprehensive integration tests)
Keep enabled when:
- Services communicate across team or organization boundaries
- Multiple Strata implementations are in use (different languages, different versions)
- Third-party services connect to your bus
- Development or staging environments (where bugs are most common)
Troubleshooting
"Envelope validation failed" Errors
Your client is receiving a failed response with a validation error message.
Check:
- All required fields are present (see tables above)
payloadis an object ({}) -- notnull, not an array, not a primitivemetadatais an object if presentmessageTypematches the expected value ('request','response', or'post')- String fields are non-empty
- For responses:
statusis one of'succeeded','failed','pending' - For responses:
messagesis an array with validseverityvalues
Validation Passes But Handler Fails
This means the envelope structure is correct, but the payload contents are wrong. Envelope validation only checks structure -- not business logic. Validate payload contents in your handlers or use the Payload Validation Middleware.
Best Practices
- Leave validation enabled. The 0-2% overhead is negligible. The safety is not.
- Validate payload contents yourself. Envelope validation checks structure, not business data. Use middleware or handler-level checks for payload validation.
- Handle validation errors in clients. Check for
failedresponses with validation error messages and handle them appropriately. - Monitor validation failures. Log and alert on validation errors -- they indicate a bug in a service or client.
- Test with validation on. Always run tests with validation enabled to catch structural issues early.
Next Steps
- Backends -- backend-specific configuration, including
validateEnvelopes. - Configuration -- full configuration reference.
- The Protocol -- the envelope format that validation enforces.