flare-plugin-host 0.1.1

Flare 能力插件的宿主接入库:声明、注册、注销
Documentation
# 插件契约

平台与插件之间只有一条 RPC。这份文档说清它的每个字段是什么意思,以及
哪些约定是**强制的**(违反了会被拒),哪些只是建议。

## 1. 一条 RPC

```protobuf
service ExtensionPlugin {
  rpc Call(GenericRequest) returns (GenericResponse);
}
```

平台侧只认两个概念:**能力 id**(一个开放字符串)和 **JSON 载荷**。
它不生成也不依赖你的类型,不知道你内部是 gRPC、HTTP 还是别的什么。

这带来一个直接结论:**JSON 与你原生接口之间的转换发生在你这边**。
放到平台侧就意味着平台要编进你的 proto,那样每加一个插件都得改平台并发版。

## 2. 请求

| 字段 | 内容 |
|---|---|
| `operation` | 能力 id,如 `vendorx.invoice.create` |
| `payload` | `Any`,其 `value` **直接就是 JSON 字节** |
| `metadata` | `tenant_id` / `user_id` / `trace_id` |
| `request_id` | 调用方给的幂等/追踪标识,原样回填到响应里 |

`payload.type_url` 是 `type.googleapis.com/flare.capability.v1.PayloadJson`。
这不是 protobuf 编码的 `Any` —— 别拿 prost 去解,直接当 JSON 字节读。

⚠️ **`metadata` 里的上下文必须由你自己还原。** 平台是进程外调用,不走
HTTP header,所以框架的 Context 中间件不会替你注入。漏掉这步的表现是所有
调用都报「找不到上下文」——看着像鉴权问题,排查方向会完全跑偏。

## 3. 响应

```protobuf
message GenericResponse {
  bool   ok            = 1;
  Any    payload       = 2;   // 同样是 JSON 字节
  string error_code    = 3;
  string error_message = 4;
  string request_id    = 5;
}
```

业务失败要**明确回错**(`ok=false` + `error_code`),别回一个空的成功。
静默成功会让调用方以为业务做了 —— 这类缺陷在链路里几乎无法定位。

## 4. 能力 id 命名

建议 `{域}.{子系统}.{动作}`,例如 `vendorx.invoice.create`。

域用你自己的标识,别占用平台或他人的前缀。这不是技术限制(能力 id 是开放
字符串,平台不校验命名),而是多插件共存时唯一能靠的约定。

## 5. 路由:声明即边界

平台的路由簿按 `(租户, 插件)` 作键 —— 你的进程只有**一条**注册记录,
承接哪些 operation 由 `declared_operations` 决定。

**注册一次即承接全部声明。** `capability_id` 只是这条记录的标签,
填一个最好认的入口即可,它不限制你能接到的调用。

声明之外的调用会被平台拒绝,请求根本到不了你的进程。

> 如果你读到过「按 operation 最长前缀匹配」的说法:那是平台**进程内**
> handler 的路由规则。第三方插件是进程外的,走的是上面这条精确匹配。

## 6. 两类失败方向相反

| 类别 | 行为 | 例子 |
|---|---|---|
| **授权失败** | 拒绝,不降级 | 未安装、租户开关关闭、无用户授权 |
| **可用性失败** | 降级,不拒绝 | 你超时、不可达、健康检查未通过 |

授权是 fail-closed 的:拒绝就是拒绝。而你的实例不可用时,平台不会因此拒绝
整条链路,只会按健康状态把你排到候选末尾。

把这两类搞反是最贵的错误:授权侧一放行就是越权,可用性侧一拒绝就是
一个实例抖动拖垮整条业务链。

## 7. 健康检查

默认走通用协议:平台调你的 `ExtensionPlugin.Call`,`operation` 为
`flare.capability.v1.health_check`,返回 `ok: true` 即健康。

需要特殊探活语义时,可在注册的 `labels` 里声明协议名 —— 但对应的探针实现
必须已部署在平台侧,否则平台会把你判为「声明的协议没有探针」而不是静默
降级成通用检查。静默降级等于把装配缺失伪装成健康。

## 8. 注册契约

见 [自己开发一个插件](./build-your-own-plugin.md)。字段全集与语义在
[`flare-plugin-host`](https://docs.rs/flare-plugin-host) 的 API 文档里。