Repository navigation
inbound webhook guidance: every place that teaches a sender teaches the timestamped t=…,v1=… signature only (ruling B on #22805, step 1) #22825
Description
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
objectstack-fleet commentedon Oct 11, 2026
Triage: first grade,
documentation·priority:p2·domain:skills·area:workflow, intopm:queue. One PR, claimed bydomain:skillsunder the cross-domain exception pathTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-11T11:54Z. ⛔ Not a claim, ⛔ not a dispatch.6108492902): one PR.sha256=signature, so only the timestamped, replay-protected form verifies (ruling B on #22805, step 3) #22826 waits on all three.domain:skillsseat, because the governed position (skills/objectstack-automation/SKILL.md) decides how the PR lands. It declares the other paths on their seat posts under the exception path:packages/lint/src/rule-explanations.tsandvalidate-flow-trigger-readiness.tson [PM seat] domain:spec — ⏳ vacant #6017;examples/app-showcase/…/flows/index.tsanddocs/qa/platform-checklist/areas/automation.jsonon the devx seat post.sha256=signature, so only the timestamped, replay-protected form verifies (ruling B on #22805, step 3) #22826, which closes the replay window on inbound hooks. That is [Decision] 入站 webhook 签名:要不要在入站门拒收不带时间戳的旧签名(sha256=正文签名)?发送方指引何时改为推荐带时间戳的新签名? #22805's grade.webhooks.mdx§6.2),signHttpBody, the flowhttpnode and the messaging outbox stay as published, as the ruling says.signHttpBodyAt.Clause-②: no(text only).objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 3
Session:
session_011u73oxZ5X95qrARPTeoU6vAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-22825-inbound-hook-timestamped-guidanceWorktree:
objectstack-issue-22825Domain:
domain:skillsSeat:
domain:skills#2File 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.tsandpackages/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) anddocs/qa/platform-checklist/areas/automation.json(the QA item's request recipe), both declared on #6023 · one changeset; ⛔ no change tocontent/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 …atefcbac73c: 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 refuseThread-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 mergesorigin/maincarrying #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-remoteshows noclaude/issue-22825-*branch; #22585 (pm:blockedbehind #19939) later edits the sameSKILL.mdand 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
objectstack-fleet commentedon Oct 11, 2026
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
objectstack-fleet commentedon Oct 11, 2026
ACCEPT — skills seat 2,
session_011u73oxZ5X95qrARPTeoU6v(zhuangjianguo) · 2026-10-11T14:09Z · PR #22846, headcaf9fa094(on base1eff3224d).Checklist against GitHub, not the report: draft to
main; first lineFixes #22825;Clause-②: noat line start;## 维护者速读(草稿)present, the final posted on the PR; changeset@objectstack/lintpatch +@objectstack/skillspatch (both publish; the showcase package is private,docs/qais no package), so noskip-changeset; nocontent/docs/releases/; files exactly the five positions the ruling names plus the changeset (+43/−16); closing keywords: onlyFixes #22825. Spot checks on the diff, read in full: the five texts matchpackages/core/src/security/http-signature.tsonorigin/main(HTTP_SIGNATURE_TOLERANCE_SECONDS = 300;signHttpBodyAtwritest=the timestamp,v1=the HMAC-SHA256 oftimestamp.body); the done-when grep at the head readssha256=0 hits over the five files andv1=present in each; the two skill deletions that pay the ratchet are restated text (the section opener and the Prerequisite table'squeuerow 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 oncaf9fa094: 34 check-runs oncaf9fa094: 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 Corewith its six shards,Dogfood Regression Gatewith its three shards,Temporal Conformance (live PG + MySQL),Build Core,Governed Surface Queue Guard,Check Changesetand 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/mainmerged again at1eff3224dbefore opening. Deviations accepted: based8c7d3864instead of the dispatch'sefcbac73c; the model-named trailer amended to the model-free pair before any push;git fetch --deepen=4000on the shared store; lint suite and typecheck on435fd3b3eonly (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-checklistred onorigin/main(five absent-symbol anchors inaccess-security.jsonandattachments-storage.json, byte-identical on base and branch) — not a card by the filing gate (apnpm check:*repo gate is not a public door: HTTP, UI,os validateor 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 withneeds-user-decisionand review requested fromos-zhuangandhotlong; an authorized APPROVED review lands it through the seat. The card keepspm:dispatchedand its assignee until MERGED; #22826 unblocks on that merge.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Landed — skills seat 2,
session_011u73oxZ5X95qrARPTeoU6v(zhuangjianguo) · 2026-10-11T16:07ZPR #22846 MERGED by
os-zhuangat 2026-10-11T14:40Z aseed637c09onorigin/main(the queue's squash of headcaf9fa094; the queue refpr-22846-…is gone). Content read onorigin/main, not inferred: the Inbound webhook section ofskills/objectstack-automation/SKILL.mdteachest=UNIX_SECONDS,v1=HEXwith the 300-second window and carries nosha256=;packages/lint/src/rule-explanations.tscarries the timestamped form;.changeset/22825-inbound-hook-timestamped-guidance.mdis on the tree. This card closedcompletedby the PR'sFixes(closed_by_pull_requestslists #22846 alone);pm:dispatchedis stripped in this act, the assignee stays as the record of the claim. Lane reconciliation after the merge: the only card that left the opendomain:skillsset is this one, expected; #22826 (Blocked-by: #22825, services lane) already readspm:queue, so the unlock is in its lane's hands.Landing path, for the record: Tier H;
os-zhuangapproved 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: thecheck:platform-checklistred onorigin/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.mdkeeps a token headroom of 1 for #22585.Generated by Claude Code