dd_error_report_api
See also: JSON API v1 · dispatch
The master installation's error-report intake: remote installations' servers relay admin-submitted error reports here machine-to-machine. It is a TS-native surface with no twin in the previous engine (WC-017), and its one pre-auth action is receive_report.
The registry entry is src/core/api/dispatch.ts → errorReportApiActions (src/core/api/handlers/dd_error_report_api.ts). Reachability is gated before the handler by the dispatcher's Gate 1c: the request is refused unless the receiver is explicitly enabled (DEDALO_ERROR_REPORT_RECEIVER) and the caller IP is on the allowlist. There is no session and no CSRF token — it is machine-to-machine intake, not a browser action.
How to call
- POST JSON with
dd_api: "dd_error_report_api"andaction: "receive_report". The report payload rides inoptions.
Common fields
optionscarries the self-reported report body against a strict shared schema (src/core/error_report/schema.ts) —entity,dedalo_version,user_id,section_tipo,section_id,page_url,description,js_errors, and context fields (langs,user_agent,screenshot, …). Unknown fields are rejected.
The intake discipline runs in a fixed order
The handler owns, in order: (1) a per-trusted-hop-IP sliding-window throttle — every request spends budget, so junk floods and token-guessing loops rate-limit alike; (2) an optional constant-time shared-token check — a wrong/missing token answers the exact unregistered-action shape, no existence leak; (3) a total-size clamp (the 256 MiB global body cap is useless here); (4) the strict shared schema (unknown fields rejected); (5) append to the store, stamping source_ip from the trusted hop. It never fetches or resolves any URL-shaped field (no SSRF), never logs report text (log-injection), and never echoes internals in error envelopes.
receive_report
- Purpose: Accept and store one error report relayed from a remote installation.
- Accepts: the report body in
options, matching the strict wire schema; the shared token (when configured) travels out-of-band, checked constant-time. - Returns: envelope v2. A stored report is
{ ok: true, request_id, data: true, report_id: <int> }—report_idis the top-level extension key the relaying installation reads besideok. Any refusal is{ ok: false, request_id, error: { code, category, message, label_key, retryable } }; the message is always a terse registry sentence, never the payload or field-level detail.
Example Request: receive_report
{
"dd_api": "dd_error_report_api",
"action": "receive_report",
"options": {
"entity": "Example Archive",
"dedalo_version": "7.0.0",
"user_id": 42,
"section_tipo": "oh1",
"section_id": 3,
"page_url": "https://example.org/…",
"description": "Save button did nothing",
"js_errors": []
}
}
Example Response: receive_report
{
"ok": true,
"request_id": "c0ffee70",
"data": true,
"report_id": 128
}
Notes
- Refusals are deliberately indistinguishable: a disabled receiver, a wrong token, and an unregistered action all answer the same body —
request.unknown_action(HTTP 400, "Undefined or unauthorized method (action)"), down to the echoeddetails.action. An oversize or schema-invalid body answersrequest.invalid(HTTP 400, "Invalid error report"); a throttled caller getsrate.limited(HTTP 429); a store failure answersinternal.unexpected(HTTP 500) and never echoes the report.
| code | status | when |
|---|---|---|
request.unknown_action |
400 | receiver disabled, caller IP not allowed, wrong/missing shared token, or the action is not receive_report. |
request.invalid |
400 | body over the endpoint size clamp, or it failed the strict schema (unknown fields included). |
rate.limited |
429 | the trusted-hop IP spent its sliding-window budget. |
internal.unexpected |
500 | the intake store could not append. |
- Only source_ip (the trusted-hop address) and received_at are master-trusted; every other field is stored as self-reported context. |