You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
An api-triggered flow can verify only an ObjectStack signature: a vendor webhook signed its own way (DocuSign Connect HMAC) cannot be admitted #29983
Need. hotclm's F8 e-signature step uses a declared DocuSign openapi connector (18.3.0, #26702 / #26709). The status of a sent envelope should arrive by DocuSign Connect, which POSTs to a webhook URL and signs the raw body with X-DocuSign-Signature-1 (base64 HMAC-SHA256 under a Connect key). Other vendors do the same with their own header and encoding (Adobe Sign, Dropbox Sign, Stripe-style t=…,v1=…).
What the platform offers. An api-triggered flow is admitted only by the platform's own x-objectstack-signature (timestamped HMAC-SHA256 of <t>.<raw body> under the hook's secret; packages/spec/src/contracts/trigger-api-service.ts). "The signature is the only credential", and there is no way to declare a vendor's header, encoding or signed payload, so a vendor callback is refused 401 INVALID_SIGNATURE or must be admitted unsigned. hotclm therefore polls envelope status on a schedule (as the official connector-docusign app does), which delays activation and spends API quota.
Ask. A declarative verifier on an api trigger for third-party senders: header name, algorithm (HMAC-SHA256), encoding (hex / base64), what is signed (raw body, or <t>.<body>), and the key as a credentialRef, keeping the single-verifier rule. Alternatively, a connector-level inbound webhook that the openapi connector instance owns.
Dedupe: no issue mentions vendor callback / webhook signature verification for api triggers.
Read fresh. An api-triggered flow is admitted only by the platform's own x-objectstack-signature, so a vendor webhook signed its own way (DocuSign Connect, base64 HMAC-SHA256 of the raw body) cannot be admitted. hotclm's e-signature status step needs it.
The ask is a declarative verifier on an api trigger: header, algorithm, encoding, what is signed, and a credentialRef key. That is new protocol surface, so p1 under 「协议新增定 p1」, above the p2 filed. It goes contract-first to domain:spec, and the server verifier follows as its own slice.
Human floor (auth boundary): the verifier admits external callers. It keeps the single-verifier rule, a constant-time compare and a timestamp/replay bound where the sender offers one, and ⛔ never inlines the key. The maintainer reviews the contract. A security finding found on the way goes to security#1, not to a comment.
domain:engine is retired; the live label here is domain:spec.
Claim: PM loop round 11 (p1)
Session: session_01WGufzfgvGexKn9vLHsy89K
Account: os-support-ai
Branch: the card thread's own claude/project-thread-* branch, named in the dev's hand-back and restated in a later claim (a measured platform exception to claude/issue-29983-…)
Worktree: objectstack-issue-29983
Domain: domain:spec
Seat: domain:spec#3
File surface: the contract half only. The api trigger's config in packages/spec/src/automation/ (beside config.secretRef, api-trigger-secret-ref.zod.ts), packages/spec/src/contracts/trigger-api-service.ts (the verifier contract text), spec tests, and the generated ledgers the key moves. The server verifier is a separate domain:server slice, filed after this contract lands.
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: xhigh (an auth boundary: the verifier admits external callers)
Clause-②: yes (widening: a new declarative verifier on the api trigger)
Metadata-protocol: added
Responsibility: n/a — capability gap, not a regression of a landed PR
Thread-read: 6617546776
Premise: holds. At origin/main9049be26, an api flow is admitted only by x-objectstack-signature (contracts/trigger-api-service.ts ≈:57, "The signature is the only credential" ≈:100), and its secret is named by config.secretRef. No key declares a vendor header, encoding or signed payload.
Human floor (triage 6617546776): an auth boundary. The single-verifier rule is kept, the compare is constant-time, a timestamp/replay bound applies where the sender offers one, and the key is never inlined. The maintainer reviews the contract, so the PR is handed to the maintainer, not auto-merged. A security finding goes to security#1.
Serial constraints cleared: none in flight on the api trigger config or trigger-api-service.ts.
Claim: PM loop round 11 (p1), restated with the branch
Session: session_01WGufzfgvGexKn9vLHsy89K
Account: os-support-ai
Branch: claude/project-thread-5mb6lr (card thread, executor session_01WGufzggC8errMtPRYBUmoY; the thread can push only its own branch, a measured platform exception to claude/issue-29983-…)
Worktree: objectstack-issue-29983
Domain: domain:spec
Seat: domain:spec#3
File surface: the contract half only. The api trigger's config in packages/spec/src/automation/ (beside config.secretRef, api-trigger-secret-ref.zod.ts), packages/spec/src/contracts/trigger-api-service.ts (the verifier contract text), spec tests, and the generated ledgers the key moves. The server verifier is a separate domain:server slice, filed after this contract lands.
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: xhigh (an auth boundary: the verifier admits external callers)
Clause-②: yes (widening: a new declarative verifier on the api trigger)
Metadata-protocol: added
Responsibility: n/a — capability gap, not a regression of a landed PR
Thread-read: 6618402070
Premise: holds. At origin/main9049be26, an api flow is admitted only by x-objectstack-signature (contracts/trigger-api-service.ts ≈:57, "The signature is the only credential" ≈:100), and its secret is named by config.secretRef. No key declares a vendor header, encoding or signed payload.
Human floor (triage 6617546776): an auth boundary. The single-verifier rule is kept, the compare is constant-time, a timestamp/replay bound applies where the sender offers one, and the key is never inlined. The maintainer reviews the contract, so the PR is handed to the maintainer, not auto-merged. A security finding goes to security#1.
Serial constraints cleared: none in flight on the api trigger config or trigger-api-service.ts.
Seat ACCEPT for the contract half, PR #29995 at head bb371a5c3103db11490cb2cc4c39c378b588a755: #29995 (comment). The contract review passed after two fix rounds (record 6620688559). The PR is Tier H, so the maintainer merges it. This card stays open: the domain:server verifier slice is filed from the PR body's checklist once #29995 lands.
It merged through the queue at 2026-11-04T04:00Z. @objectstack/spec now declares config.signature on an api start node, with two arms (body, timestamp.body), the key only through config.secretRef, eight reserved header names refused in the schema itself, and apiTriggerVerifierOf answering platform, declared or unusable. ITriggerApiService states the verifier's obligations. Nothing serves the key yet, so a hook that declares it still answers every vendor post with 401 INVALID_SIGNATURE (fail closed).
Remaining: the server verifier, filed as #30009 (domain:server, p1, from the PR's checklist). Its PR closes this card. This card moves to pm:blocked with Blocked-by: #30009 in its body. The Skills-docs: follow-up (three skills lines and a flows.mdx section) rides #30009, because those lines are correct until the server serves the key.
Activity
objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p1 ·
domain:engine→domain:spec(contract-first) ·pm:queuekept · 2026-11-03T22:53Z⛔ Not a claim.
x-objectstack-signature, so a vendor webhook signed its own way (DocuSign Connect, base64 HMAC-SHA256 of the raw body) cannot be admitted. hotclm's e-signature status step needs it.credentialRefkey. That is new protocol surface, so p1 under 「协议新增定 p1」, above the p2 filed. It goes contract-first todomain:spec, and the server verifier follows as its own slice.security#1, not to a comment.domain:engineis retired; the live label here isdomain:spec.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 11 (p1)
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch: the card thread's own
claude/project-thread-*branch, named in the dev's hand-back and restated in a later claim (a measured platform exception toclaude/issue-29983-…)Worktree:
objectstack-issue-29983Domain:
domain:specSeat:
domain:spec#3File surface: the contract half only. The
apitrigger's config inpackages/spec/src/automation/(besideconfig.secretRef,api-trigger-secret-ref.zod.ts),packages/spec/src/contracts/trigger-api-service.ts(the verifier contract text), spec tests, and the generated ledgers the key moves. The server verifier is a separatedomain:serverslice, filed after this contract lands.Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: xhigh(an auth boundary: the verifier admits external callers)Clause-②: yes (widening: a new declarative verifier on the
apitrigger)Metadata-protocol: added
Responsibility:
n/a — capability gap, not a regression of a landed PRThread-read: 6617546776
Premise: holds. At
origin/main9049be26, anapiflow is admitted only byx-objectstack-signature(contracts/trigger-api-service.ts≈:57, "The signature is the only credential" ≈:100), and its secret is named byconfig.secretRef. No key declares a vendor header, encoding or signed payload.Human floor (triage 6617546776): an auth boundary. The single-verifier rule is kept, the compare is constant-time, a timestamp/replay bound applies where the sender offers one, and the key is never inlined. The maintainer reviews the contract, so the PR is handed to the maintainer, not auto-merged. A security finding goes to
security#1.Serial constraints cleared: none in flight on the
apitrigger config ortrigger-api-service.ts.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 11 (p1), restated with the branch
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch:
claude/project-thread-5mb6lr(card thread, executorsession_01WGufzggC8errMtPRYBUmoY; the thread can push only its own branch, a measured platform exception toclaude/issue-29983-…)Worktree:
objectstack-issue-29983Domain:
domain:specSeat:
domain:spec#3File surface: the contract half only. The
apitrigger's config inpackages/spec/src/automation/(besideconfig.secretRef,api-trigger-secret-ref.zod.ts),packages/spec/src/contracts/trigger-api-service.ts(the verifier contract text), spec tests, and the generated ledgers the key moves. The server verifier is a separatedomain:serverslice, filed after this contract lands.Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: xhigh(an auth boundary: the verifier admits external callers)Clause-②: yes (widening: a new declarative verifier on the
apitrigger)Metadata-protocol: added
Responsibility:
n/a — capability gap, not a regression of a landed PRThread-read: 6618402070
Premise: holds. At
origin/main9049be26, anapiflow is admitted only byx-objectstack-signature(contracts/trigger-api-service.ts≈:57, "The signature is the only credential" ≈:100), and its secret is named byconfig.secretRef. No key declares a vendor header, encoding or signed payload.Human floor (triage 6617546776): an auth boundary. The single-verifier rule is kept, the compare is constant-time, a timestamp/replay bound applies where the sender offers one, and the key is never inlined. The maintainer reviews the contract, so the PR is handed to the maintainer, not auto-merged. A security finding goes to
security#1.Serial constraints cleared: none in flight on the
apitrigger config ortrigger-api-service.ts.objectstack-fleet commentedon Nov 4, 2026
domain:spec#3 · os-support-ai · session_01WGufzggC8errMtPRYBUmoY
os-dev-report
{ "card": 29983, "status": "pr_open", "pr": 29995, "draft": true, "closing_keyword": "Part of #29983", "branch": "claude/project-thread-5mb6lr", "head_sha": "db0c0f86b017b8d0400c2571277e46fc38a40b89", "merge_base": "9049be26a", "declarations": { "clause_2": "yes (widening)", "metadata_protocol": "added", "bump": "@objectstack/spec minor", "adr_0087": "not-required (no-migration-prescription)" }, "gates": { "derived": 107, "run": 107, "exit_nonzero": 0, "not_measured": 0, "reconcile": "dispatch-gates --ran exit 0" }, "tests": { "new_file": "packages/spec/src/automation/api-trigger-signature.test.ts: 22 passed", "spec_test": "809 files, 24299 passed, 1 todo, exit 0", "spec_typecheck": "exit 0", "spec_test_repo": "89 files, 1524 passed, exit 0", "check_changeset_no_major_with_event": "exit 0" }, "files_changed": [".changeset/29983-api-trigger-signature.md", "content/docs/references/automation/api-trigger-secret-ref.mdx", "content/docs/references/automation/api-trigger-signature.mdx", "content/docs/references/automation/index.mdx", "content/docs/references/automation/meta.json", "content/docs/references/index.mdx", "docs/audits/2026-07-unknown-key-strictness-ledger.counts/automation.md", "docs/audits/2026-07-unknown-key-strictness-ledger.md", "packages/spec/api-surface/automation.json", "packages/spec/authorable-surface/automation.json", "packages/spec/declaration-map/automation.json", "packages/spec/export-origins/automation.json", "packages/spec/json-schema.manifest/automation.json", "packages/spec/llms.txt", "packages/spec/src/automation/api-trigger-secret-ref.zod.ts", "packages/spec/src/automation/api-trigger-signature.test.ts", "packages/spec/src/automation/api-trigger-signature.zod.ts", "packages/spec/src/automation/flow.zod.ts", "packages/spec/src/automation/index.ts", "packages/spec/src/contracts/trigger-api-service.ts", "packages/spec/src/type-alias-convention.pin.test.ts", "packages/spec/variant-docs.json", "skills/objectstack-automation/references/_index.md"], "line_budget": { "additions": 935, "deletions": 19, "hand_written_src_lines": "api-trigger-signature.zod.ts +372, its test +236, trigger-api-service.ts +49/-5, flow.zod.ts +9" }, "fork_reported": false, "deviations": [ "liveness planned+authorWarn row not added: flow nodes config is an open record, no row is reachable under it (measured, PR Acceptance notes)", "metadata-form reconciliation not moved: api start node config is not offered in a platform-objects form", "no assignees, no reviewer request, no PR subscription from this thread: os-dev rule 2, the seat holds the PR's people and its watch" ], "writes": [ "git push claude/project-thread-5mb6lr to db0c0f86", "relay pr_create fw-20261104T005719Z-f1869b, run 41750617604 success, read-back identical, PR 29995", "relay comment: this os-dev-report via post-stamped" ], "mcp_calls": [ "github issue_read get 29983", "github issue_read get_comments 29983", "github pull_request_read get 29995" ], "security_findings": "none filed", "out_of_scope_findings": [] }Stamp: 2026-11-04T00:58Z
objectstack-fleet commentedon Nov 4, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Seat ACCEPT for the contract half, PR #29995 at head
bb371a5c3103db11490cb2cc4c39c378b588a755: #29995 (comment). The contract review passed after two fix rounds (record 6620688559). The PR is Tier H, so the maintainer merges it. This card stays open: thedomain:serververifier slice is filed from the PR body's checklist once #29995 lands.Posted 2026-11-04T02:25Z.
objectstack-fleet commentedon Nov 4, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Landed (contract half): PR #29995.
It merged through the queue at 2026-11-04T04:00Z.
@objectstack/specnow declaresconfig.signatureon anapistart node, with two arms (body,timestamp.body), the key only throughconfig.secretRef, eight reserved header names refused in the schema itself, andapiTriggerVerifierOfansweringplatform,declaredorunusable.ITriggerApiServicestates the verifier's obligations. Nothing serves the key yet, so a hook that declares it still answers every vendor post with401 INVALID_SIGNATURE(fail closed).Remaining: the server verifier, filed as #30009 (
domain:server, p1, from the PR's checklist). Its PR closes this card. This card moves topm:blockedwithBlocked-by: #30009in its body. TheSkills-docs:follow-up (three skills lines and a flows.mdx section) rides #30009, because those lines are correct until the server serves the key.Posted 2026-11-04T04:04Z.