把 MeetingBaaS 指向你自己的对象存储,之后每一份录像、转写文本、音频分块和 log 都会写到那里。两套凭据、一次真实的访问校验,以及关于你要付出什么代价的坦诚说明。

「录像存在哪里?」是让企业客户的合作卡住的那个问题。对银行、医院,或者任何有数据驻留要求的机构来说,「在我们的 bucket 里,已加密」不是一个能拿去交给合规团队的答案。
所以现在你可以给我们一个 bucket。从那一刻起,你的 bot 产出的每一份产物,包括录像、音频分块、转写文本、截图和 log,都会写入你自己拥有的存储,用的是你签发、也能随时吊销的凭据。
自带存储(Bring Your Own Storage)适用于所有套餐,包括按量计费套餐,无需升级存储功能。
GET /v2/storage-config your current configuration, 404 if unset
PUT /v2/storage-config set or replace it
POST /v2/storage-config/test re-run the access check
DELETE /v2/storage-config back to MeetingBaaS storage或者在 dashboard 中选择「设置 → 存储」,背后使用相同的四个 handler,通过 session 认证。
两组凭证,按物理归属分离
这是我最坚持的设计决策,让我先从这里讲起。
你需要提供两对访问密钥,而不是一对:
| 权限 | 存放位置 | |
|---|---|---|
| ingest | PutObject、PutObjectTagging、AbortMultipartUpload | 传给录制 bot,会离开我们的网络。 |
| service | GetObject、ListBucket、PutObject、DeleteObject | 仅限我们的 API 服务器使用,永不离开。 |
ingest 密钥是唯一会接触你会议的凭证。它被传入一个在通话中运行无头浏览器的 pod,这是整个系统中凭证所处最暴露的位置。它可以写入对象,但无法读取、无法列举已有内容,也无法删除任何东西。即使 pod 被攻破,攻击者最多只能向某个前缀写入垃圾数据,仅此而已。
service 密钥负责其余所有操作:通过签名 URL 提供录像、转写结果到达时写入、以及在保留期到期时删除文件。

有一点需要说清楚。service 密钥并非只读,如果这样描述,迟早你会发现这是谎言。它既能写入也能删除,因为 API 确实需要执行这两种操作。分离的意义在于:处于高风险位置的凭证权限更弱。这真实地缩小了爆炸半径,但与「最小权限无处不在」不是同一回事。我宁愿明确告诉你得到的是哪一种保障。
配置步骤
{
"endpoint": "https://s3.eu-west-3.amazonaws.com",
"region": "eu-west-3",
"force_path_style": false,
"artifacts_bucket": "acme-meeting-artifacts",
"audio_chunks_bucket": "acme-meeting-audio-chunks",
"logs_bucket": "acme-meeting-logs",
"allow_transient_spill": false,
"ingest_access_key_id": "AKIAIOSFODNN7EXAMPLE",
"ingest_secret_access_key": "...",
"service_access_key_id": "AKIAI44QH8DHBEXAMPLE",
"service_secret_access_key": "..."
}三个存储桶名称可以指向同一个存储桶。对象键无论如何都会以 bot id 作为前缀,因此不会发生冲突。force_path_style 是 MinIO、Ceph 以及大多数自托管网关所需的参数;AWS 和 Scaleway 无需设置此项。
endpoint 必须是公开的 HTTPS 地址,不得包含凭证或 query string。存储桶应设为私有,因为文件通过短期签名 URL 提供访问,但它们必须能从互联网访问:转写服务提供商会直接从签名 URL 拉取音频,而不是通过我们代理。
在操作存储桶时,请为 dashboard 的源地址开放对文件存储桶的 CORS 权限。dashboard 会直接拉取文件内容并读取转写 JSON,而非直接链接到它们,因此这些请求是对你 endpoint 的跨域读取。若没有 CORS 规则,浏览器会在任何内容渲染之前丢弃 response。你只需在 dashboard 源地址上配置 GET 和 HEAD 即可。需要注意的是,PutBucketCors 的权限级别高于 service 密钥持有的对象权限,因此需要所有者级别的凭证——service 密钥返回 Forbidden 是权限范围生效的正常表现,而非 bug。
读取配置时会返回两个访问密钥 ID,让你知道当前使用的是哪组凭证,但不会返回任何密钥。永远不会。替换配置需要重新输入两个密钥,因为我们无法向你展示已存储的内容。
访问检查,以及为何这不是走过场
PUT 会在存储配置之前对其进行验证,而不是简单地调用 ListBuckets 就算完成。
ingest 密钥会向每个独立存储桶写入一个标记对象,然后 service 密钥读取、写入并删除该对象。
每个操作都由在生产环境中实际执行该操作的凭证来完成,因此探测失败的方式与生产环境一致。这四个操作缺一不可:ingest 上传对应录像的到达,service 上传对应转写的写入,读取对应文件的提供,删除对应保留期到期时的清理。
真正有价值的地方在于将写入与读取分离。标记由一个凭证写入,必须由另一个凭证找到。这能捕捉到最容易被忽视的故障:两对密钥各自完全有效,但指向不同的存储桶或不同的账户。世界上任何单凭证健康检查都会通过这种配置,然后你就会丢失一条录像。
若没有此检查,存储桶名称拼写错误的第一个信号,将是在真实会议结束时、pod 本地磁盘已清空后的上传失败。当检查失败时,我们会原样展示服务提供商的错误信息,而不是通用错误,因为「ingest 密钥无法向文件存储桶写入」本身就是运行此检查的全部价值。
你可以随时使用 POST /v2/storage-config/test 重新运行检查,在轮换密钥或修改存储桶策略后尤其推荐这样做。
有一件事它无法检测:CORS。探测从我们的服务器发起,CORS 在此不适用,因此存储桶可能通过以上所有操作验证,却仍然拒绝向浏览器返回录像。这个缺口需要我们来弥补,在此之前,允许 dashboard 源地址的那条说明,就是绿色检查与 dashboard 显示空白之间的唯一屏障。
其余一切遵循的不变量
bot 始终解析到它被写入时所对应的存储,而不是团队当前配置的存储。
当 bot 被调度时,当时生效的存储配置会被记录到 bot 记录中。该 bot 的所有读取、所有签名 URL 生成以及所有删除操作,都通过这条记录来解析,而不是通过团队配置。
这听起来像实现细节,但它正是这个功能可以安全启用的原因。以下四个结论直接由此推导而来:
**修改配置会插入一条新记录并禁用旧记录。**配置永远不会原地更新。原地修改会静默地将所有现有 bot 的文件重定向到一个它们并不存在的存储桶。
**DELETE /v2/storage-config 只是翻转一个标志位。**两端都不会有任何数据被删除。硬删除会剥夺旧 bot 仍然需要的凭证,使你账户中的所有录像成为孤儿。
**解析会刻意忽略配置是否已启用。**禁用配置会阻止新文件写入你的存储桶,但不得使已有文件成为孤儿。
**找不到已记录的配置时,系统会抛出错误,而不是回退到我们的存储桶。**回退会将你的文件报告为缺失,而在保留期删除时还会静默地保留你的数据,同时报告成功。响亮的失败才是正确的行为。
实际使用中:随时可以设置、修改或移除配置。旧的录像在整个过程中持续可用。唯一会导致旧文件不可读的操作,是你在自己这侧撤销我们的访问权限,这是你的决定,效果与你预期的完全一致。
你实际做出的权衡
对于使用自有存储的团队,allow_transient_spill 默认为 false,与我们平台的默认值相反。以下是完整的含义说明,因为这是这个功能唯一需要你付出代价的地方。
通常情况下,当上传失败时,我们会将文件镜像到自己的网络卷,由调和任务稍后推送。这条路径会复制完整的会议内容:视频、音频、说话人分离输出以及所有分片——全部复制到我们的基础设施和账户中。
这恰恰是数据驻留客户极力避免的情况。更糟的是,数据驻留客户触发这条路径的概率比平台客户更高,因为他们的存储桶不在我们的网络中,往往还在完全不同的服务提供商处。
因此,启用 allow_transient_spill: false 后,回退机制会在所有 bot 类型和消费者上全部关闭。耗尽重试次数的上传会被报告为失败,文件将会丢失。
这就是权衡:用持久性换取数据驻留。我们通过给 bot 的上传客户端提供六次重试机会(而非 SDK 默认的三次)来部分弥补这一损失,因此短暂波动不会导致录像丢失,但你的存储桶持续中断时仍然会。
如果你更希望保留录像而不是保持数据驻留保证,请将其设置为 true。翻转此选项时,dashboard 会显示一条警告,明确说明你放弃了什么,因为这不应该是一个随手勾选的复选框。
如果录像丢失,且你的存储桶曾发生中断,这是首要排查项。
不涵盖的内容
以下四点,预先说明,避免事后发现。
**已录制的内容不会被迁移。**不存在迁移机制。每个 bot 仍然解析到其被写入的位置,这使得启用此功能是安全的,但也意味着历史数据仍留在我们的存储桶中。
**不支持按存储桶配置前缀。**键的格式为 <bot_uuid>/…,固定不变。
单独使用此功能无法实现完整的数据驻留。Bot pod 在会议进行期间仍会将录像保存在本地磁盘上。此功能解决的是录像最终落地于共享 MeetingBaaS 基础设施的那条路径。
**Dashboard 资产仍存储在我们的存储桶中。**Logo、用户头像和支持附件不属于会议数据,不在覆盖范围内。
回滚
DELETE /v2/storage-config,随时可安全执行。新 bot 会立即使用我们的存储。现有 bot 继续解析到你的存储桶,正常运行。
- 六家转写服务商,一套 API —— 把 bucket 区域和服务商区域配成一对,实现端到端的数据驻留
- 自托管 Meeting BaaS —— 如果只拥有 bucket 还不够
- Meeting BaaS API 参考文档
如果你正在满足数据驻留要求,并想了解哪些组件涉及哪些数据,请联系我们。我们会给你与本文相同的答案。