首页
/ Puter 后端贡献指南:从架构分层到 API 演进规范,读懂 Puter 开源项目的协作工程实践

Puter 后端贡献指南:从架构分层到 API 演进规范,读懂 Puter 开源项目的协作工程实践

2026-09-05 15:08:39作者:廉彬冶Miranda

Puter(The Internet Computer)是一个免费、开源、可自托管的“浏览器里的桌面系统”,其后端采用 Controller–Service–Store–Client 分层架构。本文基于仓库根目录的贡献指南 CONTRIBUTING.md 全文展开,逐条解读其六条核心贡献规则与 PR 提交规范,并结合 doc/architecture.mddoc/contributing-apis.mdpackage.json 中的真实测试脚本与 src/backend/testUtil.ts 的测试环境实现,说明每条规则在代码层面如何落地验证。读完本文,你将知道在 Puter 后端提交一个合格 PR 需要满足哪些硬性条件、如何在本地跑通端到端测试,以及改动公共 API 时“文档、类型、测试同一 PR 提交”这一契约的完整工作流。

一、贡献总则:规则不强制,但让每个 PR 更容易合并

贡献指南开宗明义:

These rules aren't strictly enforced — but following them makes every PR easier.

即这些规则不会被机械卡死,但遵循它们能显著降低评审成本。对于后端新手,指南给出的第一个入口是 doc/architecture.md——它定义了 Puter 后端的分层、依赖注入方式与命名约定,是后续所有规则的“source of truth”。该架构文档明确指出,后端组织为一个层叠结构,每层只依赖其下层的模块,由入口类 PuterServersrc/backend/server.ts)按顺序实例化各层并通过构造函数向下传递实例:

HTTP 请求
  → Controllers   路由处理、输入校验、鉴权/限流门控、响应整形
  → Drivers       可选,/drivers/* 上的 RPC 处理器
  → Services      业务逻辑(假设调用方已鉴权)
  → Stores        持久化与领域模型封装
  → Clients       sql / redis / s3 / dynamodb / email 等协议适配
  → Config        config.*.json → IConfig

这个分层图决定了贡献规则里“Follow existing patterns”的具体含义:新代码必须放进正确的层,且不能跨层伸手(例如 Controller 不得直接调用 Client)。

二、规则一:Test it. Run it.——“能构建”不等于“能工作”

第一条规则要求:开 PR 之前,把受影响的代码路径端到端跑一遍;对新行为、新端点或 bug 修复要补测试;如果某处确实难以测试,要在 PR 描述里明说。

这条规则在 Puter 仓库中有明确的工程支撑——仓库自带一套完整的本地测试基础设施:

  • 后端测试npm run test:backend(见 package.jsonscripts 字段)等价于 setupExtensions && build:workerLib && vitest run --config src/backend/vitest.config.ts,即先装配 extensions 目录、构建 worker 运行库,再用 Vitest 执行 src/backend/vitest.config.ts 配置下的全部后端测试。
  • Postgres 变体npm run test:puterjs 之外还有 npm run test:backend:postgres,通过环境变量 PUTER_TEST_DB_ENGINE=postgres 切换数据库引擎,验证 MySQL/Postgres 双实现路径。
  • 三平台 SDK 测试npm run test:puterjs:node / :browser / :workerd 分别在 Node、浏览器、Cloudflare workerd 上跑同一套 puter-js API 测试套件(src/puter-js/tests/api/),贡献 API 文档明确要求“never write per-platform tests”,即一套用例三平台复用。

测试设施的核心是一个真实的内存服务器,而非大量 mock。src/backend/testUtil.ts 中导出的 setupPuterTestEnv 会:

  1. 分配一个真实临时端口(allocateEphemeralPort());
  2. 使用 puter.localhost 这样的真实域名而非 127.0.0.1——因为 API 路由以 api 子域做门控,而 *.localhost 在现代平台上解析到回环地址;
  3. 加载真实的 extensions 目录(客户端依赖 /whoami 等落在扩展中的端点);
  4. 预置 admin / user / otheruser 三个确定凭据的测试用户,测试代码可持预铸 token 或直接 POST /login 认证。

也就是说,“跑通受影响的代码路径”在 Puter 语境下有明确操作定义:起一个带真实端口、真实扩展、真实凭据的服务器,把改动过的路由真实调用一遍。doc/contributing-apis.md 的测试章节同样把“prefer the in-memory test server (setupPuterTestEnv) over mocking”写成了惯例。

三、规则二:Follow existing patterns——与既有代码同形

第二条规则要求新代码与仓库中相似代码保持同构,分层、接线、命名以 doc/architecture.md 为准;若认为某个既有模式有问题,应当在 PR 或讨论中提出,而不是“悄悄偏离”。

该规则还包含一条少见的类型化约定,值得单独展开:

在纯 JS 文件中,鼓励使用 JSDoc @type 注解借用 TypeScript 类型系统,共享形状用 @typedef;除非值是透传给上游层(由上游拥有其类型),否则不要把 API 表面标为 unknown 或未标注的 ...args

这与仓库的 lint 体系一致:eslint.config.jssrc/backend/**/*.tsextensions/**/*.ts 启用 @typescript-eslint 的 flat/recommended 规则集并开启类型感知解析(projectService: { defaultProject: project }),对 src/backendextensions 下的 JS 文件启用 js/recommended + Prettier,且 prettier/prettier 规则为 error——即格式与类型卫生是提交前可自动检查的部分。

架构文档中与“同形”直接相关的三条惯例可归纳为:

  • TypeScript 优先:新代码可行范围内用 TS;已有 JS 文件可以在改动时“顺手转换”;
  • 命名:变量/函数 camelCase,类与含类文件 PascalCase(如 AuthService.tsKVStoreDriver.ts);
  • 去重:两个服务需要同一逻辑时,提升到 util/helper,而不是让同层服务横向调用彼此——“services should not depend on other services for code reuse”。

新增路由或 driver 时的“同形”标准在 doc/contributing-apis.md 中有代码级定义:Controller 继承 PuterControllersrc/backend/controllers/types.ts),用 @Controller(prefix) 类装饰器加 @Get/@Post 方法装饰器声明路由(src/backend/core/http/decorators.ts);Driver 继承 PuterDriversrc/backend/drivers/types.ts)并打上 @Driver(interfaceName, opts) 装饰器。核心代码在 src/backend/controllers/index.tssrc/backend/drivers/index.ts 注册,扩展则通过 extension.registerController(...) / extension.registerDriver(...) 接入。

四、规则三:Don't expose system or user information——把信息暴露当作 diff 审查项

第三条规则把“不泄露系统与用户信息”写成了对 diff 的扫描动作:检查是否有散落的日志、调试路由、内部路径、密钥、token 或错误响应/响应体中的用户数据;拿不准时,返回更少;涉及鉴权、权限或数据导出的改动要在 PR 描述中标注。

这条规则与 API 贡献流程中的“第 7 步:Security pass”完全对应(doc/contributing-apis.md):开 PR 前扫描 diff——错误/日志中不泄露内部细节、响应不过度宽泛、鉴权门控齐全,并将任何 auth/permission/data-export 相关改动在 PR 描述中显式标出。

从源码结构看,这条规则背后是一整套默认拒绝(default-deny)的门控机制:

  • Controller 路由接受 RouteOptionssrc/backend/core/http/types.ts):requireAuthrequireUserActoradminOnlyallowedAppIdssubdomain 路由、每路由 rateLimit、body parser 等,且文档提示“auth flavors are subtle and default-deny — read the JSDoc on each field before picking”;
  • Driver 方法通过 @Driver 选项声明 per-method rateLimitconcurrent 在途上限、requireSubscriptionrequireReputationnoUserSession,由 /drivers/call 统一执行;
  • 预算类门控 requireCredits: true 会在 handler 执行前用 402 拒绝无预算的账户,实现位于 src/backend/services/metering/enforcement.ts

此外,指南指向 SECURITY.md:私有安全漏洞不走公开 issue/PR/讨论,而是发邮件至官方安全邮箱,报告需附可复现步骤、明确影响与受影响版本号。对贡献者而言,这条边界意味着:发现疑似漏洞时,正确动作是走私报渠道,而不是在 PR 里公开讨论。

五、规则四:AI-assisted code is fine — understood code is required

第四条规则对 AI 辅助开发给出明确立场:可以使用,但不允许提交你自己写不出、调试不了、答辩不来的代码。要求是:读 diff、运行它、在 review 中能够解释它。

这是当前开源仓库中少见的、把“AI 生成代码的可理解性”写成明文规则的做法。结合规则一可以推断其评审含义:AI 生成的 PR 不会因为是 AI 写的被拒,但“我没有跑过端到端测试”“我解释不了这段重试逻辑为什么这样写”会成为拒绝理由——因为规则一已经要求“run the affected code path end-to-end”。

六、规则五:Adding or changing APIs——文档、类型、测试同一 PR 移动

第五条规则针对“新增或修改公共 API”(endpoint、driver 方法或 puter-js 方法):必须遵循 doc/contributing-apis.md,并且向后兼容性、开发者文档、类型声明、测试四项必须在同一个 PR 中完成——“backward compatibility, developer docs, types, and tests all move in the same PR”。

该 API 文档的第一原则是“Rule zero: don't break callers”。其背景约束非常具体:puter.js 以无版本锁定的方式在线分发,所有现存应用在变更部署的瞬间就会拿到新行为,因此“每一个可观察行为(参数处理、响应字段、错误码、顺序)都要假设有依赖者”。由此导出一组硬约束:

  • 新增参数必须是可选的,默认值精确复现旧行为;
  • 永不重命名、挪用或删除既有参数、响应字段、错误码;
  • 可能让既有调用者意外的新行为必须放在 opt-in flag 后面;
  • “Docs, types, and tests move in the same PR as the behavior. A signature change with stale docs is a bug”;
  • 破坏性变更需要维护者显式签核 + 文档化的迁移路径 + 上线计划(先加新面、再弃旧面、很久以后才谈删除)。

新增 API 的七步流程(设计表面 → 后端 → puter.js → 类型 → 文档 → 测试 → 安全复查)中,对贡献者最有操作价值的几点:

  1. 设计先行:先定签名、选项、返回形状与错误用例,再找两三个最相似的既有 API 对齐约定;新参数/字段名用 camelCase;返回列表的 API 遵循分页约定(doc/pagination.mdlimit/cursor 进,{ items, cursor, total } 出)。
  2. Controller 还是 Driver:需要细粒度控制(URL 形状、HTTP 动词、每路由门控、流式响应)选 Controller;一组 RPC 方法实现某个命名接口(puter-kvstoreputer-chat-completion 等)且通用 driver 管线够用,则选 Driver。
  3. 类型即文档源:在实现处写 JSDoc(@param/@returns、每接受一种调用形式一个 @overload、新形状用 @typedef {Object} + @property),声明文件由 JSDoc 生成,无需手工同步;运行 npm run check:puterjs:types 验证发布面。src/puter-js/types/ 是 gitignore 的构建产物,永远不要手改;跨模块共享形状放 src/puter-js/src/lib/types.js
  4. 文档页:在 src/docs/src/<Area>/<method>.md 添加方法页(frontmatter、语法、参数、返回值、至少一个可运行示例)并更新 Area 总览页。

限额变更的同 PR 义务

第五条规则还包含一段针对限额的专门条款,原文值得完整保留:

The same rule applies to limits: rate limits, concurrency caps, quotas, and allowances are published in src/docs/src/rate-limits-and-quotas.md, and a PR that changes one of those numbers updates that page in the same PR. A limit nobody published is a limit developers only discover as a service failure.

即:改任何速率限制、并发上限、配额或额度数字的 PR,必须在同一 PR 里更新 src/docs/src/rate-limits-and-quotas.md。该页面是开发者可见限额的唯一权威来源,其中按 paid / free / anonymous 三档列出 AI、KV、文件系统、Workers 等各接口的滚动窗口限额与并发数(如 AI 每 10 秒请求数 200/30/20,KV get/set 每 10 秒 400/400/200),并区分三类独立机制——usage credit(402 insufficient_funds)、rate limit(429 too_many_requests)、storage quota(413 storage_limit_reached)。

工程含义:在 Puter,限额不是“服务端内部常量”,而是对外契约的一部分。从源码结构看,限额执行散落在 driver 的 @Driver 策略(per-method rateLimit/concurrent)与 controller 的 RouteOptions.rateLimit 上,而数字的“发布”动作落在文档页——贡献流程强制把两者绑定在同一变更单元,避免出现“代码里限了 300,文档写着 400”的漂移。

七、规则六:Boy Scout Rule——让仓库比你来时好 1%

第六条规则借用“童子军规则”:修掉你路过的拼写错误、死导入、缺失的测试、让你读了两遍的代码片段;但清理必须与变更成比例——不要借 bug 修复之名单独夹带重构。

这条规则与规则二(Follow existing patterns)形成互补:规则二要求你“不要悄悄偏离既有模式”,规则六要求“不要为了纠正你认为错误的模式而扩大变更面”。两者合力界定了 Puter PR 的变更半径:一次 PR 只解决一件事,顺手的清理限级于本次已经打开的文件与逻辑。

八、开 PR 的四个清单项

贡献指南对 PR 本身的描述虽短但完整,四条如下:

要求 含义
One thing per PR where possible 一个 PR 只做一件事,降低评审与回滚成本
Describe what and why; the diff shows how PR 描述回答动机,不重复 diff 已呈现的实现细节
Mention how you tested user-visible changes 与规则一呼应:说明端到端验证方式
Drafts welcome 草稿 PR 被欢迎,早期对齐成本低于后期打回

再叠加 doc/contributing-apis.md 的 “Definition of done” 清单(向后兼容、选对核心/扩展归属、puter.js 约定 { message, code } 错误形状、类型与运行时行为一致、文档页与示例、后端 + 三平台 SDK 测试 + 桌面 UI 的 Playwright e2e、diff 安全复查、你亲自端到端跑过),一个 API 级 PR 的完成标准在仓库内是双重可核对的。

九、本地实操:把规则落成可执行的检查步骤

把以上规则翻译成贡献者的本地工作流,仓库给出的命令面是 package.json 的 scripts 与 Node >= 24 要求(engines.node: ">=24.0.0"):

# 1. 启动开发环境(GUI + 后端)
npm start          # 等价于 node ./tools/start.mjs

# 2. 端到端验证受影响的代码路径(规则一)
npm run test:backend                    # 后端全量 Vitest
npm run test:puterjs:node               # puter-js API 套件(Node 平台)

# 3. 类型面检查(规则二 / API 流程第 4 步)
npm run check:puterjs:types             # 生成类型声明并做发布面类型检查

# 4. 完整构建(规则一:确认产物可构建)
npm run build

其中 test:backend 的完整命令是 npm run setupExtensions && npm run build:workerLib && vitest run --config src/backend/vitest.config.ts——注意它先执行 setupExtensionstools/extensionSetup.mjs)装配 extensions/ 目录,这与架构文档中“extensions 与核心层栈平行、可注册进任意层”的设计一致。贡献指南引用的“好扩展”范例(extensions/thumbnails.tsextensions/serverInfo.tsextensions/devWatcher.ts)都是可选功能干净地挂载其上;而 extensions/whoami.ts 被架构文档点名为反面教材——一个所有已认证客户端都依赖它的“承重扩展”。对贡献者而言这是一个可操作的判断测试:“删掉它 Puter 还能不能工作?” 能,就放 extensions/;不能,就进核心层。

规则速查表

规则 一句话要求 仓库内的可核对证据
1. Test it. Run it. PR 前端到端跑通受影响路径,新行为补测试 package.json scripts、src/backend/testUtil.ts
2. Follow existing patterns 分层/命名/接线同形;JS 用 JSDoc 类型化 doc/architecture.mdeslint.config.js
3. Don't expose information 扫描 diff 中的日志/路径/密钥/用户数据;拿不准就少返回 SECURITY.mdsrc/backend/core/http/types.ts 门控
4. AI code ok, understood code required 提交前读、跑、能在 review 中解释 CONTRIBUTING.md 原文
5. API changes move as one unit 兼容 + 文档 + 类型 + 测试同 PR;限额变更同 PR 更新限额页 doc/contributing-apis.mdsrc/docs/src/rate-limits-and-quotas.md
6. Boy Scout Rule 顺手 1% 改进,拒绝夹带重构 CONTRIBUTING.md 原文

十、小结

Puter 的贡献规范本质是一份“变更半径 + 证据义务”的约定:变更半径由规则二、六与“One thing per PR”划定;证据义务由规则一(端到端运行)、规则三(安全复查)、规则五(文档/类型/测试/限额同 PR 移动)承担。它不依赖 CI 卡点强迫执行,而是把 doc/architecture.md 的分层定义、doc/contributing-apis.md 的七步 API 流程、src/docs/src/rate-limits-and-quotas.md 的限额契约和 setupPuterTestEnv 这类可自证的工具链组合成一套可核对的标准——贡献者无论是否使用 AI 辅助,都能用同一组文件判断自己的 PR 是否达标。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384