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{ {