关于 Filelayer
Filelayer 为需要管理用户或租户私有文件的 SaaS 应用提供授权中间层,复用应用现有数据库和存储。它集中处理访问判定、可撤销分享与审计,不提供数据库或对象存储。
主要功能
- 01统一文件访问授权
- 02支持分享、到期与撤销
- 03记录访问与拒绝审计
- 04原子下载次数限制
- 05兼容现有数据库和存储
适用场景
- 01管理用户私有文档和附件
- 02创建可撤销的文件分享链接
- 03按组织成员身份控制读取
- 04追查文件访问与拒绝记录
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @filelayer/coreDATABASE_URL密钥Your own PostgreSQL, where this library keeps file ownership, grants and the audit chain. Apply node_modules/@filelayer/core/schema.sql once before starting.FILELAYER_ASThe ONE subject this server speaks for, in your own id space. Fixed for the life of the process: no tool reads it as an argument and no tool can change it, which is the only reason exposing these tools to an agent is safe. Write it yourself or have your provisioning write it, never derive it from a conversation. Serving several people means starting several servers.FILELAYER_ORGThe organisation this server is scoped to, in your own id space.FILELAYER_DATA_DIRA local directory for the bytes. Needs no bucket and no IAM user. Set this OR the S3_* block: with neither, the server refuses to start rather than falling back to memory, which would work in a demo and lose the first real file.S3_ENDPOINTAn S3-compatible endpoint, for example https://<account>.r2.cloudflarestorage.com. Setting it makes S3_BUCKET, S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY required.S3_BUCKETThe bucket. It must be private: this library decides who may be GIVEN a URL and has no say in who the object store will answer. `npx -p @filelayer/core filelayer doctor` reports whether yours serves unauthenticated reads.S3_REGIONRegion, or `auto` for Cloudflare R2.S3_ACCESS_KEY_ID密钥Access key id for the bucket.S3_SECRET_ACCESS_KEY密钥Secret access key for the bucket.FILELAYER_MCP_AGENT_LABELHow this agent names itself in the audit trail, recorded on every event these tools produce. It is what lets the chain separate "the partner opened this document" from "the partner's assistant opened this document".FILELAYER_MCP_ALLOW_DESTRUCTIVESet to exactly `true` to register delete_file. Off by default because deleting is not recoverable and an agent that misreads a sentence deletes the wrong thing silently.FILELAYER_MCP_EXPOSE_AUDITSet to exactly `true` to register file_audit. Off by default because the trail is an organisation's whole history and a tool that returns it copies every file name, actor and denial into the model's context. Reading it is itself audited since 0.27.0, which was the previous reason; the default did not move.FILELAYER_MCP_RETURN_BYTESSet to exactly `true` to register read_file. Off by default because a document returned by a tool is a document copied into a model's context; file_info plus a share link is usually what was actually needed.FILELAYER_BASE_URLThe base of the share URLs this instance mints. Without it, share links come back without a URL.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Filelayer
SaaS 应用的文件层。 公共文件和私有文件,共用一套授权模型。→ filelayer.dev
你告诉 Filelayer 调用者是谁。Filelayer 决定他们可以对文件执行哪些操作,使用合适的标头提供文件字节,并写入审计事件。对于通过 Filelayer 进行的文件访问,所有决策都集中在一个地方:packages/core/src/authz.ts。因此,你无需在路由处理程序中编写所有权检查,无需处理预签名 URL 过期逻辑,也无需自行制定每条路由的访问规则。
正在寻找 3 到 5 个设计合作伙伴。 我们会帮助你完成集成,并在你集成期间随时提供支持,作为回报,希望你告诉我们它在哪些地方会出问题。 Alpha 阶段,以及这个词背后的具体情况,都集中在一个页面上: 你应该依赖它吗? · 它不做什么 · 通往 1.0 的路线图
// Public avatar
const { url } = await fl.files.put(bytes, { public: true });
// Private, user-owned
const { id } = await fl.files.put(bytes, { owner: 'user_123' });
const file = await fl.files.get(id, { as: 'user_123' });
// Multi-tenant, role-controlled
await fl.files.put(bytes, { org: 'acme', owner: 'user_123' });
// Shared with an expiry, a password and a download cap, and revocable
const share = await fl.shares.create(id, {
as: 'user_123', expiresIn: 3600, maxDownloads: 3, password: 'hunter2',
});
await fl.shares.revoke(share.grantId, { as: 'user_123' }); // this link, now
// Shared with a named user, and taken back. `unshare` is the one you want for
// a person: `create()` is not idempotent, so revoking a single grant id you
// happen to be holding can leave an earlier one live.
await fl.shares.create(id, { as: 'user_123', withUser: 'user_456' });
await fl.shares.unshare(id, { as: 'user_123', user: 'user_456' }); // all of it
从最符合你问题的那一行开始。 复杂度逐步递增:每个层级都会引入一个概念,而你无需为未使用的概念付出代价。→ docs/QUICKSTART.md
三层架构
your application fl.files.put / get / list, fl.shares, fl.orgs
you pass YOUR user id; nothing here knows a uuid
───────────────────────────────────────────────────────────────────────
Filelayer ONE authorization function answers every request
grants, roles, expiry, download caps, revocation
the audit event, written in the decision's own
transaction so the log cannot disagree with it
───────────────────────────────────────────────────────────────────────
your Postgres 9 tables. Filelayer owns the schema, you own the
your bucket database. Bytes go to S3, R2, B2 or a directory
behind one adapter; storage is replaceable
Filelayer 位于你现有的两个组件之间。它不附带数据库,也不自带存储,并且零运行时依赖。
它是授权中间件,不是行级安全。 它会在你的进程中,对通过它发起的调用作出决策。该架构还会拒绝所有写入者(包括 psql 会话)授予的跨租户授权、跨项目身份,以及会放大权限的委托——这些由约束和触发器实现。但 schema.sql 中没有 RLS 策略,直接对这些表执行 SELECT 不会受到任何过滤。Filelayer 可以与数据库级强制机制配合使用,但不能取而代之。
适用场景与非适用场景
值得承担额外开销的场景: 文件是私有的,属于特定个人或租户,且其权限会随时间变化。文档、合同、附件、导出文件,以及任何你可能之后想要撤销的分享链接。
不值得承担额外开销的场景:
| 你的需求 | 改用 | 原因 |
|---|---|---|
| CDN 级流量的公共图片 | 由 CDN 支持的存储桶 | 默认交付方式会代理每个字节。重定向交付(见下文)可以移除代理,但需选择启用,且适用范围有限。 |
| 大规模视频或音频 | CDN / 媒体服务 | 支持定位播放——随附的路由会以 206 响应 Range。但默认交付方式会通过你的服务器代理每个字节,这条路径上没有 CDN。 |
| 开发期间,浏览器 → 存储直接上传,且服务器不经过数据路径 | 正常的存储桶 | FsStorage 会生成凭证,因此客户端代码保持一致,但它会亲自接收数据,并明确说明:via: 'server'。只有 S3 和 R2 能让你的进程避开数据路径。 |
| 可续传或分片的直接上传 | 直接使用预签名 S3 / tus | 一次已签名的 PUT,一个对象。无法续传。 |
| 缩略图、转换、格式协商 | Cloudinary / imgix | 我们不提供这些功能。 |
与你原本会采用的两种方案相比
| 手工编写预签名 URL | Supabase Storage | Filelayer | |
|---|---|---|---|
| 撤销某人已经持有的链接 | 不行。 签名在过期前始终有效;你只能设置较短的 TTL,或者自行代理字节流 | 不行。 签名 URL 使用内部密钥签名,无法撤销 | 可以,在下一次请求时通过委托链逐级生效 |
| “此组织中的所有人都可以读取” | 需要自行编写关联查询,并在每次加入和离开时重新计算 | 可通过 RLS 策略表达 | 一条授权记录;在请求时解析成员资格 |
| 谁访问过,包括被拒绝的访问 | 取决于你编写了什么 | 不是产品功能;Postgres 日志不是访问日志 | 每个租户采用哈希链,并在决策所在的事务中写入 |
| 下载上限 | 自行编写计数器,还要处理其中的竞争条件 | 无 | 原子操作,在传输字节前预留额度 |
| 私有路径上的 CDN | 有,这正是无法撤销链接的原因 | 有,同样需要权衡 | 没有——这两个事实本就是同一个事实 |
| 大量公开图片 | 可以 | 可以 | 不行。使用置于 CDN 后面的存储桶 |
| 公开头像所需代码行数 | ~10 | 10 | 16 |
最后两项是我们的短板,而且差距不小。我们在
ARCHITECTURE-PROGRESSIVE.md
§5 中公布了完整对比,包括我们落后的所有场景。简而言之:对于公开头像,Supabase 需要 10 行代码,而 Filelayer 需要
16 行。 如果你只需要处理头像,就用 Supabase。
五项安全属性
这就是产品的核心。每项属性都由架构或授权引擎强制执行,而不是依赖约定,并且每项都有以其命名的测试。
| 属性 | 实际含义 | |
|---|---|---|
| P1 | 默认拒绝 | 架构中没有任何 public 布尔值。公开交付通过明确、可撤销、可审计的授权记录来实现——没有可能因疏忽而一直开启的标记。这是架构的属性,而非对象存储的属性:Filelayer 无法将你的存储桶设为私有,而公开存储桶会绕过本页所述的一切。 这是它无法替你完成的唯一配置步骤——QUICKSTART §7。 |
| P2 | 不存在环境权限 | 知道对象键、URL 或文件 ID,不会因此获得任何权限。存储位置绝不会作为决策依据。 |
| P3 | 从结构上杜绝写入跨租户授权 | 授权记录的 org_id 必须与其文件的 org_id 相同,并由复合外键 FOREIGN KEY (file_id, org_id) REFERENCES file (id, org_id) 强制执行。任何写入者——包括迁移或 psql 会话——都无法创建指向其他租户文件的授权记录。这是写入端的完整性约束,而非读取过滤:它让这类记录在结构上无法表示。读取由引擎限定范围,而非由数据库限定。 |
| P4 | URL 的有效期不会超过其权限 | 每个签名 URL 都嵌入授权 ID,并在每次请求时沿整条委派链重新验证。即使 URL 仍有效,撤销也优先。 |
| P5 | 审计每项决策,包括拒绝的决策 | 每个租户的审计记录均采用哈希链。无法归属到某个租户的探测会写入系统链,而不会被丢弃。日志使用你的标识符回答问题——marco、contract.pdf、acme——并同时包含内部标识符,因此无需编写 SQL 就能查明“谁访问了这个文件?”。 |
还有三项属性由架构强制执行,并记录在
packages/core/SEMANTICS.md 中:原子下载计数器(P6)、将删除实现为存活性谓词而非级联操作(P7),以及每个项目各自的标识符命名空间(P8)。
采用前有两点值得了解:
- 撤销确实有效。
fl.files.unpublish(id)会让已经打印、编入索引并粘贴到支持工单中的 URL 从下一次请求起失效。无需删除对象、轮换密钥或清除缓存。Supabase 的getPublicUrl()只是离线字符串拼接,因此没有可以做出该决策的请求;删除对象是唯一的撤回方式。 - 这意味着字节传输路径需要由你承担。 P4 正是默认直接提供字节数据,而非发放预签名 URL 的原因,也正是默认路径前面没有 CDN 的原因。这两点其实是同一件事。重定向交付会以有限的撤销延迟为代价换取 CDN,且需选择启用;详见
packages/core/SEMANTICS.md。
这些依据是什么
在这里说一次,因为其中的每个数字都经过门禁检查;如果公开接口与实际运行结果不一致,构建就会失败:
| 测试套件 | 736 项测试,每次提交都会运行,全部针对真实的 PostgreSQL |
| 并发 | 8 项测试,在真实的 PostgreSQL 上使用两个后端运行——刻意安排竞争条件,而非仅靠推理。其中 3 项带有对照测试,会移除保护措施并断言确实会出现不良结果 |
| 对抗测试 | 27 次攻击,0 次突破。 还会在已知存在漏洞的较早版本上运行,该版本出现 3 次突破。只会通过的测试套件无法证明自身的可靠性 |
| 实时对象存储 | 每次提交都会针对实时 Cloudflare R2 和实时 AWS S3 各运行 12 项测试,夜间运行时还会各增加第 13 项测试:将一个 11 MB 的分段上传文件重新组装,并逐字节验证。R2 兼容 S3,但并非 S3,因此两者都会测试 |
| 架构迁移 | 将每个迁移文件应用于上一个版本的 schema.sql,并在真实的 PostgreSQL 上将结果与下一个版本进行比较 |
| 已发布的制品 | 将压缩包安装到空目录中,并通过完整生命周期进行测试;通过 HTTP 启动起始项目并执行 25 项检查;验证压缩包中的每个相对路径都能从压缩包内部解析 |
这些测试都无法衡量负载,也无法衡量任何人的生产环境。 完整表格(包括数值为 0 的行)见 信任页面。
会让人打消念头的五点
完整列表见 LIMITATIONS.md——共十四项,每项都附有原因,并随 npm tarball 一同发布,因此安装后本地就有。这五点如果适用于你,就应该让你关掉这个页面:
-
私有交付路径不使用 CDN。 数据会经由你的应用代理,而经过身份验证的响应会携带
Cache-Control: no-store。这不是疏忽,而是 P4:可以撤销的 URL,就必须先询问。可选的重定向模式以最长 300 秒的有限撤销窗口为代价,换取可缓存性。如果你的工作负载是大量公开图片,请使用置于 CDN 后方的存储桶,等你需要撤销 URL 时再回来。 -
授权 ID 不代表某个人的访问权限,而且默认情况下,每次调用
shares.create()仍会插入一行记录。 两次带有expiresIn的调用代表两个合法的不同时间窗口,因此引擎无法自行去重。传入idempotencyKey,重试时就会返回已存在的授权;如果不传,双击分享按钮就会让一个读者获得两份授权。现在,revoke()会返回subjectCanStillRead,因此撤销一份授权却没有移除某人的访问权限,会直接显示在响应中,而不是等到之后审计时才发现。使用shares.unshare(fileId, { as, user })移除指定人员,并注意目前仍未限制单个主体可以持有多少份有效授权。这带来的成本已不再像以前那样成为问题。在
0.17.0之前,每份授权都会让读取操作执行一次递归链遍历:五份授权时为 4.3 ms,十万份时为 5 911 ms。短路逻辑已移至 SQL,性能断崖也随之消失——在同一文件上,二十万份集中授权时为 30.4 ms,是所有者读取耗时的 1.13 倍,测量结果见benchmark/load/RESULTS.mdH4b。剩下的是正确性陷阱,而非性能断崖。 -
不支持可恢复上传或分段上传。 一个签名 PUT,一个对象。大型上传失败后必须从头开始,而
fl.files.put()会将整个请求体保存在内存中——只有核心的fl.upload()接受流,并且两者都不设定大小上限,因此需要由你自行限制。 -
组织管理员和所有者可以读取
private文件。 这是有意为之:如果负责留存和法律保全的人无法查看自己保管的内容,这些措施就毫无意义。但这是一项策略决定;如果你需要排除运营方,就需要信封加密,而我们目前不提供此功能。 -
目前仍处于 Alpha 阶段,架构已更改十二次。 任何
0.x→0.(x+1)都可能破坏 API、架构,或两者兼有。每项破坏性更改都在MIGRATIONS.md中有编号条目,并在migrations/下提供可运行的文件;CI 会将其应用到上一版本的架构,并检查是否生成下一版本——但没有 LTS 分支,不提供回移植,也没有独立的安全审查。
安装
npm install @filelayer/core
需要 Node ≥ 22.18。该软件包附带编译后的 JavaScript 和类型声明,位于 dist/ 中;tarball 中也包含 TypeScript 源码和完整测试套件,因此本页的每项说明都可以通过你安装的内容进行核查。
安装后没有任何运行时依赖。 Filelayer 会连接到你的 Postgres 和你的存储桶,因此不会随包附带它们。下面的临时实例是唯一例外:quickstart() 运行于内嵌的 WebAssembly Postgres,并将其声明为可选对等依赖,因此它绝不会进入生产环境的 node_modules。如果你需要临时实例,就安装它;否则跳过即可。createTestDb() 会明确告诉你是否需要安装,以及需要安装哪个包:
npm install --save-dev "@electric-sql/pglite@^0.3.11"
安装 PGlite 时请使用该版本约束。 Filelayer 支持 0.3.x 系列,这也是 peerDependencies 声明的版本,也是测试套件运行所针对的版本。不支持 0.5.x:我们使用 0.5.8 运行完整测试套件,但未能通过,因此没有扩大版本范围。npm 上的 PGlite latest 是 0.5.x 版本,省略版本约束可能会安装超出支持范围的版本,随后 npm 会因 ERESOLVE 拒绝整个依赖树。引号是给 shell 用的,不是给 npm 用的:在启用了 extendedglob 的 zsh 中,^ 是 glob 运算符;在 cmd.exe 中,它是转义字符。
import { Filelayer } from '@filelayer/core';
const fl2 = await Filelayer.quickstart(); // PGlite + in-memory bytes
const { url: avatarUrl } = await fl2.files.put(bytes, { public: true });
quickstart() 是临时的:进程退出时,一切都会丢失。
提供字节数据
授权读取和响应请求不是一回事。下面两个处理器负责后者,具体使用哪一个取决于你的运行时,而非个人偏好:
deliveryHandler(fl, opts?) | 返回 (req: IncomingMessage, res: ServerResponse) => Promise<void>。适用于 Node 自带的 HTTP 服务,以及会将这些对象交给你的 Express、Fastify 和 Koa。 |
deliveryFetch(fl, opts?) | 返回 (req: Request) => Promise<Response | null>。适用于 Next.js App Router、Hono、Cloudflare Workers、Deno 和 Bun;它们会将 Request 交给你,并需要你返回 Response。 |
两者对应相同的两个路由,行为也相同。对于不归它处理的请求,deliveryFetch 会返回 null,因此兜底路由可以将请求交给应用的其余部分,而不是直接吞掉请求。
我们遇到的集成失败中,最常见的就是挂载了错误的处理器。 在 App Router 路由中使用 node:http 处理器,会导出一个形状错误的函数;症状不是挂载点出现类型错误,而是请求始终没有响应。
import { deliveryFetch } from '@filelayer/core';
const serve = deliveryFetch(fl, {
principal: async () => ({ as: await currentUserId() }),
});
export async function GET(req: Request) {
return (await serve(req)) ?? new Response('Not found', { status: 404 });
}
两者都会解析 Range,这比听起来更重要:在范围请求得到响应之前,<video> 元素不会允许用户拖动播放进度;PDF 阅读器则会先请求文件末尾。按照 RFC 9110 的要求,无效范围请求会以 200 响应并返回完整内容;只有解析成功但无法满足的范围请求才会以 416 响应,并附上 Content-Range: bytes */<size>。普通 200 响应也会包含 Accept-Ranges: bytes,因为客户端正是通过它得知是否可以跳转播放位置。
挂载任一处理器前,有两个默认行为需要了解。disposition 默认值为 attachment,这会触发下载而非播放,因此媒体路由需要设置 disposition: 'inline'。此外,默认不会重定向到预签名 URL,而是代理文件;重定向需要显式启用,因为在 URL 有效期间,撤销操作会转移到你的进程之外。它要求存储适配器支持签名,并且在配置中明确启用之前,会以 redirect_not_acknowledged 拒绝请求。
docs/guides/serving-private-files.md 提供了对这两种方式的实测比较。
其他内容,一项一页
对于大多数应用来说,这就是全部集成流程:安装,运行 quickstart() 看看效果,然后接入真正的数据库和以下某个处理程序。其余内容各占一页,而且每一页都包含在 npm 压缩包中,因此安装后文件都会在本地。
| 选择哪个存储适配器,以及如何将存储桶设为私有 | QUICKSTART §7 |
在终端检查部署(filelayer doctor) | docs/CLI.md |
| Agent Skill,供 Agent 执行集成时使用 | docs/AGENT-SKILL.md |
| 浏览器 → 存储桶上传,服务器不参与传输 | docs/DIRECT-UPLOAD.md |
| MCP 服务,让 Agent 能够操作文件 | docs/MCP.md |
29 个错误代码,以及 FilelayerError | QUICKSTART §9 · ERRORS.md |
验证你自己的路由是否有效,而不是我们的(auditIntegration()) | QUICKSTART §8 |
| 查看审计日志,设定时间范围且无需存储适配器 | QUICKSTART §4 |
| 从克隆的仓库自行运行测试套件 | CONTRIBUTING · 从安装包开始 |
这些指南也会呈现在 filelayer.dev/guides 上,内容与仓库中的 Markdown 相同,阅读起来也比仓库文件更方便。
版本管理
尚未达到 1.0。版本号格式为 0.MINOR.PATCH,承诺范围刻意保持狭窄:
0.x→0.(x+1)可能会破坏 API、架构,或两者兼有。所有破坏性变更都记录在packages/core/CHANGELOG.md中,所有架构变更都在packages/core/MIGRATIONS.md中提供迁移方案。0.x.y→0.x.(y+1)仅包含增补或修复。不会有需要采取行动的架构变更,也不会有签名变更。- 1.0 之前没有长期支持分支,也不会进行回移。
架构已变更十二次。 MIGRATIONS.md 有十五个带编号的条目,其中十个包含延续至今的 SQL;另外两次架构变更早于仓库中最早的标签,因此无法根据更早的架构进行验证。规划升级时,请以该文件为依据,而不是以本段为依据。本段中的所有数字都由 check:versions 根据 migrations/manifest.json 生成;如果其中任何数字有误,该检查就会导致构建失败——因为这些数字上次由人工更正是在 0.15.0,到了 0.33.0 又再次出现偏差。
数据库的当前版本可以直接从数据库中读取。 schemaStatus(db) 会报告数据库当前所处版本、本次构建的预期版本,以及 migrations/ 下尚未执行的文件。它不会执行任何 DDL:迁移运行程序由你的应用负责,这正是 MIGRATIONS.md §2 采取并一直坚持的做法。
目录结构
| 路径 | 内容 |
|---|---|
packages/core/schema.sql | 数据模型。每项安全属性都在实施该属性的约束处附有注释。如果你正在审查我们的项目,请先阅读此文件。 |
packages/core/src/authz.ts | 授权引擎。两个函数,一个决策核心。 |
packages/core/src/simple.ts | 分层 API(files、orgs、shares)。不包含授权逻辑,只是一个外观层。 |
packages/core/src/delivery.ts | 字节传输。负责响应头,因此你不会弄错它们。 |
packages/core/test/ | 属性测试套件。包含在 tarball 中。 |
packages/core/dev/ | 诊断脚本。不发布。 |
examples/tier1-avatar … tier3-org-roles | 每个层级各有一个可运行示例 |
examples/vault | 一个通过 HTTP 提供服务的完整 B2B 文档工作区 |
examples/starter | 可部署的服务端:使用你自己的 Postgres,挂载 FsStorage 和路由辅助工具 |
examples/nextjs | App Router:在 catch-all 路由中使用 deliveryFetch,并支持上传、共享和撤销 |
examples/mcp | 可启动的 MCP 服务,通过自身的 verify.mjs 使用真实 stdio 驱动 |
文档
docs/QUICKSTART.md— 从安装到第一个文件,再到高级功能LIMITATIONS.md— 完整列出本项目不做什么:共 14 项,并说明原因ROADMAP.md— 外部评估者认为会改变其评价的 9 件事,其中 8 件已完成,以及我们明确不会提供的功能docs/guides/— 解答人们实际会问的 4 个问题;无论你是否使用这个库,这些内容都很有用;其中的代码会在 CI 中执行packages/core/SEMANTICS.md— 所有边界情况的确切行为:存续性、委派、删除和审计链packages/core/MIGRATIONS.md— 如何交付架构变更,以及 1.0 之前的兼容性承诺packages/core/CHANGELOG.md— 真实的历史记录,包括我们发现的自身安全缺陷ARCHITECTURE-PROGRESSIVE.md— 层级模型、与替代方案的代码行数对比,以及我们的不足之处architecture/TIER5-DESIGN-NOTE.md— 大文件、流式传输、范围请求和 CDNllms.txt— 同一份索引,专为实现集成的 Agent 编写openapi.yaml— 本库提供的两个 HTTP 路由,采用 OpenAPI 3.1 格式
上面所有链接都指向 GitHub,而安装了本包的使用者可能没有网络或浏览器。因此,npm 包也会在磁盘上的 node_modules/@filelayer/core/ 下包含相同的文件,其中三个文件可通过子路径导入——
@filelayer/core/llms.txt、@filelayer/core/openapi.json 和
@filelayer/core/schema.sql——这样读者就不必猜测 node_modules 的目录结构了。
许可证
Apache-2.0,包含明确的专利授权条款和专利报复终止条款。你自己的代码无需遵守 copyleft 义务;保留声明并注明重大变更即可。LICENSE 和 NOTICE 都随 tarball 一并发布,因此授权随制品一起提供。
SPDX-License-Identifier: Apache-2.0
Copyright 2026 Technology Pro Bono S.L.
依赖项。 无运行时依赖项,而且 CI 中有一项任务会在出现运行时依赖时使构建失败。第三方软件包全都是 devDependencies:
@electric-sql/pglite(Apache-2.0),也是可选的对等依赖项,也是 quickstart() 的运行基础;此外还有 pg 和 embedded-postgres,用于需要两个真实后端的竞争测试。生产环境安装不包含这些依赖项,本仓库中也没有复制或嵌入任何第三方代码。
贡献、安全与行为规范
CONTRIBUTING.md— 如何进行设置、npm run verify涵盖的内容,以及拉取请求需要满足的要求。SECURITY.md— 请私下报告漏洞,切勿作为公开议题提交。文件还包含我们的披露时间表以及适用和不适用范围。CODE_OF_CONDUCT.md— Contributor Covenant 2.1。
Bug、问题以及“本文档有误”之类的报告,请提交到 GitHub Issues。如果产品的行为与本 README 的描述不符,这类报告对我们最有价值;迄今收到的这类报告都在当天得到了解决。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。