news 2026/9/13 15:15:46

Alchemy v2.0.0-beta.56 深度解析:230 个 Cloudflare 资源、unsafe nuke 与 S3 状态存储

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Alchemy v2.0.0-beta.56 深度解析:230 个 Cloudflare 资源、unsafe nuke 与 S3 状态存储

Alchemy v2.0.0-beta.56 深度解析:230 个 Cloudflare 资源、unsafe nuke 与 S3 状态存储

【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code

本文基于 alchemy(Infrastructure-as-Effects)v2.0.0-beta.56 版本发布说明展开,核心讲解该版本将 Cloudflare 提供商从 22 个资源扩展到 230 个的实现路径、Provider.list生命周期操作与alchemy unsafe nuke命令、GitHub.events事件源、以及 S3 后端状态存储等关键能力。读完本文,你将掌握如何用一个 Effect 程序描述完整的 Zero Trust 部署、如何用测试驱动的方式批量生成云端资源、如何在 Worker 中订阅 GitHub 事件并校验签名,以及如何为 AWS 栈配置远程状态后端。


版本概览:一次跨越 542 个文件的 Cloudflare 资源扩张

v2.0.0-beta.56 是 alchemy 的一次里程碑式发布:Cloudflare 提供商覆盖的资源类型从22 个跃升至 230 个。这一轮扩张由单个 PR 落地,累计约 10.1 万行代码、横跨 542 个文件,并新增了189 个 live-test 测试套件——每个套件都针对真实 Cloudflare 账户运行。最终覆盖超过一百个 Cloudflare 服务,包括:

  • Zero Trust 全家桶:Access、Tunnels、Devices、Gateway、DLP
  • 网络与流量:Magic Transit、Magic Network Monitoring、Load Balancing、Spectrum
  • DNS 与安全:DNS、DNS Firewall、SSL/TLS 与证书、API Shield、Bot Management、Page Shield、Turnstile
  • 边缘计算:Workers for Platforms、Snippets、Zaraz、Waiting Rooms、Logpush
  • 其他长尾服务:Registrar、Addressing、Alerting 等

从仓库源码结构看,这一扩张在 Cloudflare 目录 中体现得尤为直观:Access/Tunnel/Devices/Gateway/Dlp/MagicTransit/MagicNetworkMonitoring/LoadBalancer/WaitingRoom/Logpush/Spectrum/Turnstile/ApiShield/BotManagement/PageShield/WorkersForPlatforms/Snippets/Zaraz/Registrar/Alerting/等子目录均已落地为独立资源模块,每个子目录内的.ts文件即对应一个个可声明、可计划、可部署的资源类型。

用一段 Effect 代码部署完整 Zero Trust 网络

资源类型覆盖的意义不止于数量。它意味着一个完整的 Zero Trust 部署——隧道、私有网络路由、身份门控的应用、WARP 设备配置——现在只需要一个 Effect 程序。Zero Trust 资源由 Andy Jefferson 在贡献 PR 中完成:

// 一个仅能通过 WARP + Access 访问的私有网络 const tunnel = yield* Cloudflare.Tunnel("Corp", { ingress: [ { hostname: "dashboard.example.com", service: "http://localhost:3000" }, { service: "http_status:404" }, ], }); yield* Cloudflare.TunnelRoute("PrivateNet", { tunnelId: tunnel.tunnelId, network: "10.4.0.0/16", }); const allowCorp = yield* Cloudflare.AccessPolicy("AllowCorp", { name: "Allow corp users", decision: "allow", include: [{ emailDomain: { domain: "example.com" } }], }); yield* Cloudflare.AccessApplication("Dashboard", { type: "self_hosted", domain: "dashboard.example.com", sessionDuration: "24h", policies: [allowCorp.policyId], }); yield* Cloudflare.DeviceDefaultProfile("Default", { mode: "exclude", splitTunnelExclude: [{ address: "10.0.0.0/8", description: "RFC1918" }], excludeOfficeIps: true, });

这段代码的要点:

  • Cloudflare.Tunnel声明隧道及其 ingress 规则:dashboard.example.com转发到本机 3000 端口,其余请求返回 HTTP 404;
  • Cloudflare.TunnelRoute10.4.0.0/16私有网段路由进隧道;
  • Cloudflare.AccessPolicy定义"仅允许 example.com 域邮箱用户"的访问策略,返回的policyId直接注入AccessApplication
  • Cloudflare.DeviceDefaultProfile配置 WARP 设备默认配置文件,将 RFC1918 私网段10.0.0.0/8排除在隧道之外,并开启excludeOfficeIps

资源之间通过tunnel.tunnelIdallowCorp.policyId输出属性互相引用,形成依赖图。这正是 alchemy"基础设施即 Effect"的核心体验:没有 YAML,没有第二个运行时,基础设施声明与业务逻辑同处一个类型安全的 Effect 程序里(见 README)。


实现路径:测试驱动的 SDK 飞轮

230 个资源不是手工一行行写的。发布说明揭示了背后的方法论:资源由 AI Agent 集群按服务逐个实现,每个 Agent 针对 distilled——alchemy 自研的生成式 Cloudflare SDK——工作。每个 Agent 先实现一个服务的资源,然后对真实 API 做 live-test;当测试撞上类型没有覆盖的 API 行为时,修复被写进 SDK,成为类型化错误补丁(typed-error patch),而不是在消费端加一个 catch 块:

// distilled/packages/cloudflare/patches/turnstile/getWidget.json { "errors": { "WidgetNotFound": [{ "code": 10404 }, { "code": 10407 }], "Forbidden": [{ "status": 403 }] } }

这个补丁文件的含义:当 Turnstile 的getWidget接口返回错误码1040410407时,SDK 应将其识别为类型化的WidgetNotFound错误;返回 HTTP 403 时识别为Forbidden。这样一来,API 的真实行为就以类型的形式被记录下来,惠及 SDK 的每一个未来消费者——错误处理是编译期可见的 Effect 错误,而非运行时猜测。

把这个循环跑在 288 个 live-test 套件上,效果显著:SDK 的补丁集从一个发布周期内的369 个增长到 1,087 个操作,覆盖 Cloudflare 114 个服务中的 94 个。每个补丁都是一条"API 实际如何表现"的类型化记录。

这个循环的完整故事——闭环、补丁以及驱动它们的工厂——记录在 Looping the Generation of IaC and SDKs 一文中,感兴趣的读者可以继续深入。


Feature Flags:Cloudflare.FlagshipApp

Cloudflare 的 Flagship 特性开关(feature flag)服务在本版本以完整资源形态落地,并附带了 Effect 原生的 Worker 绑定。声明应用及其开关:

const app = yield* Cloudflare.FlagshipApp("Flags", {}); yield* Cloudflare.FlagshipFlag("NewCheckout", { appId: app.appId, key: "new-checkout", defaultVariation: "off", variations: { off: false, on: true }, });

从 App.ts 源码 可以看到资源实现细节:

  • AppProps仅含可选的name字段,省略时按${app}-${stage}-${id}生成唯一名称;
  • AppAttributes返回服务端生成的appId——它跨更新保持稳定,作为 flag、Worker 绑定以及所有求值调用的作用域标识;此外还有accountIdcreatedAtupdatedAtupdatedBy等元数据;
  • 应用名称可原地修改,但appId一旦生成永不变更。

FlagshipApp.bind(app)把绑定挂到外围 Worker 上,并返回一个运行时客户端用于求值:

export const App = Cloudflare.FlagshipApp("Flags", {}); export default Cloudflare.Worker( "FlagsWorker", { main: import.meta.filename }, Effect.gen(function* () { const flags = yield* Cloudflare.FlagshipApp.bind(App); return { fetch: Effect.gen(function* () { const enabled = yield* flags.getBooleanValue("new-checkout", false, { userId: "user-42", }); return HttpServerResponse.text(enabled ? "on" : "off"); }), }; }).pipe(Effect.provide(Cloudflare.FlagshipBindingLive)), );

关键设计:运行时Flagship绑定的每一个方法都被镜像为 Effect(源码中ReadFlagsReadFlagsBindingReadFlagsHttpClientReadFlagsLocal等模块共同支撑了这条链路),因此 Worker 代码里无需Effect.tryPromise包装即可直接yield*调用;异步 Workers 则可以把 app 绑定在env上,直接使用原始绑定。


Provider.listalchemy unsafe nuke

list():枚举环境的全部实时资源

本版本为 Provider 引入了新的生命周期操作list():它枚举当前账户/区域/Zone 中该类型的所有实时资源,返回的每一项都与read产出的Attributes形状一致,因此可以直接被删除。这为后续的清理能力打下了基础。

alchemy unsafe nuke:一键清空测试环境

基于list()构建的alchemy unsafe nuke命令,枚举 profile 下所有 Provider 能看到的资源并全部删除。当 230 种资源类型都可枚举时,清理一个被"烧毁"的测试账户就变成一行命令:

alchemy unsafe nuke \ --include "Cloudflare.*" \ --filter 'resource.workerName === "alchemy-state-store"' \ --dry-run

命令的完整语义在 nuke 文档 中有详尽说明,这里提炼关键点:

  • --include/--exclude接受provider-ID glob(picomatch 语法,如'Cloudflare.*''Cloudflare.Worker'),可重复指定,--exclude--include之后应用;
  • --filter接受JavaScript 表达式,求值时resource在作用域内(另有合成的TypeLogicalId字段);表达式为 truthy 的资源会被"赦免"(spared),不会被删除;表达式抛异常或非布尔值时按 false 处理;
  • --dry-run只预览不删除;--yes跳过确认提示;
  • 该命令刻意从--help中隐藏——它删除的是真实基础设施,危险操作不该被轻易发现。

nuke 的安全边界

nuke 的破坏力远超一般认知,需要特别注意:

  • 不限定于某个 stack、stage 或状态存储:stack 文件仅用于发现注册了哪些 Provider,随后每个 Provider 的list()会枚举该账户/区域/Zone 中的所有同类型资源——包括 alchemy 从未创建过的资源——并全部删除,不可撤销
  • 状态存储全程不被读写:枚举只基于实时云状态,删除后状态条目会残留为 stale,需要用alchemy state clear清理;
  • 幸存清单:nuke.singleton设置(始终存在的配置,其 delete 只是重置默认值)、nuke.skip资源(没有真实删除 API 的资源)、被--include/--exclude过滤掉的 Provider、被--filter赦免的资源、以及删除持续失败的资源(会在运行结束时汇报);
  • 删除采用多轮扫描:每轮尝试所有剩余资源,失败的进入下一轮,依赖顺序无需构建图即可自然浮现;当某轮无进展时停止并报告N resource(s) could not be deleted
  • --concurrency控制 Provider 并行扫描/删除数(默认 16,0表示不限制——但通常更慢,因为会触发 Provider 侧限流与重试退避;同一 Provider 内的资源始终并发删除);
  • --timeout是每个list/delete调用的 per-Provider 超时(默认 120 秒),防止单个慢 Provider 拖垮整次运行。

大多数情况下,你真正想要的是alchemy destroy(见 destroy 文档)——它是 stack 作用域、状态驱动的正规卸载方式;unsafe nuke是烧毁测试环境的最后手段。


GitHub 仓库与 Webhook 事件

GitHub.Repository:稳定 ID 驱动的收敛式仓库管理

Justin Bennett 贡献了GitHub.Repository资源——完整的仓库生命周期管理,其核心亮点是一个收敛式 reconciler(converging reconciler):它按稳定的数字 ID 读取仓库。这意味着即使有人在 stack 之外把仓库重命名了,状态也不会被污染(不会因为 name 变化而误判为删除+重建):

const repo = yield* GitHub.Repository("internal-tools", { owner: "my-org", name: "internal-tools", visibility: "private", deleteBranchOnMerge: true, });

GitHub.events:把仓库变成 Worker 的事件源

Repository之上,GitHub.events把一个仓库变成 Cloudflare Workers 的事件源。在 Worker 的 init 阶段订阅,会顺带配置一个指向 Worker URL 的GitHub.Webhook;运行时,每次投递都用常数时间的 HMAC-SHA256 签名校验验证,然后以完全类型化的 payload交到你的 handler 手中:

const secret = yield* Config.redacted("GITHUB_WEBHOOK_SECRET"); // `event.name` 被收窄为 "push" | "pull_request" yield* GitHub.events({ owner: "my-org", repository: "my-repo", events: ["push", "pull_request"], secret, }).subscribe((event) => Effect.log(`received ${event.name} (${event.id})`), );

从 RepositoryEventSource.ts 源码 可以看到事件源的类型系统设计:

  • GitHubEventName源自@octokit/webhooksEmitterWebhookEventName,并排除event.action变体(如pull_request.opened)——因为 Webhook 按裸事件名配置;
  • WebhookEventName = GitHubEventName | "*""*"订阅所有事件;events默认["push"]
  • WebhookEvent是完整的可辨识联合(discriminated union),以name为判别键,每个事件都有完全类型化的payloadSelectedEvent类型会把联合收窄到你选中的事件集合,因此switch (event.name)可以穷尽收窄event.payload
  • secretRedacted.Redacted<string>(如Config.redacted产生),配置后事件源会用该 secret 配置 Webhook,运行时拒绝X-Hub-Signature-256头不匹配的投递——强烈建议设置,否则任何知道投递 URL 的人都能伪造事件。

事件源的使用也体现在 github/events 文档 中:除了GitHub.events(文档中的consumeRepositoryEvents)这种高层抽象,还有更底层的GitHub.Webhook资源——它管理 Webhook 自身的生命周期(首次部署时创建、后续部署原地更新、销毁时删除),url接受Output<string>从而可以指向同栈内的 Worker 资源。值得注意的是,Cloudflare.Workers.GitHubRepositoryEventSourceLive目前是RepositoryEventSource契约的唯一实现(暂无 AWS Lambda 事件源);在其他宿主上仍可直接用GitHub.Webhook指向任意投递 URL,但路由与签名校验需要自己实现。


S3 状态存储:AWS.state()

AWS 栈在本版本获得了一等公民的远程状态后端,与 Cloudflare 的Cloudflare.state()对齐:

const Stack = Alchemy.Stack( "my-stack", { providers: AWS.providers(), state: AWS.state() }, Effect.gen(function* () { // ... }), );

从 AWS/StateStore/State.ts 源码 可以确认状态布局与默认行为:

  • 状态对象存放于s3://{bucket}/{prefix}{stack}/{stage}/{fqn}.json
  • 默认桶是自动创建的账户区域级(account-regional)桶,命名遵循alchemy-state-{accountId}-{region}-an约定;自定义名称也必须符合<prefix>-<accountId>-<region>-an的账户区域命名空间约束;
  • prefix指定桶内键前缀(默认""即桶根,尾部/会自动补上);
  • 桶默认启用AES256服务端加密(encryption可覆盖);
  • 同 stage 前缀下还有一个__stack_output__.json记账对象,存放 stack 已解析的 output,list结果在 FQN 解码前必须把它过滤掉;批量删除遵循 S3 单次DeleteObjects最多 1000 个键的上限(DELETE_BATCH_SIZE = 1000)。

关键设计:凭证解析与桶创建都被推迟到第一次状态操作时才发生——因此构造 Layer 的阶段完全不会触碰 AWS。这意味着alchemy plan在没有任何 AWS 凭证的情况下也能快速完成 diff,只在真正读写状态时才发起网络请求。


本版本其他改进

  • bundle: falseonCloudflare.Worker:原样上传预先构建好的main(byte-for-byte),不做 rolldown 打包、不做压缩,支持用 glob 形式的rules选择附加模块。这是 OpenNext 等外部打包产物的标准工作路径。
  • alchemy plan近即时启动:warm 启动从 8.2s 降到 1.8s,手段是懒加载 TUI 和 vite 插件、延迟 distilled schema 的构建。
  • 稳定输出属性:当首个域名未变化时,worker.url跨部署保持稳定,下游引用它的资源(如 Webhook 投递 URL)不再出现"幽灵更新";同理worker.durableObjectNamespaces在 DO 类集合未变化时保持稳定。
  • Lambda 函数 URL 支持完整配置url: { authType: "AWS_IAM", cors, invokeMode: "RESPONSE_STREAM" };IAM 认证的 URL 会省略公开的权限声明。
  • alchemy dev韧性改进:apply 失败时保持存活,记录完整原因而非静默退出。
  • StaticSite 转发dev.env(含 Redacted 密钥)给外部 dev-server 命令;本地 assets binding 在alchemy dev中不再返回 500。
  • Drizzle drift 检测按 plan/apply 周期缓存:drizzle-kit 的交互式重命名提示只出现一次,不再挂死alchemy dev

延伸阅读

围绕本版本的各项能力,仓库内提供了完整的文档与源码可供继续深入:

  • Looping the Generation of IaC and SDKs——资源工厂与 SDK 生成闭环的完整故事
  • Concepts › Providers——Provider 抽象与生命周期操作
  • Concepts › State Store——状态存储概念
  • CLI guide——CLI 全景
  • nuke 命令文档——unsafe nuke的完整参数表与安全边界
  • Webhooks & events——GitHub Webhook 资源与 Worker 事件源的使用指南
  • Deploy a Lambda Function——Lambda 部署指南
  • Cloudflare Flagship App 源码——特性开关资源的属性与绑定实现
  • AWS S3 State 源码——S3 状态后端的默认值与状态布局
  • GitHub RepositoryEventSource 源码——事件源的类型系统与签名校验

【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 15:15:39

并查集反集详解:P1892团伙问题与通解思路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 15:15:04

Matter协议实战指南:智能家居出海设备接入与认证避坑

Matter协议这两年确实被聊得非常多&#xff0c;尤其是做智能家居出海方向的朋友&#xff0c;几乎每个技术群里都会有人问“你们家设备什么时候上Matter”。说实话&#xff0c;三年前大家还在观望&#xff0c;觉得Matter就是个“雷声大雨点小”的行业联盟标准&#xff0c;能不能…

作者头像 李华
网站建设 2026/9/13 15:15:01

大模型训练显存优化实战:从显存账单到LoRA与ZeRO组合策略

最近在准备大模型训练环境时&#xff0c;刚好接触到某为26.3.18这个大模型训练显存优化算法的版本更新。借着这个契机&#xff0c;我把训练显存优化这件事从头到尾理了一遍。说实话&#xff0c;大模型训练里最让人头疼的不是模型效果&#xff0c;而是显存不够用——很多刚入坑的…

作者头像 李华
网站建设 2026/9/13 15:14:54

Arm项目健康度诊断:一页纸检查框架mango原理与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 15:14:12

R语言入门指南:环境配置与15本经典书籍推荐路线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华