Skip to content

对等门户接口

对等门户消费的端点。基址 VITE_PEERING_API ?? https://api.natlan.io, 前缀 /control/v1/autopeering(下表路径均省略该前缀)。

客户端实现 apps/peering/src/lib/api.ts,类型 apps/peering/src/lib/types.ts

1. 通用约定

约定
鉴权Authorization: Bearer <门户会话令牌>/auth/login-url/auth/exchange 不带
401清掉 sessionStorage 里的令牌并触发登出转场
错误体{"detail": …}detail 为数组时是 FastAPI 422 校验错误,前端拼成 loc: msg
时间戳时序点的 t桶起点的 epoch 秒,不是 ISO 串

⚠️ TrafficPoint.t 曾被错标成 string。旧图只把它喂给 new Date()——不炸, 但 x 轴一直显示 1970 年;新图喂给 parseTs.trim() 当场抛异常、整张图画不出来。 类型必须是 number

2. 身份

端点说明
GET /auth/login-url?next=<path>{authorize_url},跳 auth.natlan.io/authorize
POST /auth/exchange {handoff}一次性 handoff 码换 {session_token, profile, expires_at}
GET /me用已存令牌恢复会话 → {profile, expires_at}
POST /auth/refresh-claims用户改过 registry 对象后重查上游 userinfo → {profile}
POST /auth/logout注销

会话令牌存 sessionStorage,是门户唯一持有的凭据。链路说明见 apps/peering.md

3. 对等生命周期

端点说明
GET /nodes可接入节点清单(NodeOffer[]
GET /requests当前身份名下的对等请求列表
GET /requests/{id}单条详情
POST /requests新建
DELETE /requests/{id}自助拆除

NodeOffer.capacity 的口径:容量来自节点的外部端口池,进度条是 used_ports / capacity——不是 provisioned / max_sessions。后者会漏掉手工建立的 peering,且 max_sessions 现在通常是 null

POST /requests 的载荷:

jsonc
{ "node_id": "hkg1", "public_key": "…",
  "endpoint": "host:port",          // 可选,对端无 dial-in 时省略
  "link_local": "fe80::…",          // 可选
  "mtu": 1420, "mp_bgp": true,
  "session_addressing": "link-local" | "ipv6-ula",
  "peer_ipv4": "…", "peer_ipv6": "…",   // ULA / 双会话形态才需要
  "notes": "…" }

NodeOffer.endpoint_hostnull 表示该节点没有 dial-in 端点(只主动外拨), 此时对端必须提供 endpoint

PeeringRequest.mtu_probenull 表示尚未探测——落成后会自动跑一次。

4. 观测(仅已落成的对等)

端点说明
GET /requests/{id}/traffic?range=流量,range6h | 24h | 7d | 30d
GET /requests/{id}/metrics?range=指标,同上四档
GET /requests/{id}/routes?family=4|6&limit=&offset=该会话学到的路由
POST /requests/{id}/probe-mtu同步 MTU 探测
POST /requests/{id}/probe-latency同步时延采样

两个 probe 是同步慢调用:MTU 最长约 60 秒,时延约 10 秒。调用方自己持有 loading 态,并且必须防抖——服务端每会话有 10 分钟冷却,触发即 429

5. 只读网络态势

控制台同名板块的门户镜像。后端复用 UI 面的同一批 builder 函数、另挂 require_portal_session,因此响应形状与控制台逐字段一致——可以直接喂给 $ui 里 的同一批看板组件。

端点说明
GET /fleet/dashboard概述 / 流量 / 路由三板块一次取全
GET /fleet/traffic-breakdown?top=当前每节点 / 每对端速率榜
GET /fleet/flap-stats?range=抖动时序与速率仪表
GET /fleet/prefix-flaps?min_score=&limit=&group=prefix前缀抖动榜,与管理面同一处理函数
GET /my/flaps「我这家 AS 抖不抖」

/fleet/dashboard 的参数:rangecompare=1origins_topas_traffic=1, 语义与控制台的 /ui/dashboard 相同,见 control.md。 前缀抖动的字段口径见 flaps.md

两处口径必须记住

  • fleet 级流量只提供 6h / 24hFleetTrafficRange),而用户自己那条对等的 观测是四档全收(TrafficRange)。两个类型不可混用。
  • GET /my/flaps 在无抖动痕迹时返回 200 + has_flaps: false,不是 404。 对用户来说「没抖」是正向结果,不该长得像出错。

脱敏口径

全网态势的 AS 号、前缀、流量占比、抖动明细不脱敏——这些本就在 DN42 registry 与 explorer 上公开。后端做白名单投影收窄两处:

  • WireGuard 对端的 public_keyendpoint(密钥材料与攻击面);
  • overview 节点行的 agent 遥测(内部运维信息)。

6. 公共机群数据

GET https://api.natlan.io/control/v1/public/fleet

不属于 autopeering 契约、不带鉴权,门户用它给节点地图放坐标、画节点间骨干链路。 调用方必须能在它失败时优雅降级——门户的实现是返回空 Map + 空数组,地图退化成无坐标态。

jsonc
{ "nodes": [ { "node_id": "hkg1",
               "geo": { "lat": , "lon": , "city": , "country": "HK", "region": 52 } } ],
  "links": [ { "a": "hkg1", "b": "sha2" } ] }

region 是 DN42 origin-region community(41..57)。country 前端会统一转大写。 同一个端点也是首页 fleet 岛的数据源。