← FailMemory home · Dashboard · Quickstart
Request normalization (norm-v2)
FailMemory identifies a failure with a versioned SHA-256 hash. The live
norm_v2 contract combines a normalized HTTP method and URL with an optional,
value-free request-body shape. Reports and lookups use exactly the same
contract.
Canonical identity
The canonical norm-v2 input is:
norm_v2\n<lowercase method>\n<normalized URL>\n<request-shape hash or empty>
FailMemory stores the digest, normalized URL, and optional request-shape digest. It does not store request-body values.
Method and URL rules
The method and URL rules are inherited from norm-v1:
- Lowercase the HTTP method.
- Canonicalize path percent encoding.
- Remove trailing slashes except the root slash.
- Remove query parameters whose name or value matches common sensitive-data patterns: email, phone, UUID, JWT, session tokens, API keys, or access tokens.
- Sort remaining query parameters by name and value.
These filters are a safety boundary, not permission to put secrets in URLs. FailMemory retains the normalized path and non-sensitive query values for matching, so callers should never place credentials or personal data in a URL path.
Value-free request shapes
When a JSON body can change whether a call fails, the client describes its structure using this grammar:
| JSON shape | Descriptor |
|---|---|
null |
n |
| string | s |
| number | d |
| boolean | b |
| missing/undefined | u |
| object | o{<sorted JSON key>:<shape>,...} |
| array | a[<unique sorted element shapes>] |
The client hashes the descriptor with SHA-256 and sends only the 64-character
hex digest as request_shape_hash.
For example, these bodies have the same descriptor and digest:
{ "email": "alice@example.com", "active": true, "quota": 10 }
{ "quota": 999, "active": false, "email": "bob@example.net" }
Their descriptor is:
o{"active":b,"email":s,"quota":d}
Values, object-key order, array order, and array length do not affect the shape. Object field names and JSON types do, because those structural differences frequently represent different API operations.
The official MCP server and first-party harness implement this algorithm from the same cross-language fixture set. They calculate the digest locally; body values never cross the FailMemory boundary.
Matching order
For a lookup with request_shape_hash, the service tries:
- the exact norm-v2 method, URL, and request-shape digest;
- a norm-v2 endpoint-wide pattern with no body shape;
- an unexpired norm-v1 endpoint-only pattern during the transition window.
A lookup without a shape tries steps 2 and 3. A hit returns match_scope as
request_shape, endpoint, or legacy_endpoint, so consumers can judge how
specific the match was.
TTL and version transition
Every promoted pattern has an absolute expires_at instant. The default TTL
is 24 hours and a fresh confirming report extends it. Expired rows are ignored
in D1 and removed from KV on read, so an old failure cannot be resurrected by a
cold-store fallback.
All new reports are norm-v2. Existing norm-v1 rows are read-only compatibility entries and disappear from the serving path when their real TTL expires. No bulk conversion guesses a body shape for historical data.
Raw HTTP compatibility
POST /v1/report temporarily accepts a JSON payload for older direct HTTP
clients. The Worker derives the same structural digest in memory and never
persists the raw body. New HTTP clients should compute and send
request_shape_hash; the official MCP server already does this.