Summary
When sending JSON-encoded messages to HyperBEAM with a data field, the content-digest field is stripped from the message map before commitment verification, causing invalid_commitment errors. This is similar to the issue fixed in PR #610 for content-type.
Environment
- HyperBEAM version: 1230ubn1
- Client: Custom hbsig library (JavaScript)
Reproduction Steps
- Create a signed message with commitment containing
data field:
{
"commitments": {
"<hmac-id>": {
"commitment-device": "httpsig@1.0",
"type": "hmac-sha256",
"keyid": "ao",
"signature": "<hmac-signature>",
"committed": ["action", "content-digest", "target", "type"]
}
},
"action": "Eval",
"content-digest": "sha-256=:SG2a/7YNuwBjsD2OI6bM9jZM4gPcOp9W8g51DrQeyt4=:",
"target": "<process-id>",
"type": "Message",
"data": "return 1"
}
-
POST to /{pid}/schedule with Content-Type: application/json
-
Observe invalid_commitment error
Expected Behavior
The content-digest field should be preserved in the message map and used for commitment verification, since it's listed in the committed array.
Actual Behavior
The error response shows the parsed message map without content-digest:
{invalid_commitment,#{<<"accept">> => <<"*/*">>,
<<"action">> => <<"Eval">>,
<<"commitments">> => #{...},
<<"data">> => <<"return 1">>,
<<"target">> => <<"...">>,
<<"type">> => <<"Message">>,
%% NOTE: content-digest is MISSING
}}
The commitment lists content-digest as signed, but HyperBEAM's message map doesn't include it, so verification fails.
Workaround
Using a different field name (e.g., code or script instead of data) works because it doesn't trigger the content-digest flow:
{
"action": "Eval",
"code": "return 1",
"target": "<process-id>",
"type": "Message"
}
However, this deviates from the AO protocol standard which uses Data for message body content.
Analysis
Similar to PR #610 which fixed content-type being stripped:
Fixes an error in which content-type was inappropriately removed from nested bundled messages in ~httpsig@1.0 form.
It appears content-digest may have the same issue - being treated as an HTTP header concept and stripped from JSON body parsing, rather than being preserved as a user-provided field that's part of the commitment.
Suggested Fix
In the JSON message parsing path (likely in hb_http.erl or the httpsig codec), preserve content-digest when it's present in the JSON body, similar to the PR #610 fix for content-type.
Additional Context
- The HMAC computation is correct (verified independently)
- The
content-digest value correctly matches sha256(data)
- Messages without
data/body fields work correctly
- Messages with arbitrary field names (
code, script) work correctly
- Only
data and body field names trigger this issue
Related
PR #614 created
Summary
When sending JSON-encoded messages to HyperBEAM with a
datafield, thecontent-digestfield is stripped from the message map before commitment verification, causinginvalid_commitmenterrors. This is similar to the issue fixed in PR #610 forcontent-type.Environment
Reproduction Steps
datafield:{ "commitments": { "<hmac-id>": { "commitment-device": "httpsig@1.0", "type": "hmac-sha256", "keyid": "ao", "signature": "<hmac-signature>", "committed": ["action", "content-digest", "target", "type"] } }, "action": "Eval", "content-digest": "sha-256=:SG2a/7YNuwBjsD2OI6bM9jZM4gPcOp9W8g51DrQeyt4=:", "target": "<process-id>", "type": "Message", "data": "return 1" }POST to
/{pid}/schedulewithContent-Type: application/jsonObserve
invalid_commitmenterrorExpected Behavior
The
content-digestfield should be preserved in the message map and used for commitment verification, since it's listed in thecommittedarray.Actual Behavior
The error response shows the parsed message map without
content-digest:{invalid_commitment,#{<<"accept">> => <<"*/*">>, <<"action">> => <<"Eval">>, <<"commitments">> => #{...}, <<"data">> => <<"return 1">>, <<"target">> => <<"...">>, <<"type">> => <<"Message">>, %% NOTE: content-digest is MISSING }}The commitment lists
content-digestas signed, but HyperBEAM's message map doesn't include it, so verification fails.Workaround
Using a different field name (e.g.,
codeorscriptinstead ofdata) works because it doesn't trigger thecontent-digestflow:{ "action": "Eval", "code": "return 1", "target": "<process-id>", "type": "Message" }However, this deviates from the AO protocol standard which uses
Datafor message body content.Analysis
Similar to PR #610 which fixed
content-typebeing stripped:It appears
content-digestmay have the same issue - being treated as an HTTP header concept and stripped from JSON body parsing, rather than being preserved as a user-provided field that's part of the commitment.Suggested Fix
In the JSON message parsing path (likely in
hb_http.erlor the httpsig codec), preservecontent-digestwhen it's present in the JSON body, similar to the PR #610 fix forcontent-type.Additional Context
content-digestvalue correctly matchessha256(data)data/bodyfields work correctlycode,script) work correctlydataandbodyfield names trigger this issueRelated
content-typekey inhttpsig@1.0bundled messages #610: fix: preservecontent-typekey inhttpsig@1.0bundled messagesPR #614 created