From 5d4c7f32f4aece1cddb4cc32bafd0e9b1ada670b Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Tue, 4 Aug 2026 00:31:53 +0000 Subject: [PATCH] [management] Add peer-facing Agent Network setup RPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Peers onboarding to the Agent Network have no way to discover which providers and models their groups authorize or which endpoint to call, so they trial-and-error into 403s at the proxy. Add GetAgentNetworkSetup, an EncryptedMessage peer RPC following the Expose service shape: the WireGuard key is the credential and the answer is caller-scoped — strictly what the calling peer's own groups authorize, computed by a new effective-setup routine in the agentnetwork manager that mirrors the proxy's enforcement exactly (policy filter as filterApplicablePolicies, model logic as policyPermitsModel, orphan and disabled providers omitted like the router synthesizer omits them). The response carries display metadata only: endpoint, provider name, catalog id, API flavor, and effective models. No keys, upstream URLs, policy or guardrail structure, and no hint of providers the caller cannot reach; "account not set up" and "caller has no access" are deliberately indistinguishable. Linear: NET-1399 --- .../internals/modules/agentnetwork/manager.go | 5 + .../internals/modules/agentnetwork/setup.go | 240 ++++++++ .../agentnetwork/setup_realstore_test.go | 271 +++++++++ .../modules/agentnetwork/types/setup.go | 40 ++ management/internals/server/boot.go | 1 + .../shared/grpc/agentnetwork_service.go | 81 +++ management/internals/shared/grpc/server.go | 3 + shared/management/proto/management.pb.go | 565 +++++++++++++----- shared/management/proto/management.proto | 38 ++ shared/management/proto/management_grpc.pb.go | 46 ++ 10 files changed, 1140 insertions(+), 150 deletions(-) create mode 100644 management/internals/modules/agentnetwork/setup.go create mode 100644 management/internals/modules/agentnetwork/setup_realstore_test.go create mode 100644 management/internals/modules/agentnetwork/types/setup.go create mode 100644 management/internals/shared/grpc/agentnetwork_service.go diff --git a/management/internals/modules/agentnetwork/manager.go b/management/internals/modules/agentnetwork/manager.go index ba2c06826..770f9c89e 100644 --- a/management/internals/modules/agentnetwork/manager.go +++ b/management/internals/modules/agentnetwork/manager.go @@ -82,6 +82,11 @@ type Manager interface { RecordAccountBudgetUsage(ctx context.Context, accountID, userID string, groupIDs []string, tokensIn, tokensOut int64, costUSD float64) error RecordUsage(ctx context.Context, in RecordUsageInput) error SelectPolicyForRequest(ctx context.Context, in PolicySelectionInput) (*PolicySelectionResult, error) + + // GetSetupForPeer returns the Agent Network connection info the peer's + // groups authorize (endpoint, providers, effective models). Caller-scoped + // by design — no user permission gate; see the implementation. + GetSetupForPeer(ctx context.Context, accountID, peerID string) (*types.EffectiveSetup, error) } // PolicySelectionInput is the per-request selection envelope. The diff --git a/management/internals/modules/agentnetwork/setup.go b/management/internals/modules/agentnetwork/setup.go new file mode 100644 index 000000000..f18b43769 --- /dev/null +++ b/management/internals/modules/agentnetwork/setup.go @@ -0,0 +1,240 @@ +package agentnetwork + +import ( + "context" + "fmt" + "sort" + + "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/catalog" + "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" + "github.com/netbirdio/netbird/management/server/store" +) + +// GetSetupForPeer returns the Agent Network setup the calling peer's +// groups authorize. It deliberately performs no user-permission check: +// peer RPCs carry no user identity — WireGuard key possession is the +// credential (same trust model as SelectPolicyForRequest) — and the +// result is caller-scoped, which is strictly tighter than any role gate. +// Setup-key/machine peers (no user attached) are fully supported since +// the computation runs on peer groups, which is what the proxy enforces. +func (m *managerImpl) GetSetupForPeer(ctx context.Context, accountID, peerID string) (*types.EffectiveSetup, error) { + groupIDs, err := m.store.GetPeerGroupIDs(ctx, store.LockingStrengthNone, accountID, peerID) + if err != nil { + return nil, fmt.Errorf("get peer groups: %w", err) + } + return m.effectiveSetupForGroups(ctx, accountID, groupIDs) +} + +// effectiveSetupForGroups computes the effective Agent Network setup for +// a set of caller groups: the account endpoint plus, per authorized +// provider, the effective model set. It mirrors what the proxy enforces +// at request time — the policy filter matches filterApplicablePolicies, +// the model logic matches policyPermitsModel, and orphan providers +// (enabled but referenced by no applicable policy) are omitted just like +// the router synthesizer omits them — so the answer never advertises +// anything the proxy would refuse. +// +// Every "nothing available" shape returns Configured=false rather than +// an error, and "account not set up" is indistinguishable from "caller +// has no access" by design: the response must not leak what exists for +// others. +func (m *managerImpl) effectiveSetupForGroups(ctx context.Context, accountID string, groupIDs []string) (*types.EffectiveSetup, error) { + notConfigured := &types.EffectiveSetup{Providers: []types.EffectiveProvider{}} + + settings, err := m.store.GetAgentNetworkSettings(ctx, store.LockingStrengthNone, accountID) + switch { + case err == nil: + case isNotFound(err): + return notConfigured, nil + default: + return nil, fmt.Errorf("get agent network settings: %w", err) + } + if settings.Endpoint() == "" { + return notConfigured, nil + } + + policies, err := m.store.GetAccountAgentNetworkPolicies(ctx, store.LockingStrengthNone, accountID) + if err != nil { + return nil, fmt.Errorf("list account policies: %w", err) + } + applicable := filterPoliciesByGroups(policies, groupIDs) + if len(applicable) == 0 { + return notConfigured, nil + } + + providers, err := m.store.GetAccountAgentNetworkProviders(ctx, store.LockingStrengthNone, accountID) + if err != nil { + return nil, fmt.Errorf("list account providers: %w", err) + } + + var guardrailsByID map[string]*types.Guardrail + if anyPolicyHasGuardrails(applicable) { + guardrailsByID, err = m.loadGuardrailsByID(ctx, accountID) + if err != nil { + return nil, err + } + } + + authorized := make([]*types.Provider, 0, len(providers)) + for _, p := range providers { + if p == nil || !p.Enabled { + continue + } + if len(policiesForProvider(applicable, p.ID)) == 0 { + continue + } + authorized = append(authorized, p) + } + if len(authorized) == 0 { + return notConfigured, nil + } + // created_at order, ID tiebreak — same deterministic order the router + // synthesizer presents. + sort.SliceStable(authorized, func(i, j int) bool { + if !authorized[i].CreatedAt.Equal(authorized[j].CreatedAt) { + return authorized[i].CreatedAt.Before(authorized[j].CreatedAt) + } + return authorized[i].ID < authorized[j].ID + }) + + out := &types.EffectiveSetup{ + Configured: true, + Endpoint: "https://" + settings.Endpoint(), + Providers: make([]types.EffectiveProvider, 0, len(authorized)), + } + for _, p := range authorized { + allAllowed, models := effectiveModelsForProvider(p, policiesForProvider(applicable, p.ID), guardrailsByID) + flavor := "" + if entry, ok := catalog.Lookup(p.ProviderID); ok { + flavor = entry.ParserID + } + out.Providers = append(out.Providers, types.EffectiveProvider{ + Name: p.Name, + CatalogID: p.ProviderID, + APIFlavor: flavor, + AllModelsAllowed: allAllowed, + Models: models, + }) + } + return out, nil +} + +// filterPoliciesByGroups returns the enabled policies whose SourceGroups +// intersect the caller's groups. Same group matching as +// filterApplicablePolicies, without the per-provider filter — the setup +// answer spans every provider the caller can reach. +func filterPoliciesByGroups(policies []*types.Policy, groupIDs []string) []*types.Policy { + groupSet := make(map[string]struct{}, len(groupIDs)) + for _, g := range groupIDs { + if g != "" { + groupSet[g] = struct{}{} + } + } + out := make([]*types.Policy, 0, len(policies)) + for _, p := range policies { + if p == nil || !p.Enabled { + continue + } + if !anyGroupMatches(p.SourceGroups, groupSet) { + continue + } + out = append(out, p) + } + return out +} + +// policiesForProvider returns the subset of policies targeting the +// provider, order preserved. +func policiesForProvider(policies []*types.Policy, providerID string) []*types.Policy { + out := make([]*types.Policy, 0, len(policies)) + for _, p := range policies { + if sliceContains(p.DestinationProviderIDs, providerID) { + out = append(out, p) + } + } + return out +} + +// effectiveModelsForProvider derives the caller's effective model set for +// one provider from the applicable policies that target it, mirroring +// policyPermitsModel: a policy with no allowlist-enabled guardrail is +// unrestricted, and one unrestricted policy makes the whole provider +// unrestricted (the proxy would admit any model through it). Otherwise +// the union of the policies' allowlists applies, intersected with the +// provider's declared models when the operator declared any — the router +// only claims declared models, so an allowlisted-but-undeclared model is +// unreachable and must not be advertised. With no declared models the +// router claims every model, so the allowlist union stands alone. +func effectiveModelsForProvider(provider *types.Provider, policies []*types.Policy, guardrailsByID map[string]*types.Guardrail) (bool, []string) { + restricted := true + union := make([]string, 0) + seen := make(map[string]struct{}) + for _, p := range policies { + policyRestricted := false + for _, gID := range p.GuardrailIDs { + g, ok := guardrailsByID[gID] + if !ok || g == nil || !g.Checks.ModelAllowlist.Enabled { + continue + } + policyRestricted = true + for _, model := range g.Checks.ModelAllowlist.Models { + key := normaliseModelID(model) + if key == "" { + continue + } + if _, dup := seen[key]; dup { + continue + } + seen[key] = struct{}{} + union = append(union, key) + } + } + if !policyRestricted { + restricted = false + } + } + + declared := declaredModelIDs(provider) + if !restricted { + return true, declared + } + if len(provider.Models) == 0 { + // No operator declaration: the router claims every model, so the + // allowlist union is the effective set as-is. + return false, union + } + out := make([]string, 0, len(declared)) + for _, id := range declared { + if _, ok := seen[normaliseModelID(id)]; ok { + out = append(out, id) + } + } + return false, out +} + +// declaredModelIDs returns the models a provider exposes: the operator's +// curated list when present, otherwise the catalog entry's models (an +// empty operator list means "all catalog models"). Gateway/custom catalog +// entries declare no models, so the result may be empty. +func declaredModelIDs(provider *types.Provider) []string { + if ids := providerModelIDs(provider); len(ids) > 0 { + return ids + } + entry, ok := catalog.Lookup(provider.ProviderID) + if !ok { + return []string{} + } + out := make([]string, 0, len(entry.Models)) + for _, m := range entry.Models { + if m.ID != "" { + out = append(out, m.ID) + } + } + return out +} + +// GetSetupForPeer on the mock manager reports "not configured" so tests +// that don't care about setup still compile. +func (*mockManager) GetSetupForPeer(_ context.Context, _, _ string) (*types.EffectiveSetup, error) { + return &types.EffectiveSetup{Providers: []types.EffectiveProvider{}}, nil +} diff --git a/management/internals/modules/agentnetwork/setup_realstore_test.go b/management/internals/modules/agentnetwork/setup_realstore_test.go new file mode 100644 index 000000000..f0862f869 --- /dev/null +++ b/management/internals/modules/agentnetwork/setup_realstore_test.go @@ -0,0 +1,271 @@ +package agentnetwork + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" + "github.com/netbirdio/netbird/management/server/store" +) + +// These tests drive the effective-setup computation through the real +// sqlite store, mirroring the policyselect realstore suite: assert on +// observable answers (configured / providers / models), not on which +// store methods get called. The computation must agree with what the +// proxy enforces — policy filtering matches filterApplicablePolicies, +// model logic matches policyPermitsModel, and orphan providers are +// omitted like the router synthesizer omits them. + +func newSetupTestMgr(t *testing.T) (*managerImpl, store.Store) { + t.Helper() + ctx := context.Background() + s, cleanup, err := store.NewTestStoreFromSQL(ctx, "", t.TempDir()) + require.NoError(t, err, "real sqlite test store must come up") + t.Cleanup(cleanup) + return &managerImpl{store: s}, s +} + +// newSetupTestGuardrail returns an allowlist-enabled guardrail. +func newSetupTestGuardrail(id string, models ...string) *types.Guardrail { + return &types.Guardrail{ + ID: id, + AccountID: testAccountID, + Name: "allowlist " + id, + Checks: types.GuardrailChecks{ + ModelAllowlist: types.GuardrailModelAllowlist{Enabled: true, Models: models}, + }, + } +} + +func TestEffectiveSetup_RealStore_NoSettingsRow(t *testing.T) { + mgr, _ := newSetupTestMgr(t) + + setup, err := mgr.effectiveSetupForGroups(context.Background(), testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + assert.False(t, setup.Configured, "account without settings must read as not configured") + assert.Empty(t, setup.Endpoint) + assert.Empty(t, setup.Providers) +} + +func TestEffectiveSetup_RealStore_NoApplicablePolicy(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, newSynthTestPolicy(provider.ID, "grp-eng", ""))) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-other"}) + require.NoError(t, err) + assert.False(t, setup.Configured, "caller outside every policy's source groups must read as not configured") + assert.Empty(t, setup.Endpoint, "no-access answer must not leak the endpoint") + assert.Empty(t, setup.Providers) +} + +func TestEffectiveSetup_RealStore_UnrestrictedPolicyListsDeclaredModels(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, newSynthTestPolicy(provider.ID, "grp-eng", ""))) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + assert.True(t, setup.Configured) + assert.Equal(t, "https://"+testEndpoint, setup.Endpoint) + require.Len(t, setup.Providers, 1) + p := setup.Providers[0] + assert.Equal(t, "OpenAI", p.Name) + assert.Equal(t, "openai_api", p.CatalogID) + assert.Equal(t, "openai", p.APIFlavor) + assert.True(t, p.AllModelsAllowed, "policy without allowlist guardrail is unrestricted") + assert.Equal(t, []string{"gpt-5.4"}, p.Models, "declared models listed as a courtesy") +} + +func TestEffectiveSetup_RealStore_AllowlistIntersectsDeclaredModels(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + provider.Models = []types.ProviderModel{{ID: "gpt-5.4"}, {ID: "gpt-4o"}} + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + // Allowlist admits gpt-5.4 (declared, odd casing/spacing) and gpt-4.1 + // (NOT declared — the router would never route it, so it must not be + // advertised). + require.NoError(t, s.SaveAgentNetworkGuardrail(ctx, newSetupTestGuardrail("guard-1", " GPT-5.4 ", "gpt-4.1"))) + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, newSynthTestPolicy(provider.ID, "grp-eng", "guard-1"))) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + require.Len(t, setup.Providers, 1) + p := setup.Providers[0] + assert.False(t, p.AllModelsAllowed) + assert.Equal(t, []string{"gpt-5.4"}, p.Models, "allowlist ∩ declared, in declared order and casing") +} + +func TestEffectiveSetup_RealStore_UnrestrictedPolicyWinsOverRestricted(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + require.NoError(t, s.SaveAgentNetworkGuardrail(ctx, newSetupTestGuardrail("guard-1", "gpt-5.4"))) + restricted := newSynthTestPolicy(provider.ID, "grp-eng", "guard-1") + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, restricted)) + open := newSynthTestPolicy(provider.ID, "grp-eng", "") + open.ID = "pol-2" + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, open)) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + require.Len(t, setup.Providers, 1) + assert.True(t, setup.Providers[0].AllModelsAllowed, + "one applicable policy without an allowlist makes the provider unrestricted — the proxy would admit any model through it") +} + +func TestEffectiveSetup_RealStore_AllowlistUnionAcrossPolicies(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + provider.Models = []types.ProviderModel{{ID: "gpt-5.4"}, {ID: "gpt-4o"}, {ID: "o4-mini"}} + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + require.NoError(t, s.SaveAgentNetworkGuardrail(ctx, newSetupTestGuardrail("guard-1", "gpt-5.4"))) + require.NoError(t, s.SaveAgentNetworkGuardrail(ctx, newSetupTestGuardrail("guard-2", "gpt-4o"))) + p1 := newSynthTestPolicy(provider.ID, "grp-eng", "guard-1") + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, p1)) + p2 := newSynthTestPolicy(provider.ID, "grp-eng", "guard-2") + p2.ID = "pol-2" + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, p2)) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + require.Len(t, setup.Providers, 1) + p := setup.Providers[0] + assert.False(t, p.AllModelsAllowed) + assert.ElementsMatch(t, []string{"gpt-5.4", "gpt-4o"}, p.Models, "union of allowlists across applicable policies") +} + +func TestEffectiveSetup_RealStore_OrphanAndDisabledProvidersOmitted(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + // Orphan: enabled but referenced by no policy. + orphan := newSynthTestProvider() + orphan.ID = "prov-orphan" + require.NoError(t, s.SaveAgentNetworkProvider(ctx, orphan)) + // Disabled but referenced by an applicable policy. + disabled := newSynthTestProvider() + disabled.ID = "prov-disabled" + disabled.Enabled = false + require.NoError(t, s.SaveAgentNetworkProvider(ctx, disabled)) + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, newSynthTestPolicy(disabled.ID, "grp-eng", ""))) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + assert.False(t, setup.Configured, "neither an orphan nor a disabled provider is reachable, so nothing is configured for the caller") + assert.Empty(t, setup.Providers) +} + +func TestEffectiveSetup_RealStore_DisabledPolicyIgnored(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + policy := newSynthTestPolicy(provider.ID, "grp-eng", "") + policy.Enabled = false + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, policy)) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + assert.False(t, setup.Configured) +} + +func TestEffectiveSetup_RealStore_UndeclaredModelsUseAllowlistAsIs(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + // Gateway-style provider: no declared models — the router claims every + // model, so the allowlist union is the effective set on its own. + provider := newSynthTestProvider() + provider.ProviderID = "litellm_proxy" + provider.Name = "LiteLLM" + provider.Models = nil + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + require.NoError(t, s.SaveAgentNetworkGuardrail(ctx, newSetupTestGuardrail("guard-1", "claude-sonnet-4-5"))) + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, newSynthTestPolicy(provider.ID, "grp-eng", "guard-1"))) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + require.Len(t, setup.Providers, 1) + p := setup.Providers[0] + assert.False(t, p.AllModelsAllowed) + assert.Equal(t, []string{"claude-sonnet-4-5"}, p.Models) +} + +func TestEffectiveSetup_RealStore_ProvidersInCreatedAtOrder(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + newer := newSynthTestProvider() + newer.ID = "prov-newer" + newer.Name = "Newer" + newer.CreatedAt = time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC) + require.NoError(t, s.SaveAgentNetworkProvider(ctx, newer)) + older := newSynthTestProvider() + older.ID = "prov-older" + older.Name = "Older" + older.CreatedAt = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + require.NoError(t, s.SaveAgentNetworkProvider(ctx, older)) + + policy := newSynthTestPolicy(newer.ID, "grp-eng", "") + policy.DestinationProviderIDs = []string{newer.ID, older.ID} + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, policy)) + + setup, err := mgr.effectiveSetupForGroups(ctx, testAccountID, []string{"grp-eng"}) + require.NoError(t, err) + require.Len(t, setup.Providers, 2) + assert.Equal(t, "Older", setup.Providers[0].Name) + assert.Equal(t, "Newer", setup.Providers[1].Name) +} + +// TestGetSetupForPeer_RealStore pins the peer entry point: the peer's +// group memberships (not any user identity) scope the answer, which is +// exactly what the proxy enforces via AllowedGroupIDs — including for +// setup-key/machine peers that have no user attached. +func TestGetSetupForPeer_RealStore(t *testing.T) { + mgr, s := newSetupTestMgr(t) + ctx := context.Background() + + require.NoError(t, s.SaveAgentNetworkSettings(ctx, newSynthTestSettings())) + provider := newSynthTestProvider() + require.NoError(t, s.SaveAgentNetworkProvider(ctx, provider)) + require.NoError(t, s.SaveAgentNetworkPolicy(ctx, newSynthTestPolicy(provider.ID, "grp-eng", ""))) + + require.NoError(t, s.AddPeerToGroup(ctx, testAccountID, "peer-in", "grp-eng")) + require.NoError(t, s.AddPeerToGroup(ctx, testAccountID, "peer-out", "grp-other")) + + setupIn, err := mgr.GetSetupForPeer(ctx, testAccountID, "peer-in") + require.NoError(t, err) + assert.True(t, setupIn.Configured) + require.Len(t, setupIn.Providers, 1) + + setupOut, err := mgr.GetSetupForPeer(ctx, testAccountID, "peer-out") + require.NoError(t, err) + assert.False(t, setupOut.Configured, "peer outside the policy's source groups gets the not-configured answer") +} diff --git a/management/internals/modules/agentnetwork/types/setup.go b/management/internals/modules/agentnetwork/types/setup.go new file mode 100644 index 000000000..e6724f6e9 --- /dev/null +++ b/management/internals/modules/agentnetwork/types/setup.go @@ -0,0 +1,40 @@ +package types + +// EffectiveSetup is the caller-scoped answer to "what may this caller +// use on the Agent Network?" — the account's proxy endpoint plus the +// providers and models the caller's groups authorize. It intentionally +// carries display metadata only: no keys, no upstream URLs, no policy or +// guardrail structure, and no hint of providers the caller cannot reach. +type EffectiveSetup struct { + // Configured is false when the account has no Agent Network set up or + // when nothing is authorized for the caller's groups — the two cases + // are deliberately indistinguishable so the response leaks nothing + // about what exists for others. + Configured bool + // Endpoint is the account's proxy base URL + // ("https://."), reachable over the NetBird tunnel + // only. Empty when Configured is false. + Endpoint string + // Providers lists the providers at least one applicable policy + // authorizes for the caller, in the account's created_at order. + Providers []EffectiveProvider +} + +// EffectiveProvider is one authorized provider in an EffectiveSetup. +type EffectiveProvider struct { + // Name is the operator-assigned label, e.g. "Bedrock prod". + Name string + // CatalogID names the catalog entry, e.g. "anthropic_api". + CatalogID string + // APIFlavor is the request-body shape the provider speaks — the + // catalog entry's parser id ("anthropic", "openai"); empty when the + // proxy dispatches the provider by URL path instead. + APIFlavor string + // AllModelsAllowed is true when no model allowlist restricts this + // provider for the caller. Models then lists the declared/catalog + // models as a courtesy (possibly none for gateway-style providers). + AllModelsAllowed bool + // Models is the effective model allowlist for the caller, or the + // declared/catalog models when AllModelsAllowed is true. + Models []string +} diff --git a/management/internals/server/boot.go b/management/internals/server/boot.go index 2a1e521aa..c671f0b67 100644 --- a/management/internals/server/boot.go +++ b/management/internals/server/boot.go @@ -211,6 +211,7 @@ func (s *BaseServer) GRPCServer() *grpc.Server { } serviceMgr := s.ServiceManager() srv.SetReverseProxyManager(serviceMgr) + srv.SetAgentNetworkSetupService(s.AgentNetworkManager()) if serviceMgr != nil { serviceMgr.StartExposeReaper(context.Background()) } diff --git a/management/internals/shared/grpc/agentnetwork_service.go b/management/internals/shared/grpc/agentnetwork_service.go new file mode 100644 index 000000000..33b3ee36c --- /dev/null +++ b/management/internals/shared/grpc/agentnetwork_service.go @@ -0,0 +1,81 @@ +package grpc + +import ( + "context" + + log "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + antypes "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// AgentNetworkSetupService is the minimal slice of agentnetwork.Manager +// the peer-facing setup RPC needs. Narrow on purpose so the gRPC server +// never sees the manager's operator-facing surface. +type AgentNetworkSetupService interface { + GetSetupForPeer(ctx context.Context, accountID, peerID string) (*antypes.EffectiveSetup, error) +} + +// GetAgentNetworkSetup handles a peer request for its Agent Network +// connection info. The WireGuard key is the credential (same trust model +// as the Expose RPCs); the response is scoped to what the calling peer's +// own groups authorize and carries display metadata only. +func (s *Server) GetAgentNetworkSetup(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { + setupReq := &proto.AgentNetworkSetupRequest{} + peerKey, err := s.parseRequest(ctx, req, setupReq) + if err != nil { + return nil, err + } + + accountID, peer, err := s.authenticateExposePeer(ctx, peerKey) + if err != nil { + return nil, err + } + + setupSvc := s.getAgentNetworkSetupService() + if setupSvc == nil { + return nil, status.Errorf(codes.Internal, "agent network manager not available") + } + + setup, err := setupSvc.GetSetupForPeer(ctx, accountID, peer.ID) + if err != nil { + log.WithContext(ctx).Errorf("get agent network setup for peer %s: %v", peer.ID, err) + return nil, status.Errorf(codes.Internal, "internal error") + } + + return s.encryptResponse(peerKey, toProtoAgentNetworkSetup(setup)) +} + +func toProtoAgentNetworkSetup(setup *antypes.EffectiveSetup) *proto.AgentNetworkSetupResponse { + resp := &proto.AgentNetworkSetupResponse{ + Configured: setup.Configured, + Endpoint: setup.Endpoint, + Providers: make([]*proto.AgentNetworkProviderInfo, 0, len(setup.Providers)), + } + for _, p := range setup.Providers { + resp.Providers = append(resp.Providers, &proto.AgentNetworkProviderInfo{ + Name: p.Name, + CatalogId: p.CatalogID, + ApiFlavor: p.APIFlavor, + AllModelsAllowed: p.AllModelsAllowed, + Models: p.Models, + }) + } + return resp +} + +func (s *Server) getAgentNetworkSetupService() AgentNetworkSetupService { + s.agentNetworkSetupMu.RLock() + defer s.agentNetworkSetupMu.RUnlock() + return s.agentNetworkSetup +} + +// SetAgentNetworkSetupService wires the agent-network setup service on +// the server. +func (s *Server) SetAgentNetworkSetupService(svc AgentNetworkSetupService) { + s.agentNetworkSetupMu.Lock() + defer s.agentNetworkSetupMu.Unlock() + s.agentNetworkSetup = svc +} diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index 485f05a92..ba536247c 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -87,6 +87,9 @@ type Server struct { reverseProxyManager rpservice.Manager reverseProxyMu sync.RWMutex + + agentNetworkSetup AgentNetworkSetupService + agentNetworkSetupMu sync.RWMutex } // NewServer creates a new Management server diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 02273a036..2925473b7 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -6653,6 +6653,201 @@ func (x *PeerIndexSet) GetPeerIndexes() []uint32 { return nil } +type AgentNetworkSetupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *AgentNetworkSetupRequest) Reset() { + *x = AgentNetworkSetupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[76] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AgentNetworkSetupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentNetworkSetupRequest) ProtoMessage() {} + +func (x *AgentNetworkSetupRequest) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[76] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentNetworkSetupRequest.ProtoReflect.Descriptor instead. +func (*AgentNetworkSetupRequest) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{76} +} + +type AgentNetworkSetupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // configured is false when the account has no Agent Network set up or + // the calling peer's groups authorize none of it — the two cases are + // deliberately indistinguishable. + Configured bool `protobuf:"varint,1,opt,name=configured,proto3" json:"configured,omitempty"` + // endpoint is the account's proxy base URL, e.g. + // "https://calm-otter.proxy.example.com". Reachable over the NetBird + // tunnel only. Empty when configured is false. + Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + Providers []*AgentNetworkProviderInfo `protobuf:"bytes,3,rep,name=providers,proto3" json:"providers,omitempty"` +} + +func (x *AgentNetworkSetupResponse) Reset() { + *x = AgentNetworkSetupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[77] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AgentNetworkSetupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentNetworkSetupResponse) ProtoMessage() {} + +func (x *AgentNetworkSetupResponse) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[77] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentNetworkSetupResponse.ProtoReflect.Descriptor instead. +func (*AgentNetworkSetupResponse) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{77} +} + +func (x *AgentNetworkSetupResponse) GetConfigured() bool { + if x != nil { + return x.Configured + } + return false +} + +func (x *AgentNetworkSetupResponse) GetEndpoint() string { + if x != nil { + return x.Endpoint + } + return "" +} + +func (x *AgentNetworkSetupResponse) GetProviders() []*AgentNetworkProviderInfo { + if x != nil { + return x.Providers + } + return nil +} + +type AgentNetworkProviderInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // name is the operator-assigned provider label, e.g. "Bedrock prod". + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // catalog_id names the catalog entry, e.g. "anthropic_api", "bedrock_api". + CatalogId string `protobuf:"bytes,2,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty"` + // api_flavor is the request-body shape the provider speaks ("anthropic", + // "openai"); empty when the proxy dispatches it by URL path instead. + ApiFlavor string `protobuf:"bytes,3,opt,name=api_flavor,json=apiFlavor,proto3" json:"api_flavor,omitempty"` + // all_models_allowed is true when no model allowlist restricts this + // provider for the caller; models then lists the declared/catalog models + // as a courtesy (possibly none for gateway-style providers). + AllModelsAllowed bool `protobuf:"varint,4,opt,name=all_models_allowed,json=allModelsAllowed,proto3" json:"all_models_allowed,omitempty"` + // models is the effective model allowlist for the caller (or the + // declared/catalog models when all_models_allowed is true). + Models []string `protobuf:"bytes,5,rep,name=models,proto3" json:"models,omitempty"` +} + +func (x *AgentNetworkProviderInfo) Reset() { + *x = AgentNetworkProviderInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[78] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AgentNetworkProviderInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentNetworkProviderInfo) ProtoMessage() {} + +func (x *AgentNetworkProviderInfo) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[78] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentNetworkProviderInfo.ProtoReflect.Descriptor instead. +func (*AgentNetworkProviderInfo) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{78} +} + +func (x *AgentNetworkProviderInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AgentNetworkProviderInfo) GetCatalogId() string { + if x != nil { + return x.CatalogId + } + return "" +} + +func (x *AgentNetworkProviderInfo) GetApiFlavor() string { + if x != nil { + return x.ApiFlavor + } + return "" +} + +func (x *AgentNetworkProviderInfo) GetAllModelsAllowed() bool { + if x != nil { + return x.AllModelsAllowed + } + return false +} + +func (x *AgentNetworkProviderInfo) GetModels() []string { + if x != nil { + return x.Models + } + return nil +} + type PortInfo_Range struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -6665,7 +6860,7 @@ type PortInfo_Range struct { func (x *PortInfo_Range) Reset() { *x = PortInfo_Range{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[77] + mi := &file_management_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6678,7 +6873,7 @@ func (x *PortInfo_Range) String() string { func (*PortInfo_Range) ProtoMessage() {} func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[77] + mi := &file_management_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7744,99 +7939,127 @@ var file_management_proto_rawDesc = []byte{ 0x0c, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, - 0x2a, 0x3a, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, - 0x0e, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x10, - 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x10, 0x01, - 0x12, 0x0a, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x2a, 0x93, 0x01, 0x0a, - 0x0e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, - 0x19, 0x0a, 0x15, 0x50, 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x65, - 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, - 0x50, 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x50, - 0x76, 0x36, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x50, - 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, - 0x10, 0x03, 0x2a, 0x5d, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, - 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, - 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, - 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x45, 0x54, 0x42, 0x49, 0x52, 0x44, 0x5f, 0x53, 0x53, 0x48, 0x10, - 0x06, 0x2a, 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, - 0x54, 0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, - 0x04, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x2a, 0x63, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6f, 0x73, - 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x58, 0x50, - 0x4f, 0x53, 0x45, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, - 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, - 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, - 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, - 0x45, 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x32, 0xd0, 0x07, 0x0a, - 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, - 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, - 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, - 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, - 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, - 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, - 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, - 0x12, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, - 0x3b, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x03, - 0x4a, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x11, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x41, - 0x75, 0x74, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, + 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9b, 0x01, 0x0a, + 0x19, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x70, + 0x69, 0x5f, 0x66, 0x6c, 0x61, 0x76, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x70, 0x69, 0x46, 0x6c, 0x61, 0x76, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x6c, 0x6c, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, + 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2a, + 0x3a, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, + 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x10, 0x01, 0x12, + 0x0a, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x2a, 0x93, 0x01, 0x0a, 0x0e, + 0x50, 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x19, + 0x0a, 0x15, 0x50, 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, + 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x65, 0x65, + 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x50, + 0x65, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x50, 0x76, + 0x36, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x50, 0x65, + 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x10, + 0x03, 0x2a, 0x5d, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, + 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, + 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, + 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, 0x12, + 0x0f, 0x0a, 0x0b, 0x4e, 0x45, 0x54, 0x42, 0x49, 0x52, 0x44, 0x5f, 0x53, 0x53, 0x48, 0x10, 0x06, + 0x2a, 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, + 0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, + 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x2a, 0x63, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x58, 0x50, 0x4f, + 0x53, 0x45, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x50, + 0x4f, 0x53, 0x45, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, + 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x45, + 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x45, + 0x58, 0x50, 0x4f, 0x53, 0x45, 0x5f, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x32, 0xa6, 0x08, 0x0a, 0x11, + 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0b, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x45, - 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, + 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, + 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x73, - 0x65, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, + 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, - 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, + 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, + 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x03, 0x4a, + 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, + 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x11, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x41, 0x75, + 0x74, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0b, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x45, 0x78, + 0x70, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, + 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x54, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7852,7 +8075,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 83) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 86) var file_management_proto_goTypes = []interface{}{ (JobStatus)(0), // 0: management.JobStatus (PeerCapability)(0), // 1: management.PeerCapability @@ -7938,15 +8161,18 @@ var file_management_proto_goTypes = []interface{}{ (*PolicyIds)(nil), // 81: management.PolicyIds (*UserIDList)(nil), // 82: management.UserIDList (*PeerIndexSet)(nil), // 83: management.PeerIndexSet - nil, // 84: management.SSHAuth.MachineUsersEntry - (*PortInfo_Range)(nil), // 85: management.PortInfo.Range - nil, // 86: management.NetworkMapComponentsFull.RoutersMapEntry - nil, // 87: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - nil, // 88: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - nil, // 89: management.NetworkMapComponentsFull.PostureFailedPeersEntry - nil, // 90: management.PolicyCompact.AuthorizedGroupsEntry - (*timestamppb.Timestamp)(nil), // 91: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 92: google.protobuf.Duration + (*AgentNetworkSetupRequest)(nil), // 84: management.AgentNetworkSetupRequest + (*AgentNetworkSetupResponse)(nil), // 85: management.AgentNetworkSetupResponse + (*AgentNetworkProviderInfo)(nil), // 86: management.AgentNetworkProviderInfo + nil, // 87: management.SSHAuth.MachineUsersEntry + (*PortInfo_Range)(nil), // 88: management.PortInfo.Range + nil, // 89: management.NetworkMapComponentsFull.RoutersMapEntry + nil, // 90: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + nil, // 91: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + nil, // 92: management.NetworkMapComponentsFull.PostureFailedPeersEntry + nil, // 93: management.PolicyCompact.AuthorizedGroupsEntry + (*timestamppb.Timestamp)(nil), // 94: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 95: google.protobuf.Duration } var file_management_proto_depIdxs = []int32{ 11, // 0: management.JobRequest.bundle:type_name -> management.BundleParameters @@ -7958,7 +8184,7 @@ var file_management_proto_depIdxs = []int32{ 39, // 6: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig 36, // 7: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap 54, // 8: management.SyncResponse.Checks:type_name -> management.Checks - 91, // 9: management.SyncResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp + 94, // 9: management.SyncResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp 64, // 10: management.SyncResponse.NetworkMapEnvelope:type_name -> management.NetworkMapEnvelope 21, // 11: management.SyncMetaRequest.meta:type_name -> management.PeerSystemMeta 21, // 12: management.LoginRequest.meta:type_name -> management.PeerSystemMeta @@ -7971,10 +8197,10 @@ var file_management_proto_depIdxs = []int32{ 27, // 19: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig 34, // 20: management.LoginResponse.peerConfig:type_name -> management.PeerConfig 54, // 21: management.LoginResponse.Checks:type_name -> management.Checks - 91, // 22: management.LoginResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp + 94, // 22: management.LoginResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp 21, // 23: management.ExtendAuthSessionRequest.meta:type_name -> management.PeerSystemMeta - 91, // 24: management.ExtendAuthSessionResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp - 91, // 25: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 94, // 24: management.ExtendAuthSessionResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp + 94, // 25: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 28, // 26: management.NetbirdConfig.stuns:type_name -> management.HostConfig 33, // 27: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig 28, // 28: management.NetbirdConfig.signal:type_name -> management.HostConfig @@ -7982,7 +8208,7 @@ var file_management_proto_depIdxs = []int32{ 30, // 30: management.NetbirdConfig.flow:type_name -> management.FlowConfig 31, // 31: management.NetbirdConfig.metrics:type_name -> management.MetricsConfig 6, // 32: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol - 92, // 33: management.FlowConfig.interval:type_name -> google.protobuf.Duration + 95, // 33: management.FlowConfig.interval:type_name -> google.protobuf.Duration 28, // 34: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig 40, // 35: management.PeerConfig.sshConfig:type_name -> management.SSHConfig 35, // 36: management.PeerConfig.autoUpdate:type_name -> management.AutoUpdateSettings @@ -7995,7 +8221,7 @@ var file_management_proto_depIdxs = []int32{ 56, // 43: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule 57, // 44: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule 37, // 45: management.NetworkMap.sshAuth:type_name -> management.SSHAuth - 84, // 46: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry + 87, // 46: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry 40, // 47: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig 32, // 48: management.SSHConfig.jwtConfig:type_name -> management.JWTConfig 7, // 49: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider @@ -8009,7 +8235,7 @@ var file_management_proto_depIdxs = []int32{ 4, // 57: management.FirewallRule.Action:type_name -> management.RuleAction 2, // 58: management.FirewallRule.Protocol:type_name -> management.RuleProtocol 55, // 59: management.FirewallRule.PortInfo:type_name -> management.PortInfo - 85, // 60: management.PortInfo.range:type_name -> management.PortInfo.Range + 88, // 60: management.PortInfo.range:type_name -> management.PortInfo.Range 4, // 61: management.RouteFirewallRule.action:type_name -> management.RuleAction 2, // 62: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol 55, // 63: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo @@ -8031,10 +8257,10 @@ var file_management_proto_depIdxs = []int32{ 49, // 79: management.NetworkMapComponentsFull.all_dns_records:type_name -> management.SimpleRecord 48, // 80: management.NetworkMapComponentsFull.account_zones:type_name -> management.CustomZone 78, // 81: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw - 86, // 82: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry - 87, // 83: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - 88, // 84: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - 89, // 85: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry + 89, // 82: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry + 90, // 83: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + 91, // 84: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + 92, // 85: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry 66, // 86: management.NetworkMapComponentsFull.proxy_patch:type_name -> management.ProxyPatch 39, // 87: management.ProxyPatch.peers:type_name -> management.RemotePeerConfig 39, // 88: management.ProxyPatch.offline_peers:type_name -> management.RemotePeerConfig @@ -8044,49 +8270,52 @@ var file_management_proto_depIdxs = []int32{ 57, // 92: management.ProxyPatch.forwarding_rules:type_name -> management.ForwardingRule 4, // 93: management.PolicyCompact.action:type_name -> management.RuleAction 2, // 94: management.PolicyCompact.protocol:type_name -> management.RuleProtocol - 85, // 95: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range - 90, // 96: management.PolicyCompact.authorized_groups:type_name -> management.PolicyCompact.AuthorizedGroupsEntry + 88, // 95: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range + 93, // 96: management.PolicyCompact.authorized_groups:type_name -> management.PolicyCompact.AuthorizedGroupsEntry 72, // 97: management.PolicyCompact.source_resource:type_name -> management.ResourceCompact 72, // 98: management.PolicyCompact.destination_resource:type_name -> management.ResourceCompact 51, // 99: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer 80, // 100: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry - 38, // 101: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes - 79, // 102: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList - 81, // 103: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIds - 82, // 104: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList - 83, // 105: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet - 73, // 106: management.PolicyCompact.AuthorizedGroupsEntry.value:type_name -> management.UserNameList - 8, // 107: management.ManagementService.Login:input_type -> management.EncryptedMessage - 8, // 108: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 26, // 109: management.ManagementService.GetServerKey:input_type -> management.Empty - 26, // 110: management.ManagementService.isHealthy:input_type -> management.Empty - 8, // 111: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 112: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 113: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage - 8, // 114: management.ManagementService.Logout:input_type -> management.EncryptedMessage - 8, // 115: management.ManagementService.Job:input_type -> management.EncryptedMessage - 8, // 116: management.ManagementService.ExtendAuthSession:input_type -> management.EncryptedMessage - 8, // 117: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage - 8, // 118: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage - 8, // 119: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage - 8, // 120: management.ManagementService.Login:output_type -> management.EncryptedMessage - 8, // 121: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 25, // 122: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 26, // 123: management.ManagementService.isHealthy:output_type -> management.Empty - 8, // 124: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 8, // 125: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage - 26, // 126: management.ManagementService.SyncMeta:output_type -> management.Empty - 26, // 127: management.ManagementService.Logout:output_type -> management.Empty - 8, // 128: management.ManagementService.Job:output_type -> management.EncryptedMessage - 8, // 129: management.ManagementService.ExtendAuthSession:output_type -> management.EncryptedMessage - 8, // 130: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage - 8, // 131: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage - 8, // 132: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage - 120, // [120:133] is the sub-list for method output_type - 107, // [107:120] is the sub-list for method input_type - 107, // [107:107] is the sub-list for extension type_name - 107, // [107:107] is the sub-list for extension extendee - 0, // [0:107] is the sub-list for field type_name + 86, // 101: management.AgentNetworkSetupResponse.providers:type_name -> management.AgentNetworkProviderInfo + 38, // 102: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes + 79, // 103: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList + 81, // 104: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIds + 82, // 105: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList + 83, // 106: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet + 73, // 107: management.PolicyCompact.AuthorizedGroupsEntry.value:type_name -> management.UserNameList + 8, // 108: management.ManagementService.Login:input_type -> management.EncryptedMessage + 8, // 109: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 26, // 110: management.ManagementService.GetServerKey:input_type -> management.Empty + 26, // 111: management.ManagementService.isHealthy:input_type -> management.Empty + 8, // 112: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 113: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 114: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage + 8, // 115: management.ManagementService.Logout:input_type -> management.EncryptedMessage + 8, // 116: management.ManagementService.Job:input_type -> management.EncryptedMessage + 8, // 117: management.ManagementService.ExtendAuthSession:input_type -> management.EncryptedMessage + 8, // 118: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage + 8, // 119: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage + 8, // 120: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage + 8, // 121: management.ManagementService.GetAgentNetworkSetup:input_type -> management.EncryptedMessage + 8, // 122: management.ManagementService.Login:output_type -> management.EncryptedMessage + 8, // 123: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 25, // 124: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 26, // 125: management.ManagementService.isHealthy:output_type -> management.Empty + 8, // 126: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 8, // 127: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage + 26, // 128: management.ManagementService.SyncMeta:output_type -> management.Empty + 26, // 129: management.ManagementService.Logout:output_type -> management.Empty + 8, // 130: management.ManagementService.Job:output_type -> management.EncryptedMessage + 8, // 131: management.ManagementService.ExtendAuthSession:output_type -> management.EncryptedMessage + 8, // 132: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage + 8, // 133: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage + 8, // 134: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage + 8, // 135: management.ManagementService.GetAgentNetworkSetup:output_type -> management.EncryptedMessage + 122, // [122:136] is the sub-list for method output_type + 108, // [108:122] is the sub-list for method input_type + 108, // [108:108] is the sub-list for extension type_name + 108, // [108:108] is the sub-list for extension extendee + 0, // [0:108] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -9007,7 +9236,43 @@ func file_management_proto_init() { return nil } } + file_management_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AgentNetworkSetupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } file_management_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AgentNetworkSetupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AgentNetworkProviderInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PortInfo_Range); i { case 0: return &v.state @@ -9040,7 +9305,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 8, - NumMessages: 83, + NumMessages: 86, NumExtensions: 0, NumServices: 1, }, diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 598f7a579..8dccbcc00 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -68,6 +68,13 @@ service ManagementService { // StopExpose terminates an active expose session rpc StopExpose(EncryptedMessage) returns (EncryptedMessage) {} + + // GetAgentNetworkSetup returns the Agent Network connection info the + // calling peer's groups authorize: proxy endpoint plus effective + // providers and models. Caller-scoped by the peer's WireGuard key. + // EncryptedMessage of the request has a body of AgentNetworkSetupRequest. + // EncryptedMessage of the response has a body of AgentNetworkSetupResponse. + rpc GetAgentNetworkSetup(EncryptedMessage) returns (EncryptedMessage) {} } message EncryptedMessage { @@ -1207,3 +1214,34 @@ message UserIDList { message PeerIndexSet { repeated uint32 peer_indexes = 1; } + +message AgentNetworkSetupRequest {} + +message AgentNetworkSetupResponse { + // configured is false when the account has no Agent Network set up or + // the calling peer's groups authorize none of it — the two cases are + // deliberately indistinguishable. + bool configured = 1; + // endpoint is the account's proxy base URL, e.g. + // "https://calm-otter.proxy.example.com". Reachable over the NetBird + // tunnel only. Empty when configured is false. + string endpoint = 2; + repeated AgentNetworkProviderInfo providers = 3; +} + +message AgentNetworkProviderInfo { + // name is the operator-assigned provider label, e.g. "Bedrock prod". + string name = 1; + // catalog_id names the catalog entry, e.g. "anthropic_api", "bedrock_api". + string catalog_id = 2; + // api_flavor is the request-body shape the provider speaks ("anthropic", + // "openai"); empty when the proxy dispatches it by URL path instead. + string api_flavor = 3; + // all_models_allowed is true when no model allowlist restricts this + // provider for the caller; models then lists the declared/catalog models + // as a courtesy (possibly none for gateway-style providers). + bool all_models_allowed = 4; + // models is the effective model allowlist for the caller (or the + // declared/catalog models when all_models_allowed is true). + repeated string models = 5; +} diff --git a/shared/management/proto/management_grpc.pb.go b/shared/management/proto/management_grpc.pb.go index ce98e4019..6582fc0b3 100644 --- a/shared/management/proto/management_grpc.pb.go +++ b/shared/management/proto/management_grpc.pb.go @@ -65,6 +65,12 @@ type ManagementServiceClient interface { RenewExpose(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*EncryptedMessage, error) // StopExpose terminates an active expose session StopExpose(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*EncryptedMessage, error) + // GetAgentNetworkSetup returns the Agent Network connection info the + // calling peer's groups authorize: proxy endpoint plus effective + // providers and models. Caller-scoped by the peer's WireGuard key. + // EncryptedMessage of the request has a body of AgentNetworkSetupRequest. + // EncryptedMessage of the response has a body of AgentNetworkSetupResponse. + GetAgentNetworkSetup(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*EncryptedMessage, error) } type managementServiceClient struct { @@ -237,6 +243,15 @@ func (c *managementServiceClient) StopExpose(ctx context.Context, in *EncryptedM return out, nil } +func (c *managementServiceClient) GetAgentNetworkSetup(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*EncryptedMessage, error) { + out := new(EncryptedMessage) + err := c.cc.Invoke(ctx, "/management.ManagementService/GetAgentNetworkSetup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ManagementServiceServer is the server API for ManagementService service. // All implementations must embed UnimplementedManagementServiceServer // for forward compatibility @@ -288,6 +303,12 @@ type ManagementServiceServer interface { RenewExpose(context.Context, *EncryptedMessage) (*EncryptedMessage, error) // StopExpose terminates an active expose session StopExpose(context.Context, *EncryptedMessage) (*EncryptedMessage, error) + // GetAgentNetworkSetup returns the Agent Network connection info the + // calling peer's groups authorize: proxy endpoint plus effective + // providers and models. Caller-scoped by the peer's WireGuard key. + // EncryptedMessage of the request has a body of AgentNetworkSetupRequest. + // EncryptedMessage of the response has a body of AgentNetworkSetupResponse. + GetAgentNetworkSetup(context.Context, *EncryptedMessage) (*EncryptedMessage, error) mustEmbedUnimplementedManagementServiceServer() } @@ -334,6 +355,9 @@ func (UnimplementedManagementServiceServer) RenewExpose(context.Context, *Encryp func (UnimplementedManagementServiceServer) StopExpose(context.Context, *EncryptedMessage) (*EncryptedMessage, error) { return nil, status.Errorf(codes.Unimplemented, "method StopExpose not implemented") } +func (UnimplementedManagementServiceServer) GetAgentNetworkSetup(context.Context, *EncryptedMessage) (*EncryptedMessage, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAgentNetworkSetup not implemented") +} func (UnimplementedManagementServiceServer) mustEmbedUnimplementedManagementServiceServer() {} // UnsafeManagementServiceServer may be embedded to opt out of forward compatibility for this service. @@ -592,6 +616,24 @@ func _ManagementService_StopExpose_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _ManagementService_GetAgentNetworkSetup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EncryptedMessage) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagementServiceServer).GetAgentNetworkSetup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/management.ManagementService/GetAgentNetworkSetup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagementServiceServer).GetAgentNetworkSetup(ctx, req.(*EncryptedMessage)) + } + return interceptor(ctx, in, info, handler) +} + // ManagementService_ServiceDesc is the grpc.ServiceDesc for ManagementService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -643,6 +685,10 @@ var ManagementService_ServiceDesc = grpc.ServiceDesc{ MethodName: "StopExpose", Handler: _ManagementService_StopExpose_Handler, }, + { + MethodName: "GetAgentNetworkSetup", + Handler: _ManagementService_GetAgentNetworkSetup_Handler, + }, }, Streams: []grpc.StreamDesc{ {