# @domandigital/synthetic 中文文档

规范地址：https://npmcn.com/package/%40domandigital/synthetic/

版本：0\.2\.2
npm 元数据更新时间：2026-11-02T00:37:35.483Z
中文译文更新时间：2026-11-12T15:33:32.659Z
软件包许可：Apache-2\.0

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40domandigital/synthetic
原文对照：https://npmcn.com/package/%40domandigital/synthetic/?lang=original
项目主页：https://github.com/Doman-Digital/dd-packages/tree/main/packages/synthetic#readme
源码仓库：https://github.com/Doman-Digital/dd-packages

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：97.7 kB，9 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.12
- 运行依赖：0 个
- 首次发布：2026-10-08T11:33:41.464Z
- 最新版本发布：2026-11-02T00:37:35.158Z
- 近 30 天下载量：5140（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：5（近 12 个月 5 个）

## 中文简介

多曼数字客户端表格的已签名的合成提交协议:签名和验证,再播放后卫,收件人锁定,Turnstile配置自查,信标和清洗收据. 只有网络加密 .

## 安装

```sh
npm install @domandigital/synthetic
```

也可以使用：`pnpm add @domandigital/synthetic`、`yarn add @domandigital/synthetic`、`bun add @domandigital/synthetic`

## README

# @ doman数字/合成

多曼数字客户端表格的被签名的合成提交协议. 数字 检查( E)`dd-checks`)通过客户的真实形式提交真正的询问 调度器; Turnstile 屏蔽合成浏览器, 所以处理器 接受一个已签名的跳过请求 **只有转动的符号**。 。 。 .

网络加密, 没有依赖性: 它运行在 Cloudflare Workers, Node 22+ 和 dd- checks 中的浏览器边签名助手。 两次测试 节点和工人.

协议在DOM-487上的"DD Checks: plan"文档中指定, 页:1 这个包是作为代码的这个部分.

## 安装

```sh
pnpm add @domandigital/synthetic
```

## 在窗体处理器中

```ts
import {
  verifyRequest, keysFromEnv, turnstileConfigCheck, assertCanaryRecipient,
  syntheticReport, sendBeacon, upstashReplayGuard,
} from "@domandigital/synthetic";

export async function POST(request: Request) {
  const verified = await verifyRequest(request, {
    keys: keysFromEnv(env),                 // empty => synthetic path off
    host: "www.example.co.uk",              // the public host
    path: "/api/enquiry",
    contentTypes: ["application/json"],
    replayGuard: upstashReplayGuard({ url: env.UPSTASH_REDIS_REST_URL, token: env.UPSTASH_REDIS_REST_TOKEN }),
  });

  if (!verified.ok && verified.rejected) {
    // Handle as an ordinary request (Turnstile still applies) and tell dd-checks.
    ctx.waitUntil(sendBeacon(reporter, { form: "enquiry", code: "synthetic_rejected", reason: verified.reason }));
  }

  const synthetic = verified.ok ? verified.synthetic : null; // frozen; the only source of truth
  // 1. same-origin  2. schema validation
  // 3. synthetic ? turnstileConfigCheck({...}) : verify the Turnstile token
  // 4. the real rate limiter, identifier `synthetic:<form>` when synthetic
  // 5. full mode only: DB write (record_origin='synthetic', synthetic_run_id, purge_after),
  //    staff email redirected to canaryAddress("s", runId); submitter email to
  //    canaryAddress("u", runId) on the daily run only. assertCanaryRecipient() first.
  // 6. skip SMS, CRM, marketing lists, trackLead; report them as "skipped".
  // 7. respond with { ..., synthetic: syntheticReport(synthetic, stages) } only when synthetic
}
```

规则代码为您执行:

-   **默认关闭.** 无 `DD_SYNTHETIC_SECRET` (和 `DD_SYNTHETIC_KID`每个请求都是普通的.
-   **签名前的形状 .** 方法,主机,路径和内容类型在机构被散列前进行检查.
-   **被拒绝的请求是普通请求**从来没有失败. `verify` 从来没有扔过.
-   **需要回放守卫** 并在签名后运行,所以一个被伪造的请求不能燃烧一个真实运行的id.
-   **文体和查询从不读取** 页:1 `synthetic`编辑 `SyntheticContext` 仅由已核实信头构建并被冻结.

重放守护选项 : `upstashReplayGuard` (单位:千美元)`SET synth:<runId> NX EX 300`),或 您自己的功能相对于一个独特的制约 `synthetic_run_id`数字 :

```ts
replayGuard: async (runId) => {
  try { await db.insert(syntheticRuns).values({ syntheticRunId: runId }); return true; }
  catch (e) { if (isUniqueViolation(e)) return false; throw e; }
}
```

## 旋转

一个网站最多持有两把钥匙: `DD_SYNTHETIC_SECRET` + 键 `DD_SYNTHETIC_KID`,以及 `DD_SYNTHETIC_SECRET_NEXT` + 键 `DD_SYNTHETIC_KID_NEXT`设置新密钥为 `_NEXT` 和一个新孩子, 切换平台,推广它, 然后删除 旧的 一个身份不明的孩子被拒绝了 秘诀为32个随机字节,基数64url:

```sh
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"
```

## 签名(dd-checks)

```ts
const { headers, runId } = await sign({
  key: { kid: "example-2026-10", secret },
  url: "https://www.example.co.uk/api/enquiry",
  contentType: "application/json",
  body: rawBodyString,
  client: "example", form: "enquiry", mode: "probe",
});
```

`runId` 16 个字符 `[a-z2-7]`生成新鲜的,每一次来自 CSPRNG(英语:CSPRNG). 重放非曲直 然后进入金丝雀地址 所以每跑一次 是独一无二的 客户端的无一能键不能去除一个.

## 线条格式

标题 : `X-DD-Synth: v1`, (中文(简体) ), `X-DD-Synth-Kid`, (中文(简体) ), `-Ts` (unix 秒),(unix seconds),(英语:, `-Run`, (中文(简体) ), `-Mode` (单位:千美元)`probe` 或者说 `full`, (中文(简体) ), `-Client`, (中文(简体) ), `-Form`, (中文(简体) ), `-Sig: v1=<hex>`。 。 。 .

签名是HMAC-SHA256 上面有32字节的秘密:

```
DD-SYNTH-V1\n<kid>\n<ts>\n<runId>\n<mode>\n<client>\n<form>\nPOST\n<host>\n<path>\n<content-type>\n<hex sha256(raw body)>
```

`host` 小写,并仅在 URL 有端口时包含端口. `path` 已经 查询字符串已删除. `content-type` 信头与发送的完全相同(a) 多段边界是它的一部分). 窗口:±120 s. 比较:恒定时间.

**测试向量** 发表于 [`test-vectors.json`](https://npmcn.com/package/%40domandigital/synthetic/test-vectors.json) (单位:千美元)`@domandigital/synthetic/test-vectors.json`\- 一个测试秘密,每个案例 输入、词条、身体散列和预期签名。 他们 以 `node:crypto` 以 `scripts/gen-vectors.mjs`,独立于 这个包裹的代码。 任何其他执行( Python 检查器、 Go 服务) 必须复制它们.

## 收件人锁定

```ts
assertCanaryRecipient(submitterEmail);   // throws SyntheticRecipientError
```

仅 `@canary.domandigital.co.uk` 通过,作为一个简单的地址。 显示名称, 列表、子域和外观类似域都失败。 在一切发生之前就叫它 发送.

## 旋转配置自查

`turnstileConfigCheck({ siteKey, secretKey, expectedHostname, publicHost })` 在经核实的合成请求中替换符号。 它回来了 `{ ok, outcome }`,与 `outcome` 其中一个:

| 结果 | 含义 |
| --- | --- |
| `ok` | 存在密钥, 主机名右侧, 被网站验证接受的秘密 |
| `missing_keys` | 键或 `TURNSTILE_EXPECTED_HOSTNAME` 未设置 |
| `hostname_mismatch` | `TURNSTILE_EXPECTED_HOSTNAME`已正常化的不是公共主机(在客户端网站被忽略了几个星期的失败) |
| `test_key_on_production` | a 生产主机上的 Cloudflare 测试键 |
| `invalid-input-secret` | 校对:soup |
| `siteverify_unexpected` | 网站验证回答其他问题(例如 `success` 用于生产时的假币) |
| `siteverify_unreachable` | 网络出错、 超时或非 JSON ; 不描述配置 |

Hostnames 比较小写与方案,端口,路径和后继点已删除; `www.` 因为它是一个不同的宿主。 显示主机接受测试 键,因为运行浏览器的行程使用 Cloudflare 总是通过键.

## 灯塔和清理收据

```ts
const reporter = { key, client: "example", waitUntil: ctx.waitUntil.bind(ctx) };
sendBeacon(reporter, { form: "enquiry", code: "accepted" });
sendPurgeReceipt(reporter, { form: "enquiry", deleted: 24, remaining: 0 });
```

两者均用网站的钥匙签名(`-Mode: beacon` 编号 : `receipt`),员额改为 `https://checks.domandigital.co.uk/beacon` 财务报告和审定财务报表 `/purge-receipt`,为 火和遗忘,和 **永不抛出或拒绝**:一个糟糕的争论被放弃了. A 类 信标只持有代码(`accepted`, (中文(简体) ), `hostname_mismatch`, (中文(简体) ), `invalid-input-secret`, (中文(简体) ), `misconfigured`, (中文(简体) ), `service_unavailable`, (中文(简体) ), `delivery_failed`, (中文(简体) ), `captcha_failed`, (中文(简体) ), `synthetic_rejected` 加一个 `reason`, (中文(简体) ), 也从来不是个人资料.

## 让信头远离哨兵和日志

```ts
Sentry.init({ beforeSend: scrubSentryEvent, beforeSendTransaction: scrubSentryEvent });
logger.info(scrubText(headerDump));
const safe = scrubHeaders(request.headers);
```

## 发展

```sh
pnpm --filter @domandigital/synthetic test    # Node, then workerd
```

## 常见问题

### @domandigital/synthetic 是什么？

多曼数字客户端表格的已签名的合成提交协议:签名和验证,再播放后卫,收件人锁定,Turnstile配置自查,信标和清洗收据\. 只有网络加密 \.

### 如何安装 @domandigital/synthetic？

在项目目录运行 npm install @domandigital/synthetic，或使用 pnpm add / yarn add / bun add。

### @domandigital/synthetic 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @domandigital/synthetic 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @domandigital/synthetic 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.12。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @domandigital/synthetic 可以商用吗？

@domandigital/synthetic 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @domandigital/synthetic 的下载量有多少？

根据 npm 官方下载统计，@domandigital/synthetic 在 2026/10/13 – 2026/11/11 这 30 天内被下载 5,140 次，日均约 171 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @domandigital/synthetic 还在维护吗？

最新版本 0\.2\.2 发布于 2026/11/02（11 天前）；至今共发布 5 个版本，其中过去 12 个月发布了 5 个；首次发布于 2026/10/08。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

