Skip to content

inbound webhook guidance: every place that teaches a sender teaches the timestamped t=…,v1=… signature only (ruling B on #22805, step 1) #22825

Closed

Description

@objectstack-fleet

Filing gate ③, a maintainer-directed task. Ruling B on #22805 (6108492902, batch #317 item 2), maintainer, verbatim: 「同意」. The ruling's State section: "The domain:services seat that filed this card files the guidance card and the refusal card (the latter Blocked-by: the former), per the body's 裁后执行 under B, then closes this card as their decision record." Filed by the domain:services seat 1 (seat post #6021, session_01CBAfsWMSfM3EToQGVStEcp). ⛔ Not a claim.

Reader: triage first, for the lane. The ruling prices this as "about 100–200 lines, across domain:skills (Tier H), domain:spec (lint text) and domain:devx (showcase and checklist), or one PR by the cross-domain exception path; triage's call". The claimant then writes the text.

What the ruling asks (step 1 of B)

Every place that teaches an inbound-hook sender teaches only the timestamped form that @objectstack/core's signHttpBodyAt produces: x-objectstack-signature: t=<unix seconds>,v1=<hex>. The hex is the HMAC-SHA256 of <t>.<raw body> under the flow's secret, verified inside a 300-second window (HTTP_SIGNATURE_TOLERANCE_SECONDS).

The positions, read on origin/main b7cd1af9d, each still teaching the body-only sha256=<hex> form:

  • skills/objectstack-automation/SKILL.md:351, the Inbound webhook section: "Signature: sha256=<hex> (GitHub/Stripe style)". This is a published skill, so it is Tier H: the maintainer's APPROVED review is owed.
  • packages/lint/src/rule-explanations.ts:1462–:1463: the explanation says the header "carries 'sha256=' and the hex HMAC-SHA256 of" the body.
  • packages/lint/src/validate-flow-trigger-readiness.ts:937: the hint says the same.
  • examples/app-showcase/src/automation/flows/index.ts:1581: the example comment.
  • docs/qa/platform-checklist/areas/automation.json:557: the QA item's request recipe.

⛔ Out of scope, kept as published: the outbound contract (content/docs/automation/webhooks.mdx §6.2), the messaging outbox and the flow http node keep signing body-only (signHttpBody) for third-party receivers. The ruling: "Moving our outbound to a timestamped form is not ruled here and has no measured pull."

Done when

  • Each position above teaches the timestamped form only, and the QA item's recipe signs with it.
  • No sender-facing surface outside the outbound contract still teaches sha256= for an inbound hook. Measure it with a grep over the shipped surfaces, with a positive control on the timestamped spelling.
  • On merge, the refusal card (ruling B, step 3), which is Blocked-by: this card, unblocks.

Duplicate check

Two MCP search_issues queries on this repo, closed cards included:

  • "inbound webhook sender guidance teach timestamped signature t= v1= instead of body-only sha256= in skill lint explanation showcase" → 4 hits;
  • "inbound hook door refuse body-only sha256 signature, drop body-only branch of the verifier, replay protection only timestamped form" → 5 hits.

None is this card. The nearest are #22805 (the decision), #22769 (closed: it added the timestamped form) and #20657 (closed: an earlier skills edit to the same file).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, documentation · priority:p2 · domain:skills · area:workflow, into pm:queue. One PR, claimed by domain:skills under the cross-domain exception path

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-11T11:54Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3
    Session: session_011u73oxZ5X95qrARPTeoU6v
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-22825-inbound-hook-timestamped-guidance
    Worktree: objectstack-issue-22825
    Domain: domain:skills
    Seat: domain:skills#2
    File surface (cross-domain exception path, triage 6108751309: one PR, claimed by this lane): skills/objectstack-automation/SKILL.md (the Inbound webhook section's Signature line; the file's token headroom is 2, so the edit pays with a same-file deletion) · skills/objectstack-automation/evals/** only if an eval token names the retired spelling · packages/lint/src/rule-explanations.ts and packages/lint/src/validate-flow-trigger-readiness.ts (the explanation and hint strings, declared on #6017) · examples/app-showcase/src/automation/flows/index.ts (the example comment) and docs/qa/platform-checklist/areas/automation.json (the QA item's request recipe), both declared on #6023 · one changeset; ⛔ no change to content/docs/automation/webhooks.mdx §6.2, packages/core/**, packages/services/**, packages/triggers/** or any verifier or sender (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: CONTRACT_REVIEW_TIER (dispatch-gates --tier --repo objectstack-ai/objectstack skills/objectstack-automation/SKILL.md … at efcbac73c: MANDATORY — skills/objectstack-automation/SKILL.md ⇢ 'skills/**' — clause ① (2026-09-10 ruling, verbatim 「必须 fable的还包括对外发布的skills」); no exit taken)
    Clause-②: no — teaching text, lint prose, an example comment and a QA recipe; the accept set of the inbound door is #22826's and is not touched here; skills/** is a governed rule-text face, so the at-tier contract review is owed and in seat.
    Responsibility: skills/objectstack-automation/SKILL.md and four sender-facing texts, the five places ruling 6108492902 names | none — no gate reads the signature spelling these texts teach; the only reader is the author who copies it | every AI author and every custom integration that follows the published skill, the lint hint or the showcase comment to sign an inbound hook; today all five teach the replayable body-only form that #22826 will refuse
    Thread-read: 6108751309
    Serial constraints cleared: one open PR touches one of the five paths — PR #22828 (the lint one-line-verdicts slice, in the merge queue at this read) changes packages/lint/src/rule-explanations.ts, so this PR is serial after it: the dev merges origin/main carrying #22828 before opening the PR, and the seat re-merges before landing if it has not landed by then; PR #22832 (#19939 pass 4 S2), #22794 and #22833 were diffed by name against the five and hit none (open PR list read 2026-10-11T12:33Z); git ls-remote shows no claude/issue-22825-* branch; #22585 (pm:blocked behind #19939) later edits the same SKILL.md and is serial after this PR; the lint files are the spec lane's and the showcase and checklist files the devx lane's, declared on their seat posts (#6017, #6023) this act under the exception path.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22825,
      "status": "done",
      "branch": "claude/issue-22825-inbound-hook-timestamped-guidance",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22846",
      "session": "session_011u73oxZ5X95qrARPTeoU6v",
      "premise_still_valid": true,
      "summary": "Ruling B on #22805 step 1 implemented as one cross-domain PR: the five sender-facing texts for an inbound api hook (skills/objectstack-automation/SKILL.md Signature line, the flow-api-trigger-secret-missing explanation in packages/lint/src/rule-explanations.ts and its hint in packages/lint/src/validate-flow-trigger-readiness.ts, the InboundTaskWebhookFlow doc comment in examples/app-showcase/src/automation/flows/index.ts, and the api step of item automation.trigger-type-matrix in docs/qa/platform-checklist/areas/automation.json, revision 2 to 3 with a history entry) now teach only the timestamped form signHttpBodyAt writes: t=UNIX_SECONDS,v1=HEX, HEX = HMAC-SHA256 of UNIX_SECONDS.RAW_BODY under the flow secret, refused when t is more than HTTP_SIGNATURE_TOLERANCE_SECONDS (300) from the server clock; plus one changeset (@objectstack/lint patch, @objectstack/skills patch). S3 readings: before, on origin/main d8c7d3864, git grep -n sha256= over the five files = 5 hits (one per file: SKILL.md:351, rule-explanations.ts:1463, validate-flow-trigger-readiness.ts:937, flows/index.ts:1581, automation.json:557); after, on head caf9fa094, the same grep = 0 hits; positive control git grep -c v1= = automation.json 2, flows/index.ts 1, rule-explanations.ts 1, validate-flow-trigger-readiness.ts 1, SKILL.md 1, and packages/core/src/security/http-signature.ts still carries signHttpBodyAt (6 hits). The wider grep still hits only webhooks.mdx (outbound contract), deployment docs (artifact pin), releases/CHANGELOG (release-owned) and the verifier code and tests (#22826's). Verifier, senders, packages/core, webhooks.mdx section 6.2 untouched. S5: #22828 had landed as d8c7d3864 before the branch was cut, so the branch is based on it; origin/main merged again at 1eff3224d (cli, verify, qa, content/docs only) before opening.",
      "tests": "gates: lint suite on 435fd3b3e via os-verify-lock (vitest run --maxWorkers=2, whole package) Test Files 137 passed (137), Tests 6450 passed | 5 skipped (6455); the two directly affected files first, 107 passed (107) (the os-explain pin on ADR-0041 / HMAC lives there); pnpm --filter @objectstack/lint typecheck via lock: tsc clean, check:test-typecheck OK 2 file(s) / 6 error(s) / 2 pinned signature(s) held; derived union node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack = 74 families, run twice in the foreground with exit codes recorded as printed, first on 435fd3b3e then on the final head caf9fa094 (same family set); final --ran verdict on caf9fa094: dispatch-gates --ran: 74 derived famil(ies) accounted for — 72 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3), 0 UNRUN; non-zero exits on caf9fa094: pnpm check:i18n-coverage exit 3 (prerequisite: built CLI; diff touches no label/translation/form) and pnpm check:dual-build-cjs-loads exit 3 (prerequisite: all 81 dists; diff changes string literals only) = NOT MEASURED, declared to CI; pnpm check:platform-checklist exit 1 with a red set byte-identical to the untouched base d8c7d3864 (5 unique absent-symbol-anchor findings in access-security.json and attachments-storage.json, none on automation.json; checklist-status.yml keeps that gate out of per-PR CI); four first-pass exit-3 refusals re-run green after their prerequisite was met (check:docs-transcript-drift and lint check:doc-formula-expressions after building @objectstack/lint; check:lean-entry-closure after building @objectstack/objectql; check-plugin-teardown-shape --self-test 48 cases after git fetch --deepen=4000 reached fixture 621a4876); dispatch-named gates outside the derived list: pnpm --filter @objectstack/spec run check:skill-refs exit 0 (9 generated files in sync), check:skill-examples exit 0 after building @objectstack/client-react, check:skill-docs exit 0 (frontmatter untouched, nothing to regenerate); check-skills-token-ratchet + --self-test exit 0 on both heads; check:nul-bytes exit 0 on both heads plus a control-character self-scan of the six files (clean); check:pm-governed-merges, check:doc-authoring, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration exit 0; pre-push check:commit-card-trailers green on every pushed commit. | line_budget: skills/objectstack-automation/SKILL.md 5783 → 5784 tokens (ceil(utf8 bytes/4); ceiling 5785; headroom 2 → 1), 23132 → 23135 bytes, 438 → 438 lines; package skills/objectstack-automation/**/*.md 56541 → 56544 bytes, 1036 → 1036 lines; catalog skills/**/SKILL.md 210520 → 210523 bytes, 4409 → 4409 lines; the Signature line grew 103 bytes, paid by two same-file deletions of text restated beside it (the api row clause 'every api flow is bound to its hook endpoint and', 51 bytes, restated by the section's opening sentence; the Queue-backed clause 'Requires the queue service (see prerequisite)', 49 bytes, restated by the prerequisites table); no re-wrap, ceiling not moved, evals untouched (no eval names the signature). | deviations: (1) the worktree was cut from origin/main d8c7d3864, not the efcbac73c the dispatch named, because origin/main had moved to #22828's merge by then, which is the serial condition itself; (2) the first local commit carried the harness reminder's co-author trailer with a model name; the pre-push hook refused it while still unpublished and it was amended to AGENTS.md's model-free pair before any push, reported here per the harness-trailer clause, no published history rewritten; (3) git fetch --deepen=4000 was run on the shared object store (depth 4201 → 8204) to reach the teardown self-test fixture; (4) the lint suite and typecheck were not re-run on the merge head caf9fa094: the merged commits touch no file under packages/lint, packages/spec, packages/formula or packages/sdui-parser (git diff --name-only d8c7d3864 1eff3224d, zero hits); (5) @objectstack/example-showcase typecheck not run: the diff is inside one doc comment; (6) GitHub text spells the placeholders UNIX_SECONDS / HEX / RAW_BODY, while the five files keep the angle-bracket spelling http-signature.ts uses. | files_changed: skills/objectstack-automation/SKILL.md (+3/-3), packages/lint/src/rule-explanations.ts (+5/-3), packages/lint/src/validate-flow-trigger-readiness.ts (+4/-3), examples/app-showcase/src/automation/flows/index.ts (+7/-5), docs/qa/platform-checklist/areas/automation.json (+8/-2), .changeset/22825-inbound-hook-timestamped-guidance.md (+16/-0); 43 insertions, 16 deletions, under the 3000-line human-merge threshold; Tier H via skills/**.",
      "mcp_calls": "0 — no MCP GitHub tool was called. Reads were REST GET via curl (issue 22825 and its comments, comment 6108492902 on #22805, check-runs of d8c7d3864).",
      "api_writes": "2 strokes, 3 REST requests — (1) scripts/pm/fleet-write/dispatch.mjs pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft forced) and its assignees leg POST /repos/objectstack-ai/objectstack/issues/22846/assignees (zhuangjianguo) inside the same stroke; (2) scripts/pm/post-stamped.mjs --comment=22825: POST /repos/objectstack-ai/objectstack/issues/22825/comments (this report). No label write (a changeset is present; no label was named). Three git pushes to the branch, not REST writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: public door `pnpm check:platform-checklist` answers exit 1 on origin/main d8c7d3864 and on 1eff3224d (unchanged by this PR) · evidence: 5 unique findings, byte-identical on base and branch — ABSENT SYMBOL anchors packages/metadata-protocol/src/protocol.ts#envWideRawViewRows, #anonymousFormIntakeOrgScopeRefusal, #anonymousFormIntakeReopenRefusal in areas/access-security.json; packages/services/service-storage/src/attachment-access-hooks.ts#canEdit in areas/attachments-storage.json and SYMBOL ANCHORS LOST 27 vs floor 28 there; not per-PR CI (checklist-status.yml), read by the 02:51 checklist watchdog · dedupe words: platform-checklist absent symbol envWideRawViewRows canEdit anchors lost",
        "carrier: #22826 or the next revision of checklist item automation.flow-node-type-matrix · noted, not filed — docs/qa/platform-checklist/areas/automation.json line 429 (that item's webhook-chain step) says 'a valid x-objectstack-signature HMAC (secret showcase-webhook-secret)' and names no form; not one of the ruling's five, left as is; once #22826 lands, 'valid' means the timestamped form by construction, and the showcase comment it points at now teaches it."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — skills seat 2, session_011u73oxZ5X95qrARPTeoU6v (zhuangjianguo) · 2026-10-11T14:09Z · PR #22846, head caf9fa094 (on base 1eff3224d).

    Checklist against GitHub, not the report: draft to main; first line Fixes #22825; Clause-②: no at line start; ## 维护者速读(草稿) present, the final posted on the PR; changeset @objectstack/lint patch + @objectstack/skills patch (both publish; the showcase package is private, docs/qa is no package), so no skip-changeset; no content/docs/releases/; files exactly the five positions the ruling names plus the changeset (+43/−16); closing keywords: only Fixes #22825. Spot checks on the diff, read in full: the five texts match packages/core/src/security/http-signature.ts on origin/main (HTTP_SIGNATURE_TOLERANCE_SECONDS = 300; signHttpBodyAt writes t= the timestamp ,v1= the HMAC-SHA256 of timestamp.body); the done-when grep at the head reads sha256= 0 hits over the five files and v1= present in each; the two skill deletions that pay the ratchet are restated text (the section opener and the Prerequisite table's queue row say them); the lint rule's id, severity and verdict are untouched (hint string and one explanation paragraph only); the checklist item moves to revision 3 with a history entry. Dev evidence read: 74 derived gates, 72 run, 2 NOT MEASURED by prerequisite and declared to CI; lint suite 137 files, 6450 passed under the verify lock; token ratchet 5783 to 5784 of 5785; CI on caf9fa094: 34 check-runs on caf9fa094: 31 success, 3 skipped by condition (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)), none red; Lint & Repo Gates (the token ratchet and the skill gates run there), TypeScript Type Check, Test Core with its six shards, Dogfood Regression Gate with its three shards, Temporal Conformance (live PG + MySQL), Build Core, Governed Surface Queue Guard, Check Changeset and the three claim guards all success; no review and no comment on the PR (read 2026-10-11T14:06Z).

    Serial state: PR #22828 (rule-explanations.ts) had landed before the branch was cut, so the branch is on it; origin/main merged again at 1eff3224d before opening. Deviations accepted: base d8c7d3864 instead of the dispatch's efcbac73c; the model-named trailer amended to the model-free pair before any push; git fetch --deepen=4000 on the shared store; lint suite and typecheck on 435fd3b3e only (the merged commits touch none of the lint, spec, formula or sdui-parser trees); no showcase typecheck for a doc comment. Out-of-scope findings, one line each: check:platform-checklist red on origin/main (five absent-symbol anchors in access-security.json and attachments-storage.json, byte-identical on base and branch) — not a card by the filing gate (a pnpm check:* repo gate is not a public door: HTTP, UI, os validate or a save), so Acceptance notes with a carrier: the devx @ objectstack seat's next checklist-author sweep (seat post #6023) and the weekly checklist publisher; dedupe by title over the 124 open cards: none names it; raised to the maintainer in the round report; the flow-node-type-matrix step's "a valid x-objectstack-signature HMAC" with no form — Acceptance notes, carrier #22826.

    Contract review (skills/** governed rule text face, in seat at tier) PASS on the PR. Tier H: the PR stays draft with needs-user-decision and review requested from os-zhuang and hotlong; an authorized APPROVED review lands it through the seat. The card keeps pm:dispatched and its assignee until MERGED; #22826 unblocks on that merge.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — skills seat 2, session_011u73oxZ5X95qrARPTeoU6v (zhuangjianguo) · 2026-10-11T16:07Z

    PR #22846 MERGED by os-zhuang at 2026-10-11T14:40Z as eed637c09 on origin/main (the queue's squash of head caf9fa094; the queue ref pr-22846-… is gone). Content read on origin/main, not inferred: the Inbound webhook section of skills/objectstack-automation/SKILL.md teaches t=UNIX_SECONDS,v1=HEX with the 300-second window and carries no sha256=; packages/lint/src/rule-explanations.ts carries the timestamped form; .changeset/22825-inbound-hook-timestamped-guidance.md is on the tree. This card closed completed by the PR's Fixes (closed_by_pull_requests lists #22846 alone); pm:dispatched is stripped in this act, the assignee stays as the record of the claim. Lane reconciliation after the merge: the only card that left the open domain:skills set is this one, expected; #22826 (Blocked-by: #22825, services lane) already reads pm:queue, so the unlock is in its lane's hands.

    Landing path, for the record: Tier H; os-zhuang approved on the landing head (review 5483623195), flipped ready, armed auto-merge and enqueued it himself; the contract review of record is 6109885041 on that head. Cross-lane: the declarations on #6017 (6109160652) and #6023 (6109163925) are discharged by this merge. Carried, not filed: the check:platform-checklist red on origin/main (Acceptance notes, carrier the devx @ objectstack seat) and the flow-node-type-matrix step's unnamed signature form (carrier #22826). Hot-file entries for the five files are released on the seat post; skills/objectstack-automation/SKILL.md keeps a token headroom of 1 for #22585.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:workflowApprovals and automation — the work that runs without a person driving itdocumentationImprovements or additions to documentationdomain:skillspriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions