跳转到内容
AsterDrive Developer Docs开发者

内部存储协议(Follower)

这组接口是主节点和 follower 节点之间的内部对象存储协议,不是给浏览器前端或第三方普通客户端用的公开 API。

这页描述的是 follower 侧实际执行对象读写的 /api/v1/internal/storage/*。primary 侧还有一组 reverse tunnel 内部入口 /api/v1/internal/remote-tunnel/*,用于让不能被 primary 直连的 follower 主动连回 primary。

以下路径都相对于:

/api/v1/internal/storage

并且只会在 follower 节点注册。

远端节点的对象协议有两层,别混在一起看:

  • /api/v1/internal/storage/* 只在 follower 注册,是实际对象读写、绑定同步、远程存储目标管理的协议。
  • /api/v1/internal/remote-tunnel/* 只在 primary 注册,是 reverse tunnel 的控制面和传输入口。

direct 模式下,primary 直接请求 follower 的 /api/v1/internal/storage/*reverse_tunnel 模式下,primary 把同样的内部存储请求登记到 tunnel registry,follower 主动向 primary 轮询或建立 WebSocket 连接取走请求,再在本地调用内部存储处理逻辑并回传响应。

primary 侧 reverse tunnel 当前入口:

方法路径说明
POST/api/v1/internal/remote-tunnel/pollfollower 长轮询待处理请求
POST/api/v1/internal/remote-tunnel/completefollower 回传轮询请求的处理结果
GET/api/v1/internal/remote-tunnel/connectfollower 建立 WebSocket 流式 tunnel

这组 reverse tunnel 接口同样使用远端节点签名鉴权,不是浏览器或第三方客户端 API。

当前有两种访问方式:

  • 主节点签名请求
    • x-aster-access-key
    • x-aster-timestamp
    • x-aster-nonce
    • x-aster-signature
  • 预签名 query
    • aster_access_key
    • aster_expires
    • aster_signature

常规控制面接口都要求签名头;对象 GET / PUT 会按场景支持预签名 URL。

方法路径说明
GET/capabilities读取 follower 声明的协议能力
GET/capacity读取 follower 当前远端存储目标的容量观测状态
PUT/binding同步主节点维护的远端节点绑定信息
GET/targets列出当前绑定可用的远程存储目标
POST/targets创建远程存储目标
PATCH/targets/{target_key}更新远程存储目标
DELETE/targets/{target_key}删除远程存储目标
POST/compose把多个 part 对象拼成目标对象
GET/objects按前缀列举对象 key
GET/objects/{tail}/metadata读取对象元信息
PUT/objects/{tail}上传对象内容
GET/objects/{tail}读取对象内容
HEAD/objects/{tail}探测对象是否存在并返回头信息
DELETE/objects/{tail}删除对象

0.4.0 已移除旧 /ingress-profiles/ingress-profiles/{target_key} 兼容路径;primary 与 follower 必须统一使用 /targets

返回仍然走统一 JSON 包装,典型字段包括:

  • protocol_version
  • min_supported_protocol_version
  • server_version
  • features
  • browser_cors
  • limits
  • supports_list
  • supports_range_read
  • supports_stream_upload
  • supports_capacity

当前协议版本是 v5,最低兼容版本是 v4,所以当前节点声明的本地支持区间是 v4-v5v4 / v5v2 / v3 不再 wire-compatible:内部存储 JSON 包装里的顶层 code 已经从旧数字码改成稳定字符串 ApiErrorCode。跨过这个边界时,先同时升级 primary 和 follower,再绑定 remote 策略。

v5 在能力响应中增加了远程存储目标 driver 能力。Rust 模型字段名是 remote_storage_target,但为了兼容 v4 / v5 节点,wire JSON 仍序列化为 managed_ingress,同时接受 remote_storage_target 作为反序列化 alias。不要根据 wire 字段的旧名字把它重新解释成另一套产品模型。

兼容规则是显式且有边界的:

  • v4 follower 没有声明这组能力时,primary 会按旧协议语义把 Local 和 S3 视为可用 driver。
  • v5 follower 必须显式声明远程存储目标能力;能力缺失或禁用时,不再套用 v4 的隐式 Local / S3 fallback。
  • primary 只展示 follower 声明且当前版本已注册 descriptor 的 driver;未知的未来 driver id 会被保留为协议数据,但不会自动变成可配置项。

主节点在加载远端策略或刷新绑定时会做能力协商:

  • protocol_version / min_supported_protocol_version 必须和本地支持区间有交集,当前本地区间是 v4-v5
  • 基础远端策略要求 object_getobject_headobject_putobject_deletemetadatarange_getaccept_ranges_headerlistcompose
  • 如果远端策略启用浏览器预签名下载,browser_cors 必须声明允许 range 请求头,并暴露 Accept-RangesContent-RangeContent-Length
  • 如果远端策略启用浏览器预签名上传,browser_cors 必须声明允许 content-type 请求头,并暴露 ETag

当前 follower 返回的 browser_cors.allowed_headers 至少包含 content-typerangebrowser_cors.exposed_headers 会覆盖 GET/PUT 预签名所需的缓存、Range、长度、类型和 ETag 响应头。

返回 follower 当前远端存储目标 driver 的 StorageCapacityInfo

{
"code": "success",
"msg": "",
"data": {
"capacity": {
"status": "supported",
"total_bytes": 1099511627776,
"available_bytes": 549755813888,
"used_bytes": 549755813888,
"source": "local_filesystem",
"observed_at": "2026-05-28T12:00:00Z"
}
}
}

实现约定:

  • follower 直接调用当前 target driver 的 capacity_info()
  • local target 通常返回真实文件系统容量
  • S3 target 明确返回 StorageErrorKind::Unsupported,primary 侧会把它转换成用户可见的 unsupported 容量状态
  • 这个接口只用于管理端容量观测和迁移 preflight,不在上传 / 下载热路径里调用

主节点会用这条接口把 follower 绑定信息同步过去,请求体字段包括:

  • name
  • is_enabled

这条接口只更新绑定元信息,不直接搬运对象数据。对象命名空间来自 follower 本地保存的 master binding,不由这条请求体传入。

这组接口用于 primary 管理 follower 侧的远程存储目标,控制后续对象写入实际落到 follower 本地还是 follower 管理的 S3。当前请求 / 响应 DTO 使用 target_key 字段名。

创建本地目标的请求体形态:

{
"driver_type": "local",
"name": "local-default",
"base_path": "data/storage",
"is_default": true
}

创建 S3 目标的请求体形态:

{
"driver_type": "s3",
"name": "edge-s3",
"endpoint": "https://s3.example.com",
"bucket": "aster-edge",
"access_key": "AKIA...",
"secret_key": "...",
"base_path": "objects/",
"is_default": false
}

更新接口使用扁平字段,支持修改 namedriver_type、连接参数、base_pathis_default。当前 target driver 只有 locals3;实际可选项还会受到 follower 的 remote_storage_target 能力声明约束。这些控制面接口只接受主节点签名头,不使用预签名 query。

这条接口用于把多个上传 part 合成为最终对象,请求体包括:

  • target_key
  • part_keys
  • expected_size

成功后返回 bytes_written。实现上会在拼接成功后清理被消费的 part 对象。

写入一个对象。请求必须带 Content-Length,follower 会按 ingress 策略检查对象大小上限。

返回原始对象字节流,不走 JSON 包装。

可选 query:

  • offset
  • length
  • response-cache-control
  • response-content-disposition
  • response-content-type

也就是说,这条接口既支持整对象读取,也支持范围读取和响应头覆写。范围读取也可以通过标准 Range: bytes=... 请求头触发;返回部分内容时使用 206 Partial Content

返回对象是否存在以及基础响应头,常用于轻量探测。

返回统一 JSON 包装,data 里当前主要有:

  • size
  • content_type

删除对象,成功时返回空的统一成功响应。

支持以下 query:

  • prefix:只返回匹配前缀的对象 key。
  • cursor:从相对位置继续列举;通常使用上一页返回的 next_cursor
  • limit:请求页大小,必须大于 0;服务端会把它钳制到内部页大小上限。

新客户端应始终发送 limit。响应形态如下:

{
"code": "success",
"msg": "",
"data": {
"items": ["files/part-001", "files/part-002"],
"next_cursor": 2
}
}

只有后面仍有数据时才会返回 next_cursor。不传 limit 时,follower 保留旧客户端使用的无分页响应,并一次返回全部匹配项。

当前返回体里的 items 是 follower 绑定命名空间下的相对 key,不会把 provider 内部前缀原样暴露回去。

下面这些情况,不要再去普通 files / upload / shares 路由里瞎找:

  • 主节点写远端存储节点失败
  • 受管 follower 拼 part 失败
  • 远端节点健康正常,但对象列举 / 读取 / 删除异常
  • 远端节点 enrollment 成功后,后续对象同步行为不对