# 附加信息

每个版本上按命名空间存放的 JSON 元数据。内置的结构、自定义命名空间，以及把一个命名空间交给一个工具独占。

附加信息（sidecar）是挂在文件某一个**版本**上、属于某个**命名空间**的一份 JSON。一个视频版本可以同时带着媒体信息、转写、AI 标签、生成它的提示词、大家的评分和你的质检结果，各在各的命名空间里，各由负责这件事的人或工具来写。

搜索读的是附加信息，应用里“信息”面板显示的是它，[版本比较](/docs/files#版本)比的也是它，AI agent 通过 MCP 了解一个文件时读它，而不用下载文件。

## 读和写

| 要做的事 | 调用 |
|---|---|
| 一个版本的全部附加信息 | `GET /v1/spaces/{sid}/versions/{vid}/sidecars` |
| 某个命名空间 | `GET /v1/spaces/{sid}/versions/{vid}/sidecars/{ns}` |
| 写入（整份替换这个命名空间） | `PUT /v1/spaces/{sid}/versions/{vid}/sidecars/{ns}`，带 `{"doc": {…}}` |
| 删除 | `DELETE /v1/spaces/{sid}/versions/{vid}/sidecars/{ns}` |

```sh tab=curl
curl -X PUT "https://side.example.com/v1/spaces/$SPACE/versions/$VERSION/sidecars/x-acme.qc" \
  -H "Authorization: Bearer $SIDESPACE_TOKEN" -H "Content-Type: application/json" \
  -d '{"doc": {"passed": false, "issues": [{"at_s": 12.4, "kind": "dropped_frame"}]}, "producer": {"name": "acme-qc", "version": "1.4"}}'
```

```ts tab=TypeScript
await side.putSidecar("Film A", "/Ep01/cut.mp4", "x-acme.qc", { passed: false, issues: [{ at_s: 12.4, kind: "dropped_frame" }] });
const tags = await side.getSidecar("Film A", "/Ep01/cut.mp4", "ai.tags");
```

```python tab=Python
side.put_sidecar("Film A", "/Ep01/cut.mp4", "x-acme.qc", {"passed": False, "issues": [{"at_s": 12.4, "kind": "dropped_frame"}]})
tags = side.get_sidecar("Film A", "/Ep01/cut.mp4", "ai.tags")
```

```sh tab=CLI
sidespace sidecar put "Film A" Ep01/cut.mp4 x-acme.qc '{"passed": false}'
sidespace sidecar get "Film A" Ep01/cut.mp4 ai.tags
```

写入需要编辑者权限，令牌需要 `sidecars:write`，或者只针对一个命名空间的 `sidecars:write:<ns>`（一组用 `sidecars:write:x-acme.*`）。`producer` 记录是哪个工具写的，应用里会显示。

## 内置命名空间

内置命名空间写入时按 JSON Schema 校验（在仓库的 `proto/sidecar/` 里），所以所有工具都能放心依赖它们的结构。

| 命名空间 | 谁写 | 内容 |
|---|---|---|
| `core` | 服务器 | `content_hash`、`size`、`mime`、`author_id`、`device_id`、`client_mtime`、`created_at` |
| `media` | 服务器（ffprobe） | `duration_s`、`container`、`bitrate`、`width`、`height`（按显示方向）、`fps`、`video_codec`、`audio_codec`、`audio_channels`、`creation_time` |
| `transcript` | `side.transcribe` 或你的工具 | `text`、`language`、`duration`、`segments`（`start`、`end`、`text`、`speaker`）、`model`、`provider` |
| `ai.tags` | `side.tags` 或你的工具 | `description`、`tags`、`shot_type`、`setting`、`mood`、`people`、`text_on_screen`、`frames`、`model` |
| `ai.generation` | 生成素材的工具 | `tool`（必填）、`model`、`provider`、`prompt`、`negative_prompt`、`seed`、`parameters`、`inputs`、`job_id`、`cost`、`regenerate_url` |
| `user` | 应用里的成员 | `tags`、`rating`、`label`、`note`、`status`（草稿、审阅中、已通过…） |
| `derived` | 生成新文件的动作 | `from`（`node_id`、`version_id`、`path`）、`action`、`params`、`run_id` |

`core` 和 `media` 只有服务器能写。保存新版本时，`user` 和 `ai.generation` 会从上一个版本带过来（`producer.inherited_from` 记录来源），因为重新导出不会改变提示词和评分；其余的描述的是具体字节，会重新生成。

### 记录 AI 素材是怎么来的

如果你的工具生成图片或视频，上传时顺便写 `ai.generation`。搜索就能按提示词找到文件，应用里也会显示模型，并给出回到原工具重新生成的链接。

```json
{
  "tool": "cliptap",
  "model": "video-model-x",
  "prompt": "A fishing harbor at night, slow dolly in, rain",
  "seed": 1234,
  "inputs": [{ "role": "reference", "node_id": "nod_…", "version_id": "ver_…" }],
  "regenerate_url": "https://cliptap.example/jobs/9f2c"
}
```

## 自定义命名空间

自定义命名空间以 `x-` 开头，一般写成 `x-<公司>.<工具>`（`x-acme.qc`）。在服务器大小限制内的任何 JSON 对象都可以，内容由你自己负责。

> [!TIP]
> 能写进附加信息，就不要在原文件旁边新建文件。它始终跟着正确的版本，能被搜索到，文件改名、移动也不会断。

## 一个命名空间只给一个成员写

空间管理员可以把某个命名空间交给一个成员，通常是一个 [AI 成员](/docs/authentication#ai-成员)。之后在这个空间里只有它能写，其他人只能读，多个 AI 工具就不会互相覆盖结果。

```sh
sidespace namespace claim "Film A" x-qc "Clip Tagger"
sidespace namespace list "Film A"
sidespace namespace release "Film A" x-qc
```

能交出去的是 `ai.generation` 和自定义的 `x-…`。`core`、`media` 属于服务器，`user` 是大家共用的。
