mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
feat(pluginapi): HostService 账号目录返回结构化只读元数据
纯宿主侧改动,向后兼容。 - 契约(pkg/pluginapi/v1/plugin.proto,已重新生成):ListAccounts 增补 repeated AccountInfo accounts(保留 account_ids 向后兼容),新增 metadata_json 只读快照字段。HostServiceAPIVersion 1→2(InitHostServices 运行时协商,加法式, 旧调用方忽略新字段即可)。 - 权限边界:可见范围由清单能力经 pluginCapabilityAccountScopeGrants 推导, ListAccounts 与 ResolveOutboundIdentity 均以该范围为权威边界。 - metadata_json 采用 denylist:剥离 Credentials 与含反向引用的关联对象 Groups/AccountGroups(避免 encoding/json 循环引用),其余字段自动透传, 数据模型新增非机密字段无需改宿主。 测试:范围/元数据/无凭据泄漏用例、denylist 反射护栏 + 循环引用安全断言、纵深过滤用例; build/vet/gofmt、-tags unit 与 -race 全绿。
This commit is contained in:
@@ -1704,8 +1704,19 @@ func (x *ListAccountsRequest) GetAccountType() string {
|
||||
}
|
||||
|
||||
type ListAccountsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
AccountIds []int64 `protobuf:"varint,1,rep,packed,name=account_ids,json=accountIds,proto3" json:"account_ids,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// account_ids is retained for plugins built against host_service_api_version 1.
|
||||
// The host keeps filling it (the ids of the accounts in `accounts`) so those
|
||||
// plugins keep working; new plugins should read `accounts` instead.
|
||||
AccountIds []int64 `protobuf:"varint,1,rep,packed,name=account_ids,json=accountIds,proto3" json:"account_ids,omitempty"`
|
||||
// accounts carries the full readable metadata for every account in the
|
||||
// plugin's scope — including active accounts that are currently NOT
|
||||
// schedulable because they are paused (rate-limited / temp-unschedulable /
|
||||
// overloaded). This lets a plugin make its own decisions (e.g. skip paused
|
||||
// accounts) instead of hammering them. (Administratively disabled / expired
|
||||
// accounts are already excluded upstream by the host.) Available when
|
||||
// host_service_api_version >= 2.
|
||||
Accounts []*AccountInfo `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -1747,6 +1758,138 @@ func (x *ListAccountsResponse) GetAccountIds() []int64 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ListAccountsResponse) GetAccounts() []*AccountInfo {
|
||||
if x != nil {
|
||||
return x.Accounts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AccountInfo is the host's readable, non-secret view of one account. It never
|
||||
// contains credentials (token / refresh_token / cookies); use
|
||||
// ResolveOutboundIdentity for those.
|
||||
//
|
||||
// The typed fields are a small, STABLE decision core. Everything else about the
|
||||
// account — including any field added to the account model later — is carried in
|
||||
// metadata_json, so new fields reach plugins WITHOUT a contract change, a host
|
||||
// mapper edit, or a plugin rebuild. Only a newly added *secret* field would ever
|
||||
// require a host change (a one-line denylist entry), which is the safe default.
|
||||
type AccountInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Platform string `protobuf:"bytes,2,opt,name=platform,proto3" json:"platform,omitempty"`
|
||||
AccountType string `protobuf:"bytes,3,opt,name=account_type,json=accountType,proto3" json:"account_type,omitempty"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// status is the coarse lifecycle state: active / disabled / error / expired.
|
||||
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
|
||||
// schedulable is the host-authoritative decision (IsSchedulable) — false when
|
||||
// the account is active but paused for ANY reason (rate limit,
|
||||
// temp-unschedulable, overload, expiry auto-pause, quota). This is the primary
|
||||
// signal a plugin should use to skip an account so it stops probing paused ones.
|
||||
Schedulable bool `protobuf:"varint,6,opt,name=schedulable,proto3" json:"schedulable,omitempty"`
|
||||
IsShadow bool `protobuf:"varint,7,opt,name=is_shadow,json=isShadow,proto3" json:"is_shadow,omitempty"`
|
||||
// metadata_json is a host-produced JSON object holding the account's readable
|
||||
// field set (status timestamps, rate-limit / overload / temp-unschedulable
|
||||
// windows and reason, last-used, expiry, session window, group ids, priority,
|
||||
// concurrency, quota dimension, extra config, proxy, and any future field).
|
||||
// Keys are the account model's field names. Times are RFC3339 strings (JSON
|
||||
// null when unset). Only two things are withheld: the raw Credentials blob
|
||||
// (the refresh_token — a long-lived secret the outbound-identity RPC does not
|
||||
// hand out; the short-lived access token and proxy URL come from that RPC), and
|
||||
// the Groups/AccountGroups relation graphs (cyclic — group_ids conveys
|
||||
// membership). Plugins read whatever keys they need; the host does not have to
|
||||
// change when the account model gains a field.
|
||||
MetadataJson []byte `protobuf:"bytes,20,opt,name=metadata_json,json=metadataJson,proto3" json:"metadata_json,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AccountInfo) Reset() {
|
||||
*x = AccountInfo{}
|
||||
mi := &file_plugin_proto_msgTypes[29]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AccountInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AccountInfo) ProtoMessage() {}
|
||||
|
||||
func (x *AccountInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_plugin_proto_msgTypes[29]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AccountInfo.ProtoReflect.Descriptor instead.
|
||||
func (*AccountInfo) Descriptor() ([]byte, []int) {
|
||||
return file_plugin_proto_rawDescGZIP(), []int{29}
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetId() int64 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetPlatform() string {
|
||||
if x != nil {
|
||||
return x.Platform
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetAccountType() string {
|
||||
if x != nil {
|
||||
return x.AccountType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetStatus() string {
|
||||
if x != nil {
|
||||
return x.Status
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetSchedulable() bool {
|
||||
if x != nil {
|
||||
return x.Schedulable
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetIsShadow() bool {
|
||||
if x != nil {
|
||||
return x.IsShadow
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AccountInfo) GetMetadataJson() []byte {
|
||||
if x != nil {
|
||||
return x.MetadataJson
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResolveOutboundIdentityRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
AccountId int64 `protobuf:"varint,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"`
|
||||
@@ -1756,7 +1899,7 @@ type ResolveOutboundIdentityRequest struct {
|
||||
|
||||
func (x *ResolveOutboundIdentityRequest) Reset() {
|
||||
*x = ResolveOutboundIdentityRequest{}
|
||||
mi := &file_plugin_proto_msgTypes[29]
|
||||
mi := &file_plugin_proto_msgTypes[30]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1768,7 +1911,7 @@ func (x *ResolveOutboundIdentityRequest) String() string {
|
||||
func (*ResolveOutboundIdentityRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ResolveOutboundIdentityRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_plugin_proto_msgTypes[29]
|
||||
mi := &file_plugin_proto_msgTypes[30]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1781,7 +1924,7 @@ func (x *ResolveOutboundIdentityRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ResolveOutboundIdentityRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ResolveOutboundIdentityRequest) Descriptor() ([]byte, []int) {
|
||||
return file_plugin_proto_rawDescGZIP(), []int{29}
|
||||
return file_plugin_proto_rawDescGZIP(), []int{30}
|
||||
}
|
||||
|
||||
func (x *ResolveOutboundIdentityRequest) GetAccountId() int64 {
|
||||
@@ -1806,7 +1949,7 @@ type ResolveOutboundIdentityResponse struct {
|
||||
|
||||
func (x *ResolveOutboundIdentityResponse) Reset() {
|
||||
*x = ResolveOutboundIdentityResponse{}
|
||||
mi := &file_plugin_proto_msgTypes[30]
|
||||
mi := &file_plugin_proto_msgTypes[31]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1818,7 +1961,7 @@ func (x *ResolveOutboundIdentityResponse) String() string {
|
||||
func (*ResolveOutboundIdentityResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ResolveOutboundIdentityResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_plugin_proto_msgTypes[30]
|
||||
mi := &file_plugin_proto_msgTypes[31]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1831,7 +1974,7 @@ func (x *ResolveOutboundIdentityResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ResolveOutboundIdentityResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ResolveOutboundIdentityResponse) Descriptor() ([]byte, []int) {
|
||||
return file_plugin_proto_rawDescGZIP(), []int{30}
|
||||
return file_plugin_proto_rawDescGZIP(), []int{31}
|
||||
}
|
||||
|
||||
func (x *ResolveOutboundIdentityResponse) GetFound() bool {
|
||||
@@ -2010,10 +2153,20 @@ const file_plugin_proto_rawDesc = "" +
|
||||
"\x04keys\x18\x01 \x03(\tR\x04keys\"T\n" +
|
||||
"\x13ListAccountsRequest\x12\x1a\n" +
|
||||
"\bplatform\x18\x01 \x01(\tR\bplatform\x12!\n" +
|
||||
"\faccount_type\x18\x02 \x01(\tR\vaccountType\"7\n" +
|
||||
"\faccount_type\x18\x02 \x01(\tR\vaccountType\"s\n" +
|
||||
"\x14ListAccountsResponse\x12\x1f\n" +
|
||||
"\vaccount_ids\x18\x01 \x03(\x03R\n" +
|
||||
"accountIds\"?\n" +
|
||||
"accountIds\x12:\n" +
|
||||
"\baccounts\x18\x02 \x03(\v2\x1e.sub2api.plugin.v1.AccountInfoR\baccounts\"\xec\x01\n" +
|
||||
"\vAccountInfo\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x1a\n" +
|
||||
"\bplatform\x18\x02 \x01(\tR\bplatform\x12!\n" +
|
||||
"\faccount_type\x18\x03 \x01(\tR\vaccountType\x12\x12\n" +
|
||||
"\x04name\x18\x04 \x01(\tR\x04name\x12\x16\n" +
|
||||
"\x06status\x18\x05 \x01(\tR\x06status\x12 \n" +
|
||||
"\vschedulable\x18\x06 \x01(\bR\vschedulable\x12\x1b\n" +
|
||||
"\tis_shadow\x18\a \x01(\bR\bisShadow\x12#\n" +
|
||||
"\rmetadata_json\x18\x14 \x01(\fR\fmetadataJson\"?\n" +
|
||||
"\x1eResolveOutboundIdentityRequest\x12\x1d\n" +
|
||||
"\n" +
|
||||
"account_id\x18\x01 \x01(\x03R\taccountId\"\x80\x03\n" +
|
||||
@@ -2058,7 +2211,7 @@ func file_plugin_proto_rawDescGZIP() []byte {
|
||||
return file_plugin_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 34)
|
||||
var file_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 35)
|
||||
var file_plugin_proto_goTypes = []any{
|
||||
(*GetInfoRequest)(nil), // 0: sub2api.plugin.v1.GetInfoRequest
|
||||
(*GetInfoResponse)(nil), // 1: sub2api.plugin.v1.GetInfoResponse
|
||||
@@ -2089,54 +2242,56 @@ var file_plugin_proto_goTypes = []any{
|
||||
(*KVListResponse)(nil), // 26: sub2api.plugin.v1.KVListResponse
|
||||
(*ListAccountsRequest)(nil), // 27: sub2api.plugin.v1.ListAccountsRequest
|
||||
(*ListAccountsResponse)(nil), // 28: sub2api.plugin.v1.ListAccountsResponse
|
||||
(*ResolveOutboundIdentityRequest)(nil), // 29: sub2api.plugin.v1.ResolveOutboundIdentityRequest
|
||||
(*ResolveOutboundIdentityResponse)(nil), // 30: sub2api.plugin.v1.ResolveOutboundIdentityResponse
|
||||
nil, // 31: sub2api.plugin.v1.ForwardRequestStart.HeadersEntry
|
||||
nil, // 32: sub2api.plugin.v1.ForwardResponseStart.HeadersEntry
|
||||
nil, // 33: sub2api.plugin.v1.ResolveOutboundIdentityResponse.HeadersEntry
|
||||
(*AccountInfo)(nil), // 29: sub2api.plugin.v1.AccountInfo
|
||||
(*ResolveOutboundIdentityRequest)(nil), // 30: sub2api.plugin.v1.ResolveOutboundIdentityRequest
|
||||
(*ResolveOutboundIdentityResponse)(nil), // 31: sub2api.plugin.v1.ResolveOutboundIdentityResponse
|
||||
nil, // 32: sub2api.plugin.v1.ForwardRequestStart.HeadersEntry
|
||||
nil, // 33: sub2api.plugin.v1.ForwardResponseStart.HeadersEntry
|
||||
nil, // 34: sub2api.plugin.v1.ResolveOutboundIdentityResponse.HeadersEntry
|
||||
}
|
||||
var file_plugin_proto_depIdxs = []int32{
|
||||
31, // 0: sub2api.plugin.v1.ForwardRequestStart.headers:type_name -> sub2api.plugin.v1.ForwardRequestStart.HeadersEntry
|
||||
32, // 0: sub2api.plugin.v1.ForwardRequestStart.headers:type_name -> sub2api.plugin.v1.ForwardRequestStart.HeadersEntry
|
||||
11, // 1: sub2api.plugin.v1.ForwardRequest.start:type_name -> sub2api.plugin.v1.ForwardRequestStart
|
||||
32, // 2: sub2api.plugin.v1.ForwardResponseStart.headers:type_name -> sub2api.plugin.v1.ForwardResponseStart.HeadersEntry
|
||||
33, // 2: sub2api.plugin.v1.ForwardResponseStart.headers:type_name -> sub2api.plugin.v1.ForwardResponseStart.HeadersEntry
|
||||
13, // 3: sub2api.plugin.v1.ForwardResponse.start:type_name -> sub2api.plugin.v1.ForwardResponseStart
|
||||
14, // 4: sub2api.plugin.v1.ForwardResponse.end:type_name -> sub2api.plugin.v1.ForwardResponseEnd
|
||||
15, // 5: sub2api.plugin.v1.ForwardResponse.error:type_name -> sub2api.plugin.v1.ForwardResponseError
|
||||
33, // 6: sub2api.plugin.v1.ResolveOutboundIdentityResponse.headers:type_name -> sub2api.plugin.v1.ResolveOutboundIdentityResponse.HeadersEntry
|
||||
10, // 7: sub2api.plugin.v1.ForwardRequestStart.HeadersEntry.value:type_name -> sub2api.plugin.v1.HeaderValues
|
||||
10, // 8: sub2api.plugin.v1.ForwardResponseStart.HeadersEntry.value:type_name -> sub2api.plugin.v1.HeaderValues
|
||||
10, // 9: sub2api.plugin.v1.ResolveOutboundIdentityResponse.HeadersEntry.value:type_name -> sub2api.plugin.v1.HeaderValues
|
||||
0, // 10: sub2api.plugin.v1.TransportPlugin.GetInfo:input_type -> sub2api.plugin.v1.GetInfoRequest
|
||||
2, // 11: sub2api.plugin.v1.TransportPlugin.Health:input_type -> sub2api.plugin.v1.HealthRequest
|
||||
4, // 12: sub2api.plugin.v1.TransportPlugin.ValidateConfig:input_type -> sub2api.plugin.v1.ValidateConfigRequest
|
||||
6, // 13: sub2api.plugin.v1.TransportPlugin.ApplyConfig:input_type -> sub2api.plugin.v1.ApplyConfigRequest
|
||||
8, // 14: sub2api.plugin.v1.TransportPlugin.TestConfig:input_type -> sub2api.plugin.v1.TestConfigRequest
|
||||
12, // 15: sub2api.plugin.v1.TransportPlugin.Forward:input_type -> sub2api.plugin.v1.ForwardRequest
|
||||
17, // 16: sub2api.plugin.v1.TransportPlugin.InitHostServices:input_type -> sub2api.plugin.v1.InitHostServicesRequest
|
||||
19, // 17: sub2api.plugin.v1.HostService.KVGet:input_type -> sub2api.plugin.v1.KVGetRequest
|
||||
21, // 18: sub2api.plugin.v1.HostService.KVSet:input_type -> sub2api.plugin.v1.KVSetRequest
|
||||
23, // 19: sub2api.plugin.v1.HostService.KVDelete:input_type -> sub2api.plugin.v1.KVDeleteRequest
|
||||
25, // 20: sub2api.plugin.v1.HostService.KVList:input_type -> sub2api.plugin.v1.KVListRequest
|
||||
27, // 21: sub2api.plugin.v1.HostService.ListAccounts:input_type -> sub2api.plugin.v1.ListAccountsRequest
|
||||
29, // 22: sub2api.plugin.v1.HostService.ResolveOutboundIdentity:input_type -> sub2api.plugin.v1.ResolveOutboundIdentityRequest
|
||||
1, // 23: sub2api.plugin.v1.TransportPlugin.GetInfo:output_type -> sub2api.plugin.v1.GetInfoResponse
|
||||
3, // 24: sub2api.plugin.v1.TransportPlugin.Health:output_type -> sub2api.plugin.v1.HealthResponse
|
||||
5, // 25: sub2api.plugin.v1.TransportPlugin.ValidateConfig:output_type -> sub2api.plugin.v1.ValidateConfigResponse
|
||||
7, // 26: sub2api.plugin.v1.TransportPlugin.ApplyConfig:output_type -> sub2api.plugin.v1.ApplyConfigResponse
|
||||
9, // 27: sub2api.plugin.v1.TransportPlugin.TestConfig:output_type -> sub2api.plugin.v1.TestConfigResponse
|
||||
16, // 28: sub2api.plugin.v1.TransportPlugin.Forward:output_type -> sub2api.plugin.v1.ForwardResponse
|
||||
18, // 29: sub2api.plugin.v1.TransportPlugin.InitHostServices:output_type -> sub2api.plugin.v1.InitHostServicesResponse
|
||||
20, // 30: sub2api.plugin.v1.HostService.KVGet:output_type -> sub2api.plugin.v1.KVGetResponse
|
||||
22, // 31: sub2api.plugin.v1.HostService.KVSet:output_type -> sub2api.plugin.v1.KVSetResponse
|
||||
24, // 32: sub2api.plugin.v1.HostService.KVDelete:output_type -> sub2api.plugin.v1.KVDeleteResponse
|
||||
26, // 33: sub2api.plugin.v1.HostService.KVList:output_type -> sub2api.plugin.v1.KVListResponse
|
||||
28, // 34: sub2api.plugin.v1.HostService.ListAccounts:output_type -> sub2api.plugin.v1.ListAccountsResponse
|
||||
30, // 35: sub2api.plugin.v1.HostService.ResolveOutboundIdentity:output_type -> sub2api.plugin.v1.ResolveOutboundIdentityResponse
|
||||
23, // [23:36] is the sub-list for method output_type
|
||||
10, // [10:23] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
29, // 6: sub2api.plugin.v1.ListAccountsResponse.accounts:type_name -> sub2api.plugin.v1.AccountInfo
|
||||
34, // 7: sub2api.plugin.v1.ResolveOutboundIdentityResponse.headers:type_name -> sub2api.plugin.v1.ResolveOutboundIdentityResponse.HeadersEntry
|
||||
10, // 8: sub2api.plugin.v1.ForwardRequestStart.HeadersEntry.value:type_name -> sub2api.plugin.v1.HeaderValues
|
||||
10, // 9: sub2api.plugin.v1.ForwardResponseStart.HeadersEntry.value:type_name -> sub2api.plugin.v1.HeaderValues
|
||||
10, // 10: sub2api.plugin.v1.ResolveOutboundIdentityResponse.HeadersEntry.value:type_name -> sub2api.plugin.v1.HeaderValues
|
||||
0, // 11: sub2api.plugin.v1.TransportPlugin.GetInfo:input_type -> sub2api.plugin.v1.GetInfoRequest
|
||||
2, // 12: sub2api.plugin.v1.TransportPlugin.Health:input_type -> sub2api.plugin.v1.HealthRequest
|
||||
4, // 13: sub2api.plugin.v1.TransportPlugin.ValidateConfig:input_type -> sub2api.plugin.v1.ValidateConfigRequest
|
||||
6, // 14: sub2api.plugin.v1.TransportPlugin.ApplyConfig:input_type -> sub2api.plugin.v1.ApplyConfigRequest
|
||||
8, // 15: sub2api.plugin.v1.TransportPlugin.TestConfig:input_type -> sub2api.plugin.v1.TestConfigRequest
|
||||
12, // 16: sub2api.plugin.v1.TransportPlugin.Forward:input_type -> sub2api.plugin.v1.ForwardRequest
|
||||
17, // 17: sub2api.plugin.v1.TransportPlugin.InitHostServices:input_type -> sub2api.plugin.v1.InitHostServicesRequest
|
||||
19, // 18: sub2api.plugin.v1.HostService.KVGet:input_type -> sub2api.plugin.v1.KVGetRequest
|
||||
21, // 19: sub2api.plugin.v1.HostService.KVSet:input_type -> sub2api.plugin.v1.KVSetRequest
|
||||
23, // 20: sub2api.plugin.v1.HostService.KVDelete:input_type -> sub2api.plugin.v1.KVDeleteRequest
|
||||
25, // 21: sub2api.plugin.v1.HostService.KVList:input_type -> sub2api.plugin.v1.KVListRequest
|
||||
27, // 22: sub2api.plugin.v1.HostService.ListAccounts:input_type -> sub2api.plugin.v1.ListAccountsRequest
|
||||
30, // 23: sub2api.plugin.v1.HostService.ResolveOutboundIdentity:input_type -> sub2api.plugin.v1.ResolveOutboundIdentityRequest
|
||||
1, // 24: sub2api.plugin.v1.TransportPlugin.GetInfo:output_type -> sub2api.plugin.v1.GetInfoResponse
|
||||
3, // 25: sub2api.plugin.v1.TransportPlugin.Health:output_type -> sub2api.plugin.v1.HealthResponse
|
||||
5, // 26: sub2api.plugin.v1.TransportPlugin.ValidateConfig:output_type -> sub2api.plugin.v1.ValidateConfigResponse
|
||||
7, // 27: sub2api.plugin.v1.TransportPlugin.ApplyConfig:output_type -> sub2api.plugin.v1.ApplyConfigResponse
|
||||
9, // 28: sub2api.plugin.v1.TransportPlugin.TestConfig:output_type -> sub2api.plugin.v1.TestConfigResponse
|
||||
16, // 29: sub2api.plugin.v1.TransportPlugin.Forward:output_type -> sub2api.plugin.v1.ForwardResponse
|
||||
18, // 30: sub2api.plugin.v1.TransportPlugin.InitHostServices:output_type -> sub2api.plugin.v1.InitHostServicesResponse
|
||||
20, // 31: sub2api.plugin.v1.HostService.KVGet:output_type -> sub2api.plugin.v1.KVGetResponse
|
||||
22, // 32: sub2api.plugin.v1.HostService.KVSet:output_type -> sub2api.plugin.v1.KVSetResponse
|
||||
24, // 33: sub2api.plugin.v1.HostService.KVDelete:output_type -> sub2api.plugin.v1.KVDeleteResponse
|
||||
26, // 34: sub2api.plugin.v1.HostService.KVList:output_type -> sub2api.plugin.v1.KVListResponse
|
||||
28, // 35: sub2api.plugin.v1.HostService.ListAccounts:output_type -> sub2api.plugin.v1.ListAccountsResponse
|
||||
31, // 36: sub2api.plugin.v1.HostService.ResolveOutboundIdentity:output_type -> sub2api.plugin.v1.ResolveOutboundIdentityResponse
|
||||
24, // [24:37] is the sub-list for method output_type
|
||||
11, // [11:24] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_plugin_proto_init() }
|
||||
@@ -2161,7 +2316,7 @@ func file_plugin_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_plugin_proto_rawDesc), len(file_plugin_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 34,
|
||||
NumMessages: 35,
|
||||
NumExtensions: 0,
|
||||
NumServices: 2,
|
||||
},
|
||||
|
||||
@@ -27,10 +27,13 @@ service HostService {
|
||||
rpc KVSet(KVSetRequest) returns (KVSetResponse);
|
||||
rpc KVDelete(KVDeleteRequest) returns (KVDeleteResponse);
|
||||
rpc KVList(KVListRequest) returns (KVListResponse);
|
||||
// Account directory: enumerate the accounts the plugin is bound to act on and
|
||||
// resolve the outbound identity (credentials/headers/proxy) the host would
|
||||
// attach to a live request. The host restricts both calls to accounts the
|
||||
// requesting plugin's capability already covers.
|
||||
// Account directory: enumerate the accounts the plugin is bound to act on
|
||||
// (with full readable metadata) and resolve the outbound identity
|
||||
// (credentials/headers/proxy) the host would attach to a live request. The
|
||||
// host restricts both calls to the account scope the requesting plugin's
|
||||
// declared capabilities cover; a plugin can never widen that scope. Metadata
|
||||
// never carries raw credentials — those are only handed out by
|
||||
// ResolveOutboundIdentity, the dedicated (equally sensitive) credential channel.
|
||||
rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse);
|
||||
rpc ResolveOutboundIdentity(ResolveOutboundIdentityRequest) returns (ResolveOutboundIdentityResponse);
|
||||
}
|
||||
@@ -206,7 +209,54 @@ message ListAccountsRequest {
|
||||
}
|
||||
|
||||
message ListAccountsResponse {
|
||||
// account_ids is retained for plugins built against host_service_api_version 1.
|
||||
// The host keeps filling it (the ids of the accounts in `accounts`) so those
|
||||
// plugins keep working; new plugins should read `accounts` instead.
|
||||
repeated int64 account_ids = 1;
|
||||
// accounts carries the full readable metadata for every account in the
|
||||
// plugin's scope — including active accounts that are currently NOT
|
||||
// schedulable because they are paused (rate-limited / temp-unschedulable /
|
||||
// overloaded). This lets a plugin make its own decisions (e.g. skip paused
|
||||
// accounts) instead of hammering them. (Administratively disabled / expired
|
||||
// accounts are already excluded upstream by the host.) Available when
|
||||
// host_service_api_version >= 2.
|
||||
repeated AccountInfo accounts = 2;
|
||||
}
|
||||
|
||||
// AccountInfo is the host's readable, non-secret view of one account. It never
|
||||
// contains credentials (token / refresh_token / cookies); use
|
||||
// ResolveOutboundIdentity for those.
|
||||
//
|
||||
// The typed fields are a small, STABLE decision core. Everything else about the
|
||||
// account — including any field added to the account model later — is carried in
|
||||
// metadata_json, so new fields reach plugins WITHOUT a contract change, a host
|
||||
// mapper edit, or a plugin rebuild. Only a newly added *secret* field would ever
|
||||
// require a host change (a one-line denylist entry), which is the safe default.
|
||||
message AccountInfo {
|
||||
int64 id = 1;
|
||||
string platform = 2;
|
||||
string account_type = 3;
|
||||
string name = 4;
|
||||
// status is the coarse lifecycle state: active / disabled / error / expired.
|
||||
string status = 5;
|
||||
// schedulable is the host-authoritative decision (IsSchedulable) — false when
|
||||
// the account is active but paused for ANY reason (rate limit,
|
||||
// temp-unschedulable, overload, expiry auto-pause, quota). This is the primary
|
||||
// signal a plugin should use to skip an account so it stops probing paused ones.
|
||||
bool schedulable = 6;
|
||||
bool is_shadow = 7;
|
||||
// metadata_json is a host-produced JSON object holding the account's readable
|
||||
// field set (status timestamps, rate-limit / overload / temp-unschedulable
|
||||
// windows and reason, last-used, expiry, session window, group ids, priority,
|
||||
// concurrency, quota dimension, extra config, proxy, and any future field).
|
||||
// Keys are the account model's field names. Times are RFC3339 strings (JSON
|
||||
// null when unset). Only two things are withheld: the raw Credentials blob
|
||||
// (the refresh_token — a long-lived secret the outbound-identity RPC does not
|
||||
// hand out; the short-lived access token and proxy URL come from that RPC), and
|
||||
// the Groups/AccountGroups relation graphs (cyclic — group_ids conveys
|
||||
// membership). Plugins read whatever keys they need; the host does not have to
|
||||
// change when the account model gains a field.
|
||||
bytes metadata_json = 20;
|
||||
}
|
||||
|
||||
message ResolveOutboundIdentityRequest {
|
||||
|
||||
@@ -373,10 +373,13 @@ type HostServiceClient interface {
|
||||
KVSet(ctx context.Context, in *KVSetRequest, opts ...grpc.CallOption) (*KVSetResponse, error)
|
||||
KVDelete(ctx context.Context, in *KVDeleteRequest, opts ...grpc.CallOption) (*KVDeleteResponse, error)
|
||||
KVList(ctx context.Context, in *KVListRequest, opts ...grpc.CallOption) (*KVListResponse, error)
|
||||
// Account directory: enumerate the accounts the plugin is bound to act on and
|
||||
// resolve the outbound identity (credentials/headers/proxy) the host would
|
||||
// attach to a live request. The host restricts both calls to accounts the
|
||||
// requesting plugin's capability already covers.
|
||||
// Account directory: enumerate the accounts the plugin is bound to act on
|
||||
// (with full readable metadata) and resolve the outbound identity
|
||||
// (credentials/headers/proxy) the host would attach to a live request. The
|
||||
// host restricts both calls to the account scope the requesting plugin's
|
||||
// declared capabilities cover; a plugin can never widen that scope. Metadata
|
||||
// never carries raw credentials — those are only handed out by
|
||||
// ResolveOutboundIdentity, the dedicated (equally sensitive) credential channel.
|
||||
ListAccounts(ctx context.Context, in *ListAccountsRequest, opts ...grpc.CallOption) (*ListAccountsResponse, error)
|
||||
ResolveOutboundIdentity(ctx context.Context, in *ResolveOutboundIdentityRequest, opts ...grpc.CallOption) (*ResolveOutboundIdentityResponse, error)
|
||||
}
|
||||
@@ -462,10 +465,13 @@ type HostServiceServer interface {
|
||||
KVSet(context.Context, *KVSetRequest) (*KVSetResponse, error)
|
||||
KVDelete(context.Context, *KVDeleteRequest) (*KVDeleteResponse, error)
|
||||
KVList(context.Context, *KVListRequest) (*KVListResponse, error)
|
||||
// Account directory: enumerate the accounts the plugin is bound to act on and
|
||||
// resolve the outbound identity (credentials/headers/proxy) the host would
|
||||
// attach to a live request. The host restricts both calls to accounts the
|
||||
// requesting plugin's capability already covers.
|
||||
// Account directory: enumerate the accounts the plugin is bound to act on
|
||||
// (with full readable metadata) and resolve the outbound identity
|
||||
// (credentials/headers/proxy) the host would attach to a live request. The
|
||||
// host restricts both calls to the account scope the requesting plugin's
|
||||
// declared capabilities cover; a plugin can never widen that scope. Metadata
|
||||
// never carries raw credentials — those are only handed out by
|
||||
// ResolveOutboundIdentity, the dedicated (equally sensitive) credential channel.
|
||||
ListAccounts(context.Context, *ListAccountsRequest) (*ListAccountsResponse, error)
|
||||
ResolveOutboundIdentity(context.Context, *ResolveOutboundIdentityRequest) (*ResolveOutboundIdentityResponse, error)
|
||||
mustEmbedUnimplementedHostServiceServer()
|
||||
|
||||
@@ -17,7 +17,11 @@ const (
|
||||
// HostServiceAPIVersion 是宿主反向服务(HostService)的契约版本。它独立于
|
||||
// TransportAPIVersion:宿主服务是叠加在传输契约之上的可选能力,通过
|
||||
// InitHostServices 在运行时协商,因此新增宿主能力不会使既有插件失效。
|
||||
HostServiceAPIVersion = 1
|
||||
//
|
||||
// v2:ListAccounts 在 account_ids 之外增补 AccountInfo 全量可读元数据
|
||||
// (状态/调度/限流/暂停原因等),供插件排除不可调度账号。纯加法变更,v1
|
||||
// 插件忽略新字段即可继续工作。
|
||||
HostServiceAPIVersion = 2
|
||||
// TransportPluginName 是 go-plugin 中注册的唯一能力名称。
|
||||
TransportPluginName = "oauth_transport"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user