Skip to content

content/docs/automation/webhooks.mdx still documents custom headers as living in sys_webhook.definition_json, and omits headers_secret entirely — the doc points authors at the cleartext column #7986 moved them out of #9928

Description

@os-warren

⛔ Unassigned; for triage to grade. Found while working #9756 (the enable block census) — out of scope there, and not touched by PR #9927, whose file surface is plugin-webhooks only.

Measured on origin/main @ b0300556d

#7986 moved sys_webhook's custom HTTP headers out of the definition_json blob and into headers_secret — a Field.secret() column the engine encrypts at rest into sys_secret, whose reads return a mask. The webhooks guide was not updated with it, and still describes the pre-#7986 world in three places:

  • line 87 — "The transport configuration (headers, timeout, method) is carried in ..." (definition_json)
  • line 104, the sys_webhook field table:

    definition_json | textarea | Serialised Webhook JSON ... — carries the transport config: custom headers and timeoutMs. Not the signing secret.

  • line 110 — "the headers and per-attempt timeout are parsed out of definition_json when an event is enqueued"

⇒ and the field table has no headers_secret row at all, though the column ships and is admin-authorable. The table documents its sibling signing_secret correctly, so the omission reads as "this field does not exist" rather than as an obvious gap.

Why this is worth a card rather than a typo fix

The doc does not merely lag — it points an author at the wrong column, in the direction that reintroduces the defect. An author (or an AI author) following this table writes an Authorization: Bearer … into definition_json, which is an ordinary textarea returned in full by GET /api/v1/data/sys_webhook. The runtime still accepts that: readLegacyHeaders (packages/plugins/plugin-webhooks/src/auto-enqueuer.ts:579) reads such a map, delivers from it, and logs a warn naming it cleartext — so following the doc produces working deliveries and a warning nobody is looking for, not an error.

The same page already carries the pattern this needs, one section down: sys_http_delivery's table documents headers_json as internal with the read-path rule spelled out (line 159), and there is a Callout explaining the #7799 signing-secret move (line ~117) with no counterpart for the headers move.

Suggested shape (not a ruling — triage's call)

  • add a headers_secret row to the sys_webhook field table, mirroring the signing_secret row's wording (encrypted into sys_secret, reads return a mask, leave the mask untouched to keep the current value);
  • correct lines 87 / 104 / 110 so definition_json is described as carrying timeoutMs and the rest of the envelope, with the headers named as legacy-only — still read for un-migrated rows, migrated by the boot sweep;
  • consider extending the existing [security] The webhook signing secret is stored in cleartext in sys_webhook.definition_json #7799 callout to cover the headers move, since the two landed as siblings.

Not claimed

Refs: #7986 (moved the headers to headers_secret) · #7799 (the signing-secret sibling, whose callout this page does carry) · #8566 (the headers_secret plaintext shape gate) · #9756 / PR #9927 (where this was noticed)


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions