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
Commit 5374967
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec): tencentcloud-tc3-hmac-sha256 joins the signed connector auth schemes (ADR-0143 D2.3, slice 2 of #26710) (#28279)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D6hhjvTokP3gaKQjZx1GtR5)_
domain:server#3 · os-elon-musk · session_016WV4cxRP1AqTeP2PWAhwGK
Refs #26710
Clause-②: yes (widening: one member added to the closed signing-scheme
list and one shape to the `signed` union; one schema export and its type
added. `ConnectorRequestSigner.scheme` is now typed by the scheme list,
but that interface is in no release yet: it ships with #28273's pending
changeset, so no published surface narrows)
Metadata-protocol: added
## 维护者速读
- 改了什么:`signed` 认证新增第二个签名方案,腾讯云 API 3.0 签名
v3(`tencentcloud-tc3-hmac-sha256`)。元数据仍只写方案名、公开的 SecretId(`keyId`)和密钥引用
`credentialRef`;`rest` / `openapi` 每次请求(含重试)用新的时间戳重新签名。
- 为什么改:#26710 第二片(ADR-0143 Q4 A 的第二个成员)。腾讯官方文档示例把 SecretKey 打了码,无法复现,所以按
D2.3 改用腾讯官方 Node SDK 的测试向量,测试里写明了 SDK 和版本。
- 拒绝什么:与第一片相同。内联密钥按名拒绝、手写 `signature` 拒绝、`mcp` 上拒绝;`secretId` 提示改写为
`keyId`;未知 scheme 的提示现在列出两个方案。
- 没变的:阿里云方案、`bearer` / `api-key` / `basic` 不变;签名接缝只把 `scheme`
的类型从任意字符串收紧到方案列表(该接口尚未发布)。
-
先看:`packages/services/service-automation/src/request-signing/tencentcloud-tc3-hmac-sha256.ts`
和它的测试。
- 两分钟验证:`pnpm --filter @objectstack/service-automation exec vitest run
src/request-signing`,看到腾讯 SDK 的 POST 与 GET 两个签名逐字节复现,错误密钥得到不同签名。
- 需要你做的:无。契约复审(CONTRACT_REVIEW_TIER)待做;Tier S,席位验收后落地。
## What changes
**`@objectstack/spec`**
- `ConnectorSigningSchemeSchema` gains `tencentcloud-tc3-hmac-sha256`.
The `signed` union gains
`ConnectorInstanceSignedTencentCloudTc3AuthSchema`: `{ type: 'signed',
scheme: 'tencentcloud-tc3-hmac-sha256', keyId, credentialRef }`, a
`strictObject`. `keyId` is the SecretId. The SecretKey is only ever a
`credentialRef`.
- The publish door refuses on the new shape what it refuses on the first
one: `secret`, `privateKey`, `secretKey` and `accessKeySecret` by name
with the `credentialRef` prescription, an authored `signature`, and
`signed` on `provider: 'mcp'`. `secretId` is answered with `keyId`. The
unknown-scheme refusal names both shipped schemes.
- The review nit from #28273 is fixed: `ConnectorRequestSigner.scheme`
is typed `ConnectorSigningScheme` rather than `string`.
**`@objectstack/service-automation`**
- `request-signing/tencentcloud-tc3-hmac-sha256.ts`:
- The canonical request is the method, the path, the query exactly as
sent, `content-type:` and `host:`, `content-type;host`, and the hex
SHA-256 of the body.
- The string to sign is `TC3-HMAC-SHA256`, the timestamp, the scope
`<date>/<service>/tc3_request` and the hashed canonical request.
- The key is derived `HMAC("TC3"+SecretKey, date)` → service →
`tc3_request`.
- Every attempt gets a fresh `X-TC-Timestamp`.
- The service is the first label of the endpoint host (regional
endpoints included).
- `X-TC-Action` / `-Version` / `-Region` stay unsigned, as in the
vendor's SDK.
- A request that names no content type is signed and sent with
`application/x-www-form-urlencoded` (what the SDK sends on a GET), so
the signed header is the sent header.
- The registry gains the case. Its exhaustive check now tests `auth`
itself against `never`, which still names an unhandled scheme at compile
time (ablation below).
**Docs:** `connectors.mdx` "Request signing" gains the Tencent example
and the scheme's header rules. The `variant-docs.json` scheme key lists
both members. The references are regenerated with `check:generated
--fix`.
The surface is inside the amended claim 6535175098.
## Why
ADR-0143 D2.3 / Q4 A: the closed list grows one scheme at a time, each
with its signer and vectors. D2.3's vector rule takes the vendor's
worked example when it is complete, and otherwise the vendor's official
SDK test suite named with its version. Tencent's own example masks the
SecretKey, so this slice takes the second branch.
## Verification
- **Vectors:** `tencentcloud-tc3-hmac-sha256.test.ts` reproduces, byte
for byte, the two `sign3` cases of the official
`tencentcloud-sdk-nodejs` release 4.1.336, `test/common/sign.test.ts`:
POST `application/json` and GET `application/x-www-form-urlencoded`. It
uses that suite's own placeholder key pair, timestamp and service, and
the file header names the SDK, release and file. The suite's multipart
case is not carried, because a signed body is text here. A wrong key
gives a different signature. The bound signer at the vector's instant
gives the vector.
- **Beyond the vectors:**
- The unsigned API headers do not move the signature.
- A request with no content type gets the form type, and its signature
equals the GET vector. CONTROL: a typed request is not given another
type.
- A regional host scopes to `cvm`.
- The query is signed as sent.
- The timestamp is fresh per request.
- The registry binds the scheme, and the key is absent from the output.
- 23 signing tests pass.
- **Publish door:** `connector-auth-signed.test.ts` adds:
- a CONTROL accept on `rest` and `openapi`
- the inline-key refusals over both schemes
- the `secretId` alias
- both names in the unknown-scheme message
- union membership
- **End to end:** `connector-rest-plugin.test.ts` runs a real host with
the rest provider and a `tencentcloud-tc3-hmac-sha256` instance. The
call carries a TC3 `Authorization` scoped to `cvm` and an
`X-TC-Timestamp`, and the key is absent from calls and descriptors.
- **Suites:**
- spec signed, fetch-policy, grant-kinds, connector-auth and pins: 74
passed.
- service-automation signing, materialization and credential source: 105
passed.
- connector-rest: 33 passed.
- connector-openapi: 75 passed.
- `pnpm --filter <pkg> typecheck` is green for spec, service-automation
and connector-rest.
- **Ablations:**
- With the `tencentcloud-tc3-hmac-sha256` registry case removed,
`service-automation` typecheck fails, naming the unhandled shape (TS2322
at the `never` check).
- With the `"TC3"` prefix dropped from the key derivation, 6 vector pins
go red.
- With the code as pushed, everything is green.
- Every family `dispatch-gates --commands` derives (105) ran and exited
0, reconciled with `--ran`: 105 run, 0 NOT-MEASURED. `pnpm lint` exits
0.
Skills-docs: `grep -rlnE "credentialRef|app-credential|api-key" skills`
hits
`skills/objectstack-automation/{SKILL.md,references/integration-recipes.md,evals/integrations-declared.json}`.
The `signed` text rides #28226, as for #28273, because a `skills/**`
edit would make this slice Tier H.
`content/docs/automation/connectors.mdx` carries it here.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(The signer is in `service-automation`.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request (slice 2 of 3 of #26710).
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
lane's maintainer summary).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_016WV4cxRP1AqTeP2PWAhwGK
---------
Co-authored-by: Claude <noreply@anthropic.com>
<!-- adr-0087: not-required (no-migration-prescription) One member is added to the closed signing-scheme list and one shape to the `signed` union; the signer seam's `scheme` member is typed as that list instead of any string; that interface is in no release yet (it ships with the pending `26710-signed-connector-auth-aliyun` changeset), so no published surface narrows. Nothing an author wrote before is refused or re-shaped, so there is nothing for a migration to rewrite. -->
12
+
13
+
A declarative connector instance can now declare Tencent Cloud's API 3.0 signature (v3) as its signing scheme. It joins Alibaba Cloud's V3 signature, which shipped with the `signed` kind.
- The `signed` union gains its closed shape, `{ type: 'signed', scheme: 'tencentcloud-tc3-hmac-sha256', keyId, credentialRef }`. `keyId` is the SecretId. The SecretKey is only ever a `credentialRef`.
18
+
- The same publish refusals apply as for the first scheme: an inline key spelling by name, an authored `signature`, and `signed` on `provider: 'mcp'`. `secretId` is answered with `keyId`. An unknown scheme's refusal now lists both shipped schemes.
19
+
-`ConnectorRequestSigner.scheme` is typed as `ConnectorSigningScheme` rather than `string`. The interface ships for the first time in this release, together with the first scheme.
20
+
-**`@objectstack/service-automation`**
21
+
- The signer registry binds a `tencentcloud-tc3-hmac-sha256` instance's key to its TC3-HMAC-SHA256 signer.
22
+
- Every attempt is signed with a fresh `X-TC-Timestamp`, over `content-type` and `host`. The scope's service is the first label of the endpoint host.
23
+
- A request without a content type is signed and sent as `application/x-www-form-urlencoded`.
Copy file name to clipboardExpand all lines: content/docs/references/index.mdx
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,7 @@
1
1
---
2
2
title: Protocol reference — every schema by module
3
3
navTitle: Protocol Reference
4
-
description: Every schema published by @objectstack/spec — 1569 schemas across 14 protocol modules
4
+
description: Every schema published by @objectstack/spec — 1570 schemas across 14 protocol modules
5
5
---
6
6
7
7
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -25,7 +25,7 @@ counts are sums of the rows they head. Regenerate with
25
25
|[Automation Protocol](/docs/references/automation)| 15 | 77 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
26
26
|[Data Protocol](/docs/references/data)| 31 | 174 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
@@ -302,6 +302,15 @@ const result = ConnectorSchema.parse(data);
302
302
|**keyId**|`string`| ✅ | The AccessKey ID (not a secret); sent in the `Credential` of every signature. |
303
303
|**credentialRef**|`string`| ✅ | Secrets-layer reference resolved to the AccessKey secret, the HMAC key every request is signed with. Never an inline secret. |
|**keyId**|`string`| ✅ | The SecretId (not a secret); sent in the `Credential` of every signature. |
312
+
|**credentialRef**|`string`| ✅ | Secrets-layer reference resolved to the SecretKey every request's signing key is derived from. Never an inline secret. |
|**keyId**|`string`| ✅ | The SecretId (not a secret); sent in the `Credential` of every signature. |
663
+
|**credentialRef**|`string`| ✅ | Secrets-layer reference resolved to the SecretKey every request's signing key is derived from. Never an inline secret. |
|**keyId**|`string`| ✅ | The SecretId (not a secret); sent in the `Credential` of every signature. |
679
+
|**credentialRef**|`string`| ✅ | Secrets-layer reference resolved to the SecretKey every request's signing key is derived from. Never an inline secret. |
|**keyId**|`string`| ✅ | The AccessKey ID (not a secret); sent in the `Credential` of every signature. |
825
864
|**credentialRef**|`string`| ✅ | Secrets-layer reference resolved to the AccessKey secret, the HMAC key every request is signed with. Never an inline secret. |
|**keyId**|`string`| ✅ | The SecretId (not a secret); sent in the `Credential` of every signature. |
873
+
|**credentialRef**|`string`| ✅ | Secrets-layer reference resolved to the SecretKey every request's signing key is derived from. Never an inline secret. |
0 commit comments