From 2de02839715f2bae0c7833595064a37ebb69f6c7 Mon Sep 17 00:00:00 2001 From: crn4 Date: Wed, 29 Apr 2026 15:07:55 +0200 Subject: [PATCH 01/42] init int inds migration --- dns/nameserver.go | 3 + management/server/account.go | 8 + management/server/account_test.go | 10 + management/server/group.go | 20 + management/server/migration/account_seq.go | 156 ++++++ management/server/nameserver.go | 8 + .../server/networks/resources/manager.go | 7 + .../networks/resources/types/resource.go | 3 + management/server/networks/routers/manager.go | 12 + .../server/networks/routers/manager_test.go | 1 + .../server/networks/routers/types/router.go | 3 + management/server/policy.go | 8 + management/server/route.go | 7 + management/server/store/account_seq_test.go | 465 ++++++++++++++++++ management/server/store/sql_store.go | 194 +++++++- management/server/store/store.go | 23 + management/server/store/store_mock.go | 15 + .../server/types/account_seq_counter.go | 27 + management/server/types/group.go | 4 + management/server/types/policy.go | 4 + route/route.go | 3 + 21 files changed, 965 insertions(+), 16 deletions(-) create mode 100644 management/server/migration/account_seq.go create mode 100644 management/server/store/account_seq_test.go create mode 100644 management/server/types/account_seq_counter.go diff --git a/dns/nameserver.go b/dns/nameserver.go index 81c616c50f6..c05c0971588 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -53,6 +53,9 @@ type NameServerGroup struct { ID string `gorm:"primaryKey"` // AccountID is a reference to Account that this object belongs AccountID string `gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_nameserver_groups_account_seq_id;not null;default:0"` // Name group name Name string // Description group description diff --git a/management/server/account.go b/management/server/account.go index 8e4e595f0e2..bfb1bad37c8 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1621,6 +1621,14 @@ func (am *DefaultAccountManager) SyncUserJWTGroups(ctx context.Context, userAuth return nil } + for _, g := range newGroupsToCreate { + seq, err := transaction.AllocateAccountSeqID(ctx, userAuth.AccountId, types.AccountSeqEntityGroup) + if err != nil { + return fmt.Errorf("error allocating group seq id: %w", err) + } + g.AccountSeqID = seq + } + if err = transaction.CreateGroups(ctx, userAuth.AccountId, newGroupsToCreate); err != nil { return fmt.Errorf("error saving groups: %w", err) } diff --git a/management/server/account_test.go b/management/server/account_test.go index ba621030ce1..31da3f5338e 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -3036,6 +3036,16 @@ func TestAccount_SetJWTGroups(t *testing.T) { user, err := manager.Store.GetUserByUserID(context.Background(), store.LockingStrengthNone, "user2") assert.NoError(t, err, "unable to get user") assert.Len(t, user.AutoGroups, 1, "new group should be added") + + var newJWTGroup *types.Group + for _, g := range groups { + if g.Name == "group3" { + newJWTGroup = g + break + } + } + require.NotNil(t, newJWTGroup, "JIT-created JWT group not found") + assert.NotZero(t, newJWTGroup.AccountSeqID, "JIT-created JWT group must have a non-zero AccountSeqID") }) t.Run("remove all JWT groups when list is empty", func(t *testing.T) { diff --git a/management/server/group.go b/management/server/group.go index 870a441acf3..dd1068ee159 100644 --- a/management/server/group.go +++ b/management/server/group.go @@ -96,6 +96,12 @@ func (am *DefaultAccountManager) CreateGroup(ctx context.Context, accountID, use return err } + seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityGroup) + if err != nil { + return status.Errorf(status.Internal, "failed to allocate group seq id: %v", err) + } + newGroup.AccountSeqID = seq + if err := transaction.CreateGroup(ctx, newGroup); err != nil { return status.Errorf(status.Internal, "failed to create group: %v", err) } @@ -170,6 +176,8 @@ func (am *DefaultAccountManager) UpdateGroup(ctx context.Context, accountID, use return err } + newGroup.AccountSeqID = oldGroup.AccountSeqID + if err = transaction.UpdateGroup(ctx, newGroup); err != nil { return err } @@ -221,6 +229,12 @@ func (am *DefaultAccountManager) CreateGroups(ctx context.Context, accountID, us newGroup.AccountID = accountID + seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityGroup) + if err != nil { + return err + } + newGroup.AccountSeqID = seq + if err = transaction.CreateGroup(ctx, newGroup); err != nil { return err } @@ -320,6 +334,12 @@ func (am *DefaultAccountManager) updateSingleGroup(ctx context.Context, accountI newGroup.AccountID = accountID + oldGroup, err := transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, newGroup.ID) + if err != nil { + return err + } + newGroup.AccountSeqID = oldGroup.AccountSeqID + if err := transaction.UpdateGroup(ctx, newGroup); err != nil { return err } diff --git a/management/server/migration/account_seq.go b/management/server/migration/account_seq.go new file mode 100644 index 00000000000..ce8095d258a --- /dev/null +++ b/management/server/migration/account_seq.go @@ -0,0 +1,156 @@ +package migration + +import ( + "context" + "fmt" + + log "github.com/sirupsen/logrus" + "gorm.io/gorm" + + "github.com/netbirdio/netbird/management/server/types" +) + +// BackfillAccountSeqIDs assigns a deterministic per-account sequential id to all +// rows of `model` whose account_seq_id is zero, then seeds account_seq_counters +// with the next free id per account. Idempotent: safe to re-run; both steps +// no-op once everything is consistent. +// +// Implemented as two table-wide SQL statements with window functions, one +// transaction. Backfilling 246k rows across 154k accounts on Postgres takes +// well under a second instead of the per-account-loop ~2 minutes. +// +// orderColumn is the column to use when assigning the deterministic ordering +// (typically the primary-key string id). +func BackfillAccountSeqIDs[T any]( + ctx context.Context, + db *gorm.DB, + entity types.AccountSeqEntity, + orderColumn string, +) error { + var model T + if !db.Migrator().HasTable(&model) { + log.WithContext(ctx).Debugf("backfill seq id: table for %T missing, skip", model) + return nil + } + + stmt := &gorm.Statement{DB: db} + if err := stmt.Parse(&model); err != nil { + return fmt.Errorf("parse model: %w", err) + } + table := quoteIdent(db, stmt.Schema.Table) + orderCol := quoteIdent(db, orderColumn) + + return db.Transaction(func(tx *gorm.DB) error { + var pending int64 + if err := tx.Raw( + fmt.Sprintf("SELECT count(*) FROM %s WHERE account_seq_id IS NULL OR account_seq_id = 0", table), + ).Scan(&pending).Error; err != nil { + return fmt.Errorf("count pending on %s: %w", table, err) + } + + if pending > 0 { + log.WithContext(ctx).Infof("backfill seq id: %s — %d rows pending", table, pending) + if err := backfillRankSQL(tx, table, orderCol); err != nil { + return fmt.Errorf("rank %s: %w", table, err) + } + } + + if err := seedCountersSQL(tx, table, entity); err != nil { + return fmt.Errorf("seed counters for %s: %w", entity, err) + } + return nil + }) +} + +func quoteIdent(db *gorm.DB, name string) string { + switch db.Dialector.Name() { + case "mysql": + return "`" + name + "`" + case "postgres": + return `"` + name + `"` + default: + return name + } +} + +func backfillRankSQL(db *gorm.DB, table, orderCol string) error { + dialect := db.Dialector.Name() + var sql string + switch dialect { + case "postgres", "sqlite": + sql = fmt.Sprintf(` +WITH max_seq AS ( + SELECT account_id, COALESCE(MAX(account_seq_id), 0) AS max_seq + FROM %s + GROUP BY account_id +), +ranked AS ( + SELECT p.id, + m.max_seq + ROW_NUMBER() OVER (PARTITION BY p.account_id ORDER BY p.%s) AS new_seq + FROM %s p + JOIN max_seq m ON p.account_id = m.account_id + WHERE p.account_seq_id IS NULL OR p.account_seq_id = 0 +) +UPDATE %s SET account_seq_id = ranked.new_seq +FROM ranked +WHERE %s.id = ranked.id +`, table, orderCol, table, table, table) + case "mysql": + sql = fmt.Sprintf(` +UPDATE %s p +JOIN ( + SELECT account_id, COALESCE(MAX(account_seq_id), 0) AS max_seq + FROM %s + GROUP BY account_id +) m ON p.account_id = m.account_id +JOIN ( + SELECT id, ROW_NUMBER() OVER (PARTITION BY account_id ORDER BY %s) AS rn + FROM %s + WHERE account_seq_id IS NULL OR account_seq_id = 0 +) r ON p.id = r.id +SET p.account_seq_id = m.max_seq + r.rn +`, table, table, orderCol, table) + default: + return fmt.Errorf("unsupported dialect: %s", dialect) + } + return db.Exec(sql).Error +} + +func seedCountersSQL(db *gorm.DB, table string, entity types.AccountSeqEntity) error { + dialect := db.Dialector.Name() + var sql string + switch dialect { + case "postgres": + sql = fmt.Sprintf(` +INSERT INTO account_seq_counters (account_id, entity, next_id) +SELECT account_id, ?, MAX(account_seq_id) + 1 +FROM %s +WHERE account_seq_id IS NOT NULL AND account_seq_id > 0 +GROUP BY account_id +ON CONFLICT (account_id, entity) DO UPDATE + SET next_id = GREATEST(account_seq_counters.next_id, EXCLUDED.next_id) +`, table) + case "sqlite": + sql = fmt.Sprintf(` +INSERT INTO account_seq_counters (account_id, entity, next_id) +SELECT account_id, ?, MAX(account_seq_id) + 1 +FROM %s +WHERE account_seq_id IS NOT NULL AND account_seq_id > 0 +GROUP BY account_id +ON CONFLICT (account_id, entity) DO UPDATE + SET next_id = max(account_seq_counters.next_id, excluded.next_id) +`, table) + case "mysql": + sql = fmt.Sprintf(` +INSERT INTO account_seq_counters (account_id, entity, next_id) +SELECT account_id, ?, MAX(account_seq_id) + 1 +FROM %s +WHERE account_seq_id IS NOT NULL AND account_seq_id > 0 +GROUP BY account_id +ON DUPLICATE KEY UPDATE next_id = GREATEST(next_id, VALUES(next_id)) +`, table) + default: + return fmt.Errorf("unsupported dialect: %s", dialect) + } + return db.Exec(sql, string(entity)).Error +} diff --git a/management/server/nameserver.go b/management/server/nameserver.go index 5859bfb0d79..4277d09965d 100644 --- a/management/server/nameserver.go +++ b/management/server/nameserver.go @@ -69,6 +69,12 @@ func (am *DefaultAccountManager) CreateNameServerGroup(ctx context.Context, acco return err } + seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityNameserverGroup) + if err != nil { + return err + } + newNSGroup.AccountSeqID = seq + if err = transaction.SaveNameServerGroup(ctx, newNSGroup); err != nil { return err } @@ -120,6 +126,8 @@ func (am *DefaultAccountManager) SaveNameServerGroup(ctx context.Context, accoun return err } + nsGroupToSave.AccountSeqID = oldNSGroup.AccountSeqID + if err = transaction.SaveNameServerGroup(ctx, nsGroupToSave); err != nil { return err } diff --git a/management/server/networks/resources/manager.go b/management/server/networks/resources/manager.go index 5a0e2653349..fb098a5690a 100644 --- a/management/server/networks/resources/manager.go +++ b/management/server/networks/resources/manager.go @@ -125,6 +125,12 @@ func (m *managerImpl) CreateResource(ctx context.Context, userID string, resourc return fmt.Errorf("failed to get network: %w", err) } + seq, err := transaction.AllocateAccountSeqID(ctx, resource.AccountID, nbtypes.AccountSeqEntityNetworkResource) + if err != nil { + return fmt.Errorf("failed to allocate network resource seq id: %w", err) + } + resource.AccountSeqID = seq + err = transaction.SaveNetworkResource(ctx, resource) if err != nil { return fmt.Errorf("failed to save network resource: %w", err) @@ -231,6 +237,7 @@ func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resourc if err != nil { return fmt.Errorf("failed to get network resource: %w", err) } + resource.AccountSeqID = oldResource.AccountSeqID err = transaction.SaveNetworkResource(ctx, resource) if err != nil { diff --git a/management/server/networks/resources/types/resource.go b/management/server/networks/resources/types/resource.go index 1fa9083934d..1ced3ab918c 100644 --- a/management/server/networks/resources/types/resource.go +++ b/management/server/networks/resources/types/resource.go @@ -32,6 +32,9 @@ type NetworkResource struct { ID string `gorm:"primaryKey"` NetworkID string `gorm:"index"` AccountID string `gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_network_resources_account_seq_id;not null;default:0"` Name string Description string Type NetworkResourceType diff --git a/management/server/networks/routers/manager.go b/management/server/networks/routers/manager.go index c7c3f2ff440..3a985a5b0d6 100644 --- a/management/server/networks/routers/manager.go +++ b/management/server/networks/routers/manager.go @@ -102,6 +102,12 @@ func (m *managerImpl) CreateRouter(ctx context.Context, userID string, router *t router.ID = xid.New().String() + seq, err := transaction.AllocateAccountSeqID(ctx, router.AccountID, serverTypes.AccountSeqEntityNetworkRouter) + if err != nil { + return fmt.Errorf("failed to allocate network router seq id: %w", err) + } + router.AccountSeqID = seq + err = transaction.SaveNetworkRouter(ctx, router) if err != nil { return fmt.Errorf("failed to create network router: %w", err) @@ -166,6 +172,12 @@ func (m *managerImpl) UpdateRouter(ctx context.Context, userID string, router *t return status.NewRouterNotPartOfNetworkError(router.ID, router.NetworkID) } + oldRouter, err := transaction.GetNetworkRouterByID(ctx, store.LockingStrengthNone, router.AccountID, router.ID) + if err != nil { + return fmt.Errorf("failed to get existing network router: %w", err) + } + router.AccountSeqID = oldRouter.AccountSeqID + err = transaction.SaveNetworkRouter(ctx, router) if err != nil { return fmt.Errorf("failed to update network router: %w", err) diff --git a/management/server/networks/routers/manager_test.go b/management/server/networks/routers/manager_test.go index 6be90baa7a9..e89fc323cab 100644 --- a/management/server/networks/routers/manager_test.go +++ b/management/server/networks/routers/manager_test.go @@ -195,6 +195,7 @@ func Test_UpdateRouterSuccessfully(t *testing.T) { if err != nil { require.NoError(t, err) } + router.ID = "testRouterId" s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir()) if err != nil { diff --git a/management/server/networks/routers/types/router.go b/management/server/networks/routers/types/router.go index 1293a9934ba..7325599b215 100644 --- a/management/server/networks/routers/types/router.go +++ b/management/server/networks/routers/types/router.go @@ -13,6 +13,9 @@ type NetworkRouter struct { ID string `gorm:"primaryKey"` NetworkID string `gorm:"index"` AccountID string `gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_network_routers_account_seq_id;not null;default:0"` Peer string PeerGroups []string `gorm:"serializer:json"` Masquerade bool diff --git a/management/server/policy.go b/management/server/policy.go index 40f3908e320..e33a33a4397 100644 --- a/management/server/policy.go +++ b/management/server/policy.go @@ -69,6 +69,8 @@ func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, user return err } + policy.AccountSeqID = existingPolicy.AccountSeqID + if err = transaction.SavePolicy(ctx, policy); err != nil { return err } @@ -78,6 +80,12 @@ func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, user return err } + seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPolicy) + if err != nil { + return err + } + policy.AccountSeqID = seq + if err = transaction.CreatePolicy(ctx, policy); err != nil { return err } diff --git a/management/server/route.go b/management/server/route.go index a9561faf050..4d87b68debb 100644 --- a/management/server/route.go +++ b/management/server/route.go @@ -178,6 +178,12 @@ func (am *DefaultAccountManager) CreateRoute(ctx context.Context, accountID stri return err } + seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityRoute) + if err != nil { + return err + } + newRoute.AccountSeqID = seq + if err = transaction.SaveRoute(ctx, newRoute); err != nil { return err } @@ -231,6 +237,7 @@ func (am *DefaultAccountManager) SaveRoute(ctx context.Context, accountID, userI return err } routeToSave.AccountID = accountID + routeToSave.AccountSeqID = oldRoute.AccountSeqID if err = transaction.SaveRoute(ctx, routeToSave); err != nil { return err diff --git a/management/server/store/account_seq_test.go b/management/server/store/account_seq_test.go new file mode 100644 index 00000000000..4a8134559f9 --- /dev/null +++ b/management/server/store/account_seq_test.go @@ -0,0 +1,465 @@ +package store + +import ( + "context" + "errors" + "net/netip" + "testing" + + "github.com/stretchr/testify/require" + + nbdns "github.com/netbirdio/netbird/dns" + resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" + routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" + "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/route" +) + +var errRollback = errors.New("intentional rollback") + +func TestAllocateAccountSeqID_SequentialPerAccount(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accA = "acc-a" + const accB = "acc-b" + + require.NoError(t, store.ExecuteInTransaction(ctx, func(tx Store) error { + got, err := tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityPolicy) + require.NoError(t, err) + require.Equal(t, uint32(1), got) + + got, err = tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityPolicy) + require.NoError(t, err) + require.Equal(t, uint32(2), got) + + got, err = tx.AllocateAccountSeqID(ctx, accB, types.AccountSeqEntityPolicy) + require.NoError(t, err) + require.Equal(t, uint32(1), got, "different account starts from 1") + + got, err = tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityGroup) + require.NoError(t, err) + require.Equal(t, uint32(1), got, "different entity starts from 1") + + return nil + })) + + require.NoError(t, store.ExecuteInTransaction(ctx, func(tx Store) error { + got, err := tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityPolicy) + require.NoError(t, err) + require.Equal(t, uint32(3), got, "counter persists across transactions") + return nil + })) +} + +func TestPolicyBackfill_AssignsSeqIDsToExistingPolicies(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + policies, err := store.GetAccountPolicies(ctx, LockingStrengthNone, accountID) + require.NoError(t, err) + require.NotEmpty(t, policies, "test fixture must have policies") + + seen := make(map[uint32]bool) + for _, p := range policies { + require.NotZero(t, p.AccountSeqID, "policy %s must have a non-zero AccountSeqID after migration", p.ID) + require.False(t, seen[p.AccountSeqID], "duplicate AccountSeqID %d in account %s", p.AccountSeqID, accountID) + seen[p.AccountSeqID] = true + } +} + +func TestPolicyUpdate_PreservesSeqID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" + const policyID = "cs1tnh0hhcjnqoiuebf0" + + original, err := store.GetPolicyByID(ctx, LockingStrengthNone, accountID, policyID) + require.NoError(t, err) + originalSeq := original.AccountSeqID + require.NotZero(t, originalSeq, "fixture must have non-zero AccountSeqID after backfill") + + updated := &types.Policy{ + ID: policyID, + AccountID: accountID, + Name: "renamed", + Enabled: false, + Rules: original.Rules, + } + require.Zero(t, updated.AccountSeqID, "incoming struct should have zero AccountSeqID like an HTTP handler would") + + require.NoError(t, store.SavePolicy(ctx, updated)) + + got, err := store.GetPolicyByID(ctx, LockingStrengthNone, accountID, policyID) + require.NoError(t, err) + require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must not be reset by update path") + require.Equal(t, "renamed", got.Name) +} + +func TestGroupUpdate_PreservesSeqID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + groups, err := store.GetAccountGroups(ctx, LockingStrengthNone, accountID) + require.NoError(t, err) + require.NotEmpty(t, groups) + + original := groups[0] + originalSeq := original.AccountSeqID + require.NotZero(t, originalSeq) + + updated := &types.Group{ + ID: original.ID, + AccountID: accountID, + Name: "renamed", + Issued: original.Issued, + } + require.Zero(t, updated.AccountSeqID) + + require.NoError(t, store.UpdateGroup(ctx, updated)) + + got, err := store.GetGroupByID(ctx, LockingStrengthNone, accountID, original.ID) + require.NoError(t, err) + require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must not be reset by UpdateGroup") + require.Equal(t, "renamed", got.Name) +} + +func TestSaveAccount_AllocatesSeqIDsForDefaultGroupAndPolicy(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "save-account-seqid-test" + + account := &types.Account{ + Id: accountID, + CreatedBy: "user1", + Domain: "example.test", + DNSSettings: types.DNSSettings{}, + Settings: &types.Settings{}, + Network: &types.Network{ + Identifier: "net-test", + }, + Users: map[string]*types.User{ + "user1": {Id: "user1", AccountID: accountID, Role: types.UserRoleOwner}, + }, + } + require.NoError(t, account.AddAllGroup(false), "AddAllGroup should populate default Group + Policy") + require.Len(t, account.Groups, 1, "default 'All' group must be present") + require.Len(t, account.Policies, 1, "default policy must be present") + + for _, g := range account.Groups { + require.Zero(t, g.AccountSeqID, "default group must start with seq=0") + } + require.Zero(t, account.Policies[0].AccountSeqID, "default policy must start with seq=0") + + require.NoError(t, store.SaveAccount(ctx, account)) + + groups, err := store.GetAccountGroups(ctx, LockingStrengthNone, accountID) + require.NoError(t, err) + require.Len(t, groups, 1) + require.NotZerof(t, groups[0].AccountSeqID, "default group must have seq>0 after SaveAccount") + + policies, err := store.GetAccountPolicies(ctx, LockingStrengthNone, accountID) + require.NoError(t, err) + require.Len(t, policies, 1) + require.NotZerof(t, policies[0].AccountSeqID, "default policy must have seq>0 after SaveAccount") + + require.ErrorIs(t, store.ExecuteInTransaction(ctx, func(tx Store) error { + next, err := tx.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityGroup) + require.NoError(t, err) + require.Equal(t, groups[0].AccountSeqID+1, next, "next group seq must be max+1") + + next, err = tx.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPolicy) + require.NoError(t, err) + require.Equal(t, policies[0].AccountSeqID+1, next, "next policy seq must be max+1") + return errRollback + }), errRollback) +} + +func TestSaveAccount_PreservesExistingSeqIDs(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + account, err := store.GetAccount(ctx, accountID) + require.NoError(t, err) + + groupSeqs := make(map[string]uint32) + policySeqs := make(map[string]uint32) + routeSeqs := make(map[route.ID]uint32) + nsgSeqs := make(map[string]uint32) + resourceSeqs := make(map[string]uint32) + routerSeqs := make(map[string]uint32) + + for _, g := range account.Groups { + require.NotZero(t, g.AccountSeqID, "fixture group must have seq>0 after backfill") + groupSeqs[g.ID] = g.AccountSeqID + } + for _, p := range account.Policies { + require.NotZero(t, p.AccountSeqID, "fixture policy must have seq>0") + policySeqs[p.ID] = p.AccountSeqID + } + for _, r := range account.Routes { + require.NotZero(t, r.AccountSeqID, "fixture route must have seq>0") + routeSeqs[r.ID] = r.AccountSeqID + } + for _, n := range account.NameServerGroups { + require.NotZero(t, n.AccountSeqID, "fixture name_server_group must have seq>0") + nsgSeqs[n.ID] = n.AccountSeqID + } + for _, nr := range account.NetworkResources { + require.NotZero(t, nr.AccountSeqID, "fixture network_resource must have seq>0") + resourceSeqs[nr.ID] = nr.AccountSeqID + } + for _, nr := range account.NetworkRouters { + require.NotZero(t, nr.AccountSeqID, "fixture network_router must have seq>0") + routerSeqs[nr.ID] = nr.AccountSeqID + } + + require.NoError(t, store.SaveAccount(ctx, account)) + + after, err := store.GetAccount(ctx, accountID) + require.NoError(t, err) + for _, g := range after.Groups { + require.Equal(t, groupSeqs[g.ID], g.AccountSeqID, "group %s seq must be preserved on re-save", g.ID) + } + for _, p := range after.Policies { + require.Equal(t, policySeqs[p.ID], p.AccountSeqID, "policy %s seq must be preserved", p.ID) + } + for _, r := range after.Routes { + require.Equal(t, routeSeqs[r.ID], r.AccountSeqID, "route %s seq must be preserved (slice-of-value addressability)", r.ID) + } + for _, n := range after.NameServerGroups { + require.Equal(t, nsgSeqs[n.ID], n.AccountSeqID, "name_server_group %s seq must be preserved (slice-of-value addressability)", n.ID) + } + for _, nr := range after.NetworkResources { + require.Equal(t, resourceSeqs[nr.ID], nr.AccountSeqID, "network_resource %s seq must be preserved", nr.ID) + } + for _, nr := range after.NetworkRouters { + require.Equal(t, routerSeqs[nr.ID], nr.AccountSeqID, "network_router %s seq must be preserved", nr.ID) + } +} + +func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "save-account-all-entities" + + addr, err := netip.ParseAddr("8.8.8.8") + require.NoError(t, err) + + account := &types.Account{ + Id: accountID, + CreatedBy: "user1", + Domain: "example.test", + Settings: &types.Settings{}, + Network: &types.Network{Identifier: "net-test"}, + Users: map[string]*types.User{ + "user1": {Id: "user1", AccountID: accountID, Role: types.UserRoleOwner}, + }, + Groups: map[string]*types.Group{ + "g1": {ID: "g1", AccountID: accountID, Name: "g1", Issued: types.GroupIssuedAPI}, + }, + Policies: []*types.Policy{ + {ID: "p1", AccountID: accountID, Name: "p1", Enabled: true, + Rules: []*types.PolicyRule{{ID: "r1", PolicyID: "p1", Enabled: true}}}, + }, + Routes: map[route.ID]*route.Route{ + "rt1": {ID: "rt1", AccountID: accountID, NetID: "net1", Peer: "peer1"}, + }, + NameServerGroups: map[string]*nbdns.NameServerGroup{ + "nsg1": {ID: "nsg1", AccountID: accountID, Name: "nsg1", Enabled: true, + NameServers: []nbdns.NameServer{{IP: addr, NSType: nbdns.UDPNameServerType, Port: 53}}}, + }, + NetworkResources: []*resourceTypes.NetworkResource{ + {ID: "nr1", AccountID: accountID, NetworkID: "net1", Name: "res1", Enabled: true}, + }, + NetworkRouters: []*routerTypes.NetworkRouter{ + {ID: "nrt1", AccountID: accountID, NetworkID: "net1", Peer: "peer1", Enabled: true}, + }, + } + + require.NoError(t, store.SaveAccount(ctx, account)) + + after, err := store.GetAccount(ctx, accountID) + require.NoError(t, err) + + require.Len(t, after.Groups, 1) + require.Len(t, after.Policies, 1) + require.Len(t, after.Routes, 1) + require.Len(t, after.NameServerGroups, 1) + require.Len(t, after.NetworkResources, 1) + require.Len(t, after.NetworkRouters, 1) + + for _, g := range after.Groups { + require.NotZero(t, g.AccountSeqID, "group seq must be allocated") + } + for _, p := range after.Policies { + require.NotZero(t, p.AccountSeqID, "policy seq must be allocated") + } + for _, r := range after.Routes { + require.NotZero(t, r.AccountSeqID, "route seq must be allocated (slice-of-value addressability)") + } + for _, n := range after.NameServerGroups { + require.NotZero(t, n.AccountSeqID, "name_server_group seq must be allocated (slice-of-value addressability)") + } + for _, nr := range after.NetworkResources { + require.NotZero(t, nr.AccountSeqID, "network_resource seq must be allocated") + } + for _, nr := range after.NetworkRouters { + require.NotZero(t, nr.AccountSeqID, "network_router seq must be allocated") + } + + require.NoError(t, store.SaveAccount(ctx, after)) + final, err := store.GetAccount(ctx, accountID) + require.NoError(t, err) + for _, r := range final.Routes { + require.Equal(t, after.Routes[r.ID].AccountSeqID, r.AccountSeqID, "route seq preserved on re-save") + } + for _, n := range final.NameServerGroups { + require.Equal(t, after.NameServerGroups[n.ID].AccountSeqID, n.AccountSeqID, "name_server_group seq preserved on re-save") + } +} + +func TestAllocateAccountSeqID_ConcurrentSameAccountEntity(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "concurrent-test" + const entity = types.AccountSeqEntityPolicy + const goroutines = 32 + + type result struct { + seq uint32 + err error + } + results := make(chan result, goroutines) + start := make(chan struct{}) + + for i := 0; i < goroutines; i++ { + go func() { + <-start + var allocated uint32 + err := store.ExecuteInTransaction(ctx, func(tx Store) error { + seq, err := tx.AllocateAccountSeqID(ctx, accountID, entity) + allocated = seq + return err + }) + results <- result{seq: allocated, err: err} + }() + } + close(start) + + seen := make(map[uint32]int, goroutines) + for i := 0; i < goroutines; i++ { + r := <-results + require.NoError(t, r.err, "concurrent allocate must not fail") + require.NotZero(t, r.seq, "allocated seq must be non-zero") + seen[r.seq]++ + } + + require.Lenf(t, seen, goroutines, "every concurrent allocation must yield a unique id; got duplicates in %v", seen) + for i := uint32(1); i <= goroutines; i++ { + require.Equalf(t, 1, seen[i], "id %d must appear exactly once across concurrent allocations", i) + } +} + +func TestStoreCreateGroups_AllocatedSeqIDIsNotClobbered(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + groups := []*types.Group{ + {ID: "seq-test-g1", AccountID: accountID, Name: "g1", Issued: "jwt", AccountSeqID: 7777}, + {ID: "seq-test-g2", AccountID: accountID, Name: "g2", Issued: "jwt", AccountSeqID: 7778}, + } + require.NoError(t, store.CreateGroups(ctx, accountID, groups)) + + for _, want := range groups { + got, err := store.GetGroupByID(ctx, LockingStrengthNone, accountID, want.ID) + require.NoError(t, err) + require.Equal(t, want.AccountSeqID, got.AccountSeqID, "seq id from caller must be persisted on insert") + } + + groups[0].Name = "g1-renamed" + groups[0].AccountSeqID = 0 + require.NoError(t, store.CreateGroups(ctx, accountID, groups[:1])) + + got, err := store.GetGroupByID(ctx, LockingStrengthNone, accountID, "seq-test-g1") + require.NoError(t, err) + require.Equal(t, "g1-renamed", got.Name, "upsert path still updates other columns") + require.Equal(t, uint32(7777), got.AccountSeqID, "upsert path must NOT overwrite account_seq_id") +} + +func TestPolicyCreate_AllocatesSeqID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + ctx := context.Background() + const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + existing, err := store.GetAccountPolicies(ctx, LockingStrengthNone, accountID) + require.NoError(t, err) + maxSeq := uint32(0) + for _, p := range existing { + if p.AccountSeqID > maxSeq { + maxSeq = p.AccountSeqID + } + } + + require.NoError(t, store.ExecuteInTransaction(ctx, func(tx Store) error { + seq, err := tx.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPolicy) + if err != nil { + return err + } + require.Equal(t, maxSeq+1, seq, "next id should be max+1 after backfill") + + newPolicy := &types.Policy{ + ID: "bench-new-policy", + AccountID: accountID, + AccountSeqID: seq, + Enabled: true, + Rules: []*types.PolicyRule{{ + ID: "bench-new-policy-rule", + PolicyID: "bench-new-policy", + Enabled: true, + Action: types.PolicyTrafficActionAccept, + Sources: []string{"groupA"}, + Destinations: []string{"groupC"}, + Bidirectional: true, + }}, + } + return tx.CreatePolicy(ctx, newPolicy) + })) + + created, err := store.GetPolicyByID(ctx, LockingStrengthNone, accountID, "bench-new-policy") + require.NoError(t, err) + require.Equal(t, maxSeq+1, created.AccountSeqID) +} diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 8cf37de56a0..be0e7f2161d 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -137,6 +137,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine types.Engine, met &networkTypes.Network{}, &routerTypes.NetworkRouter{}, &resourceTypes.NetworkResource{}, &types.AccountOnboarding{}, &types.Job{}, &zones.Zone{}, &records.Record{}, &types.UserInviteRecord{}, &rpservice.Service{}, &rpservice.Target{}, &domain.Domain{}, &accesslogs.AccessLogEntry{}, &proxy.Proxy{}, + &types.AccountSeqCounter{}, ) if err != nil { return nil, fmt.Errorf("auto migratePreAuto: %w", err) @@ -307,6 +308,10 @@ func (s *SqlStore) SaveAccount(ctx context.Context, account *types.Account) erro return result.Error } + if err := s.assignAccountSeqIDs(ctx, tx, account); err != nil { + return fmt.Errorf("assign seq ids: %w", err) + } + result = tx. Session(&gorm.Session{FullSaveAssociations: true}). Clauses(clause.OnConflict{UpdateAll: true}). @@ -658,6 +663,22 @@ func (s *SqlStore) SaveUser(ctx context.Context, user *types.User) error { } // CreateGroups creates the given list of groups to the database. +// groupUpsertColumns is the explicit allowlist of columns that get updated when +// CreateGroups / UpdateGroups hit a PK conflict. account_seq_id is intentionally +// omitted so a caller passing an entity with the zero value (e.g. an HTTP +// handler-built struct) cannot reset the persisted seq id during an upsert. +// Keep this in sync with the Group schema in management/server/types/group.go. +func groupUpsertColumns() clause.Set { + return clause.AssignmentColumns([]string{ + "account_id", + "name", + "issued", + "integration_ref_id", + "integration_ref_integration_type", + "resources", + }) +} + func (s *SqlStore) CreateGroups(ctx context.Context, accountID string, groups []*types.Group) error { if len(groups) == 0 { return nil @@ -667,8 +688,9 @@ func (s *SqlStore) CreateGroups(ctx context.Context, accountID string, groups [] result := tx. Clauses( clause.OnConflict{ + Columns: []clause.Column{{Name: "id"}}, Where: clause.Where{Exprs: []clause.Expression{clause.Eq{Column: "groups.account_id", Value: accountID}}}, - UpdateAll: true, + DoUpdates: groupUpsertColumns(), }, ). Omit(clause.Associations). @@ -692,8 +714,9 @@ func (s *SqlStore) UpdateGroups(ctx context.Context, accountID string, groups [] result := tx. Clauses( clause.OnConflict{ + Columns: []clause.Column{{Name: "id"}}, Where: clause.Where{Exprs: []clause.Expression{clause.Eq{Column: "groups.account_id", Value: accountID}}}, - UpdateAll: true, + DoUpdates: groupUpsertColumns(), }, ). Omit(clause.Associations). @@ -1995,7 +2018,7 @@ func (s *SqlStore) getUsers(ctx context.Context, accountID string) ([]types.User } func (s *SqlStore) getGroups(ctx context.Context, accountID string) ([]*types.Group, error) { - const query = `SELECT id, account_id, name, issued, resources, integration_ref_id, integration_ref_integration_type FROM groups WHERE account_id = $1` + const query = `SELECT id, account_id, account_seq_id, name, issued, resources, integration_ref_id, integration_ref_integration_type FROM groups WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2005,7 +2028,7 @@ func (s *SqlStore) getGroups(ctx context.Context, accountID string) ([]*types.Gr var resources []byte var refID sql.NullInt64 var refType sql.NullString - err := row.Scan(&g.ID, &g.AccountID, &g.Name, &g.Issued, &resources, &refID, &refType) + err := row.Scan(&g.ID, &g.AccountID, &g.AccountSeqID, &g.Name, &g.Issued, &resources, &refID, &refType) if err == nil { if refID.Valid { g.IntegrationReference.ID = int(refID.Int64) @@ -2030,7 +2053,7 @@ func (s *SqlStore) getGroups(ctx context.Context, accountID string) ([]*types.Gr } func (s *SqlStore) getPolicies(ctx context.Context, accountID string) ([]*types.Policy, error) { - const query = `SELECT id, account_id, name, description, enabled, source_posture_checks FROM policies WHERE account_id = $1` + const query = `SELECT id, account_id, account_seq_id, name, description, enabled, source_posture_checks FROM policies WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2039,7 +2062,7 @@ func (s *SqlStore) getPolicies(ctx context.Context, accountID string) ([]*types. var p types.Policy var checks []byte var enabled sql.NullBool - err := row.Scan(&p.ID, &p.AccountID, &p.Name, &p.Description, &enabled, &checks) + err := row.Scan(&p.ID, &p.AccountID, &p.AccountSeqID, &p.Name, &p.Description, &enabled, &checks) if err == nil { if enabled.Valid { p.Enabled = enabled.Bool @@ -2057,7 +2080,7 @@ func (s *SqlStore) getPolicies(ctx context.Context, accountID string) ([]*types. } func (s *SqlStore) getRoutes(ctx context.Context, accountID string) ([]route.Route, error) { - const query = `SELECT id, account_id, network, domains, keep_route, net_id, description, peer, peer_groups, network_type, masquerade, metric, enabled, groups, access_control_groups, skip_auto_apply FROM routes WHERE account_id = $1` + const query = `SELECT id, account_id, account_seq_id, network, domains, keep_route, net_id, description, peer, peer_groups, network_type, masquerade, metric, enabled, groups, access_control_groups, skip_auto_apply FROM routes WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2067,7 +2090,7 @@ func (s *SqlStore) getRoutes(ctx context.Context, accountID string) ([]route.Rou var network, domains, peerGroups, groups, accessGroups []byte var keepRoute, masquerade, enabled, skipAutoApply sql.NullBool var metric sql.NullInt64 - err := row.Scan(&r.ID, &r.AccountID, &network, &domains, &keepRoute, &r.NetID, &r.Description, &r.Peer, &peerGroups, &r.NetworkType, &masquerade, &metric, &enabled, &groups, &accessGroups, &skipAutoApply) + err := row.Scan(&r.ID, &r.AccountID, &r.AccountSeqID, &network, &domains, &keepRoute, &r.NetID, &r.Description, &r.Peer, &peerGroups, &r.NetworkType, &masquerade, &metric, &enabled, &groups, &accessGroups, &skipAutoApply) if err == nil { if keepRoute.Valid { r.KeepRoute = keepRoute.Bool @@ -2109,7 +2132,7 @@ func (s *SqlStore) getRoutes(ctx context.Context, accountID string) ([]route.Rou } func (s *SqlStore) getNameServerGroups(ctx context.Context, accountID string) ([]nbdns.NameServerGroup, error) { - const query = `SELECT id, account_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled FROM name_server_groups WHERE account_id = $1` + const query = `SELECT id, account_id, account_seq_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled FROM name_server_groups WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2118,7 +2141,7 @@ func (s *SqlStore) getNameServerGroups(ctx context.Context, accountID string) ([ var n nbdns.NameServerGroup var ns, groups, domains []byte var primary, enabled, searchDomainsEnabled sql.NullBool - err := row.Scan(&n.ID, &n.AccountID, &n.Name, &n.Description, &ns, &groups, &primary, &domains, &enabled, &searchDomainsEnabled) + err := row.Scan(&n.ID, &n.AccountID, &n.AccountSeqID, &n.Name, &n.Description, &ns, &groups, &primary, &domains, &enabled, &searchDomainsEnabled) if err == nil { if primary.Valid { n.Primary = primary.Bool @@ -2345,7 +2368,7 @@ func (s *SqlStore) getNetworks(ctx context.Context, accountID string) ([]*networ } func (s *SqlStore) getNetworkRouters(ctx context.Context, accountID string) ([]*routerTypes.NetworkRouter, error) { - const query = `SELECT id, network_id, account_id, peer, peer_groups, masquerade, metric, enabled FROM network_routers WHERE account_id = $1` + const query = `SELECT id, network_id, account_id, account_seq_id, peer, peer_groups, masquerade, metric, enabled FROM network_routers WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2355,7 +2378,7 @@ func (s *SqlStore) getNetworkRouters(ctx context.Context, accountID string) ([]* var peerGroups []byte var masquerade, enabled sql.NullBool var metric sql.NullInt64 - err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.Peer, &peerGroups, &masquerade, &metric, &enabled) + err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.AccountSeqID, &r.Peer, &peerGroups, &masquerade, &metric, &enabled) if err == nil { if masquerade.Valid { r.Masquerade = masquerade.Bool @@ -2383,7 +2406,7 @@ func (s *SqlStore) getNetworkRouters(ctx context.Context, accountID string) ([]* } func (s *SqlStore) getNetworkResources(ctx context.Context, accountID string) ([]*resourceTypes.NetworkResource, error) { - const query = `SELECT id, network_id, account_id, name, description, type, domain, prefix, enabled FROM network_resources WHERE account_id = $1` + const query = `SELECT id, network_id, account_id, account_seq_id, name, description, type, domain, prefix, enabled FROM network_resources WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2392,7 +2415,7 @@ func (s *SqlStore) getNetworkResources(ctx context.Context, accountID string) ([ var r resourceTypes.NetworkResource var prefix []byte var enabled sql.NullBool - err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.Name, &r.Description, &r.Type, &r.Domain, &prefix, &enabled) + err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.AccountSeqID, &r.Name, &r.Description, &r.Type, &r.Domain, &prefix, &enabled) if err == nil { if enabled.Valid { r.Enabled = enabled.Bool @@ -3565,6 +3588,145 @@ func (s *SqlStore) withTx(tx *gorm.DB) Store { } } +// AllocateAccountSeqID returns the next per-account integer id for the given +// component kind. Must be called inside ExecuteInTransaction so the increment +// is serialized with the component insert. +func (s *SqlStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (uint32, error) { + return allocateAccountSeqID(ctx, s.db, s.storeEngine, accountID, entity) +} + +func allocateAccountSeqID(_ context.Context, db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity) (uint32, error) { + switch engine { + case types.PostgresStoreEngine, types.SqliteStoreEngine: + return allocateAccountSeqIDReturning(db, accountID, entity) + case types.MysqlStoreEngine: + return allocateAccountSeqIDMysql(db, accountID, entity) + default: + return 0, fmt.Errorf("unsupported store engine for account_seq allocator: %v", engine) + } +} + +// allocateAccountSeqIDReturning runs a single atomic INSERT ... ON CONFLICT +// DO UPDATE ... RETURNING that gives us the allocated id without a separate +// SELECT FOR UPDATE. Two concurrent allocations for the same (account, entity) +// produce two distinct ids: one wins the INSERT, the other wins the UPDATE +// branch and returns next_id+1. +func allocateAccountSeqIDReturning(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (uint32, error) { + const sqlStr = ` + INSERT INTO account_seq_counters (account_id, entity, next_id) + VALUES (?, ?, 2) + ON CONFLICT (account_id, entity) DO UPDATE + SET next_id = account_seq_counters.next_id + 1 + RETURNING (next_id - 1) + ` + var allocated uint32 + if err := db.Raw(sqlStr, accountID, string(entity)).Scan(&allocated).Error; err != nil { + return 0, fmt.Errorf("upsert account seq counter: %w", err) + } + if allocated == 0 { + return 0, fmt.Errorf("upsert account seq counter returned 0") + } + return allocated, nil +} + +// allocateAccountSeqIDMysql is the MySQL equivalent of allocateAccountSeqIDReturning. +// MySQL has no RETURNING on ON DUPLICATE KEY UPDATE, so we use the LAST_INSERT_ID +// trick: passing an expression to LAST_INSERT_ID(expr) both sets the session value +// and returns it from the INSERT. The INSERT's value uses LAST_INSERT_ID(2) so the +// no-conflict path also surfaces the new next_id, keeping the read-back uniform. +// LAST_INSERT_ID is per-connection; GORM transactions pin a single connection, +// so the follow-up SELECT sees the same value. +func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (uint32, error) { + const upsertSQL = ` + INSERT INTO account_seq_counters (account_id, entity, next_id) + VALUES (?, ?, LAST_INSERT_ID(2)) + ON DUPLICATE KEY UPDATE next_id = LAST_INSERT_ID(next_id + 1) + ` + if err := db.Exec(upsertSQL, accountID, string(entity)).Error; err != nil { + return 0, fmt.Errorf("upsert account seq counter: %w", err) + } + var newNext uint64 + if err := db.Raw("SELECT LAST_INSERT_ID()").Scan(&newNext).Error; err != nil { + return 0, fmt.Errorf("get last insert id: %w", err) + } + if newNext == 0 { + return 0, fmt.Errorf("LAST_INSERT_ID returned 0; account_seq_counters misconfigured") + } + return uint32(newNext - 1), nil +} + +// assignAccountSeqIDs allocates a per-account integer id for any component on +// the in-memory account whose AccountSeqID is zero. Called from SaveAccount so +// the canonical "save the whole account" path produces the same persisted seq +// ids that the manager-level Create paths produce. Update flows that go +// through SaveAccount preserve existing non-zero values. +func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account *types.Account) error { + for i := range account.GroupsG { + g := account.GroupsG[i] + if g == nil || g.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityGroup) + if err != nil { + return err + } + g.AccountSeqID = seq + } + for _, p := range account.Policies { + if p == nil || p.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityPolicy) + if err != nil { + return err + } + p.AccountSeqID = seq + } + for i := range account.RoutesG { + r := &account.RoutesG[i] + if r.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityRoute) + if err != nil { + return err + } + r.AccountSeqID = seq + } + for i := range account.NameServerGroupsG { + ng := &account.NameServerGroupsG[i] + if ng.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNameserverGroup) + if err != nil { + return err + } + ng.AccountSeqID = seq + } + for _, nr := range account.NetworkResources { + if nr == nil || nr.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetworkResource) + if err != nil { + return err + } + nr.AccountSeqID = seq + } + for _, nr := range account.NetworkRouters { + if nr == nil || nr.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetworkRouter) + if err != nil { + return err + } + nr.AccountSeqID = seq + } + return nil +} + // transaction wraps a GORM transaction with MySQL-specific FK checks handling // Use this instead of db.Transaction() directly to avoid deadlocks on MySQL/Aurora func (s *SqlStore) transaction(fn func(*gorm.DB) error) error { @@ -3754,7 +3916,7 @@ func (s *SqlStore) UpdateGroup(ctx context.Context, group *types.Group) error { return status.Errorf(status.InvalidArgument, "group is nil") } - if err := s.db.Omit(clause.Associations).Save(group).Error; err != nil { + if err := s.db.Omit(clause.Associations, "account_seq_id").Save(group).Error; err != nil { log.WithContext(ctx).Errorf("failed to save group to store: %v", err) return status.Errorf(status.Internal, "failed to save group to store") } @@ -3842,7 +4004,7 @@ func (s *SqlStore) CreatePolicy(ctx context.Context, policy *types.Policy) error // SavePolicy saves a policy to the database. func (s *SqlStore) SavePolicy(ctx context.Context, policy *types.Policy) error { - result := s.db.Session(&gorm.Session{FullSaveAssociations: true}).Save(policy) + result := s.db.Session(&gorm.Session{FullSaveAssociations: true}).Omit("account_seq_id").Save(policy) if err := result.Error; err != nil { log.WithContext(ctx).Errorf("failed to save policy to the store: %s", err) return status.Errorf(status.Internal, "failed to save policy to store") diff --git a/management/server/store/store.go b/management/server/store/store.go index a723c1fc316..28aa2e26452 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -220,6 +220,11 @@ type Store interface { GetStoreEngine() types.Engine ExecuteInTransaction(ctx context.Context, f func(store Store) error) error + // AllocateAccountSeqID returns the next per-account integer id for the given + // component kind. Must run inside a transaction so the increment is serialized + // with the component insert. + AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (uint32, error) + GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*networkTypes.Network, error) GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networkTypes.Network, error) SaveNetwork(ctx context.Context, network *networkTypes.Network) error @@ -522,6 +527,24 @@ func getMigrationsPostAuto(ctx context.Context) []migrationFunc { func(db *gorm.DB) error { return migration.DropIndex[proxy.Proxy](ctx, db, "idx_proxy_account_id_unique") }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[types.Policy](ctx, db, types.AccountSeqEntityPolicy, "id") + }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[types.Group](ctx, db, types.AccountSeqEntityGroup, "id") + }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[route.Route](ctx, db, types.AccountSeqEntityRoute, "id") + }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[resourceTypes.NetworkResource](ctx, db, types.AccountSeqEntityNetworkResource, "id") + }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[routerTypes.NetworkRouter](ctx, db, types.AccountSeqEntityNetworkRouter, "id") + }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[dns.NameServerGroup](ctx, db, types.AccountSeqEntityNameserverGroup, "id") + }, } } diff --git a/management/server/store/store_mock.go b/management/server/store/store_mock.go index d5162960693..25c7438658e 100644 --- a/management/server/store/store_mock.go +++ b/management/server/store/store_mock.go @@ -746,6 +746,21 @@ func (mr *MockStoreMockRecorder) EphemeralServiceExists(ctx, lockStrength, accou return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EphemeralServiceExists", reflect.TypeOf((*MockStore)(nil).EphemeralServiceExists), ctx, lockStrength, accountID, peerID, domain) } +// AllocateAccountSeqID mocks base method. +func (m *MockStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types2.AccountSeqEntity) (uint32, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllocateAccountSeqID", ctx, accountID, entity) + ret0, _ := ret[0].(uint32) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AllocateAccountSeqID indicates an expected call of AllocateAccountSeqID. +func (mr *MockStoreMockRecorder) AllocateAccountSeqID(ctx, accountID, entity interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocateAccountSeqID", reflect.TypeOf((*MockStore)(nil).AllocateAccountSeqID), ctx, accountID, entity) +} + // ExecuteInTransaction mocks base method. func (m *MockStore) ExecuteInTransaction(ctx context.Context, f func(Store) error) error { m.ctrl.T.Helper() diff --git a/management/server/types/account_seq_counter.go b/management/server/types/account_seq_counter.go new file mode 100644 index 00000000000..e21f7331760 --- /dev/null +++ b/management/server/types/account_seq_counter.go @@ -0,0 +1,27 @@ +package types + +// AccountSeqEntity identifies the kind of component that uses a per-account sequence. +type AccountSeqEntity string + +const ( + AccountSeqEntityPolicy AccountSeqEntity = "policy" + AccountSeqEntityGroup AccountSeqEntity = "group" + AccountSeqEntityRoute AccountSeqEntity = "route" + AccountSeqEntityNetworkResource AccountSeqEntity = "network_resource" + AccountSeqEntityNetworkRouter AccountSeqEntity = "network_router" + AccountSeqEntityNameserverGroup AccountSeqEntity = "nameserver_group" +) + +// AccountSeqCounter tracks the next per-account integer id for a given component +// kind. Reads/writes go through the store inside the same transaction as the +// component insert so two concurrent inserts cannot collide on the same id. +type AccountSeqCounter struct { + AccountID string `gorm:"primaryKey;size:255"` + Entity string `gorm:"primaryKey;size:32"` + NextID uint32 `gorm:"not null;default:1"` +} + +// TableName overrides the GORM-derived table name. +func (AccountSeqCounter) TableName() string { + return "account_seq_counters" +} diff --git a/management/server/types/group.go b/management/server/types/group.go index b4f50080a93..f47bca60c03 100644 --- a/management/server/types/group.go +++ b/management/server/types/group.go @@ -19,6 +19,10 @@ type Group struct { // AccountID is a reference to Account that this object belongs AccountID string `json:"-" gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_groups_account_seq_id;not null;default:0"` + // Name visible in the UI Name string diff --git a/management/server/types/policy.go b/management/server/types/policy.go index d410aec8ddf..e3f94e178d3 100644 --- a/management/server/types/policy.go +++ b/management/server/types/policy.go @@ -59,6 +59,10 @@ type Policy struct { // AccountID is a reference to Account that this object belongs AccountID string `json:"-" gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_policies_account_seq_id;not null;default:0"` + // Name of the Policy Name string diff --git a/route/route.go b/route/route.go index 97b9721f619..4a8c342b2f1 100644 --- a/route/route.go +++ b/route/route.go @@ -95,6 +95,9 @@ type Route struct { ID ID `gorm:"primaryKey"` // AccountID is a reference to Account that this object belongs AccountID string `gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_routes_account_seq_id;not null;default:0"` // Network and Domains are mutually exclusive Network netip.Prefix `gorm:"serializer:json"` Domains domain.List `gorm:"serializer:json"` From 4543780ef0b194bd4f33d874502e46df7b1611cc Mon Sep 17 00:00:00 2001 From: crn4 Date: Mon, 4 May 2026 13:40:47 +0200 Subject: [PATCH 02/42] grpc components encoding with optimisations --- .../shared/grpc/components_encoder.go | 649 ++++ .../shared/grpc/components_encoder_test.go | 793 +++++ management/server/peer/peer.go | 13 +- management/server/types/group.go | 8 + .../types/networkmap_wire_benchmark_test.go | 157 + management/server/types/policy.go | 7 + shared/management/proto/management.pb.go | 2642 +++++++++++++++-- shared/management/proto/management.proto | 339 +++ 8 files changed, 4369 insertions(+), 239 deletions(-) create mode 100644 management/internals/shared/grpc/components_encoder.go create mode 100644 management/internals/shared/grpc/components_encoder_test.go create mode 100644 management/server/types/networkmap_wire_benchmark_test.go diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go new file mode 100644 index 00000000000..eeda651874f --- /dev/null +++ b/management/internals/shared/grpc/components_encoder.go @@ -0,0 +1,649 @@ +package grpc + +import ( + "encoding/base64" + "strconv" + + nbdns "github.com/netbirdio/netbird/dns" + resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" + routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" + nbroute "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// wgKeyRawLen is the raw byte length of a WireGuard public key. +const wgKeyRawLen = 32 + +// ComponentsEnvelopeInput bundles the data the component-format encoder needs. +// In Step 2 the envelope is fully self-contained — every field needed by the +// client's local Calculate() comes from the components struct itself. The +// only externally-supplied data is the receiving peer's PeerConfig (which is +// computed alongside the components in the network_map controller and reused +// from the legacy proto path) and the dns_domain string. +type ComponentsEnvelopeInput struct { + Components *types.NetworkMapComponents + PeerConfig *proto.PeerConfig + DNSDomain string +} + +// EncodeNetworkMapEnvelope converts NetworkMapComponents into the component +// wire envelope. The encoder is intentionally non-deterministic: it iterates +// Go maps in their native (random) order. Indexes inside the envelope +// (peer_indexes, source_group_ids, agent_version_idx, router_peer_indexes) +// are self-consistent within a single encode, so the decoder reconstructs +// the same typed objects regardless of emit order. Tests that need to +// compare envelopes do so semantically via proto round-trip + canonicalize, +// not byte-equal. +// +// Callers must NOT concatenate or merge envelopes from different encodes — +// index spaces are local to a single envelope. Delta sync (Step 3+) will +// use a different shape for the same reason. +func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvelope { + c := in.Components + + // Phase 1: build dedup tables. Every routing peer (in c.RouterPeers) and + // every regular peer (in c.Peers) must be indexed before any encoder + // looks up indexes via e.peerOrder — otherwise routes / routers_map for + // peers that exist only in c.RouterPeers would silently lose their + // peer_index reference. + enc := newComponentEncoder(c) + enc.indexAllPeers() + routerIdxs := enc.indexRouterPeers(c.RouterPeers) + + // Phase 2: gather every policy that any consumer references (peer-pair + // policies + resource-only policies) so encodeResourcePoliciesMap can + // translate every *Policy pointer to a wire index. + allPolicies := unionPolicies(c.Policies, c.ResourcePoliciesMap) + policies, policyToIdxs := enc.encodePolicies(allPolicies) + + // Phase 3: emit. Order of struct field expressions no longer matters: + // every encoder either reads from the dedup tables or works on + // independent input. + full := &proto.NetworkMapComponentsFull{ + Serial: networkSerial(c.Network), + PeerConfig: in.PeerConfig, + Network: toAccountNetwork(c.Network), + AccountSettings: toAccountSettingsCompact(c.AccountSettings), + DnsSettings: enc.encodeDNSSettings(c.DNSSettings), + DnsDomain: in.DNSDomain, + CustomZoneDomain: c.CustomZoneDomain, + AgentVersions: enc.agentVersions, + Peers: enc.peers, + RouterPeerIndexes: routerIdxs, + Policies: policies, + Groups: enc.encodeGroups(), + Routes: enc.encodeRoutes(c.Routes), + NameserverGroups: enc.encodeNameServerGroups(c.NameServerGroups), + AllDnsRecords: encodeSimpleRecords(c.AllDNSRecords), + AccountZones: encodeCustomZones(c.AccountZones), + NetworkResources: encodeNetworkResources(c.NetworkResources), + RoutersMap: enc.encodeRoutersMap(c.RoutersMap), + ResourcePoliciesMap: encodeResourcePoliciesMap(c.ResourcePoliciesMap, policyToIdxs), + GroupIdToUserIds: enc.encodeGroupIDToUserIDs(c.GroupIDToUserIDs), + AllowedUserIds: stringSetToSlice(c.AllowedUserIDs), + PostureFailedPeers: enc.encodePostureFailedPeers(c.PostureFailedPeers), + } + + return &proto.NetworkMapEnvelope{ + Payload: &proto.NetworkMapEnvelope_Full{Full: full}, + } +} + +// networkSerial returns c.Network.CurrentSerial() with a nil guard. The +// production path always populates c.Network (account_components.go:86), but +// the encoder is exported and a hand-built components struct may omit it. +func networkSerial(n *types.Network) uint64 { + if n == nil { + return 0 + } + return n.CurrentSerial() +} + +type componentEncoder struct { + components *types.NetworkMapComponents + + peerOrder map[string]uint32 + peers []*proto.PeerCompact + + agentVersionOrder map[string]uint32 + agentVersions []string +} + +func newComponentEncoder(c *types.NetworkMapComponents) *componentEncoder { + return &componentEncoder{ + components: c, + peerOrder: make(map[string]uint32, len(c.Peers)), + peers: make([]*proto.PeerCompact, 0, len(c.Peers)), + agentVersionOrder: map[string]uint32{"": 0}, + agentVersions: []string{""}, + } +} + +func (e *componentEncoder) indexAllPeers() { + for _, p := range e.components.Peers { + if p == nil { + continue + } + e.appendPeer(p) + } +} + +func (e *componentEncoder) appendPeer(p *nbpeer.Peer) uint32 { + if idx, ok := e.peerOrder[p.ID]; ok { + return idx + } + idx := uint32(len(e.peers)) + e.peerOrder[p.ID] = idx + e.peers = append(e.peers, toPeerCompact(p, e.agentVersionIndex(p.Meta.WtVersion))) + return idx +} + +func (e *componentEncoder) agentVersionIndex(v string) uint32 { + if idx, ok := e.agentVersionOrder[v]; ok { + return idx + } + idx := uint32(len(e.agentVersions)) + e.agentVersionOrder[v] = idx + e.agentVersions = append(e.agentVersions, v) + return idx +} + +// indexRouterPeers ensures every router peer is in the peer dedup table +// (c.RouterPeers may contain peers not in c.Peers when validation rules drop +// them) and returns their wire indexes for the RouterPeerIndexes field. Must +// run before any encoder that resolves peer ids via e.peerOrder. +func (e *componentEncoder) indexRouterPeers(routers map[string]*nbpeer.Peer) []uint32 { + if len(routers) == 0 { + return nil + } + out := make([]uint32, 0, len(routers)) + for _, p := range routers { + if p == nil { + continue + } + out = append(out, e.appendPeer(p)) + } + return out +} + +func (e *componentEncoder) encodeGroups() []*proto.GroupCompact { + if len(e.components.Groups) == 0 { + return nil + } + + out := make([]*proto.GroupCompact, 0, len(e.components.Groups)) + for _, g := range e.components.Groups { + if !g.HasSeqID() { + continue + } + peerIdxs := make([]uint32, 0, len(g.Peers)) + for _, peerID := range g.Peers { + if idx, ok := e.peerOrder[peerID]; ok { + peerIdxs = append(peerIdxs, idx) + } + } + out = append(out, &proto.GroupCompact{ + Id: g.AccountSeqID, + Name: g.Name, + PeerIndexes: peerIdxs, + }) + } + return out +} + +// encodePolicies flattens Policy{Rules} → []PolicyCompact. Returns the wire +// list and a map from policy pointer to the indexes of its emitted rules in +// that list — used by encodeResourcePoliciesMap to translate +// ResourcePoliciesMap[resourceID][]*Policy into wire-side indexes. +func (e *componentEncoder) encodePolicies(policies []*types.Policy) ([]*proto.PolicyCompact, map[*types.Policy][]uint32) { + if len(policies) == 0 { + return nil, nil + } + + out := make([]*proto.PolicyCompact, 0, len(policies)) + idxByPolicy := make(map[*types.Policy][]uint32, len(policies)) + + for _, pol := range policies { + if !pol.HasSeqID() || !pol.Enabled { + continue + } + for _, r := range pol.Rules { + if r == nil || !r.Enabled { + continue + } + pc := &proto.PolicyCompact{ + Id: pol.AccountSeqID, + Action: getProtoAction(string(r.Action)), + Protocol: getProtoProtocol(string(r.Protocol)), + Bidirectional: r.Bidirectional, + Ports: portsToUint32(r.Ports), + PortRanges: portRangesToProto(r.PortRanges), + SourceGroupIds: make([]uint32, 0, len(r.Sources)), + DestinationGroupIds: make([]uint32, 0, len(r.Destinations)), + } + for _, gid := range r.Sources { + if seq, ok := e.groupSeq(gid); ok { + pc.SourceGroupIds = append(pc.SourceGroupIds, seq) + } + } + for _, gid := range r.Destinations { + if seq, ok := e.groupSeq(gid); ok { + pc.DestinationGroupIds = append(pc.DestinationGroupIds, seq) + } + } + idxByPolicy[pol] = append(idxByPolicy[pol], uint32(len(out))) + out = append(out, pc) + } + } + return out, idxByPolicy +} + +// unionPolicies merges c.Policies with every policy referenced by +// c.ResourcePoliciesMap, deduplicating by pointer identity. Resource-only +// policies (relevant to a NetworkResource but not to peer-pair traffic) +// only live in ResourcePoliciesMap; without this union step they'd be lost +// from the wire and the client's resource-policy lookup would come back +// empty. +func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*types.Policy) []*types.Policy { + seen := make(map[*types.Policy]struct{}, len(policies)) + out := make([]*types.Policy, 0, len(policies)) + for _, p := range policies { + if p == nil { + continue + } + if _, ok := seen[p]; ok { + continue + } + seen[p] = struct{}{} + out = append(out, p) + } + for _, list := range resourcePolicies { + for _, p := range list { + if p == nil { + continue + } + if _, ok := seen[p]; ok { + continue + } + seen[p] = struct{}{} + out = append(out, p) + } + } + return out +} + +func (e *componentEncoder) groupSeq(groupID string) (uint32, bool) { + g, ok := e.components.Groups[groupID] + if !ok || !g.HasSeqID() { + return 0, false + } + return g.AccountSeqID, true +} + +func (e *componentEncoder) encodeDNSSettings(s *types.DNSSettings) *proto.DNSSettingsCompact { + if s == nil || len(s.DisabledManagementGroups) == 0 { + return nil + } + out := &proto.DNSSettingsCompact{ + DisabledManagementGroupIds: make([]uint32, 0, len(s.DisabledManagementGroups)), + } + for _, gid := range s.DisabledManagementGroups { + if seq, ok := e.groupSeq(gid); ok { + out.DisabledManagementGroupIds = append(out.DisabledManagementGroupIds, seq) + } + } + return out +} + +func (e *componentEncoder) encodeRoutes(routes []*nbroute.Route) []*proto.RouteRaw { + if len(routes) == 0 { + return nil + } + out := make([]*proto.RouteRaw, 0, len(routes)) + for _, r := range routes { + if r == nil { + continue + } + rr := &proto.RouteRaw{ + Id: r.AccountSeqID, + NetId: string(r.NetID), + Description: r.Description, + KeepRoute: r.KeepRoute, + NetworkType: int32(r.NetworkType), + Masquerade: r.Masquerade, + Metric: int32(r.Metric), + Enabled: r.Enabled, + SkipAutoApply: r.SkipAutoApply, + Domains: r.Domains.ToPunycodeList(), + GroupIds: e.groupIDsToSeq(r.Groups), + AccessControlGroupIds: e.groupIDsToSeq(r.AccessControlGroups), + PeerGroupIds: e.groupIDsToSeq(r.PeerGroups), + } + if r.Network.IsValid() { + rr.NetworkCidr = r.Network.String() + } + if r.Peer != "" { + if idx, ok := e.peerOrder[r.Peer]; ok { + rr.PeerIndexSet = true + rr.PeerIndex = idx + } + } + out = append(out, rr) + } + return out +} + +func (e *componentEncoder) groupIDsToSeq(groupIDs []string) []uint32 { + if len(groupIDs) == 0 { + return nil + } + out := make([]uint32, 0, len(groupIDs)) + for _, gid := range groupIDs { + if seq, ok := e.groupSeq(gid); ok { + out = append(out, seq) + } + } + return out +} + +func (e *componentEncoder) encodeNameServerGroups(nsgs []*nbdns.NameServerGroup) []*proto.NameServerGroupRaw { + if len(nsgs) == 0 { + return nil + } + out := make([]*proto.NameServerGroupRaw, 0, len(nsgs)) + for _, nsg := range nsgs { + if nsg == nil { + continue + } + entry := &proto.NameServerGroupRaw{ + Id: nsg.AccountSeqID, + Name: nsg.Name, + Description: nsg.Description, + Nameservers: encodeNameServers(nsg.NameServers), + GroupIds: e.groupIDsToSeq(nsg.Groups), + Primary: nsg.Primary, + Domains: nsg.Domains, + Enabled: nsg.Enabled, + SearchDomainsEnabled: nsg.SearchDomainsEnabled, + } + out = append(out, entry) + } + return out +} + +func encodeNameServers(servers []nbdns.NameServer) []*proto.NameServer { + if len(servers) == 0 { + return nil + } + out := make([]*proto.NameServer, 0, len(servers)) + for _, s := range servers { + out = append(out, &proto.NameServer{ + IP: s.IP.String(), + NSType: int64(s.NSType), + Port: int64(s.Port), + }) + } + return out +} + +func encodeSimpleRecords(records []nbdns.SimpleRecord) []*proto.SimpleRecord { + if len(records) == 0 { + return nil + } + out := make([]*proto.SimpleRecord, 0, len(records)) + for _, r := range records { + out = append(out, &proto.SimpleRecord{ + Name: r.Name, + Type: int64(r.Type), + Class: r.Class, + TTL: int64(r.TTL), + RData: r.RData, + }) + } + return out +} + +func encodeCustomZones(zones []nbdns.CustomZone) []*proto.CustomZone { + if len(zones) == 0 { + return nil + } + out := make([]*proto.CustomZone, 0, len(zones)) + for _, z := range zones { + out = append(out, &proto.CustomZone{ + Domain: z.Domain, + Records: encodeSimpleRecords(z.Records), + SearchDomainDisabled: z.SearchDomainDisabled, + NonAuthoritative: z.NonAuthoritative, + }) + } + return out +} + +func encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto.NetworkResourceRaw { + if len(resources) == 0 { + return nil + } + out := make([]*proto.NetworkResourceRaw, 0, len(resources)) + for _, r := range resources { + if r == nil { + continue + } + entry := &proto.NetworkResourceRaw{ + Id: r.AccountSeqID, + NetworkId: r.NetworkID, + Name: r.Name, + Description: r.Description, + Type: string(r.Type), + Address: r.Address, + DomainValue: r.Domain, + Enabled: r.Enabled, + } + if r.Prefix.IsValid() { + entry.PrefixCidr = r.Prefix.String() + } + out = append(out, entry) + } + return out +} + +func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[string]*proto.NetworkRouterList { + if len(routersMap) == 0 { + return nil + } + out := make(map[string]*proto.NetworkRouterList, len(routersMap)) + for networkID, routers := range routersMap { + if len(routers) == 0 { + continue + } + entries := make([]*proto.NetworkRouterEntry, 0, len(routers)) + for peerID, r := range routers { + if r == nil { + continue + } + entry := &proto.NetworkRouterEntry{ + Id: r.AccountSeqID, + PeerGroupIds: e.groupIDsToSeq(r.PeerGroups), + Masquerade: r.Masquerade, + Metric: int32(r.Metric), + Enabled: r.Enabled, + } + if idx, ok := e.peerOrder[peerID]; ok { + entry.PeerIndexSet = true + entry.PeerIndex = idx + } + entries = append(entries, entry) + } + out[networkID] = &proto.NetworkRouterList{Entries: entries} + } + return out +} + +func encodeResourcePoliciesMap(rpm map[string][]*types.Policy, policyToIdxs map[*types.Policy][]uint32) map[string]*proto.PolicyIndexes { + if len(rpm) == 0 { + return nil + } + out := make(map[string]*proto.PolicyIndexes, len(rpm)) + for resourceID, policies := range rpm { + idxs := make([]uint32, 0, len(policies)*2) + for _, pol := range policies { + idxs = append(idxs, policyToIdxs[pol]...) + } + if len(idxs) == 0 { + continue + } + out[resourceID] = &proto.PolicyIndexes{Indexes: idxs} + } + return out +} + +func (e *componentEncoder) encodeGroupIDToUserIDs(m map[string][]string) map[uint32]*proto.UserIDList { + if len(m) == 0 { + return nil + } + out := make(map[uint32]*proto.UserIDList, len(m)) + for groupID, userIDs := range m { + seq, ok := e.groupSeq(groupID) + if !ok || len(userIDs) == 0 { + continue + } + out[seq] = &proto.UserIDList{UserIds: userIDs} + } + return out +} + +func stringSetToSlice(s map[string]struct{}) []string { + if len(s) == 0 { + return nil + } + out := make([]string, 0, len(s)) + for k := range s { + out = append(out, k) + } + return out +} + +func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[string]*proto.PeerIndexSet { + if len(m) == 0 { + return nil + } + out := make(map[string]*proto.PeerIndexSet, len(m)) + for checkID, failedPeerIDs := range m { + idxs := make([]uint32, 0, len(failedPeerIDs)) + for peerID := range failedPeerIDs { + if idx, ok := e.peerOrder[peerID]; ok { + idxs = append(idxs, idx) + } + } + if len(idxs) == 0 { + continue + } + out[checkID] = &proto.PeerIndexSet{PeerIndexes: idxs} + } + return out +} + +// toAccountSettingsCompact always returns a non-nil message — the client +// dereferences it unconditionally during Calculate(), so a nil here would +// crash the receiver. A missing types.AccountSettingsInfo on the server +// (which shouldn't happen in production but the encoder is exported) +// degrades to login_expiration_enabled = false, which makes +// LoginExpired() return false for every peer. +func toAccountSettingsCompact(s *types.AccountSettingsInfo) *proto.AccountSettingsCompact { + if s == nil { + return &proto.AccountSettingsCompact{} + } + return &proto.AccountSettingsCompact{ + PeerLoginExpirationEnabled: s.PeerLoginExpirationEnabled, + PeerLoginExpirationNs: int64(s.PeerLoginExpiration), + } +} + +func toAccountNetwork(n *types.Network) *proto.AccountNetwork { + if n == nil { + return nil + } + out := &proto.AccountNetwork{ + Identifier: n.Identifier, + NetCidr: n.Net.String(), + Dns: n.Dns, + Serial: n.CurrentSerial(), + } + if len(n.NetV6.IP) > 0 { + out.NetV6Cidr = n.NetV6.String() + } + return out +} + +func toPeerCompact(p *nbpeer.Peer, agentVersionIdx uint32) *proto.PeerCompact { + pc := &proto.PeerCompact{ + WgPubKey: decodeWgKey(p.Key), + SshPubKey: []byte(p.SSHKey), + DnsLabel: p.DNSLabel, + AgentVersionIdx: agentVersionIdx, + AddedWithSsoLogin: p.UserID != "", + LoginExpirationEnabled: p.LoginExpirationEnabled, + } + if p.LastLogin != nil { + pc.LastLoginUnixNano = p.LastLogin.UnixNano() + } + switch { + case !p.IP.IsValid(): + // leave Ip nil + case p.IP.Is4() || p.IP.Is4In6(): + ip := p.IP.Unmap().As4() + pc.Ip = ip[:] + default: + ip := p.IP.As16() + pc.Ip = ip[:] + } + if p.IPv6.IsValid() { + ip := p.IPv6.As16() + pc.Ipv6 = ip[:] + } + return pc +} + +// decodeWgKey returns the raw 32 bytes of a base64-encoded WireGuard public +// key, or nil for an empty / malformed key. +func decodeWgKey(s string) []byte { + if s == "" { + return nil + } + out := make([]byte, wgKeyRawLen) + n, err := base64.StdEncoding.Decode(out, []byte(s)) + if err != nil || n != wgKeyRawLen { + return nil + } + return out +} + +func portsToUint32(ports []string) []uint32 { + if len(ports) == 0 { + return nil + } + out := make([]uint32, 0, len(ports)) + for _, p := range ports { + v, err := strconv.ParseUint(p, 10, 16) + if err != nil { + continue + } + out = append(out, uint32(v)) + } + return out +} + +func portRangesToProto(ranges []types.RulePortRange) []*proto.PortInfo_Range { + if len(ranges) == 0 { + return nil + } + out := make([]*proto.PortInfo_Range, 0, len(ranges)) + for _, r := range ranges { + out = append(out, &proto.PortInfo_Range{ + Start: uint32(r.Start), + End: uint32(r.End), + }) + } + return out +} diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go new file mode 100644 index 00000000000..d353de6d8e7 --- /dev/null +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -0,0 +1,793 @@ +package grpc + +import ( + "bytes" + "cmp" + "net" + "net/netip" + "slices" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + goproto "google.golang.org/protobuf/proto" + + nbdns "github.com/netbirdio/netbird/dns" + routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" + nbroute "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/shared/management/proto" +) + +const testWgKeyA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq=" +const testWgKeyB = "BBCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq=" +const testWgKeyC = "CBCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq=" + +// canonicalize rewrites a NetworkMapComponentsFull in place into a canonical +// form: peers reordered by wg_pub_key, with the rest of the message rewritten +// to reference the new peer indexes. Groups, policies, and router indexes are +// also sorted. After canonicalize, two envelopes built from the same logical +// input compare byte-equal via proto.Equal. +// +// This lives on the test side — the encoder itself emits in map-iteration +// order. Test-side normalization is the contract for "two encodes are +// equivalent". +func canonicalize(full *proto.NetworkMapComponentsFull) { + if full == nil { + return + } + + // Canonicalize agent_versions first: sort the slice and rewrite each + // peer's AgentVersionIdx accordingly. The empty placeholder stays at + // index 0 by convention. + avRemap := make(map[uint32]uint32, len(full.AgentVersions)) + if len(full.AgentVersions) > 0 { + // Pair version → original index, sort, rebuild. + type avEntry struct { + version string + oldIdx uint32 + } + entries := make([]avEntry, len(full.AgentVersions)) + for i, v := range full.AgentVersions { + entries[i] = avEntry{version: v, oldIdx: uint32(i)} + } + // Empty stays at 0; sort the rest by string. Tiebreaker on oldIdx + // keeps the canonicalize output stable when two entries compare + // equal (the encoder dedups, but defending against future inputs). + slices.SortFunc(entries, func(a, b avEntry) int { + if a.version == "" && b.version != "" { + return -1 + } + if b.version == "" && a.version != "" { + return 1 + } + if c := cmp.Compare(a.version, b.version); c != 0 { + return c + } + return cmp.Compare(a.oldIdx, b.oldIdx) + }) + newVersions := make([]string, len(entries)) + for newIdx, e := range entries { + avRemap[e.oldIdx] = uint32(newIdx) + newVersions[newIdx] = e.version + } + full.AgentVersions = newVersions + } + for _, p := range full.Peers { + if newIdx, ok := avRemap[p.AgentVersionIdx]; ok { + p.AgentVersionIdx = newIdx + } + } + + type peerEntry struct { + peer *proto.PeerCompact + oldIdx uint32 + } + entries := make([]peerEntry, len(full.Peers)) + for i, p := range full.Peers { + entries[i] = peerEntry{peer: p, oldIdx: uint32(i)} + } + // DnsLabel is unique per peer; it tiebreaks on equal WgPubKey (e.g. both + // nil from malformed keys, or both empty for placeholders). + slices.SortFunc(entries, func(a, b peerEntry) int { + if c := bytes.Compare(a.peer.WgPubKey, b.peer.WgPubKey); c != 0 { + return c + } + return cmp.Compare(a.peer.DnsLabel, b.peer.DnsLabel) + }) + + remap := make(map[uint32]uint32, len(entries)) + newPeers := make([]*proto.PeerCompact, len(entries)) + for newIdx, e := range entries { + remap[e.oldIdx] = uint32(newIdx) + newPeers[newIdx] = e.peer + } + full.Peers = newPeers + + full.RouterPeerIndexes = remapAndSort(full.RouterPeerIndexes, remap) + for _, g := range full.Groups { + g.PeerIndexes = remapAndSort(g.PeerIndexes, remap) + } + slices.SortFunc(full.Groups, func(a, b *proto.GroupCompact) int { return cmp.Compare(a.Id, b.Id) }) + + for _, r := range full.Routes { + if r.PeerIndexSet { + if newIdx, ok := remap[r.PeerIndex]; ok { + r.PeerIndex = newIdx + } + } + slices.Sort(r.GroupIds) + slices.Sort(r.AccessControlGroupIds) + slices.Sort(r.PeerGroupIds) + } + slices.SortFunc(full.Routes, func(a, b *proto.RouteRaw) int { return cmp.Compare(a.Id, b.Id) }) + + for _, list := range full.RoutersMap { + for _, entry := range list.Entries { + if entry.PeerIndexSet { + if newIdx, ok := remap[entry.PeerIndex]; ok { + entry.PeerIndex = newIdx + } + } + slices.Sort(entry.PeerGroupIds) + } + slices.SortFunc(list.Entries, func(a, b *proto.NetworkRouterEntry) int { return cmp.Compare(a.Id, b.Id) }) + } + + for _, set := range full.PostureFailedPeers { + set.PeerIndexes = remapAndSort(set.PeerIndexes, remap) + } + + for _, p := range full.Policies { + slices.Sort(p.SourceGroupIds) + slices.Sort(p.DestinationGroupIds) + } + // Sort policies by (Id, source_group_ids, destination_group_ids) so that + // multiple PolicyCompact entries sharing the same Id (one per rule, when + // a Policy has multiple rules) still get a deterministic order. After + // sorting we remap indexes in ResourcePoliciesMap. + policyOldOrder := make(map[*proto.PolicyCompact]uint32, len(full.Policies)) + for i, p := range full.Policies { + policyOldOrder[p] = uint32(i) + } + slices.SortFunc(full.Policies, func(a, b *proto.PolicyCompact) int { + if c := cmp.Compare(a.Id, b.Id); c != 0 { + return c + } + if c := slices.Compare(a.SourceGroupIds, b.SourceGroupIds); c != 0 { + return c + } + return slices.Compare(a.DestinationGroupIds, b.DestinationGroupIds) + }) + policyRemap := make(map[uint32]uint32, len(full.Policies)) + for newIdx, p := range full.Policies { + policyRemap[policyOldOrder[p]] = uint32(newIdx) + } + for _, idxs := range full.ResourcePoliciesMap { + idxs.Indexes = remapAndSort(idxs.Indexes, policyRemap) + } + for _, list := range full.GroupIdToUserIds { + slices.Sort(list.UserIds) + } + slices.Sort(full.AllowedUserIds) +} + +func remapAndSort(idxs []uint32, remap map[uint32]uint32) []uint32 { + out := make([]uint32, 0, len(idxs)) + for _, i := range idxs { + if newIdx, ok := remap[i]; ok { + out = append(out, newIdx) + } + } + slices.Sort(out) + return out +} + +// envelopesEquivalent decodes both envelopes, canonicalizes them, and reports +// whether they're proto.Equal. Use instead of byte-comparing marshaled output: +// the encoder is intentionally non-deterministic. +func envelopesEquivalent(a, b *proto.NetworkMapEnvelope) bool { + canonicalize(a.GetFull()) + canonicalize(b.GetFull()) + return goproto.Equal(a, b) +} + +func newTestComponents() *types.NetworkMapComponents { + peerA := &nbpeer.Peer{ + ID: "peer-a", + Key: testWgKeyA, + IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}), + DNSLabel: "peera", + SSHKey: "ssh-a", + Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now()}, + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + peerB := &nbpeer.Peer{ + ID: "peer-b", + Key: testWgKeyB, + IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}), + IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), + DNSLabel: "peerb", + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.25.0"}, + } + peerC := &nbpeer.Peer{ + ID: "peer-c", + Key: testWgKeyC, + IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}), + DNSLabel: "peerc", + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + + return &types.NetworkMapComponents{ + PeerID: "peer-a", + Network: &types.Network{ + Identifier: "net-test", + Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}, + Serial: 7, + }, + AccountSettings: &types.AccountSettingsInfo{ + PeerLoginExpirationEnabled: true, + PeerLoginExpiration: 2 * time.Hour, + }, + Peers: map[string]*nbpeer.Peer{ + "peer-a": peerA, + "peer-b": peerB, + "peer-c": peerC, + }, + Groups: map[string]*types.Group{ + "group-src": {ID: "group-src", AccountSeqID: 1, Name: "Src", Peers: []string{"peer-a"}}, + "group-dst": {ID: "group-dst", AccountSeqID: 2, Name: "Dst", Peers: []string{"peer-b", "peer-c"}}, + }, + Policies: []*types.Policy{ + { + ID: "pol-1", + AccountSeqID: 10, + Enabled: true, + Rules: []*types.PolicyRule{{ + ID: "rule-1", Enabled: true, Action: types.PolicyTrafficActionAccept, + Protocol: types.PolicyRuleProtocolTCP, Bidirectional: true, + Ports: []string{"22", "80"}, + PortRanges: []types.RulePortRange{{Start: 8000, End: 8100}}, + Sources: []string{"group-src"}, + Destinations: []string{"group-dst"}, + }}, + }, + }, + RouterPeers: map[string]*nbpeer.Peer{"peer-c": peerC}, + } +} + +func TestEncodeNetworkMapEnvelope_Basic(t *testing.T) { + c := newTestComponents() + env := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ + Components: c, + DNSDomain: "netbird.cloud", + }) + + require.NotNil(t, env) + full := env.GetFull() + require.NotNil(t, full, "envelope must contain Full payload") + + assert.EqualValues(t, 7, full.Serial) + assert.Equal(t, "netbird.cloud", full.DnsDomain) + + require.NotNil(t, full.Network) + assert.Equal(t, "net-test", full.Network.Identifier) + assert.Equal(t, "100.64.0.0/10", full.Network.NetCidr) + + require.NotNil(t, full.AccountSettings) + assert.True(t, full.AccountSettings.PeerLoginExpirationEnabled) + assert.EqualValues(t, (2 * time.Hour).Nanoseconds(), full.AccountSettings.PeerLoginExpirationNs) + + require.Len(t, full.Peers, 3) + byLabel := map[string]*proto.PeerCompact{} + for _, p := range full.Peers { + assert.Len(t, p.WgPubKey, 32, "wg key must be raw 32 bytes") + assert.Len(t, p.Ip, 4, "ipv4 must be raw 4 bytes") + byLabel[p.DnsLabel] = p + } + assert.Len(t, byLabel["peerb"].Ipv6, 16, "peer-b has ipv6 → 16 bytes") +} + +func TestEncodeNetworkMapEnvelope_RepeatEncodesEquivalent(t *testing.T) { + c := newTestComponents() + + expected := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + + // Hammer it 100 times — Go map iteration is randomized per call, so each + // run produces different wire bytes, but the canonicalized form must + // match. + for i := 0; i < 100; i++ { + got := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + require.True(t, envelopesEquivalent(expected, got), + "encode #%d must be semantically equivalent to first encode", i) + } +} + +func TestEncodeNetworkMapEnvelope_ConcurrentEncodesEquivalent(t *testing.T) { + c := newTestComponents() + + expected := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + + const goroutines = 50 + var wg sync.WaitGroup + wg.Add(goroutines) + results := make([]*proto.NetworkMapEnvelope, goroutines) + for i := 0; i < goroutines; i++ { + i := i + go func() { + defer wg.Done() + results[i] = EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + }() + } + wg.Wait() + + for i, got := range results { + require.NotNil(t, got, "goroutine %d returned nil", i) + require.True(t, envelopesEquivalent(expected, got), + "goroutine %d produced inequivalent envelope", i) + } +} + +func TestEncodeNetworkMapEnvelope_GroupsByAccountSeqID(t *testing.T) { + c := newTestComponents() + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Groups, 2) + + groupByID := map[uint32]*proto.GroupCompact{} + for _, g := range full.Groups { + groupByID[g.Id] = g + } + require.Contains(t, groupByID, uint32(1)) + require.Contains(t, groupByID, uint32(2)) + assert.Equal(t, "Src", groupByID[1].Name) + assert.Equal(t, "Dst", groupByID[2].Name) + assert.Len(t, groupByID[1].PeerIndexes, 1) + assert.Len(t, groupByID[2].PeerIndexes, 2) +} + +func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) { + c := newTestComponents() + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Policies, 1) + pc := full.Policies[0] + assert.EqualValues(t, 10, pc.Id) + assert.Equal(t, proto.RuleAction_ACCEPT, pc.Action) + assert.Equal(t, proto.RuleProtocol_TCP, pc.Protocol) + assert.True(t, pc.Bidirectional) + assert.Equal(t, []uint32{22, 80}, pc.Ports) + require.Len(t, pc.PortRanges, 1) + assert.EqualValues(t, 8000, pc.PortRanges[0].Start) + assert.EqualValues(t, 8100, pc.PortRanges[0].End) + assert.Equal(t, []uint32{1}, pc.SourceGroupIds) + assert.Equal(t, []uint32{2}, pc.DestinationGroupIds) +} + +func TestEncodeNetworkMapEnvelope_RouterIndexes(t *testing.T) { + c := newTestComponents() + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.RouterPeerIndexes, 1) + idx := full.RouterPeerIndexes[0] + require.Less(t, int(idx), len(full.Peers)) + assert.Equal(t, "peerc", full.Peers[idx].DnsLabel) +} + +func TestEncodeNetworkMapEnvelope_AgentVersionDedup(t *testing.T) { + c := newTestComponents() + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.AgentVersions, 3, "empty placeholder + 2 distinct versions") + assert.Equal(t, "", full.AgentVersions[0], "index 0 reserved for empty version") + assert.ElementsMatch(t, []string{"0.40.0", "0.25.0"}, full.AgentVersions[1:], + "two distinct versions, order depends on map iteration") + + idxByLabel := map[string]uint32{} + for _, p := range full.Peers { + idxByLabel[p.DnsLabel] = p.AgentVersionIdx + } + assert.Equal(t, idxByLabel["peera"], idxByLabel["peerc"], "peers with the same agent version share an index") + assert.NotEqual(t, idxByLabel["peera"], idxByLabel["peerb"]) +} + +func TestEncodeNetworkMapEnvelope_DisabledPolicySkipped(t *testing.T) { + c := newTestComponents() + c.Policies[0].Enabled = false + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + assert.Empty(t, full.Policies) +} + +func TestEncodeNetworkMapEnvelope_GroupZeroSeqIDSkipped(t *testing.T) { + c := newTestComponents() + c.Groups["group-src"].AccountSeqID = 0 + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Groups, 1, "groups with AccountSeqID=0 are not yet persisted and must be skipped") + assert.EqualValues(t, 2, full.Groups[0].Id) + + require.Len(t, full.Policies, 1) + pc := full.Policies[0] + assert.Empty(t, pc.SourceGroupIds, "rule references a group that was filtered out → no group id on wire") + assert.Equal(t, []uint32{2}, pc.DestinationGroupIds) +} + +func TestEncodeNetworkMapEnvelope_TwoPeersSameMalformedKey(t *testing.T) { + // Both peers have nil WgPubKey after decode; canonicalize must still + // produce a stable order using DnsLabel as a tiebreaker, so 100 encodes + // canonicalize identically. + c := newTestComponents() + c.Peers["peer-a"].Key = "garbage-a-!!!" + c.Peers["peer-b"].Key = "garbage-b-!!!" + + expected := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + for i := 0; i < 100; i++ { + got := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + require.True(t, envelopesEquivalent(expected, got), + "encode #%d with two same-key peers must canonicalize equivalently", i) + } +} + +func TestEncodeNetworkMapEnvelope_MalformedWgKey(t *testing.T) { + c := newTestComponents() + c.Peers["peer-a"].Key = "not-base64-!!!" + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Peers, 3) + + var byLabel = map[string]*proto.PeerCompact{} + for _, p := range full.Peers { + byLabel[p.DnsLabel] = p + } + assert.Nil(t, byLabel["peera"].WgPubKey, "peer with malformed key encodes nil WgPubKey") + assert.Len(t, byLabel["peerb"].WgPubKey, 32, "other peers retain their key") +} + +func TestEncodeNetworkMapEnvelope_IPv6OnlyPeer(t *testing.T) { + c := newTestComponents() + v6Only := &nbpeer.Peer{ + ID: "peer-v6", + Key: testWgKeyA, + IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9}), + DNSLabel: "peerv6", + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + c.Peers["peer-v6"] = v6Only + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + var found *proto.PeerCompact + for _, p := range full.Peers { + if p.DnsLabel == "peerv6" { + found = p + } + } + require.NotNil(t, found, "ipv6-only peer must be present") + assert.Empty(t, found.Ip, "no IPv4 address → empty Ip") + assert.Len(t, found.Ipv6, 16) +} + +func TestEncodeNetworkMapEnvelope_PeerWithoutIP(t *testing.T) { + c := newTestComponents() + c.Peers["peer-noip"] = &nbpeer.Peer{ + ID: "peer-noip", + Key: testWgKeyA, + DNSLabel: "peernoip", + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + var found *proto.PeerCompact + for _, p := range full.Peers { + if p.DnsLabel == "peernoip" { + found = p + } + } + require.NotNil(t, found) + assert.Empty(t, found.Ip) + assert.Empty(t, found.Ipv6) +} + +func TestEncodeNetworkMapEnvelope_EmptyInput(t *testing.T) { + c := &types.NetworkMapComponents{ + Network: &types.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}}, + } + + env := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}) + + full := env.GetFull() + require.NotNil(t, full) + assert.Empty(t, full.Peers) + assert.Empty(t, full.Groups) + assert.Empty(t, full.Policies) + assert.Empty(t, full.RouterPeerIndexes) + require.NotNil(t, full.AccountSettings, "AccountSettingsCompact must always be emitted (client dereferences it unconditionally)") +} + +func TestEncodeNetworkMapEnvelope_PeerLoginExpirationFields(t *testing.T) { + c := newTestComponents() + now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC) + c.Peers["peer-a"].UserID = "user-1" + c.Peers["peer-a"].LoginExpirationEnabled = true + c.Peers["peer-a"].LastLogin = &now + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + var pa *proto.PeerCompact + for _, p := range full.Peers { + if p.DnsLabel == "peera" { + pa = p + } + } + require.NotNil(t, pa) + assert.True(t, pa.AddedWithSsoLogin) + assert.True(t, pa.LoginExpirationEnabled) + assert.Equal(t, now.UnixNano(), pa.LastLoginUnixNano) + + // peer-b has no UserID and no LastLogin → all fields zero-value. + var pb *proto.PeerCompact + for _, p := range full.Peers { + if p.DnsLabel == "peerb" { + pb = p + } + } + require.NotNil(t, pb) + assert.False(t, pb.AddedWithSsoLogin) + assert.False(t, pb.LoginExpirationEnabled) + assert.Zero(t, pb.LastLoginUnixNano) +} + +func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) { + c := newTestComponents() + c.Routes = []*nbroute.Route{ + { + ID: "route-peer", + AccountSeqID: 100, + NetID: "net-A", + Description: "via peer-c", + Network: netip.MustParsePrefix("10.0.0.0/16"), + Peer: "peer-c", // peer ID, not WG key + Groups: []string{"group-src"}, + AccessControlGroups: []string{"group-dst"}, + Enabled: true, + }, + { + ID: "route-peergroup", + AccountSeqID: 101, + NetID: "net-B", + Network: netip.MustParsePrefix("10.1.0.0/16"), + PeerGroups: []string{"group-src", "group-dst"}, + Enabled: true, + }, + { + ID: "route-no-seq", + AccountSeqID: 0, // unset — should still ship (no group seq filter on routes) + Network: netip.MustParsePrefix("10.2.0.0/16"), + Enabled: true, + }, + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Routes, 3) + byNetID := map[string]*proto.RouteRaw{} + for _, r := range full.Routes { + byNetID[r.NetId] = r + } + + r1 := byNetID["net-A"] + require.NotNil(t, r1) + assert.True(t, r1.PeerIndexSet, "route with peer must set peer_index_set") + require.Less(t, int(r1.PeerIndex), len(full.Peers)) + assert.Equal(t, "peerc", full.Peers[r1.PeerIndex].DnsLabel) + assert.Equal(t, []uint32{1}, r1.GroupIds, "group-src has AccountSeqID 1") + assert.Equal(t, []uint32{2}, r1.AccessControlGroupIds, "group-dst has AccountSeqID 2") + assert.Empty(t, r1.PeerGroupIds) + + r2 := byNetID["net-B"] + require.NotNil(t, r2) + assert.False(t, r2.PeerIndexSet, "route with peer_groups must NOT set peer_index_set") + assert.ElementsMatch(t, []uint32{1, 2}, r2.PeerGroupIds) +} + +func TestEncodeNetworkMapEnvelope_RouteWithMissingPeerLeavesIndexUnset(t *testing.T) { + c := newTestComponents() + c.Routes = []*nbroute.Route{{ + ID: "route-x", + AccountSeqID: 100, + Peer: "peer-not-in-components", + Network: netip.MustParsePrefix("10.0.0.0/16"), + Enabled: true, + }} + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Routes, 1) + assert.False(t, full.Routes[0].PeerIndexSet, + "missing peer reference must not pretend to point at peer index 0") +} + +func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing.T) { + c := newTestComponents() + // Policy that exists ONLY in ResourcePoliciesMap, not in c.Policies. This + // is the I1 case — without unionPolicies the encoder would silently + // drop it from the wire. + resourceOnlyPolicy := &types.Policy{ + ID: "pol-resource", AccountSeqID: 99, Enabled: true, + Rules: []*types.PolicyRule{{ + ID: "rule-r", Enabled: true, Action: types.PolicyTrafficActionAccept, + Protocol: types.PolicyRuleProtocolTCP, + Sources: []string{"group-src"}, + Destinations: []string{"group-dst"}, + }}, + } + c.ResourcePoliciesMap = map[string][]*types.Policy{ + "resource-x": {c.Policies[0], resourceOnlyPolicy}, // shared + resource-only + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.Policies, 2, "encoded policies must include both peer-traffic and resource-only") + + policyByID := map[uint32]*proto.PolicyCompact{} + policyIdxByID := map[uint32]uint32{} + for i, p := range full.Policies { + policyByID[p.Id] = p + policyIdxByID[p.Id] = uint32(i) + } + require.Contains(t, policyByID, uint32(10), "original peer-traffic policy id 10") + require.Contains(t, policyByID, uint32(99), "resource-only policy id 99") + + require.Contains(t, full.ResourcePoliciesMap, "resource-x") + idxs := full.ResourcePoliciesMap["resource-x"].Indexes + require.Len(t, idxs, 2) + assert.ElementsMatch(t, []uint32{policyIdxByID[10], policyIdxByID[99]}, idxs, + "resource policies map must reference both wire policy indexes") +} + +func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) { + c := newTestComponents() + c.NameServerGroups = []*nbdns.NameServerGroup{{ + ID: "nsg-1", AccountSeqID: 50, Name: "Main", Description: "primary", + NameServers: []nbdns.NameServer{{ + IP: netip.MustParseAddr("8.8.8.8"), NSType: nbdns.UDPNameServerType, Port: 53, + }}, + Groups: []string{"group-src", "group-not-persisted"}, + Primary: true, Enabled: true, + Domains: []string{"corp.example"}, + }} + c.Groups["group-not-persisted"] = &types.Group{ID: "group-not-persisted", AccountSeqID: 0, Peers: []string{}} + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.NameserverGroups, 1) + nsg := full.NameserverGroups[0] + assert.EqualValues(t, 50, nsg.Id) + assert.Equal(t, "Main", nsg.Name) + assert.True(t, nsg.Primary) + require.Len(t, nsg.Nameservers, 1) + assert.Equal(t, "8.8.8.8", nsg.Nameservers[0].IP) + assert.Equal(t, []uint32{1}, nsg.GroupIds, "group-not-persisted is filtered out (AccountSeqID=0)") +} + +func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { + c := newTestComponents() + c.PostureFailedPeers = map[string]map[string]struct{}{ + "check-1": { + "peer-a": {}, + "peer-b": {}, + "peer-not-in-account": {}, + }, + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Contains(t, full.PostureFailedPeers, "check-1") + idxs := full.PostureFailedPeers["check-1"].PeerIndexes + assert.Len(t, idxs, 2, "missing peer is silently dropped (filterPostureFailedPeers guarantees presence in real data)") +} + +func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { + c := newTestComponents() + c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ + "net-1": { + "peer-c": { + ID: "router-1", AccountSeqID: 200, + Peer: "peer-c", Masquerade: true, Metric: 10, Enabled: true, + }, + }, + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Contains(t, full.RoutersMap, "net-1") + entries := full.RoutersMap["net-1"].Entries + require.Len(t, entries, 1) + e := entries[0] + assert.EqualValues(t, 200, e.Id) + assert.True(t, e.PeerIndexSet) + require.Less(t, int(e.PeerIndex), len(full.Peers)) + assert.Equal(t, "peerc", full.Peers[e.PeerIndex].DnsLabel) + assert.True(t, e.Masquerade) + assert.EqualValues(t, 10, e.Metric) + assert.True(t, e.Enabled) +} + +func TestEncodeNetworkMapEnvelope_RouterPeerNotInComponentsPeers(t *testing.T) { + // Router peer in c.RouterPeers but NOT in c.Peers (validation may have + // filtered it). indexRouterPeers runs before encodeRoutersMap, so the + // peer_index reference must still resolve. + c := newTestComponents() + delete(c.Peers, "peer-c") + routerPeer := &nbpeer.Peer{ + ID: "peer-c", Key: testWgKeyC, IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}), + DNSLabel: "peerc", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + c.RouterPeers = map[string]*nbpeer.Peer{"peer-c": routerPeer} + c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ + "net-1": {"peer-c": {ID: "r-1", AccountSeqID: 1, Peer: "peer-c", Enabled: true}}, + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Contains(t, full.RoutersMap, "net-1") + require.Len(t, full.RoutersMap["net-1"].Entries, 1) + e := full.RoutersMap["net-1"].Entries[0] + assert.True(t, e.PeerIndexSet, "router peer must be indexed even when not in c.Peers") +} + +func TestEncodeNetworkMapEnvelope_DNSSettingsFiltersUnpersistedGroups(t *testing.T) { + c := newTestComponents() + c.DNSSettings = &types.DNSSettings{ + DisabledManagementGroups: []string{"group-src", "group-missing", "group-no-seq"}, + } + c.Groups["group-no-seq"] = &types.Group{ID: "group-no-seq", AccountSeqID: 0} + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.NotNil(t, full.DnsSettings) + assert.Equal(t, []uint32{1}, full.DnsSettings.DisabledManagementGroupIds, + "only group-src (AccountSeqID=1) survives — missing and unpersisted are dropped") +} + +func TestEncodeNetworkMapEnvelope_GroupIDToUserIDs(t *testing.T) { + c := newTestComponents() + c.GroupIDToUserIDs = map[string][]string{ + "group-src": {"user-1", "user-2"}, + "group-no-seq": {"user-3"}, // group not persisted → drop + "group-missing": {"user-4"}, // group not in components → drop + } + c.Groups["group-no-seq"] = &types.Group{ID: "group-no-seq", AccountSeqID: 0} + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.Len(t, full.GroupIdToUserIds, 1, "only persisted+present groups survive") + require.Contains(t, full.GroupIdToUserIds, uint32(1)) + assert.ElementsMatch(t, []string{"user-1", "user-2"}, full.GroupIdToUserIds[1].UserIds) +} + +func TestEncodeNetworkMapEnvelope_AccountSettingsAlwaysEmitted(t *testing.T) { + c := &types.NetworkMapComponents{ + Network: &types.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}}, + // AccountSettings deliberately nil + } + + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() + + require.NotNil(t, full.AccountSettings, "client dereferences AccountSettings unconditionally during Calculate(); a nil here would crash the receiver") + assert.False(t, full.AccountSettings.PeerLoginExpirationEnabled) + assert.Zero(t, full.AccountSettings.PeerLoginExpirationNs) +} diff --git a/management/server/peer/peer.go b/management/server/peer/peer.go index 2963dfcbde3..567abc7a3ae 100644 --- a/management/server/peer/peer.go +++ b/management/server/peer/peer.go @@ -13,8 +13,9 @@ import ( // Peer capability constants mirror the proto enum values. const ( - PeerCapabilitySourcePrefixes int32 = 1 - PeerCapabilityIPv6Overlay int32 = 2 + PeerCapabilitySourcePrefixes int32 = 1 + PeerCapabilityIPv6Overlay int32 = 2 + PeerCapabilityComponentNetworkMap int32 = 3 ) // Peer represents a machine connected to the network. @@ -247,6 +248,14 @@ func (p *Peer) SupportsSourcePrefixes() bool { return p.HasCapability(PeerCapabilitySourcePrefixes) } +// SupportsComponentNetworkMap reports whether the peer assembles its +// NetworkMap from server-shipped components instead of consuming a fully +// expanded NetworkMap. Determines whether the network_map controller skips +// Calculate() server-side and emits the components envelope. +func (p *Peer) SupportsComponentNetworkMap() bool { + return p.HasCapability(PeerCapabilityComponentNetworkMap) +} + func capabilitiesEqual(a, b []int32) bool { if len(a) != len(b) { return false diff --git a/management/server/types/group.go b/management/server/types/group.go index f47bca60c03..ee5e45ab314 100644 --- a/management/server/types/group.go +++ b/management/server/types/group.go @@ -45,6 +45,14 @@ type GroupPeer struct { PeerID string `gorm:"primaryKey"` } +// HasSeqID reports whether the group has been persisted long enough to have a +// per-account sequence id allocated. Wire encoders that key off AccountSeqID +// must skip groups that return false here — otherwise multiple unpersisted +// groups would collide on id 0. +func (g *Group) HasSeqID() bool { + return g != nil && g.AccountSeqID != 0 +} + func (g *Group) LoadGroupPeers() { g.Peers = make([]string, len(g.GroupPeers)) for i, peer := range g.GroupPeers { diff --git a/management/server/types/networkmap_wire_benchmark_test.go b/management/server/types/networkmap_wire_benchmark_test.go new file mode 100644 index 00000000000..f310cb0ea64 --- /dev/null +++ b/management/server/types/networkmap_wire_benchmark_test.go @@ -0,0 +1,157 @@ +package types_test + +import ( + "context" + "fmt" + "testing" + + goproto "google.golang.org/protobuf/proto" + + nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/internals/controllers/network_map/controller/cache" + mgmtgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc" + "github.com/netbirdio/netbird/management/server/types" +) + +// wireBenchScales mirrors the scales used by networkmap_benchmark_test.go but +// trimmed: encoding+marshal are linear, so we don't need the 30k peer extreme +// to see the trend. +var wireBenchScales = []benchmarkScale{ + {"100peers_5groups", 100, 5}, + {"500peers_20groups", 500, 20}, + {"1000peers_50groups", 1000, 50}, + {"5000peers_100groups", 5000, 100}, +} + +// populateAccountSeqIDs assigns deterministic AccountSeqIDs to every group and +// policy in the account so that the component encoder can reference them. The +// scalableTestAccount fixture builds entities by struct literal and skips this +// step, but production paths populate the IDs via the store layer. +func populateAccountSeqIDs(account *types.Account) { + var nextGroupSeq uint32 = 1 + for _, g := range account.Groups { + g.AccountSeqID = nextGroupSeq + nextGroupSeq++ + } + var nextPolicySeq uint32 = 1 + for _, p := range account.Policies { + p.AccountSeqID = nextPolicySeq + nextPolicySeq++ + } +} + +// BenchmarkNetworkMapWireEncode reports per-call ns and the marshaled wire +// size for both encoding paths. Run with: +// +// go test -run=^$ -bench=BenchmarkNetworkMapWireEncode -benchmem ./management/server/types/ +func BenchmarkNetworkMapWireEncode(b *testing.B) { + skipCIBenchmark(b) + + for _, scale := range wireBenchScales { + account, validatedPeers := scalableTestAccount(scale.peers, scale.groups) + populateAccountSeqIDs(account) + + ctx := context.Background() + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + + peerID := "peer-0" + peer := account.Peers[peerID] + + networkMap := account.GetPeerNetworkMapFromComponents(ctx, peerID, nbdns.CustomZone{}, nil, validatedPeers, resourcePolicies, routers, nil, groupIDToUserIDs) + components := account.GetPeerNetworkMapComponents(ctx, peerID, nbdns.CustomZone{}, nil, validatedPeers, resourcePolicies, routers, groupIDToUserIDs) + + dnsCache := &cache.DNSConfigCache{} + settings := &types.Settings{} + + // Pre-encode once so the size metric is identical for every run inside + // the same scale; the b.Loop call only re-runs encode + Marshal. + legacyResp := mgmtgrpc.ToSyncResponse(ctx, nil, nil, nil, peer, nil, nil, networkMap, "netbird.cloud", nil, dnsCache, settings, nil, nil, 0) + legacyBytes, err := goproto.Marshal(legacyResp.NetworkMap) + if err != nil { + b.Fatalf("marshal legacy networkmap: %v", err) + } + + envelopeInput := mgmtgrpc.ComponentsEnvelopeInput{ + Components: components, + PeerConfig: legacyResp.NetworkMap.PeerConfig, + DNSDomain: "netbird.cloud", + } + envelope := mgmtgrpc.EncodeNetworkMapEnvelope(envelopeInput) + envelopeBytes, err := goproto.Marshal(envelope) + if err != nil { + b.Fatalf("marshal envelope: %v", err) + } + + b.Run(fmt.Sprintf("legacy/%s", scale.name), func(b *testing.B) { + b.ReportAllocs() + b.ReportMetric(float64(len(legacyBytes)), "bytes/msg") + b.ResetTimer() + for range b.N { + resp := mgmtgrpc.ToSyncResponse(ctx, nil, nil, nil, peer, nil, nil, networkMap, "netbird.cloud", nil, dnsCache, settings, nil, nil, 0) + if _, err := goproto.Marshal(resp.NetworkMap); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(fmt.Sprintf("components/%s", scale.name), func(b *testing.B) { + b.ReportAllocs() + b.ReportMetric(float64(len(envelopeBytes)), "bytes/msg") + b.ResetTimer() + for range b.N { + env := mgmtgrpc.EncodeNetworkMapEnvelope(envelopeInput) + if _, err := goproto.Marshal(env); err != nil { + b.Fatal(err) + } + } + }) + } +} + +// BenchmarkNetworkMapWireSize is a fast snapshot of the wire size by scale +// without a tight encode loop. Run with -bench to see one ns/op + bytes per +// scale (treat the timing as informational; the sample is one Marshal per +// scale, not the full b.N loop). +func BenchmarkNetworkMapWireSize(b *testing.B) { + skipCIBenchmark(b) + + for _, scale := range wireBenchScales { + account, validatedPeers := scalableTestAccount(scale.peers, scale.groups) + populateAccountSeqIDs(account) + + ctx := context.Background() + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + + peerID := "peer-0" + peer := account.Peers[peerID] + + networkMap := account.GetPeerNetworkMapFromComponents(ctx, peerID, nbdns.CustomZone{}, nil, validatedPeers, resourcePolicies, routers, nil, groupIDToUserIDs) + components := account.GetPeerNetworkMapComponents(ctx, peerID, nbdns.CustomZone{}, nil, validatedPeers, resourcePolicies, routers, groupIDToUserIDs) + + dnsCache := &cache.DNSConfigCache{} + settings := &types.Settings{} + + legacyResp := mgmtgrpc.ToSyncResponse(ctx, nil, nil, nil, peer, nil, nil, networkMap, "netbird.cloud", nil, dnsCache, settings, nil, nil, 0) + legacyBytes, _ := goproto.Marshal(legacyResp.NetworkMap) + + env := mgmtgrpc.EncodeNetworkMapEnvelope(mgmtgrpc.ComponentsEnvelopeInput{ + Components: components, + PeerConfig: legacyResp.NetworkMap.PeerConfig, + DNSDomain: "netbird.cloud", + }) + envBytes, _ := goproto.Marshal(env) + + b.Run(fmt.Sprintf("size/%s", scale.name), func(b *testing.B) { + b.ReportMetric(float64(len(legacyBytes)), "legacy_bytes") + b.ReportMetric(float64(len(envBytes)), "components_bytes") + ratio := float64(len(envBytes)) / float64(len(legacyBytes)) + b.ReportMetric(ratio, "components/legacy") + for range b.N { + } + }) + } +} diff --git a/management/server/types/policy.go b/management/server/types/policy.go index e3f94e178d3..69c7c97623f 100644 --- a/management/server/types/policy.go +++ b/management/server/types/policy.go @@ -79,6 +79,13 @@ type Policy struct { SourcePostureChecks []string `gorm:"serializer:json"` } +// HasSeqID reports whether the policy has been persisted long enough to have +// a per-account sequence id allocated. Wire encoders that key off +// AccountSeqID must skip policies that return false here. +func (p *Policy) HasSeqID() bool { + return p != nil && p.AccountSeqID != 0 +} + // Copy returns a copy of the policy. func (p *Policy) Copy() *Policy { c := &Policy{ diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 13f4fbc8dd6..f291c53a894 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -81,6 +81,8 @@ const ( PeerCapability_PeerCapabilitySourcePrefixes PeerCapability = 1 // Client handles IPv6 overlay addresses and firewall rules. PeerCapability_PeerCapabilityIPv6Overlay PeerCapability = 2 + // Client receives NetworkMap as components and assembles it locally. + PeerCapability_PeerCapabilityComponentNetworkMap PeerCapability = 3 ) // Enum value maps for PeerCapability. @@ -89,11 +91,13 @@ var ( 0: "PeerCapabilityUnknown", 1: "PeerCapabilitySourcePrefixes", 2: "PeerCapabilityIPv6Overlay", + 3: "PeerCapabilityComponentNetworkMap", } PeerCapability_value = map[string]int32{ - "PeerCapabilityUnknown": 0, - "PeerCapabilitySourcePrefixes": 1, - "PeerCapabilityIPv6Overlay": 2, + "PeerCapabilityUnknown": 0, + "PeerCapabilitySourcePrefixes": 1, + "PeerCapabilityIPv6Overlay": 2, + "PeerCapabilityComponentNetworkMap": 3, } ) @@ -843,6 +847,11 @@ type SyncResponse struct { NetworkMap *NetworkMap `protobuf:"bytes,5,opt,name=NetworkMap,proto3" json:"NetworkMap,omitempty"` // Posture checks to be evaluated by client Checks []*Checks `protobuf:"bytes,6,rep,name=Checks,proto3" json:"Checks,omitempty"` + // NetworkMapEnvelope carries the component-based wire format for peers that + // advertise PeerCapabilityComponentNetworkMap. When set, NetworkMap (field 5) + // is left empty: management ships components and the client runs Calculate() + // locally instead of receiving an expanded NetworkMap. + NetworkMapEnvelope *NetworkMapEnvelope `protobuf:"bytes,7,opt,name=NetworkMapEnvelope,proto3" json:"NetworkMapEnvelope,omitempty"` } func (x *SyncResponse) Reset() { @@ -919,6 +928,13 @@ func (x *SyncResponse) GetChecks() []*Checks { return nil } +func (x *SyncResponse) GetNetworkMapEnvelope() *NetworkMapEnvelope { + if x != nil { + return x.NetworkMapEnvelope + } + return nil +} + type SyncMetaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4486,17 +4502,159 @@ func (*StopExposeResponse) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{52} } -type PortInfo_Range struct { +// NetworkMapEnvelope wraps either a full snapshot or a delta. Step 2 ships +// only Full; Delta is reserved for the incremental-update work. +type NetworkMapEnvelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` - End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` + // Types that are assignable to Payload: + // + // *NetworkMapEnvelope_Full + // *NetworkMapEnvelope_Delta + Payload isNetworkMapEnvelope_Payload `protobuf_oneof:"payload"` } -func (x *PortInfo_Range) Reset() { - *x = PortInfo_Range{} +func (x *NetworkMapEnvelope) Reset() { + *x = NetworkMapEnvelope{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkMapEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkMapEnvelope) ProtoMessage() {} + +func (x *NetworkMapEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[53] + 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 NetworkMapEnvelope.ProtoReflect.Descriptor instead. +func (*NetworkMapEnvelope) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{53} +} + +func (m *NetworkMapEnvelope) GetPayload() isNetworkMapEnvelope_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (x *NetworkMapEnvelope) GetFull() *NetworkMapComponentsFull { + if x, ok := x.GetPayload().(*NetworkMapEnvelope_Full); ok { + return x.Full + } + return nil +} + +func (x *NetworkMapEnvelope) GetDelta() *NetworkMapComponentsDelta { + if x, ok := x.GetPayload().(*NetworkMapEnvelope_Delta); ok { + return x.Delta + } + return nil +} + +type isNetworkMapEnvelope_Payload interface { + isNetworkMapEnvelope_Payload() +} + +type NetworkMapEnvelope_Full struct { + Full *NetworkMapComponentsFull `protobuf:"bytes,1,opt,name=full,proto3,oneof"` +} + +type NetworkMapEnvelope_Delta struct { + Delta *NetworkMapComponentsDelta `protobuf:"bytes,2,opt,name=delta,proto3,oneof"` +} + +func (*NetworkMapEnvelope_Full) isNetworkMapEnvelope_Payload() {} + +func (*NetworkMapEnvelope_Delta) isNetworkMapEnvelope_Payload() {} + +// NetworkMapComponentsFull is the full per-peer component snapshot. The +// client decodes it into a types.NetworkMapComponents and runs Calculate() +// locally to produce the same NetworkMap the legacy server path would have +// produced. Every field carries RAW component data — no server-side +// expansion (firewall rules, DNS config, SSH auth, route firewall rules, +// forwarding rules) is shipped; the client computes those itself. +type NetworkMapComponentsFull struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Serial uint64 `protobuf:"varint,1,opt,name=serial,proto3" json:"serial,omitempty"` + // Peer config for the receiving peer (legacy proto.PeerConfig kept as-is — + // it carries the receiving peer's own overlay address, FQDN, SSH config). + PeerConfig *PeerConfig `protobuf:"bytes,2,opt,name=peer_config,json=peerConfig,proto3" json:"peer_config,omitempty"` + // Account-level network metadata (id, IPv4/IPv6 overlay subnets, DNS, + // serial). Mirrors types.Network. + Network *AccountNetwork `protobuf:"bytes,3,opt,name=network,proto3" json:"network,omitempty"` + // Account-level settings the client needs for its local Calculate(). + AccountSettings *AccountSettingsCompact `protobuf:"bytes,4,opt,name=account_settings,json=accountSettings,proto3" json:"account_settings,omitempty"` + // Account DNS settings (mirrors types.DNSSettings). + DnsSettings *DNSSettingsCompact `protobuf:"bytes,5,opt,name=dns_settings,json=dnsSettings,proto3" json:"dns_settings,omitempty"` + // Domain shared across all peers in this account, e.g. "netbird.cloud". + // Each peer's FQDN is dns_label + "." + dns_domain. + DnsDomain string `protobuf:"bytes,6,opt,name=dns_domain,json=dnsDomain,proto3" json:"dns_domain,omitempty"` + // Custom-zone domain for this peer's view (c.CustomZoneDomain). Empty when + // the peer has no custom zone records. + CustomZoneDomain string `protobuf:"bytes,7,opt,name=custom_zone_domain,json=customZoneDomain,proto3" json:"custom_zone_domain,omitempty"` + // Deduplicated agent versions; PeerCompact.agent_version_idx indexes here. + // Empty string at index 0 if any peer has no version. + AgentVersions []string `protobuf:"bytes,8,rep,name=agent_versions,json=agentVersions,proto3" json:"agent_versions,omitempty"` + // All peers (deduplicated). The client splits peers into online / offline + // locally using account_settings.peer_login_expiration on receive. + Peers []*PeerCompact `protobuf:"bytes,9,rep,name=peers,proto3" json:"peers,omitempty"` + // Indexes into peers for the subset that may act as routers. + RouterPeerIndexes []uint32 `protobuf:"varint,10,rep,packed,name=router_peer_indexes,json=routerPeerIndexes,proto3" json:"router_peer_indexes,omitempty"` + // Policies that affect the receiving peer. + Policies []*PolicyCompact `protobuf:"bytes,11,rep,name=policies,proto3" json:"policies,omitempty"` + // Groups in unspecified order — clients key off id (account_seq_id). + Groups []*GroupCompact `protobuf:"bytes,12,rep,name=groups,proto3" json:"groups,omitempty"` + // Routes relevant to this peer, raw shape (mirrors []*route.Route). + Routes []*RouteRaw `protobuf:"bytes,13,rep,name=routes,proto3" json:"routes,omitempty"` + // Nameserver groups (mirrors []*nbdns.NameServerGroup). + NameserverGroups []*NameServerGroupRaw `protobuf:"bytes,14,rep,name=nameserver_groups,json=nameserverGroups,proto3" json:"nameserver_groups,omitempty"` + // All DNS records the client needs to assemble its custom zone. Reuses + // the existing SimpleRecord wire shape. + AllDnsRecords []*SimpleRecord `protobuf:"bytes,15,rep,name=all_dns_records,json=allDnsRecords,proto3" json:"all_dns_records,omitempty"` + // Custom zones (typically the peer's own zone). Reuses the existing + // CustomZone wire shape. + AccountZones []*CustomZone `protobuf:"bytes,16,rep,name=account_zones,json=accountZones,proto3" json:"account_zones,omitempty"` + // Network resources (mirrors []*resourceTypes.NetworkResource). + NetworkResources []*NetworkResourceRaw `protobuf:"bytes,17,rep,name=network_resources,json=networkResources,proto3" json:"network_resources,omitempty"` + // Routers per network. Outer key: network id (xid string). Each entry is + // the set of routers backing that network for this peer's view. + RoutersMap map[string]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // For each NetworkResource id (xid string), the indexes into policies[] + // that apply to it. + ResourcePoliciesMap map[string]*PolicyIndexes `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Group-id (account_seq_id) → user ids authorized for SSH on members. + GroupIdToUserIds map[uint32]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Account-level allowed user ids (used by Calculate() when assembling SSH + // authorized users for the receiving peer). + AllowedUserIds []string `protobuf:"bytes,21,rep,name=allowed_user_ids,json=allowedUserIds,proto3" json:"allowed_user_ids,omitempty"` + // Per posture-check id (xid string), the set of peer indexes that failed + // the check. Server-side evaluation result; clients do not re-evaluate. + PostureFailedPeers map[string]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *NetworkMapComponentsFull) Reset() { + *x = NetworkMapComponentsFull{} if protoimpl.UnsafeEnabled { mi := &file_management_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4504,13 +4662,13 @@ func (x *PortInfo_Range) Reset() { } } -func (x *PortInfo_Range) String() string { +func (x *NetworkMapComponentsFull) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PortInfo_Range) ProtoMessage() {} +func (*NetworkMapComponentsFull) ProtoMessage() {} -func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { +func (x *NetworkMapComponentsFull) ProtoReflect() protoreflect.Message { mi := &file_management_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4522,75 +4680,1526 @@ func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PortInfo_Range.ProtoReflect.Descriptor instead. -func (*PortInfo_Range) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{44, 0} +// Deprecated: Use NetworkMapComponentsFull.ProtoReflect.Descriptor instead. +func (*NetworkMapComponentsFull) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{54} } -func (x *PortInfo_Range) GetStart() uint32 { +func (x *NetworkMapComponentsFull) GetSerial() uint64 { if x != nil { - return x.Start + return x.Serial } return 0 } -func (x *PortInfo_Range) GetEnd() uint32 { +func (x *NetworkMapComponentsFull) GetPeerConfig() *PeerConfig { if x != nil { - return x.End + return x.PeerConfig + } + return nil +} + +func (x *NetworkMapComponentsFull) GetNetwork() *AccountNetwork { + if x != nil { + return x.Network + } + return nil +} + +func (x *NetworkMapComponentsFull) GetAccountSettings() *AccountSettingsCompact { + if x != nil { + return x.AccountSettings + } + return nil +} + +func (x *NetworkMapComponentsFull) GetDnsSettings() *DNSSettingsCompact { + if x != nil { + return x.DnsSettings + } + return nil +} + +func (x *NetworkMapComponentsFull) GetDnsDomain() string { + if x != nil { + return x.DnsDomain + } + return "" +} + +func (x *NetworkMapComponentsFull) GetCustomZoneDomain() string { + if x != nil { + return x.CustomZoneDomain + } + return "" +} + +func (x *NetworkMapComponentsFull) GetAgentVersions() []string { + if x != nil { + return x.AgentVersions + } + return nil +} + +func (x *NetworkMapComponentsFull) GetPeers() []*PeerCompact { + if x != nil { + return x.Peers + } + return nil +} + +func (x *NetworkMapComponentsFull) GetRouterPeerIndexes() []uint32 { + if x != nil { + return x.RouterPeerIndexes + } + return nil +} + +func (x *NetworkMapComponentsFull) GetPolicies() []*PolicyCompact { + if x != nil { + return x.Policies + } + return nil +} + +func (x *NetworkMapComponentsFull) GetGroups() []*GroupCompact { + if x != nil { + return x.Groups + } + return nil +} + +func (x *NetworkMapComponentsFull) GetRoutes() []*RouteRaw { + if x != nil { + return x.Routes + } + return nil +} + +func (x *NetworkMapComponentsFull) GetNameserverGroups() []*NameServerGroupRaw { + if x != nil { + return x.NameserverGroups + } + return nil +} + +func (x *NetworkMapComponentsFull) GetAllDnsRecords() []*SimpleRecord { + if x != nil { + return x.AllDnsRecords + } + return nil +} + +func (x *NetworkMapComponentsFull) GetAccountZones() []*CustomZone { + if x != nil { + return x.AccountZones + } + return nil +} + +func (x *NetworkMapComponentsFull) GetNetworkResources() []*NetworkResourceRaw { + if x != nil { + return x.NetworkResources + } + return nil +} + +func (x *NetworkMapComponentsFull) GetRoutersMap() map[string]*NetworkRouterList { + if x != nil { + return x.RoutersMap + } + return nil +} + +func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[string]*PolicyIndexes { + if x != nil { + return x.ResourcePoliciesMap + } + return nil +} + +func (x *NetworkMapComponentsFull) GetGroupIdToUserIds() map[uint32]*UserIDList { + if x != nil { + return x.GroupIdToUserIds + } + return nil +} + +func (x *NetworkMapComponentsFull) GetAllowedUserIds() []string { + if x != nil { + return x.AllowedUserIds + } + return nil +} + +func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[string]*PeerIndexSet { + if x != nil { + return x.PostureFailedPeers + } + return nil +} + +// AccountSettingsCompact carries the account-level settings the client needs +// to evaluate locally. Mirrors the subset of types.AccountSettingsInfo that +// Calculate() actually reads — login-expiration (used to filter expired +// peers). Inactivity expiration is purely server-side bookkeeping and is not +// shipped. +type AccountSettingsCompact struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PeerLoginExpirationEnabled bool `protobuf:"varint,1,opt,name=peer_login_expiration_enabled,json=peerLoginExpirationEnabled,proto3" json:"peer_login_expiration_enabled,omitempty"` + // Login expiration window. Unit is nanoseconds (matches time.Duration). + PeerLoginExpirationNs int64 `protobuf:"varint,2,opt,name=peer_login_expiration_ns,json=peerLoginExpirationNs,proto3" json:"peer_login_expiration_ns,omitempty"` +} + +func (x *AccountSettingsCompact) Reset() { + *x = AccountSettingsCompact{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccountSettingsCompact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccountSettingsCompact) ProtoMessage() {} + +func (x *AccountSettingsCompact) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[55] + 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 AccountSettingsCompact.ProtoReflect.Descriptor instead. +func (*AccountSettingsCompact) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{55} +} + +func (x *AccountSettingsCompact) GetPeerLoginExpirationEnabled() bool { + if x != nil { + return x.PeerLoginExpirationEnabled + } + return false +} + +func (x *AccountSettingsCompact) GetPeerLoginExpirationNs() int64 { + if x != nil { + return x.PeerLoginExpirationNs } return 0 } -var File_management_proto protoreflect.FileDescriptor +// AccountNetwork is the account-level overlay metadata. Mirrors types.Network +// so the client can populate NetworkMap.Network without a server round-trip. +type AccountNetwork struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -var file_management_proto_rawDesc = []byte{ - 0x0a, 0x10, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x0a, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x1f, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, - 0x5c, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, - 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6b, 0x0a, - 0x0a, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x12, 0x36, 0x0a, 0x06, 0x62, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x06, 0x62, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x42, 0x15, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0xac, 0x01, 0x0a, 0x0b, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x12, 0x32, 0x0a, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x42, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x06, 0x62, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x42, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1d, - 0x0a, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x26, 0x0a, - 0x0f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x46, 0x6f, - 0x72, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6c, - 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x22, 0x2d, 0x0a, 0x0c, 0x42, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x6c, + Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` + // IPv4 overlay subnet in CIDR form (e.g. "100.64.0.0/16"). + NetCidr string `protobuf:"bytes,2,opt,name=net_cidr,json=netCidr,proto3" json:"net_cidr,omitempty"` + // IPv6 ULA overlay subnet in CIDR form (e.g. "fd00:4e42::/64"). Empty when + // the account has no IPv6 overlay yet. + NetV6Cidr string `protobuf:"bytes,3,opt,name=net_v6_cidr,json=netV6Cidr,proto3" json:"net_v6_cidr,omitempty"` + Dns string `protobuf:"bytes,4,opt,name=dns,proto3" json:"dns,omitempty"` + Serial uint64 `protobuf:"varint,5,opt,name=serial,proto3" json:"serial,omitempty"` +} + +func (x *AccountNetwork) Reset() { + *x = AccountNetwork{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccountNetwork) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccountNetwork) ProtoMessage() {} + +func (x *AccountNetwork) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[56] + 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 AccountNetwork.ProtoReflect.Descriptor instead. +func (*AccountNetwork) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{56} +} + +func (x *AccountNetwork) GetIdentifier() string { + if x != nil { + return x.Identifier + } + return "" +} + +func (x *AccountNetwork) GetNetCidr() string { + if x != nil { + return x.NetCidr + } + return "" +} + +func (x *AccountNetwork) GetNetV6Cidr() string { + if x != nil { + return x.NetV6Cidr + } + return "" +} + +func (x *AccountNetwork) GetDns() string { + if x != nil { + return x.Dns + } + return "" +} + +func (x *AccountNetwork) GetSerial() uint64 { + if x != nil { + return x.Serial + } + return 0 +} + +// NetworkMapComponentsDelta is reserved for the incremental update protocol +// (Step 3 of the migration plan). Field numbers 1–100 are pre-allocated to +// keep room for the planned event types without needing a renumber. +type NetworkMapComponentsDelta struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *NetworkMapComponentsDelta) Reset() { + *x = NetworkMapComponentsDelta{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkMapComponentsDelta) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkMapComponentsDelta) ProtoMessage() {} + +func (x *NetworkMapComponentsDelta) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[57] + 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 NetworkMapComponentsDelta.ProtoReflect.Descriptor instead. +func (*NetworkMapComponentsDelta) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{57} +} + +// PeerCompact is the wire-shape of a remote peer used by the component +// format. It carries every field of types.Peer that the client's local +// Calculate() reads — including the trio needed to evaluate +// LoginExpired() (added_with_sso_login + login_expiration_enabled + +// last_login_unix_nano). Fields the client does not consume (Status, +// CreatedAt, etc.) are not shipped. +type PeerCompact struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Raw 32-byte WireGuard public key (no base64 wrapping). + WgPubKey []byte `protobuf:"bytes,1,opt,name=wg_pub_key,json=wgPubKey,proto3" json:"wg_pub_key,omitempty"` + // Raw 4-byte IPv4 overlay address. Always a /32 host route, so no prefix + // byte is needed. + Ip []byte `protobuf:"bytes,2,opt,name=ip,proto3" json:"ip,omitempty"` + // Raw 16-byte IPv6 overlay address; always a /128 host route. Empty when + // the peer has no IPv6 overlay address. + Ipv6 []byte `protobuf:"bytes,3,opt,name=ipv6,proto3" json:"ipv6,omitempty"` + // Raw SSH public key bytes (or empty). + SshPubKey []byte `protobuf:"bytes,4,opt,name=ssh_pub_key,json=sshPubKey,proto3" json:"ssh_pub_key,omitempty"` + // DNS label without the account's domain suffix. Full FQDN is + // dns_label + "." + NetworkMapComponentsFull.dns_domain. + DnsLabel string `protobuf:"bytes,5,opt,name=dns_label,json=dnsLabel,proto3" json:"dns_label,omitempty"` + // Index into NetworkMapComponentsFull.agent_versions. + AgentVersionIdx uint32 `protobuf:"varint,6,opt,name=agent_version_idx,json=agentVersionIdx,proto3" json:"agent_version_idx,omitempty"` + // True iff the peer was added via SSO login (i.e., types.Peer.UserID is + // non-empty). Combined with login_expiration_enabled and + // last_login_unix_nano this lets the client reproduce + // (*Peer).LoginExpired() locally. + AddedWithSsoLogin bool `protobuf:"varint,7,opt,name=added_with_sso_login,json=addedWithSsoLogin,proto3" json:"added_with_sso_login,omitempty"` + // True when the peer's login can expire — mirrors + // types.Peer.LoginExpirationEnabled. + LoginExpirationEnabled bool `protobuf:"varint,8,opt,name=login_expiration_enabled,json=loginExpirationEnabled,proto3" json:"login_expiration_enabled,omitempty"` + // Unix-nanosecond timestamp of the peer's last login. 0 when the peer has + // never logged in (server stores nil; client treats 0 as "epoch", which + // makes a fresh peer immediately expired iff login_expiration_enabled is + // true — the same semantics as types.Peer.GetLastLogin). + LastLoginUnixNano int64 `protobuf:"varint,9,opt,name=last_login_unix_nano,json=lastLoginUnixNano,proto3" json:"last_login_unix_nano,omitempty"` +} + +func (x *PeerCompact) Reset() { + *x = PeerCompact{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeerCompact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeerCompact) ProtoMessage() {} + +func (x *PeerCompact) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[58] + 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 PeerCompact.ProtoReflect.Descriptor instead. +func (*PeerCompact) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{58} +} + +func (x *PeerCompact) GetWgPubKey() []byte { + if x != nil { + return x.WgPubKey + } + return nil +} + +func (x *PeerCompact) GetIp() []byte { + if x != nil { + return x.Ip + } + return nil +} + +func (x *PeerCompact) GetIpv6() []byte { + if x != nil { + return x.Ipv6 + } + return nil +} + +func (x *PeerCompact) GetSshPubKey() []byte { + if x != nil { + return x.SshPubKey + } + return nil +} + +func (x *PeerCompact) GetDnsLabel() string { + if x != nil { + return x.DnsLabel + } + return "" +} + +func (x *PeerCompact) GetAgentVersionIdx() uint32 { + if x != nil { + return x.AgentVersionIdx + } + return 0 +} + +func (x *PeerCompact) GetAddedWithSsoLogin() bool { + if x != nil { + return x.AddedWithSsoLogin + } + return false +} + +func (x *PeerCompact) GetLoginExpirationEnabled() bool { + if x != nil { + return x.LoginExpirationEnabled + } + return false +} + +func (x *PeerCompact) GetLastLoginUnixNano() int64 { + if x != nil { + return x.LastLoginUnixNano + } + return 0 +} + +// PolicyCompact is the compact form of a policy rule. Group references use +// the per-account integer ids from account_seq_counters; the client resolves +// them against NetworkMapComponentsFull.groups. Direction is derived per-peer +// on the client (ingress when the peer is in destination_group_ids, egress +// when in source_group_ids; both when bidirectional). +type PolicyCompact struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Per-account integer id (matches policies.account_seq_id). + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Action RuleAction `protobuf:"varint,2,opt,name=action,proto3,enum=management.RuleAction" json:"action,omitempty"` + Protocol RuleProtocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` + Bidirectional bool `protobuf:"varint,4,opt,name=bidirectional,proto3" json:"bidirectional,omitempty"` + // Single ports referenced by the rule. + Ports []uint32 `protobuf:"varint,5,rep,packed,name=ports,proto3" json:"ports,omitempty"` + // Port ranges (start..end) referenced by the rule. + PortRanges []*PortInfo_Range `protobuf:"bytes,6,rep,name=port_ranges,json=portRanges,proto3" json:"port_ranges,omitempty"` + // Group ids (account_seq_id) of source / destination groups. + SourceGroupIds []uint32 `protobuf:"varint,7,rep,packed,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` + DestinationGroupIds []uint32 `protobuf:"varint,8,rep,packed,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` +} + +func (x *PolicyCompact) Reset() { + *x = PolicyCompact{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PolicyCompact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PolicyCompact) ProtoMessage() {} + +func (x *PolicyCompact) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[59] + 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 PolicyCompact.ProtoReflect.Descriptor instead. +func (*PolicyCompact) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{59} +} + +func (x *PolicyCompact) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *PolicyCompact) GetAction() RuleAction { + if x != nil { + return x.Action + } + return RuleAction_ACCEPT +} + +func (x *PolicyCompact) GetProtocol() RuleProtocol { + if x != nil { + return x.Protocol + } + return RuleProtocol_UNKNOWN +} + +func (x *PolicyCompact) GetBidirectional() bool { + if x != nil { + return x.Bidirectional + } + return false +} + +func (x *PolicyCompact) GetPorts() []uint32 { + if x != nil { + return x.Ports + } + return nil +} + +func (x *PolicyCompact) GetPortRanges() []*PortInfo_Range { + if x != nil { + return x.PortRanges + } + return nil +} + +func (x *PolicyCompact) GetSourceGroupIds() []uint32 { + if x != nil { + return x.SourceGroupIds + } + return nil +} + +func (x *PolicyCompact) GetDestinationGroupIds() []uint32 { + if x != nil { + return x.DestinationGroupIds + } + return nil +} + +// GroupCompact is the wire-shape of a group: per-account integer id, optional +// name, and indexes into NetworkMapComponentsFull.peers identifying members. +type GroupCompact struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Per-account integer id (matches groups.account_seq_id). Used by + // PolicyCompact.source_group_ids / destination_group_ids. + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Group name; only sent when non-empty (clients use it for diagnostics). + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Indexes into NetworkMapComponentsFull.peers. + PeerIndexes []uint32 `protobuf:"varint,3,rep,packed,name=peer_indexes,json=peerIndexes,proto3" json:"peer_indexes,omitempty"` +} + +func (x *GroupCompact) Reset() { + *x = GroupCompact{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupCompact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupCompact) ProtoMessage() {} + +func (x *GroupCompact) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[60] + 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 GroupCompact.ProtoReflect.Descriptor instead. +func (*GroupCompact) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{60} +} + +func (x *GroupCompact) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GroupCompact) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GroupCompact) GetPeerIndexes() []uint32 { + if x != nil { + return x.PeerIndexes + } + return nil +} + +// DNSSettingsCompact mirrors types.DNSSettings. +type DNSSettingsCompact struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Group ids (account_seq_id) whose DNS management is disabled. + DisabledManagementGroupIds []uint32 `protobuf:"varint,1,rep,packed,name=disabled_management_group_ids,json=disabledManagementGroupIds,proto3" json:"disabled_management_group_ids,omitempty"` +} + +func (x *DNSSettingsCompact) Reset() { + *x = DNSSettingsCompact{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DNSSettingsCompact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DNSSettingsCompact) ProtoMessage() {} + +func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[61] + 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 DNSSettingsCompact.ProtoReflect.Descriptor instead. +func (*DNSSettingsCompact) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{61} +} + +func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []uint32 { + if x != nil { + return x.DisabledManagementGroupIds + } + return nil +} + +// RouteRaw mirrors *route.Route (the domain type), trimmed to fields that +// types.NetworkMapComponents.Calculate() reads. Group references are +// account_seq_ids; the routing peer (when set) is referenced by index into +// NetworkMapComponentsFull.peers. +type RouteRaw struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Per-account integer id (matches routes.account_seq_id). + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + NetId string `protobuf:"bytes,2,opt,name=net_id,json=netId,proto3" json:"net_id,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // Either network_cidr (e.g. "10.0.0.0/16") or domains is set, not both. + NetworkCidr string `protobuf:"bytes,4,opt,name=network_cidr,json=networkCidr,proto3" json:"network_cidr,omitempty"` + Domains []string `protobuf:"bytes,5,rep,name=domains,proto3" json:"domains,omitempty"` + KeepRoute bool `protobuf:"varint,6,opt,name=keep_route,json=keepRoute,proto3" json:"keep_route,omitempty"` + // Routing peer reference: peer_index_set tells whether peer_index is valid + // (proto3 uint32 cannot disambiguate "0" from "unset"). Mutually exclusive + // with peer_group_ids. + // + // peer_index decodes back to types.Peer.ID (the peer's xid string), NOT + // to its WireGuard public key. This matches the server-side data flow: + // c.Routes carry route.Peer = peer.ID, and getRoutingPeerRoutes mutates + // it to peer.Key only after the route has been admitted to the network + // map. Decoders MUST set Route.Peer = peer.ID; the legacy Calculate() + // path will substitute the WG key downstream. + PeerIndexSet bool `protobuf:"varint,7,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerIndex uint32 `protobuf:"varint,8,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` + PeerGroupIds []uint32 `protobuf:"varint,9,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` + NetworkType int32 `protobuf:"varint,10,opt,name=network_type,json=networkType,proto3" json:"network_type,omitempty"` + Masquerade bool `protobuf:"varint,11,opt,name=masquerade,proto3" json:"masquerade,omitempty"` + Metric int32 `protobuf:"varint,12,opt,name=metric,proto3" json:"metric,omitempty"` + Enabled bool `protobuf:"varint,13,opt,name=enabled,proto3" json:"enabled,omitempty"` + GroupIds []uint32 `protobuf:"varint,14,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` + AccessControlGroupIds []uint32 `protobuf:"varint,15,rep,packed,name=access_control_group_ids,json=accessControlGroupIds,proto3" json:"access_control_group_ids,omitempty"` + SkipAutoApply bool `protobuf:"varint,16,opt,name=skip_auto_apply,json=skipAutoApply,proto3" json:"skip_auto_apply,omitempty"` +} + +func (x *RouteRaw) Reset() { + *x = RouteRaw{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RouteRaw) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RouteRaw) ProtoMessage() {} + +func (x *RouteRaw) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[62] + 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 RouteRaw.ProtoReflect.Descriptor instead. +func (*RouteRaw) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{62} +} + +func (x *RouteRaw) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *RouteRaw) GetNetId() string { + if x != nil { + return x.NetId + } + return "" +} + +func (x *RouteRaw) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *RouteRaw) GetNetworkCidr() string { + if x != nil { + return x.NetworkCidr + } + return "" +} + +func (x *RouteRaw) GetDomains() []string { + if x != nil { + return x.Domains + } + return nil +} + +func (x *RouteRaw) GetKeepRoute() bool { + if x != nil { + return x.KeepRoute + } + return false +} + +func (x *RouteRaw) GetPeerIndexSet() bool { + if x != nil { + return x.PeerIndexSet + } + return false +} + +func (x *RouteRaw) GetPeerIndex() uint32 { + if x != nil { + return x.PeerIndex + } + return 0 +} + +func (x *RouteRaw) GetPeerGroupIds() []uint32 { + if x != nil { + return x.PeerGroupIds + } + return nil +} + +func (x *RouteRaw) GetNetworkType() int32 { + if x != nil { + return x.NetworkType + } + return 0 +} + +func (x *RouteRaw) GetMasquerade() bool { + if x != nil { + return x.Masquerade + } + return false +} + +func (x *RouteRaw) GetMetric() int32 { + if x != nil { + return x.Metric + } + return 0 +} + +func (x *RouteRaw) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *RouteRaw) GetGroupIds() []uint32 { + if x != nil { + return x.GroupIds + } + return nil +} + +func (x *RouteRaw) GetAccessControlGroupIds() []uint32 { + if x != nil { + return x.AccessControlGroupIds + } + return nil +} + +func (x *RouteRaw) GetSkipAutoApply() bool { + if x != nil { + return x.SkipAutoApply + } + return false +} + +// NameServerGroupRaw mirrors *nbdns.NameServerGroup. Distinct from the +// legacy NameServerGroup (which is the wire-trimmed shape consumed by +// proto.DNSConfig and lacks the Name/Description/Groups/Enabled fields). +type NameServerGroupRaw struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // nameserver_groups.account_seq_id + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // Reuses the legacy NameServer wire shape (IP as string). + Nameservers []*NameServer `protobuf:"bytes,4,rep,name=nameservers,proto3" json:"nameservers,omitempty"` + // Group ids (account_seq_id) the NSG distributes nameservers to. + GroupIds []uint32 `protobuf:"varint,5,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` + Primary bool `protobuf:"varint,6,opt,name=primary,proto3" json:"primary,omitempty"` + Domains []string `protobuf:"bytes,7,rep,name=domains,proto3" json:"domains,omitempty"` + Enabled bool `protobuf:"varint,8,opt,name=enabled,proto3" json:"enabled,omitempty"` + SearchDomainsEnabled bool `protobuf:"varint,9,opt,name=search_domains_enabled,json=searchDomainsEnabled,proto3" json:"search_domains_enabled,omitempty"` +} + +func (x *NameServerGroupRaw) Reset() { + *x = NameServerGroupRaw{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[63] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NameServerGroupRaw) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NameServerGroupRaw) ProtoMessage() {} + +func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[63] + 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 NameServerGroupRaw.ProtoReflect.Descriptor instead. +func (*NameServerGroupRaw) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{63} +} + +func (x *NameServerGroupRaw) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *NameServerGroupRaw) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NameServerGroupRaw) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *NameServerGroupRaw) GetNameservers() []*NameServer { + if x != nil { + return x.Nameservers + } + return nil +} + +func (x *NameServerGroupRaw) GetGroupIds() []uint32 { + if x != nil { + return x.GroupIds + } + return nil +} + +func (x *NameServerGroupRaw) GetPrimary() bool { + if x != nil { + return x.Primary + } + return false +} + +func (x *NameServerGroupRaw) GetDomains() []string { + if x != nil { + return x.Domains + } + return nil +} + +func (x *NameServerGroupRaw) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *NameServerGroupRaw) GetSearchDomainsEnabled() bool { + if x != nil { + return x.SearchDomainsEnabled + } + return false +} + +// NetworkResourceRaw mirrors *resourceTypes.NetworkResource. +type NetworkResourceRaw struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id + NetworkId string `protobuf:"bytes,2,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` // xid string — networks have no seq id today + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + // Resource type: "host" / "subnet" / "domain". + Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"` + Address string `protobuf:"bytes,6,opt,name=address,proto3" json:"address,omitempty"` + DomainValue string `protobuf:"bytes,7,opt,name=domain_value,json=domainValue,proto3" json:"domain_value,omitempty"` // resource.Domain + PrefixCidr string `protobuf:"bytes,8,opt,name=prefix_cidr,json=prefixCidr,proto3" json:"prefix_cidr,omitempty"` + Enabled bool `protobuf:"varint,9,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *NetworkResourceRaw) Reset() { + *x = NetworkResourceRaw{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[64] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkResourceRaw) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkResourceRaw) ProtoMessage() {} + +func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[64] + 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 NetworkResourceRaw.ProtoReflect.Descriptor instead. +func (*NetworkResourceRaw) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{64} +} + +func (x *NetworkResourceRaw) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *NetworkResourceRaw) GetNetworkId() string { + if x != nil { + return x.NetworkId + } + return "" +} + +func (x *NetworkResourceRaw) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NetworkResourceRaw) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *NetworkResourceRaw) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *NetworkResourceRaw) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *NetworkResourceRaw) GetDomainValue() string { + if x != nil { + return x.DomainValue + } + return "" +} + +func (x *NetworkResourceRaw) GetPrefixCidr() string { + if x != nil { + return x.PrefixCidr + } + return "" +} + +func (x *NetworkResourceRaw) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +// NetworkRouterList carries the routers backing one network. +type NetworkRouterList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Routers in this network, keyed by peer_index (the routing peer). + Entries []*NetworkRouterEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *NetworkRouterList) Reset() { + *x = NetworkRouterList{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[65] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkRouterList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkRouterList) ProtoMessage() {} + +func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[65] + 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 NetworkRouterList.ProtoReflect.Descriptor instead. +func (*NetworkRouterList) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{65} +} + +func (x *NetworkRouterList) GetEntries() []*NetworkRouterEntry { + if x != nil { + return x.Entries + } + return nil +} + +// NetworkRouterEntry mirrors a single *routerTypes.NetworkRouter; the routing +// peer is referenced by index into NetworkMapComponentsFull.peers. +type NetworkRouterEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_routers.account_seq_id + PeerIndex uint32 `protobuf:"varint,2,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` + PeerIndexSet bool `protobuf:"varint,3,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerGroupIds []uint32 `protobuf:"varint,4,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` + Masquerade bool `protobuf:"varint,5,opt,name=masquerade,proto3" json:"masquerade,omitempty"` + Metric int32 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` + Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *NetworkRouterEntry) Reset() { + *x = NetworkRouterEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkRouterEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkRouterEntry) ProtoMessage() {} + +func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[66] + 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 NetworkRouterEntry.ProtoReflect.Descriptor instead. +func (*NetworkRouterEntry) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{66} +} + +func (x *NetworkRouterEntry) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *NetworkRouterEntry) GetPeerIndex() uint32 { + if x != nil { + return x.PeerIndex + } + return 0 +} + +func (x *NetworkRouterEntry) GetPeerIndexSet() bool { + if x != nil { + return x.PeerIndexSet + } + return false +} + +func (x *NetworkRouterEntry) GetPeerGroupIds() []uint32 { + if x != nil { + return x.PeerGroupIds + } + return nil +} + +func (x *NetworkRouterEntry) GetMasquerade() bool { + if x != nil { + return x.Masquerade + } + return false +} + +func (x *NetworkRouterEntry) GetMetric() int32 { + if x != nil { + return x.Metric + } + return 0 +} + +func (x *NetworkRouterEntry) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +// PolicyIndexes is a list of indexes into NetworkMapComponentsFull.policies. +type PolicyIndexes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Indexes []uint32 `protobuf:"varint,1,rep,packed,name=indexes,proto3" json:"indexes,omitempty"` +} + +func (x *PolicyIndexes) Reset() { + *x = PolicyIndexes{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[67] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PolicyIndexes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PolicyIndexes) ProtoMessage() {} + +func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[67] + 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 PolicyIndexes.ProtoReflect.Descriptor instead. +func (*PolicyIndexes) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{67} +} + +func (x *PolicyIndexes) GetIndexes() []uint32 { + if x != nil { + return x.Indexes + } + return nil +} + +// UserIDList is a list of user ids — used as the value type in +// NetworkMapComponentsFull.group_id_to_user_ids. +type UserIDList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserIds []string `protobuf:"bytes,1,rep,name=user_ids,json=userIds,proto3" json:"user_ids,omitempty"` +} + +func (x *UserIDList) Reset() { + *x = UserIDList{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[68] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserIDList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserIDList) ProtoMessage() {} + +func (x *UserIDList) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[68] + 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 UserIDList.ProtoReflect.Descriptor instead. +func (*UserIDList) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{68} +} + +func (x *UserIDList) GetUserIds() []string { + if x != nil { + return x.UserIds + } + return nil +} + +// PeerIndexSet is a set of peer indexes — used as the value type in +// NetworkMapComponentsFull.posture_failed_peers. +type PeerIndexSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PeerIndexes []uint32 `protobuf:"varint,1,rep,packed,name=peer_indexes,json=peerIndexes,proto3" json:"peer_indexes,omitempty"` +} + +func (x *PeerIndexSet) Reset() { + *x = PeerIndexSet{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[69] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeerIndexSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeerIndexSet) ProtoMessage() {} + +func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[69] + 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 PeerIndexSet.ProtoReflect.Descriptor instead. +func (*PeerIndexSet) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{69} +} + +func (x *PeerIndexSet) GetPeerIndexes() []uint32 { + if x != nil { + return x.PeerIndexes + } + return nil +} + +type PortInfo_Range struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` + End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *PortInfo_Range) Reset() { + *x = PortInfo_Range{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[71] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PortInfo_Range) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PortInfo_Range) ProtoMessage() {} + +func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[71] + 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 PortInfo_Range.ProtoReflect.Descriptor instead. +func (*PortInfo_Range) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{44, 0} +} + +func (x *PortInfo_Range) GetStart() uint32 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *PortInfo_Range) GetEnd() uint32 { + if x != nil { + return x.End + } + return 0 +} + +var File_management_proto protoreflect.FileDescriptor + +var file_management_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0a, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x1f, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x5c, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, + 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6b, 0x0a, + 0x0a, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x12, 0x36, 0x0a, 0x06, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x06, 0x62, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x42, 0x15, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0xac, 0x01, 0x0a, 0x0b, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x32, 0x0a, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x42, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x06, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x42, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x26, 0x0a, + 0x0f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x46, 0x6f, + 0x72, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6c, + 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x22, 0x2d, 0x0a, 0x0c, 0x42, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x22, 0x3d, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, - 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xdb, 0x02, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, + 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xab, 0x03, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, @@ -4612,7 +6221,12 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x52, 0x06, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x73, 0x22, 0x41, 0x0a, 0x0f, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, + 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x4e, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x52, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x22, 0x41, 0x0a, 0x0f, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, @@ -5168,90 +6782,387 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 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, 0x6c, 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, 0x2a, 0x4c, - 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, 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, 0xfd, 0x06, 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, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x66, + 0x75, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, + 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x48, + 0x00, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x12, 0x3d, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x48, 0x00, 0x52, + 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0x8b, 0x0e, 0x0a, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x34, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x4d, 0x0a, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x0b, 0x64, 0x6e, 0x73, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x6e, 0x73, 0x5f, 0x64, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x6e, 0x73, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x70, + 0x65, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, + 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x11, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, + 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x12, 0x30, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x06, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x73, 0x12, 0x4b, 0x0a, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x52, 0x10, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x40, + 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x12, 0x3b, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, + 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, + 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x4b, 0x0a, + 0x11, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x52, 0x10, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x34, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, + 0x70, 0x12, 0x71, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x3d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x4d, 0x61, 0x70, 0x12, 0x6a, 0x0a, 0x14, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, + 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x14, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, + 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x12, 0x28, 0x0a, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x6e, 0x0a, 0x14, 0x70, 0x6f, + 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, + 0x72, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x2e, 0x50, + 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x61, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, + 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x17, 0x10, 0x33, 0x22, + 0x94, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x65, + 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, + 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x15, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x74, + 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, + 0x43, 0x69, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x5f, 0x76, 0x36, 0x5f, 0x63, + 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x56, 0x36, + 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x21, + 0x0a, 0x19, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, + 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, + 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, + 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, 0x67, 0x65, + 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x78, 0x12, 0x2f, 0x0a, 0x14, + 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x73, 0x6f, 0x5f, 0x6c, + 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x64, 0x64, 0x65, + 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x38, 0x0a, + 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, + 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x22, 0xdc, 0x02, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x0b, + 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, + 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x57, + 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x1a, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, + 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, + 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, + 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, + 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, + 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, + 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, + 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, + 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, + 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, + 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0d, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, + 0x85, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, + 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, + 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, + 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, + 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, + 0x0a, 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, 0x4c, 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, 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, 0xfd, 0x06, 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, 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, + 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, 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, + 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, 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, + 0x00, 0x28, 0x01, 0x30, 0x01, 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, 0x28, 0x01, 0x30, 0x01, - 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, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 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 ( @@ -5267,7 +7178,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 55) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 76) var file_management_proto_goTypes = []interface{}{ (JobStatus)(0), // 0: management.JobStatus (PeerCapability)(0), // 1: management.PeerCapability @@ -5330,104 +7241,153 @@ var file_management_proto_goTypes = []interface{}{ (*RenewExposeResponse)(nil), // 58: management.RenewExposeResponse (*StopExposeRequest)(nil), // 59: management.StopExposeRequest (*StopExposeResponse)(nil), // 60: management.StopExposeResponse - nil, // 61: management.SSHAuth.MachineUsersEntry - (*PortInfo_Range)(nil), // 62: management.PortInfo.Range - (*timestamppb.Timestamp)(nil), // 63: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 64: google.protobuf.Duration + (*NetworkMapEnvelope)(nil), // 61: management.NetworkMapEnvelope + (*NetworkMapComponentsFull)(nil), // 62: management.NetworkMapComponentsFull + (*AccountSettingsCompact)(nil), // 63: management.AccountSettingsCompact + (*AccountNetwork)(nil), // 64: management.AccountNetwork + (*NetworkMapComponentsDelta)(nil), // 65: management.NetworkMapComponentsDelta + (*PeerCompact)(nil), // 66: management.PeerCompact + (*PolicyCompact)(nil), // 67: management.PolicyCompact + (*GroupCompact)(nil), // 68: management.GroupCompact + (*DNSSettingsCompact)(nil), // 69: management.DNSSettingsCompact + (*RouteRaw)(nil), // 70: management.RouteRaw + (*NameServerGroupRaw)(nil), // 71: management.NameServerGroupRaw + (*NetworkResourceRaw)(nil), // 72: management.NetworkResourceRaw + (*NetworkRouterList)(nil), // 73: management.NetworkRouterList + (*NetworkRouterEntry)(nil), // 74: management.NetworkRouterEntry + (*PolicyIndexes)(nil), // 75: management.PolicyIndexes + (*UserIDList)(nil), // 76: management.UserIDList + (*PeerIndexSet)(nil), // 77: management.PeerIndexSet + nil, // 78: management.SSHAuth.MachineUsersEntry + (*PortInfo_Range)(nil), // 79: management.PortInfo.Range + nil, // 80: management.NetworkMapComponentsFull.RoutersMapEntry + nil, // 81: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + nil, // 82: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + nil, // 83: management.NetworkMapComponentsFull.PostureFailedPeersEntry + (*timestamppb.Timestamp)(nil), // 84: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 85: google.protobuf.Duration } var file_management_proto_depIdxs = []int32{ - 11, // 0: management.JobRequest.bundle:type_name -> management.BundleParameters - 0, // 1: management.JobResponse.status:type_name -> management.JobStatus - 12, // 2: management.JobResponse.bundle:type_name -> management.BundleResult - 21, // 3: management.SyncRequest.meta:type_name -> management.PeerSystemMeta - 25, // 4: management.SyncResponse.netbirdConfig:type_name -> management.NetbirdConfig - 31, // 5: management.SyncResponse.peerConfig:type_name -> management.PeerConfig - 36, // 6: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig - 33, // 7: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap - 51, // 8: management.SyncResponse.Checks:type_name -> management.Checks - 21, // 9: management.SyncMetaRequest.meta:type_name -> management.PeerSystemMeta - 21, // 10: management.LoginRequest.meta:type_name -> management.PeerSystemMeta - 17, // 11: management.LoginRequest.peerKeys:type_name -> management.PeerKeys - 50, // 12: management.PeerSystemMeta.networkAddresses:type_name -> management.NetworkAddress - 18, // 13: management.PeerSystemMeta.environment:type_name -> management.Environment - 19, // 14: management.PeerSystemMeta.files:type_name -> management.File - 20, // 15: management.PeerSystemMeta.flags:type_name -> management.Flags - 1, // 16: management.PeerSystemMeta.capabilities:type_name -> management.PeerCapability - 25, // 17: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig - 31, // 18: management.LoginResponse.peerConfig:type_name -> management.PeerConfig - 51, // 19: management.LoginResponse.Checks:type_name -> management.Checks - 63, // 20: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp - 26, // 21: management.NetbirdConfig.stuns:type_name -> management.HostConfig - 30, // 22: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig - 26, // 23: management.NetbirdConfig.signal:type_name -> management.HostConfig - 27, // 24: management.NetbirdConfig.relay:type_name -> management.RelayConfig - 28, // 25: management.NetbirdConfig.flow:type_name -> management.FlowConfig - 6, // 26: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol - 64, // 27: management.FlowConfig.interval:type_name -> google.protobuf.Duration - 26, // 28: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig - 37, // 29: management.PeerConfig.sshConfig:type_name -> management.SSHConfig - 32, // 30: management.PeerConfig.autoUpdate:type_name -> management.AutoUpdateSettings - 31, // 31: management.NetworkMap.peerConfig:type_name -> management.PeerConfig - 36, // 32: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig - 43, // 33: management.NetworkMap.Routes:type_name -> management.Route - 44, // 34: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig - 36, // 35: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig - 49, // 36: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule - 53, // 37: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule - 54, // 38: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule - 34, // 39: management.NetworkMap.sshAuth:type_name -> management.SSHAuth - 61, // 40: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry - 37, // 41: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig - 29, // 42: management.SSHConfig.jwtConfig:type_name -> management.JWTConfig - 7, // 43: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider - 42, // 44: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 42, // 45: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 47, // 46: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup - 45, // 47: management.DNSConfig.CustomZones:type_name -> management.CustomZone - 46, // 48: management.CustomZone.Records:type_name -> management.SimpleRecord - 48, // 49: management.NameServerGroup.NameServers:type_name -> management.NameServer - 3, // 50: management.FirewallRule.Direction:type_name -> management.RuleDirection - 4, // 51: management.FirewallRule.Action:type_name -> management.RuleAction - 2, // 52: management.FirewallRule.Protocol:type_name -> management.RuleProtocol - 52, // 53: management.FirewallRule.PortInfo:type_name -> management.PortInfo - 62, // 54: management.PortInfo.range:type_name -> management.PortInfo.Range - 4, // 55: management.RouteFirewallRule.action:type_name -> management.RuleAction - 2, // 56: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol - 52, // 57: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo - 2, // 58: management.ForwardingRule.protocol:type_name -> management.RuleProtocol - 52, // 59: management.ForwardingRule.destinationPort:type_name -> management.PortInfo - 52, // 60: management.ForwardingRule.translatedPort:type_name -> management.PortInfo - 5, // 61: management.ExposeServiceRequest.protocol:type_name -> management.ExposeProtocol - 35, // 62: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes - 8, // 63: management.ManagementService.Login:input_type -> management.EncryptedMessage - 8, // 64: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 24, // 65: management.ManagementService.GetServerKey:input_type -> management.Empty - 24, // 66: management.ManagementService.isHealthy:input_type -> management.Empty - 8, // 67: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 68: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 69: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage - 8, // 70: management.ManagementService.Logout:input_type -> management.EncryptedMessage - 8, // 71: management.ManagementService.Job:input_type -> management.EncryptedMessage - 8, // 72: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage - 8, // 73: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage - 8, // 74: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage - 8, // 75: management.ManagementService.Login:output_type -> management.EncryptedMessage - 8, // 76: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 23, // 77: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 24, // 78: management.ManagementService.isHealthy:output_type -> management.Empty - 8, // 79: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 8, // 80: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage - 24, // 81: management.ManagementService.SyncMeta:output_type -> management.Empty - 24, // 82: management.ManagementService.Logout:output_type -> management.Empty - 8, // 83: management.ManagementService.Job:output_type -> management.EncryptedMessage - 8, // 84: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage - 8, // 85: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage - 8, // 86: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage - 75, // [75:87] is the sub-list for method output_type - 63, // [63:75] is the sub-list for method input_type - 63, // [63:63] is the sub-list for extension type_name - 63, // [63:63] is the sub-list for extension extendee - 0, // [0:63] is the sub-list for field type_name + 11, // 0: management.JobRequest.bundle:type_name -> management.BundleParameters + 0, // 1: management.JobResponse.status:type_name -> management.JobStatus + 12, // 2: management.JobResponse.bundle:type_name -> management.BundleResult + 21, // 3: management.SyncRequest.meta:type_name -> management.PeerSystemMeta + 25, // 4: management.SyncResponse.netbirdConfig:type_name -> management.NetbirdConfig + 31, // 5: management.SyncResponse.peerConfig:type_name -> management.PeerConfig + 36, // 6: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig + 33, // 7: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap + 51, // 8: management.SyncResponse.Checks:type_name -> management.Checks + 61, // 9: management.SyncResponse.NetworkMapEnvelope:type_name -> management.NetworkMapEnvelope + 21, // 10: management.SyncMetaRequest.meta:type_name -> management.PeerSystemMeta + 21, // 11: management.LoginRequest.meta:type_name -> management.PeerSystemMeta + 17, // 12: management.LoginRequest.peerKeys:type_name -> management.PeerKeys + 50, // 13: management.PeerSystemMeta.networkAddresses:type_name -> management.NetworkAddress + 18, // 14: management.PeerSystemMeta.environment:type_name -> management.Environment + 19, // 15: management.PeerSystemMeta.files:type_name -> management.File + 20, // 16: management.PeerSystemMeta.flags:type_name -> management.Flags + 1, // 17: management.PeerSystemMeta.capabilities:type_name -> management.PeerCapability + 25, // 18: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig + 31, // 19: management.LoginResponse.peerConfig:type_name -> management.PeerConfig + 51, // 20: management.LoginResponse.Checks:type_name -> management.Checks + 84, // 21: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 26, // 22: management.NetbirdConfig.stuns:type_name -> management.HostConfig + 30, // 23: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig + 26, // 24: management.NetbirdConfig.signal:type_name -> management.HostConfig + 27, // 25: management.NetbirdConfig.relay:type_name -> management.RelayConfig + 28, // 26: management.NetbirdConfig.flow:type_name -> management.FlowConfig + 6, // 27: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol + 85, // 28: management.FlowConfig.interval:type_name -> google.protobuf.Duration + 26, // 29: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig + 37, // 30: management.PeerConfig.sshConfig:type_name -> management.SSHConfig + 32, // 31: management.PeerConfig.autoUpdate:type_name -> management.AutoUpdateSettings + 31, // 32: management.NetworkMap.peerConfig:type_name -> management.PeerConfig + 36, // 33: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig + 43, // 34: management.NetworkMap.Routes:type_name -> management.Route + 44, // 35: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig + 36, // 36: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig + 49, // 37: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule + 53, // 38: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule + 54, // 39: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule + 34, // 40: management.NetworkMap.sshAuth:type_name -> management.SSHAuth + 78, // 41: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry + 37, // 42: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig + 29, // 43: management.SSHConfig.jwtConfig:type_name -> management.JWTConfig + 7, // 44: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider + 42, // 45: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 42, // 46: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 47, // 47: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup + 45, // 48: management.DNSConfig.CustomZones:type_name -> management.CustomZone + 46, // 49: management.CustomZone.Records:type_name -> management.SimpleRecord + 48, // 50: management.NameServerGroup.NameServers:type_name -> management.NameServer + 3, // 51: management.FirewallRule.Direction:type_name -> management.RuleDirection + 4, // 52: management.FirewallRule.Action:type_name -> management.RuleAction + 2, // 53: management.FirewallRule.Protocol:type_name -> management.RuleProtocol + 52, // 54: management.FirewallRule.PortInfo:type_name -> management.PortInfo + 79, // 55: management.PortInfo.range:type_name -> management.PortInfo.Range + 4, // 56: management.RouteFirewallRule.action:type_name -> management.RuleAction + 2, // 57: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol + 52, // 58: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo + 2, // 59: management.ForwardingRule.protocol:type_name -> management.RuleProtocol + 52, // 60: management.ForwardingRule.destinationPort:type_name -> management.PortInfo + 52, // 61: management.ForwardingRule.translatedPort:type_name -> management.PortInfo + 5, // 62: management.ExposeServiceRequest.protocol:type_name -> management.ExposeProtocol + 62, // 63: management.NetworkMapEnvelope.full:type_name -> management.NetworkMapComponentsFull + 65, // 64: management.NetworkMapEnvelope.delta:type_name -> management.NetworkMapComponentsDelta + 31, // 65: management.NetworkMapComponentsFull.peer_config:type_name -> management.PeerConfig + 64, // 66: management.NetworkMapComponentsFull.network:type_name -> management.AccountNetwork + 63, // 67: management.NetworkMapComponentsFull.account_settings:type_name -> management.AccountSettingsCompact + 69, // 68: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact + 66, // 69: management.NetworkMapComponentsFull.peers:type_name -> management.PeerCompact + 67, // 70: management.NetworkMapComponentsFull.policies:type_name -> management.PolicyCompact + 68, // 71: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact + 70, // 72: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw + 71, // 73: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw + 46, // 74: management.NetworkMapComponentsFull.all_dns_records:type_name -> management.SimpleRecord + 45, // 75: management.NetworkMapComponentsFull.account_zones:type_name -> management.CustomZone + 72, // 76: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw + 80, // 77: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry + 81, // 78: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + 82, // 79: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + 83, // 80: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry + 4, // 81: management.PolicyCompact.action:type_name -> management.RuleAction + 2, // 82: management.PolicyCompact.protocol:type_name -> management.RuleProtocol + 79, // 83: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range + 48, // 84: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer + 74, // 85: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry + 35, // 86: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes + 73, // 87: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList + 75, // 88: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIndexes + 76, // 89: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList + 77, // 90: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet + 8, // 91: management.ManagementService.Login:input_type -> management.EncryptedMessage + 8, // 92: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 24, // 93: management.ManagementService.GetServerKey:input_type -> management.Empty + 24, // 94: management.ManagementService.isHealthy:input_type -> management.Empty + 8, // 95: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 96: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 97: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage + 8, // 98: management.ManagementService.Logout:input_type -> management.EncryptedMessage + 8, // 99: management.ManagementService.Job:input_type -> management.EncryptedMessage + 8, // 100: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage + 8, // 101: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage + 8, // 102: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage + 8, // 103: management.ManagementService.Login:output_type -> management.EncryptedMessage + 8, // 104: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 23, // 105: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 24, // 106: management.ManagementService.isHealthy:output_type -> management.Empty + 8, // 107: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 8, // 108: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage + 24, // 109: management.ManagementService.SyncMeta:output_type -> management.Empty + 24, // 110: management.ManagementService.Logout:output_type -> management.Empty + 8, // 111: management.ManagementService.Job:output_type -> management.EncryptedMessage + 8, // 112: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage + 8, // 113: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage + 8, // 114: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage + 103, // [103:115] is the sub-list for method output_type + 91, // [91:103] is the sub-list for method input_type + 91, // [91:91] is the sub-list for extension type_name + 91, // [91:91] is the sub-list for extension extendee + 0, // [0:91] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -6072,7 +8032,211 @@ func file_management_proto_init() { return nil } } + file_management_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkMapEnvelope); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } file_management_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkMapComponentsFull); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountSettingsCompact); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountNetwork); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkMapComponentsDelta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeerCompact); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyCompact); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupCompact); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DNSSettingsCompact); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RouteRaw); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NameServerGroupRaw); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkResourceRaw); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkRouterList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkRouterEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyIndexes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserIDList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeerIndexSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PortInfo_Range); i { case 0: return &v.state @@ -6095,13 +8259,17 @@ func file_management_proto_init() { (*PortInfo_Port)(nil), (*PortInfo_Range_)(nil), } + file_management_proto_msgTypes[53].OneofWrappers = []interface{}{ + (*NetworkMapEnvelope_Full)(nil), + (*NetworkMapEnvelope_Delta)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 8, - NumMessages: 55, + NumMessages: 76, NumExtensions: 0, NumServices: 1, }, diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 461a614fe6a..d2bd62e6780 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -133,6 +133,12 @@ message SyncResponse { // Posture checks to be evaluated by client repeated Checks Checks = 6; + + // NetworkMapEnvelope carries the component-based wire format for peers that + // advertise PeerCapabilityComponentNetworkMap. When set, NetworkMap (field 5) + // is left empty: management ships components and the client runs Calculate() + // locally instead of receiving an expanded NetworkMap. + NetworkMapEnvelope NetworkMapEnvelope = 7; } message SyncMetaRequest { @@ -212,6 +218,8 @@ enum PeerCapability { PeerCapabilitySourcePrefixes = 1; // Client handles IPv6 overlay addresses and firewall rules. PeerCapabilityIPv6Overlay = 2; + // Client receives NetworkMap as components and assembles it locally. + PeerCapabilityComponentNetworkMap = 3; } // PeerSystemMeta is machine meta data like OS and version. @@ -709,3 +717,334 @@ message StopExposeRequest { } message StopExposeResponse {} + +// ===================================================================== +// Component-based NetworkMap wire format (PeerCapabilityComponentNetworkMap). +// +// Peers that advertise this capability receive NetworkMap building blocks +// (peers + groups + policies + routes + dns + ssh + forwarding) and run the +// expansion (Calculate) locally instead of receiving a fully-expanded +// NetworkMap from the server. +// ===================================================================== + +// NetworkMapEnvelope wraps either a full snapshot or a delta. Step 2 ships +// only Full; Delta is reserved for the incremental-update work. +message NetworkMapEnvelope { + oneof payload { + NetworkMapComponentsFull full = 1; + NetworkMapComponentsDelta delta = 2; + } +} + +// NetworkMapComponentsFull is the full per-peer component snapshot. The +// client decodes it into a types.NetworkMapComponents and runs Calculate() +// locally to produce the same NetworkMap the legacy server path would have +// produced. Every field carries RAW component data — no server-side +// expansion (firewall rules, DNS config, SSH auth, route firewall rules, +// forwarding rules) is shipped; the client computes those itself. +message NetworkMapComponentsFull { + uint64 serial = 1; + + // Peer config for the receiving peer (legacy proto.PeerConfig kept as-is — + // it carries the receiving peer's own overlay address, FQDN, SSH config). + PeerConfig peer_config = 2; + + // Account-level network metadata (id, IPv4/IPv6 overlay subnets, DNS, + // serial). Mirrors types.Network. + AccountNetwork network = 3; + + // Account-level settings the client needs for its local Calculate(). + AccountSettingsCompact account_settings = 4; + + // Account DNS settings (mirrors types.DNSSettings). + DNSSettingsCompact dns_settings = 5; + + // Domain shared across all peers in this account, e.g. "netbird.cloud". + // Each peer's FQDN is dns_label + "." + dns_domain. + string dns_domain = 6; + + // Custom-zone domain for this peer's view (c.CustomZoneDomain). Empty when + // the peer has no custom zone records. + string custom_zone_domain = 7; + + // Deduplicated agent versions; PeerCompact.agent_version_idx indexes here. + // Empty string at index 0 if any peer has no version. + repeated string agent_versions = 8; + + // All peers (deduplicated). The client splits peers into online / offline + // locally using account_settings.peer_login_expiration on receive. + repeated PeerCompact peers = 9; + + // Indexes into peers for the subset that may act as routers. + repeated uint32 router_peer_indexes = 10; + + // Policies that affect the receiving peer. + repeated PolicyCompact policies = 11; + + // Groups in unspecified order — clients key off id (account_seq_id). + repeated GroupCompact groups = 12; + + // Routes relevant to this peer, raw shape (mirrors []*route.Route). + repeated RouteRaw routes = 13; + + // Nameserver groups (mirrors []*nbdns.NameServerGroup). + repeated NameServerGroupRaw nameserver_groups = 14; + + // All DNS records the client needs to assemble its custom zone. Reuses + // the existing SimpleRecord wire shape. + repeated SimpleRecord all_dns_records = 15; + + // Custom zones (typically the peer's own zone). Reuses the existing + // CustomZone wire shape. + repeated CustomZone account_zones = 16; + + // Network resources (mirrors []*resourceTypes.NetworkResource). + repeated NetworkResourceRaw network_resources = 17; + + // Routers per network. Outer key: network id (xid string). Each entry is + // the set of routers backing that network for this peer's view. + map routers_map = 18; + + // For each NetworkResource id (xid string), the indexes into policies[] + // that apply to it. + map resource_policies_map = 19; + + // Group-id (account_seq_id) → user ids authorized for SSH on members. + map group_id_to_user_ids = 20; + + // Account-level allowed user ids (used by Calculate() when assembling SSH + // authorized users for the receiving peer). + repeated string allowed_user_ids = 21; + + // Per posture-check id (xid string), the set of peer indexes that failed + // the check. Server-side evaluation result; clients do not re-evaluate. + map posture_failed_peers = 22; + + // Reserved for future component additions (incremental_serial, parent_seq, + // etc.) without forcing a renumber. + reserved 23 to 50; +} + +// AccountSettingsCompact carries the account-level settings the client needs +// to evaluate locally. Mirrors the subset of types.AccountSettingsInfo that +// Calculate() actually reads — login-expiration (used to filter expired +// peers). Inactivity expiration is purely server-side bookkeeping and is not +// shipped. +message AccountSettingsCompact { + bool peer_login_expiration_enabled = 1; + // Login expiration window. Unit is nanoseconds (matches time.Duration). + int64 peer_login_expiration_ns = 2; +} + +// AccountNetwork is the account-level overlay metadata. Mirrors types.Network +// so the client can populate NetworkMap.Network without a server round-trip. +message AccountNetwork { + string identifier = 1; + // IPv4 overlay subnet in CIDR form (e.g. "100.64.0.0/16"). + string net_cidr = 2; + // IPv6 ULA overlay subnet in CIDR form (e.g. "fd00:4e42::/64"). Empty when + // the account has no IPv6 overlay yet. + string net_v6_cidr = 3; + string dns = 4; + uint64 serial = 5; +} + +// NetworkMapComponentsDelta is reserved for the incremental update protocol +// (Step 3 of the migration plan). Field numbers 1–100 are pre-allocated to +// keep room for the planned event types without needing a renumber. +message NetworkMapComponentsDelta { + reserved 1 to 100; +} + +// PeerCompact is the wire-shape of a remote peer used by the component +// format. It carries every field of types.Peer that the client's local +// Calculate() reads — including the trio needed to evaluate +// LoginExpired() (added_with_sso_login + login_expiration_enabled + +// last_login_unix_nano). Fields the client does not consume (Status, +// CreatedAt, etc.) are not shipped. +message PeerCompact { + // Raw 32-byte WireGuard public key (no base64 wrapping). + bytes wg_pub_key = 1; + + // Raw 4-byte IPv4 overlay address. Always a /32 host route, so no prefix + // byte is needed. + bytes ip = 2; + + // Raw 16-byte IPv6 overlay address; always a /128 host route. Empty when + // the peer has no IPv6 overlay address. + bytes ipv6 = 3; + + // Raw SSH public key bytes (or empty). + bytes ssh_pub_key = 4; + + // DNS label without the account's domain suffix. Full FQDN is + // dns_label + "." + NetworkMapComponentsFull.dns_domain. + string dns_label = 5; + + // Index into NetworkMapComponentsFull.agent_versions. + uint32 agent_version_idx = 6; + + // True iff the peer was added via SSO login (i.e., types.Peer.UserID is + // non-empty). Combined with login_expiration_enabled and + // last_login_unix_nano this lets the client reproduce + // (*Peer).LoginExpired() locally. + bool added_with_sso_login = 7; + + // True when the peer's login can expire — mirrors + // types.Peer.LoginExpirationEnabled. + bool login_expiration_enabled = 8; + + // Unix-nanosecond timestamp of the peer's last login. 0 when the peer has + // never logged in (server stores nil; client treats 0 as "epoch", which + // makes a fresh peer immediately expired iff login_expiration_enabled is + // true — the same semantics as types.Peer.GetLastLogin). + int64 last_login_unix_nano = 9; +} + +// PolicyCompact is the compact form of a policy rule. Group references use +// the per-account integer ids from account_seq_counters; the client resolves +// them against NetworkMapComponentsFull.groups. Direction is derived per-peer +// on the client (ingress when the peer is in destination_group_ids, egress +// when in source_group_ids; both when bidirectional). +message PolicyCompact { + // Per-account integer id (matches policies.account_seq_id). + uint32 id = 1; + + RuleAction action = 2; + RuleProtocol protocol = 3; + bool bidirectional = 4; + + // Single ports referenced by the rule. + repeated uint32 ports = 5; + + // Port ranges (start..end) referenced by the rule. + repeated PortInfo.Range port_ranges = 6; + + // Group ids (account_seq_id) of source / destination groups. + repeated uint32 source_group_ids = 7; + repeated uint32 destination_group_ids = 8; +} + +// GroupCompact is the wire-shape of a group: per-account integer id, optional +// name, and indexes into NetworkMapComponentsFull.peers identifying members. +message GroupCompact { + // Per-account integer id (matches groups.account_seq_id). Used by + // PolicyCompact.source_group_ids / destination_group_ids. + uint32 id = 1; + + // Group name; only sent when non-empty (clients use it for diagnostics). + string name = 2; + + // Indexes into NetworkMapComponentsFull.peers. + repeated uint32 peer_indexes = 3; +} + +// DNSSettingsCompact mirrors types.DNSSettings. +message DNSSettingsCompact { + // Group ids (account_seq_id) whose DNS management is disabled. + repeated uint32 disabled_management_group_ids = 1; +} + +// RouteRaw mirrors *route.Route (the domain type), trimmed to fields that +// types.NetworkMapComponents.Calculate() reads. Group references are +// account_seq_ids; the routing peer (when set) is referenced by index into +// NetworkMapComponentsFull.peers. +message RouteRaw { + // Per-account integer id (matches routes.account_seq_id). + uint32 id = 1; + string net_id = 2; + string description = 3; + + // Either network_cidr (e.g. "10.0.0.0/16") or domains is set, not both. + string network_cidr = 4; + repeated string domains = 5; + bool keep_route = 6; + + // Routing peer reference: peer_index_set tells whether peer_index is valid + // (proto3 uint32 cannot disambiguate "0" from "unset"). Mutually exclusive + // with peer_group_ids. + // + // peer_index decodes back to types.Peer.ID (the peer's xid string), NOT + // to its WireGuard public key. This matches the server-side data flow: + // c.Routes carry route.Peer = peer.ID, and getRoutingPeerRoutes mutates + // it to peer.Key only after the route has been admitted to the network + // map. Decoders MUST set Route.Peer = peer.ID; the legacy Calculate() + // path will substitute the WG key downstream. + bool peer_index_set = 7; + uint32 peer_index = 8; + repeated uint32 peer_group_ids = 9; + + int32 network_type = 10; + bool masquerade = 11; + int32 metric = 12; + bool enabled = 13; + repeated uint32 group_ids = 14; + repeated uint32 access_control_group_ids = 15; + bool skip_auto_apply = 16; +} + +// NameServerGroupRaw mirrors *nbdns.NameServerGroup. Distinct from the +// legacy NameServerGroup (which is the wire-trimmed shape consumed by +// proto.DNSConfig and lacks the Name/Description/Groups/Enabled fields). +message NameServerGroupRaw { + uint32 id = 1; // nameserver_groups.account_seq_id + string name = 2; + string description = 3; + // Reuses the legacy NameServer wire shape (IP as string). + repeated NameServer nameservers = 4; + // Group ids (account_seq_id) the NSG distributes nameservers to. + repeated uint32 group_ids = 5; + bool primary = 6; + repeated string domains = 7; + bool enabled = 8; + bool search_domains_enabled = 9; +} + +// NetworkResourceRaw mirrors *resourceTypes.NetworkResource. +message NetworkResourceRaw { + uint32 id = 1; // network_resources.account_seq_id + string network_id = 2; // xid string — networks have no seq id today + string name = 3; + string description = 4; + // Resource type: "host" / "subnet" / "domain". + string type = 5; + string address = 6; + string domain_value = 7; // resource.Domain + string prefix_cidr = 8; + bool enabled = 9; +} + +// NetworkRouterList carries the routers backing one network. +message NetworkRouterList { + // Routers in this network, keyed by peer_index (the routing peer). + repeated NetworkRouterEntry entries = 1; +} + +// NetworkRouterEntry mirrors a single *routerTypes.NetworkRouter; the routing +// peer is referenced by index into NetworkMapComponentsFull.peers. +message NetworkRouterEntry { + uint32 id = 1; // network_routers.account_seq_id + uint32 peer_index = 2; + bool peer_index_set = 3; + repeated uint32 peer_group_ids = 4; + bool masquerade = 5; + int32 metric = 6; + bool enabled = 7; +} + +// PolicyIndexes is a list of indexes into NetworkMapComponentsFull.policies. +message PolicyIndexes { + repeated uint32 indexes = 1; +} + +// UserIDList is a list of user ids — used as the value type in +// NetworkMapComponentsFull.group_id_to_user_ids. +message UserIDList { + repeated string user_ids = 1; +} + +// PeerIndexSet is a set of peer indexes — used as the value type in +// NetworkMapComponentsFull.posture_failed_peers. +message PeerIndexSet { + repeated uint32 peer_indexes = 1; +} From b194af48b81a73c7ae38f41b75ae9792181dcf56 Mon Sep 17 00:00:00 2001 From: crn4 Date: Mon, 4 May 2026 14:34:31 +0200 Subject: [PATCH 03/42] wire size benches fix --- .../types/networkmap_wire_benchmark_test.go | 18 +++ .../types/networkmap_wire_breakdown_test.go | 143 ++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 management/server/types/networkmap_wire_breakdown_test.go diff --git a/management/server/types/networkmap_wire_benchmark_test.go b/management/server/types/networkmap_wire_benchmark_test.go index f310cb0ea64..eb18dd04e8c 100644 --- a/management/server/types/networkmap_wire_benchmark_test.go +++ b/management/server/types/networkmap_wire_benchmark_test.go @@ -2,6 +2,8 @@ package types_test import ( "context" + "crypto/rand" + "encoding/base64" "fmt" "testing" @@ -40,6 +42,20 @@ func populateAccountSeqIDs(account *types.Account) { } } +// assignValidWgKeys overwrites every peer's Key with a valid base64-encoded +// 32-byte string. The default scalableTestAccount uses unparseable strings +// like "key-peer-0", which makes the components encoder emit a nil WgPubKey +// and the legacy encoder ship 10-char placeholders — both shrink the wire +// size in unrealistic ways. Production peers always have valid 44-char base64 +// keys, so any benchmark/breakdown that wants honest numbers must call this. +func assignValidWgKeys(account *types.Account) { + for _, p := range account.Peers { + var raw [32]byte + _, _ = rand.Read(raw[:]) + p.Key = base64.StdEncoding.EncodeToString(raw[:]) + } +} + // BenchmarkNetworkMapWireEncode reports per-call ns and the marshaled wire // size for both encoding paths. Run with: // @@ -50,6 +66,7 @@ func BenchmarkNetworkMapWireEncode(b *testing.B) { for _, scale := range wireBenchScales { account, validatedPeers := scalableTestAccount(scale.peers, scale.groups) populateAccountSeqIDs(account) + assignValidWgKeys(account) ctx := context.Background() resourcePolicies := account.GetResourcePoliciesMap() @@ -120,6 +137,7 @@ func BenchmarkNetworkMapWireSize(b *testing.B) { for _, scale := range wireBenchScales { account, validatedPeers := scalableTestAccount(scale.peers, scale.groups) populateAccountSeqIDs(account) + assignValidWgKeys(account) ctx := context.Background() resourcePolicies := account.GetResourcePoliciesMap() diff --git a/management/server/types/networkmap_wire_breakdown_test.go b/management/server/types/networkmap_wire_breakdown_test.go new file mode 100644 index 00000000000..d76e73fb9f7 --- /dev/null +++ b/management/server/types/networkmap_wire_breakdown_test.go @@ -0,0 +1,143 @@ +package types_test + +import ( + "context" + "fmt" + "testing" + + goproto "google.golang.org/protobuf/proto" + + nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/internals/controllers/network_map/controller/cache" + mgmtgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc" + "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// TestNetworkMapWireBreakdown is a one-shot diagnostic: it computes the wire +// size attributable to each top-level field of both the legacy NetworkMap and +// the components NetworkMapEnvelope at the 5000-peer scale, so the migration +// docs can attribute the size reduction to each optimization. Runs only on +// demand via -run TestNetworkMapWireBreakdown. +func TestNetworkMapWireBreakdown(t *testing.T) { + if testing.Short() { + t.Skip("size diagnostic, skipped with -short") + } + + const peerCount, groupCount = 5000, 100 + account, validatedPeers := scalableTestAccount(peerCount, groupCount) + populateAccountSeqIDs(account) + assignValidWgKeys(account) + + ctx := context.Background() + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + + peerID := "peer-0" + peer := account.Peers[peerID] + networkMap := account.GetPeerNetworkMapFromComponents(ctx, peerID, nbdns.CustomZone{}, nil, validatedPeers, resourcePolicies, routers, nil, groupIDToUserIDs) + components := account.GetPeerNetworkMapComponents(ctx, peerID, nbdns.CustomZone{}, nil, validatedPeers, resourcePolicies, routers, groupIDToUserIDs) + + dnsCache := &cache.DNSConfigCache{} + settings := &types.Settings{} + + legacyResp := mgmtgrpc.ToSyncResponse(ctx, nil, nil, nil, peer, nil, nil, networkMap, "netbird.cloud", nil, dnsCache, settings, nil, nil, 0) + legacyTotal := mustMarshalSize(t, legacyResp.NetworkMap) + + envelope := mgmtgrpc.EncodeNetworkMapEnvelope(mgmtgrpc.ComponentsEnvelopeInput{ + Components: components, + PeerConfig: legacyResp.NetworkMap.PeerConfig, + DNSDomain: "netbird.cloud", + }) + componentsTotal := mustMarshalSize(t, envelope) + + t.Logf("\n=== LEGACY NetworkMap (%d peers, %d groups) ===", peerCount, groupCount) + t.Logf(" Total: %d bytes\n", legacyTotal) + + legacyBreakdown := []struct { + name string + nm *proto.NetworkMap + }{ + {"RemotePeers", &proto.NetworkMap{RemotePeers: legacyResp.NetworkMap.RemotePeers}}, + {"OfflinePeers", &proto.NetworkMap{OfflinePeers: legacyResp.NetworkMap.OfflinePeers}}, + {"FirewallRules", &proto.NetworkMap{FirewallRules: legacyResp.NetworkMap.FirewallRules}}, + {"Routes", &proto.NetworkMap{Routes: legacyResp.NetworkMap.Routes}}, + {"RoutesFirewallRules", &proto.NetworkMap{RoutesFirewallRules: legacyResp.NetworkMap.RoutesFirewallRules}}, + {"DNSConfig", &proto.NetworkMap{DNSConfig: legacyResp.NetworkMap.DNSConfig}}, + {"PeerConfig", &proto.NetworkMap{PeerConfig: legacyResp.NetworkMap.PeerConfig}}, + {"SshAuth", &proto.NetworkMap{SshAuth: legacyResp.NetworkMap.SshAuth}}, + } + for _, e := range legacyBreakdown { + size := mustMarshalSize(t, e.nm) + t.Logf(" %-22s %8d bytes %5.1f%%", e.name, size, pct(size, legacyTotal)) + } + + full := envelope.GetFull() + t.Logf("\n=== COMPONENTS NetworkMapEnvelope (%d peers, %d groups) ===", peerCount, groupCount) + t.Logf(" Total: %d bytes (%.1f%% of legacy)\n", componentsTotal, pct(componentsTotal, legacyTotal)) + + componentsBreakdown := []struct { + name string + nm *proto.NetworkMapComponentsFull + }{ + {"Peers", &proto.NetworkMapComponentsFull{Peers: full.Peers}}, + {"Policies", &proto.NetworkMapComponentsFull{Policies: full.Policies}}, + {"Groups", &proto.NetworkMapComponentsFull{Groups: full.Groups}}, + {"Routes (raw)", &proto.NetworkMapComponentsFull{Routes: full.Routes}}, + {"NameServerGroups", &proto.NetworkMapComponentsFull{NameserverGroups: full.NameserverGroups}}, + {"AllDNSRecords", &proto.NetworkMapComponentsFull{AllDnsRecords: full.AllDnsRecords}}, + {"AccountZones", &proto.NetworkMapComponentsFull{AccountZones: full.AccountZones}}, + {"NetworkResources", &proto.NetworkMapComponentsFull{NetworkResources: full.NetworkResources}}, + {"RoutersMap", &proto.NetworkMapComponentsFull{RoutersMap: full.RoutersMap}}, + {"ResourcePoliciesMap", &proto.NetworkMapComponentsFull{ResourcePoliciesMap: full.ResourcePoliciesMap}}, + {"GroupIDToUserIDs", &proto.NetworkMapComponentsFull{GroupIdToUserIds: full.GroupIdToUserIds}}, + {"AllowedUserIDs", &proto.NetworkMapComponentsFull{AllowedUserIds: full.AllowedUserIds}}, + {"PostureFailedPeers", &proto.NetworkMapComponentsFull{PostureFailedPeers: full.PostureFailedPeers}}, + {"DNSSettings", &proto.NetworkMapComponentsFull{DnsSettings: full.DnsSettings}}, + {"PeerConfig", &proto.NetworkMapComponentsFull{PeerConfig: full.PeerConfig}}, + {"AgentVersions", &proto.NetworkMapComponentsFull{AgentVersions: full.AgentVersions}}, + } + for _, e := range componentsBreakdown { + size := mustMarshalSize(t, e.nm) + t.Logf(" %-22s %8d bytes %5.1f%%", e.name, size, pct(size, componentsTotal)) + } + + t.Logf("\n=== Per-PeerCompact average ===") + if len(full.Peers) > 0 { + t.Logf(" PeerCompact avg: %d bytes/peer", mustMarshalSize(t, &proto.NetworkMapComponentsFull{Peers: full.Peers})/len(full.Peers)) + } + if len(legacyResp.NetworkMap.RemotePeers) > 0 { + t.Logf(" RemotePeer avg: %d bytes/peer", + mustMarshalSize(t, &proto.NetworkMap{RemotePeers: legacyResp.NetworkMap.RemotePeers})/len(legacyResp.NetworkMap.RemotePeers)) + } + + t.Logf("\n=== FirewallRule expansion footprint ===") + t.Logf(" legacy FirewallRules count: %d", len(legacyResp.NetworkMap.FirewallRules)) + t.Logf(" components Policies count: %d", len(full.Policies)) + t.Logf(" components Groups count: %d", len(full.Groups)) + + totalGroupPeerIdxs := 0 + for _, g := range full.Groups { + totalGroupPeerIdxs += len(g.PeerIndexes) + } + t.Logf(" components peer-index refs across all groups: %d", totalGroupPeerIdxs) +} + +func mustMarshalSize(t *testing.T, m goproto.Message) int { + b, err := goproto.Marshal(m) + if err != nil { + t.Fatalf("marshal: %v", err) + } + return len(b) +} + +func pct(part, total int) float64 { + if total == 0 { + return 0 + } + return 100 * float64(part) / float64(total) +} + +// Stops fmt being unused if the breakdown loop above is later commented out. +var _ = fmt.Sprintf From 9083bdb97758d890ab89661383ceb23d0d44cfc4 Mon Sep 17 00:00:00 2001 From: crn4 Date: Wed, 6 May 2026 17:22:55 +0200 Subject: [PATCH 04/42] capabilities conditioning --- .../network_map/controller/controller.go | 121 +- .../controllers/network_map/interface.go | 4 + .../controllers/network_map/interface_mock.go | 33 + .../shared/grpc/components_encoder.go | 36 +- .../shared/grpc/components_encoder_test.go | 77 ++ .../grpc/components_envelope_response.go | 162 +++ management/internals/shared/grpc/server.go | 26 +- management/server/types/account_components.go | 43 + .../server/types/peer_networkmap_result.go | 25 + .../types/peer_networkmap_result_test.go | 104 ++ shared/management/proto/management.pb.go | 1003 ++++++++++------- shared/management/proto/management.proto | 28 +- 12 files changed, 1229 insertions(+), 433 deletions(-) create mode 100644 management/internals/shared/grpc/components_envelope_response.go create mode 100644 management/server/types/peer_networkmap_result.go create mode 100644 management/server/types/peer_networkmap_result_test.go diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 590773dda93..2f9274a069b 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -55,6 +55,15 @@ type Controller struct { proxyController port_forwarding.Controller integratedPeerValidator integrated_validator.IntegratedValidator + + // componentsDisabled is the kill switch for the component-based wire + // format. When true the controller emits legacy proto.NetworkMap to every + // peer regardless of capability — used to roll back instantly via a + // management restart from a bad components encoder. + // + // Set once in NewController from NB_NETWORK_MAP_COMPONENTS_DISABLE and + // never written after — readers race-free without a mutex. + componentsDisabled bool } type bufferUpdate struct { @@ -81,12 +90,30 @@ func NewController(ctx context.Context, store store.Store, metrics telemetry.App settingsManager: settingsManager, dnsDomain: dnsDomain, config: config, + componentsDisabled: parseBoolEnv("NB_NETWORK_MAP_COMPONENTS_DISABLE"), proxyController: proxyController, EphemeralPeersManager: ephemeralPeersManager, } } +// PeerNeedsComponents reports whether the gRPC layer should emit the +// component-based wire format for this peer. Combines the peer's advertised +// capability with the controller-level kill switch — callers ask exactly +// this question, so encapsulating it removes accidental double-checks. +func (c *Controller) PeerNeedsComponents(p *nbpeer.Peer) bool { + return p != nil && p.SupportsComponentNetworkMap() && !c.componentsDisabled +} + +// parseBoolEnv reads an env var via strconv.ParseBool so callers accept the +// usual "1/t/T/TRUE/true/True" set instead of being strict about a single +// literal — matches the convention used elsewhere in the codebase +// (e.g. event.go's NB_TRAFFIC_EVENT_*) and reduces operator surprises. +func parseBoolEnv(key string) bool { + v, _ := strconv.ParseBool(os.Getenv(key)) + return v +} + func (c *Controller) OnPeerConnected(ctx context.Context, accountID string, peerID string) (chan *network_map.UpdateMessage, error) { peer, err := c.repo.GetPeerByID(ctx, accountID, peerID) if err != nil { @@ -192,18 +219,26 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin c.metrics.CountCalcPostureChecksDuration(time.Since(start)) start = time.Now() - remotePeerNetworkMap := account.GetPeerNetworkMapFromComponents(ctx, p.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) + result := account.GetPeerNetworkMapResult(ctx, p.ID, c.componentsDisabled, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start)) - proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] - if ok { - remotePeerNetworkMap.Merge(proxyNetworkMap) + proxyNetworkMap := proxyNetworkMaps[peer.ID] + if result.NetworkMap != nil && proxyNetworkMap != nil { + result.NetworkMap.Merge(proxyNetworkMap) } peerGroups := account.GetPeerGroups(p.ID) start = time.Now() - update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) + var update *proto.SyncResponse + if result.IsComponents() { + // proxyNetworkMap rides the envelope as a ProxyPatch sidecar; + // the client merges it into Calculate()'s output the same + // way the legacy server did via NetworkMap.Merge. + update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, result.Components, proxyNetworkMap, dnsDomain, postureChecks, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) + } else { + update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, result.NetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) + } c.metrics.CountToSyncResponseDuration(time.Since(start)) c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{ @@ -314,11 +349,11 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe return err } - remotePeerNetworkMap := account.GetPeerNetworkMapFromComponents(ctx, peerId, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) + result := account.GetPeerNetworkMapResult(ctx, peerId, c.componentsDisabled, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) - proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] - if ok { - remotePeerNetworkMap.Merge(proxyNetworkMap) + proxyNetworkMap := proxyNetworkMaps[peer.ID] + if result.NetworkMap != nil && proxyNetworkMap != nil { + result.NetworkMap.Merge(proxyNetworkMap) } extraSettings, err := c.settingsManager.GetExtraSettings(ctx, peer.AccountID) @@ -329,7 +364,12 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe peerGroups := account.GetPeerGroups(peerId) dnsFwdPort := computeForwarderPort(maps.Values(account.Peers), network_map.DnsForwarderPortMinVersion) - update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort) + var update *proto.SyncResponse + if result.IsComponents() { + update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, result.Components, proxyNetworkMap, dnsDomain, postureChecks, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort) + } else { + update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, result.NetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort) + } c.peersUpdateManager.SendUpdate(ctx, peer.ID, &network_map.UpdateMessage{ Update: update, MessageType: network_map.MessageTypeNetworkMap, @@ -376,6 +416,67 @@ func (c *Controller) BufferUpdateAccountPeers(ctx context.Context, accountID str return nil } +// GetValidatedPeerWithComponents is the components-format counterpart of +// GetValidatedPeerWithMap. It returns raw NetworkMapComponents for capable +// peers along with the proxy NetworkMap fragment (BYOP / port-forwarding +// data the legacy server folds in via NetworkMap.Merge). The gRPC layer +// encodes both into the wire envelope. The caller is responsible for +// checking peer capability + componentsDisabled before dispatching here — +// this method does NOT branch on capability itself. +func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequiresApproval bool, accountID string, peer *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMapComponents, *types.NetworkMap, []*posture.Checks, int64, error) { + if isRequiresApproval { + network, err := c.repo.GetAccountNetwork(ctx, accountID) + if err != nil { + return nil, nil, nil, nil, 0, err + } + return peer, &types.NetworkMapComponents{Network: network.Copy()}, nil, nil, 0, nil + } + + account, err := c.requestBuffer.GetAccountWithBackpressure(ctx, accountID) + if err != nil { + return nil, nil, nil, nil, 0, err + } + + account.InjectProxyPolicies(ctx) + + approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra) + if err != nil { + return nil, nil, nil, nil, 0, err + } + + postureChecks, err := c.getPeerPostureChecks(account, peer.ID) + if err != nil { + return nil, nil, nil, nil, 0, err + } + + accountZones, err := c.repo.GetAccountZones(ctx, account.Id) + if err != nil { + return nil, nil, nil, nil, 0, err + } + + // Fetch the proxy network map fragment for this peer alongside the + // components — same single-account-load path the streaming controller + // uses, so initial-sync delivers BYOP/forwarding patches synchronously + // instead of waiting for the next streaming push. + proxyNetworkMaps, err := c.proxyController.GetProxyNetworkMaps(ctx, account.Id, peer.ID, account.Peers) + if err != nil { + log.WithContext(ctx).Errorf("failed to get proxy network maps: %v", err) + return nil, nil, nil, nil, 0, err + } + + dnsDomain := c.GetDNSDomain(account.Settings) + peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain) + + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + components := account.GetPeerNetworkMapComponents(ctx, peer.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, groupIDToUserIDs) + + dnsFwdPort := computeForwarderPort(maps.Values(account.Peers), network_map.DnsForwarderPortMinVersion) + + return peer, components, proxyNetworkMaps[peer.ID], postureChecks, dnsFwdPort, nil +} + func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresApproval bool, accountID string, peer *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error) { if isRequiresApproval { network, err := c.repo.GetAccountNetwork(ctx, accountID) diff --git a/management/internals/controllers/network_map/interface.go b/management/internals/controllers/network_map/interface.go index 44d8f7d7257..738d3a4302d 100644 --- a/management/internals/controllers/network_map/interface.go +++ b/management/internals/controllers/network_map/interface.go @@ -22,6 +22,10 @@ type Controller interface { UpdateAccountPeer(ctx context.Context, accountId string, peerId string) error BufferUpdateAccountPeers(ctx context.Context, accountID string, reason types.UpdateReason) error GetValidatedPeerWithMap(ctx context.Context, isRequiresApproval bool, accountID string, p *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error) + GetValidatedPeerWithComponents(ctx context.Context, isRequiresApproval bool, accountID string, p *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMapComponents, *types.NetworkMap, []*posture.Checks, int64, error) + // PeerNeedsComponents combines the peer's advertised capability with the + // kill-switch flag — the only public predicate gRPC layers should ask. + PeerNeedsComponents(p *nbpeer.Peer) bool GetDNSDomain(settings *types.Settings) string StartWarmup(context.Context) GetNetworkMap(ctx context.Context, peerID string) (*types.NetworkMap, error) diff --git a/management/internals/controllers/network_map/interface_mock.go b/management/internals/controllers/network_map/interface_mock.go index 073a75d3b1f..b24554d398f 100644 --- a/management/internals/controllers/network_map/interface_mock.go +++ b/management/internals/controllers/network_map/interface_mock.go @@ -130,6 +130,39 @@ func (mr *MockControllerMockRecorder) GetValidatedPeerWithMap(ctx, isRequiresApp return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatedPeerWithMap", reflect.TypeOf((*MockController)(nil).GetValidatedPeerWithMap), ctx, isRequiresApproval, accountID, p) } +// GetValidatedPeerWithComponents mocks base method. +func (m *MockController) GetValidatedPeerWithComponents(ctx context.Context, isRequiresApproval bool, accountID string, p *peer.Peer) (*peer.Peer, *types.NetworkMapComponents, *types.NetworkMap, []*posture.Checks, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidatedPeerWithComponents", ctx, isRequiresApproval, accountID, p) + ret0, _ := ret[0].(*peer.Peer) + ret1, _ := ret[1].(*types.NetworkMapComponents) + ret2, _ := ret[2].(*types.NetworkMap) + ret3, _ := ret[3].([]*posture.Checks) + ret4, _ := ret[4].(int64) + ret5, _ := ret[5].(error) + return ret0, ret1, ret2, ret3, ret4, ret5 +} + +// GetValidatedPeerWithComponents indicates an expected call of GetValidatedPeerWithComponents. +func (mr *MockControllerMockRecorder) GetValidatedPeerWithComponents(ctx, isRequiresApproval, accountID, p any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatedPeerWithComponents", reflect.TypeOf((*MockController)(nil).GetValidatedPeerWithComponents), ctx, isRequiresApproval, accountID, p) +} + +// PeerNeedsComponents mocks base method. +func (m *MockController) PeerNeedsComponents(p *peer.Peer) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PeerNeedsComponents", p) + ret0, _ := ret[0].(bool) + return ret0 +} + +// PeerNeedsComponents indicates an expected call of PeerNeedsComponents. +func (mr *MockControllerMockRecorder) PeerNeedsComponents(p any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeerNeedsComponents", reflect.TypeOf((*MockController)(nil).PeerNeedsComponents), p) +} + // OnPeerConnected mocks base method. func (m *MockController) OnPeerConnected(ctx context.Context, accountID, peerID string) (chan *UpdateMessage, error) { m.ctrl.T.Helper() diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index eeda651874f..fa21a58c122 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -23,9 +23,14 @@ const wgKeyRawLen = 32 // computed alongside the components in the network_map controller and reused // from the legacy proto path) and the dns_domain string. type ComponentsEnvelopeInput struct { - Components *types.NetworkMapComponents - PeerConfig *proto.PeerConfig - DNSDomain string + Components *types.NetworkMapComponents + PeerConfig *proto.PeerConfig + DNSDomain string + DNSForwarderPort int64 + // ProxyPatch carries pre-expanded NetworkMap fragments injected by + // external controllers (BYOP/port-forwarding). Nil when no proxy data + // is present; encoder skips the field in that case. + ProxyPatch *proto.ProxyPatch } // EncodeNetworkMapEnvelope converts NetworkMapComponents into the component @@ -43,6 +48,29 @@ type ComponentsEnvelopeInput struct { func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvelope { c := in.Components + // Graceful degrade when components is nil — matches the legacy path's + // account_components.go:43 behaviour for missing/unvalidated peers + // (return a NetworkMap with only Network populated). The receiver gets + // an envelope it can decode without crashing; AccountSettings stays + // non-nil so client-side dereferences are safe. + if c == nil { + // Match legacy missing-peer minimum: a NetworkMap with only Network + // populated (account_components.go:43). The receiver gets enough to + // bootstrap (Network identifier, dns_domain, account_settings) and + // nothing else. + return &proto.NetworkMapEnvelope{ + Payload: &proto.NetworkMapEnvelope_Full{ + Full: &proto.NetworkMapComponentsFull{ + PeerConfig: in.PeerConfig, + DnsDomain: in.DNSDomain, + DnsForwarderPort: in.DNSForwarderPort, + AccountSettings: &proto.AccountSettingsCompact{}, + ProxyPatch: in.ProxyPatch, + }, + }, + } + } + // Phase 1: build dedup tables. Every routing peer (in c.RouterPeers) and // every regular peer (in c.Peers) must be indexed before any encoder // looks up indexes via e.peerOrder — otherwise routes / routers_map for @@ -66,6 +94,8 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel PeerConfig: in.PeerConfig, Network: toAccountNetwork(c.Network), AccountSettings: toAccountSettingsCompact(c.AccountSettings), + DnsForwarderPort: in.DNSForwarderPort, + ProxyPatch: in.ProxyPatch, DnsSettings: enc.encodeDNSSettings(c.DNSSettings), DnsDomain: in.DNSDomain, CustomZoneDomain: c.CustomZoneDomain, diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go index d353de6d8e7..bcb153b541b 100644 --- a/management/internals/shared/grpc/components_encoder_test.go +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -779,6 +779,83 @@ func TestEncodeNetworkMapEnvelope_GroupIDToUserIDs(t *testing.T) { assert.ElementsMatch(t, []string{"user-1", "user-2"}, full.GroupIdToUserIds[1].UserIds) } +func TestToProxyPatch_EmptyInputReturnsNil(t *testing.T) { + assert.Nil(t, toProxyPatch(nil, "netbird.cloud", false, false)) + assert.Nil(t, toProxyPatch(&types.NetworkMap{}, "netbird.cloud", false, false), + "empty NetworkMap (no peers, rules, routes etc) → nil patch so proto3 omits the field") +} + +func TestToProxyPatch_PopulatesAllFields(t *testing.T) { + nm := &types.NetworkMap{ + Peers: []*nbpeer.Peer{{ + ID: "ext-peer", Key: testWgKeyA, IP: netip.AddrFrom4([4]byte{100, 64, 0, 9}), + DNSLabel: "extpeer", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + }}, + FirewallRules: []*types.FirewallRule{{ + PeerIP: "100.64.0.9", Action: "accept", Direction: 0, Protocol: "tcp", + }}, + } + + patch := toProxyPatch(nm, "netbird.cloud", false, false) + + require.NotNil(t, patch) + assert.Len(t, patch.Peers, 1) + assert.Len(t, patch.FirewallRules, 1) +} + +// TestEncodeNetworkMapEnvelope_ProxyPatchPropagated covers the ProxyPatch +// pass-through in both encoder branches (normal path + nil-Components +// graceful-degrade). Without this test a regression that drops `ProxyPatch:` +// from one of the struct literals in components_encoder.go would slip past CI. +func TestEncodeNetworkMapEnvelope_ProxyPatchPropagated(t *testing.T) { + patch := &proto.ProxyPatch{ + ForwardingRules: []*proto.ForwardingRule{{ + Protocol: proto.RuleProtocol_TCP, + DestinationPort: &proto.PortInfo{PortSelection: &proto.PortInfo_Port{Port: 80}}, + TranslatedAddress: net.IPv4(10, 0, 0, 1).To4(), + TranslatedPort: &proto.PortInfo{PortSelection: &proto.PortInfo_Port{Port: 8080}}, + }}, + } + + t.Run("normal_path", func(t *testing.T) { + c := newTestComponents() + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ + Components: c, + ProxyPatch: patch, + }).GetFull() + + require.NotNil(t, full.ProxyPatch, "ProxyPatch must propagate through the normal encode path") + assert.Len(t, full.ProxyPatch.ForwardingRules, 1) + }) + + t.Run("nil_components_graceful_degrade", func(t *testing.T) { + full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ + Components: nil, + ProxyPatch: patch, + }).GetFull() + + require.NotNil(t, full.ProxyPatch, "ProxyPatch must propagate through the nil-Components branch too") + assert.Len(t, full.ProxyPatch.ForwardingRules, 1) + }) +} + +func TestEncodeNetworkMapEnvelope_NilComponentsGracefulDegrade(t *testing.T) { + // nil Components → minimal envelope, no crash. Matches the legacy + // account_components.go:43 behaviour for missing/unvalidated peers. + env := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ + Components: nil, + DNSDomain: "netbird.cloud", + }) + + require.NotNil(t, env) + full := env.GetFull() + require.NotNil(t, full) + require.NotNil(t, full.AccountSettings, "AccountSettings must always be non-nil") + assert.Equal(t, "netbird.cloud", full.DnsDomain) + assert.Empty(t, full.Peers) + assert.Empty(t, full.Policies) +} + func TestEncodeNetworkMapEnvelope_AccountSettingsAlwaysEmitted(t *testing.T) { c := &types.NetworkMapComponents{ Network: &types.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}}, diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go new file mode 100644 index 00000000000..12715357fa2 --- /dev/null +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -0,0 +1,162 @@ +package grpc + +import ( + "context" + + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" + + nbconfig "github.com/netbirdio/netbird/management/internals/server/config" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/posture" + "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// ToComponentSyncResponse builds a SyncResponse carrying the compact +// NetworkMapEnvelope for capability-aware peers. The legacy proto.NetworkMap +// field is intentionally left empty — capable peers ignore it and the +// envelope alone is the authoritative wire shape. +// +// PeerConfig is computed once server-side using the receiving peer's own +// account-level network metadata. EnableSSH inside PeerConfig is left at +// peer.SSHEnabled (the peer's local setting); account-policy-driven SSH is +// computed by the client from the envelope's GroupIDToUserIDs / AllowedUserIDs +// inside Calculate(), so the SshConfig.SshEnabled bit may flip true on the +// client even though the server-side PeerConfig reports false. +func ToComponentSyncResponse( + ctx context.Context, + config *nbconfig.Config, + httpConfig *nbconfig.HttpServerConfig, + deviceFlowConfig *nbconfig.DeviceAuthorizationFlow, + peer *nbpeer.Peer, + turnCredentials *Token, + relayCredentials *Token, + components *types.NetworkMapComponents, + proxyPatch *types.NetworkMap, + dnsName string, + checks []*posture.Checks, + settings *types.Settings, + extraSettings *types.ExtraSettings, + peerGroups []string, + dnsFwdPort int64, +) *proto.SyncResponse { + network := networkOrZero(components) + enableSSH := computeSSHEnabledForPeer(components, peer.ID) + peerConfig := toPeerConfig(peer, network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH) + + includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid() + useSourcePrefixes := peer.SupportsSourcePrefixes() + + envelope := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ + Components: components, + PeerConfig: peerConfig, + DNSDomain: dnsName, + DNSForwarderPort: dnsFwdPort, + ProxyPatch: toProxyPatch(proxyPatch, dnsName, includeIPv6, useSourcePrefixes), + }) + + resp := &proto.SyncResponse{ + PeerConfig: peerConfig, + NetworkMapEnvelope: envelope, + Checks: toProtocolChecks(ctx, checks), + } + + nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings) + resp.NetbirdConfig = integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings) + + return resp +} + +// networkOrZero returns components.Network or a zero Network — toPeerConfig +// dereferences network.Net which would panic on nil. +func networkOrZero(c *types.NetworkMapComponents) *types.Network { + if c == nil || c.Network == nil { + return &types.Network{} + } + return c.Network +} + +// toProxyPatch converts a proxy-injected *types.NetworkMap into the wire +// patch the components envelope ships alongside. Returns nil when there are +// no fragments to merge — proto3 omits a nil message field, so the receiver +// sees no patch and skips the merge step entirely. +// +// We reuse the legacy proto-conversion helpers (toProtocolRoutes, +// toProtocolFirewallRules, toProtocolRoutesFirewallRules, +// appendRemotePeerConfig, ForwardingRule.ToProto) because the proxy +// delivers fragments pre-expanded — there's no raw component shape to +// derive them from. Components purity isn't violated: proxy data isn't +// policy-graph-derived, it's externally injected post-Calculate, so the +// client merges it on top of its locally-computed NetworkMap. +func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePrefixes bool) *proto.ProxyPatch { + if nm == nil { + return nil + } + if len(nm.Peers) == 0 && len(nm.OfflinePeers) == 0 && len(nm.FirewallRules) == 0 && + len(nm.Routes) == 0 && len(nm.RoutesFirewallRules) == 0 && len(nm.ForwardingRules) == 0 { + return nil + } + + patch := &proto.ProxyPatch{ + Peers: appendRemotePeerConfig(nil, nm.Peers, dnsName, includeIPv6), + OfflinePeers: appendRemotePeerConfig(nil, nm.OfflinePeers, dnsName, includeIPv6), + FirewallRules: toProtocolFirewallRules(nm.FirewallRules, includeIPv6, useSourcePrefixes), + Routes: toProtocolRoutes(nm.Routes), + RouteFirewallRules: toProtocolRoutesFirewallRules(nm.RoutesFirewallRules), + } + if len(nm.ForwardingRules) > 0 { + patch.ForwardingRules = make([]*proto.ForwardingRule, 0, len(nm.ForwardingRules)) + for _, r := range nm.ForwardingRules { + patch.ForwardingRules = append(patch.ForwardingRules, r.ToProto()) + } + } + return patch +} + +// computeSSHEnabledForPeer mirrors the SSH-server-activation bit that +// Calculate() folds into NetworkMap.EnableSSH. Components-format peers +// receive a freshly-computed PeerConfig.SshConfig.SshEnabled at sync time; +// without this helper the field would be incorrectly false for any peer +// that's the destination of an SSH-enabling policy without having +// peer.SSHEnabled set locally. +// +// Cheaper than running Calculate() because we ignore peer-pair expansion — +// only the "any matched policy with NetbirdSSH protocol" check is needed. +// The full SSH AuthorizedUsers map is still produced by the client when it +// runs Calculate() over the envelope. +func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peerID string) bool { + if c == nil { + return false + } + for _, policy := range c.Policies { + if policy == nil || !policy.Enabled { + continue + } + for _, rule := range policy.Rules { + if rule == nil || !rule.Enabled { + continue + } + if rule.Protocol != types.PolicyRuleProtocolNetbirdSSH { + continue + } + if peerInDestinations(c, rule, peerID) { + return true + } + } + } + return false +} + +// peerInDestinations reports whether peerID is in any of rule.Destinations' +// groups (or matches DestinationResource if used). +func peerInDestinations(c *types.NetworkMapComponents, rule *types.PolicyRule, peerID string) bool { + if rule.DestinationResource.ID != "" { + return rule.DestinationResource.ID == peerID + } + for _, groupID := range rule.Destinations { + if c.IsPeerInGroup(peerID, groupID) { + return true + } + } + return false +} diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index 1d823430479..27d77beb4ad 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -932,7 +932,31 @@ func (s *Server) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer return status.Errorf(codes.Internal, "failed to get peer groups %s", err) } - plainResp := ToSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, networkMap, s.networkMapController.GetDNSDomain(settings), postureChecks, nil, settings, settings.Extra, peerGroups, dnsFwdPort) + dnsName := s.networkMapController.GetDNSDomain(settings) + + var plainResp *proto.SyncResponse + if s.networkMapController.PeerNeedsComponents(peer) { + // Capable peer: discard the legacy NetworkMap that SyncAndMarkPeer + // computed and recompute the raw components instead. This wastes one + // Calculate() call per initial-sync — the component-based wire + // format is what the peer actually consumes. The streaming path + // (network_map.Controller.UpdateAccountPeers) skips this duplication + // because it dispatches by capability before computing. + // + // TODO(step-4-sync): refactor SyncPeer / SyncAndMarkPeer / their + // mocks + manager interfaces to return PeerNetworkMapResult so the + // initial-sync path stops doing duplicate work. ~13 files of churn, + // deferred until the client-side decoder lands and there's a real + // deployment of capability=3 peers worth optimizing for. + _, components, proxyPatch, _, _, err := s.networkMapController.GetValidatedPeerWithComponents(ctx, false, peer.AccountID, peer) + if err != nil { + log.WithContext(ctx).Errorf("failed to build components for peer %s on initial sync: %v", peer.ID, err) + return status.Errorf(codes.Internal, "failed to build initial sync envelope") + } + plainResp = ToComponentSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, components, proxyPatch, dnsName, postureChecks, settings, settings.Extra, peerGroups, dnsFwdPort) + } else { + plainResp = ToSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, networkMap, dnsName, postureChecks, nil, settings, settings.Extra, peerGroups, dnsFwdPort) + } key, err := s.secretsManager.GetWGKey() if err != nil { diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 2b4f7e0515a..c6f651782e2 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -16,6 +16,49 @@ import ( "github.com/netbirdio/netbird/route" ) +// GetPeerNetworkMapResult dispatches to either the legacy-NetworkMap path or +// the components path based on the peer's capability and the kill switch. +// Capable peers (PeerCapabilityComponentNetworkMap) get the raw components +// shape — the server skips Calculate() entirely for them, saving CPU +// proportional to the number of capable peers in the account. Legacy peers +// (or any peer when componentsDisabled is true) get the fully-expanded +// NetworkMap as before. +func (a *Account) GetPeerNetworkMapResult( + ctx context.Context, + peerID string, + componentsDisabled bool, + peersCustomZone nbdns.CustomZone, + accountZones []*zones.Zone, + validatedPeersMap map[string]struct{}, + resourcePolicies map[string][]*Policy, + routers map[string]map[string]*routerTypes.NetworkRouter, + metrics *telemetry.AccountManagerMetrics, + groupIDToUserIDs map[string][]string, +) PeerNetworkMapResult { + peer := a.Peers[peerID] + if !componentsDisabled && peer != nil && peer.SupportsComponentNetworkMap() { + components := a.GetPeerNetworkMapComponents( + ctx, peerID, peersCustomZone, accountZones, validatedPeersMap, resourcePolicies, routers, groupIDToUserIDs, + ) + // Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents + // returns &NetworkMap{Network: a.Network.Copy()} when components is + // nil. Match that floor so the receiving client always sees the + // account Network identifier, not a fully-empty envelope. + if components == nil { + components = &NetworkMapComponents{ + PeerID: peerID, + Network: a.Network.Copy(), + } + } + return PeerNetworkMapResult{Components: components} + } + return PeerNetworkMapResult{ + NetworkMap: a.GetPeerNetworkMapFromComponents( + ctx, peerID, peersCustomZone, accountZones, validatedPeersMap, resourcePolicies, routers, metrics, groupIDToUserIDs, + ), + } +} + func (a *Account) GetPeerNetworkMapFromComponents( ctx context.Context, peerID string, diff --git a/management/server/types/peer_networkmap_result.go b/management/server/types/peer_networkmap_result.go new file mode 100644 index 00000000000..fadbeb59936 --- /dev/null +++ b/management/server/types/peer_networkmap_result.go @@ -0,0 +1,25 @@ +package types + +// PeerNetworkMapResult is what the network_map controller produces for a +// single peer. Exactly one of NetworkMap or Components is populated depending +// on the peer's capability: +// +// - Components-capable peers (PeerCapabilityComponentNetworkMap) get +// Components: the raw types.NetworkMapComponents the client decodes and +// runs Calculate() on locally. NetworkMap stays nil — the server skips +// the expansion entirely. +// - Legacy peers (or any peer when the kill switch is set) get NetworkMap: +// the fully-expanded view the legacy gRPC path consumes. +// +// The gRPC layer (ToSyncResponseForPeer) dispatches by which field is +// non-nil; callers must not rely on both being set. +type PeerNetworkMapResult struct { + NetworkMap *NetworkMap + Components *NetworkMapComponents +} + +// IsComponents reports whether the result carries the components shape. +// Use this in preference to direct nil checks on the fields. +func (r PeerNetworkMapResult) IsComponents() bool { + return r.Components != nil +} diff --git a/management/server/types/peer_networkmap_result_test.go b/management/server/types/peer_networkmap_result_test.go new file mode 100644 index 00000000000..908581a0819 --- /dev/null +++ b/management/server/types/peer_networkmap_result_test.go @@ -0,0 +1,104 @@ +package types_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + nbdns "github.com/netbirdio/netbird/dns" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" +) + +// helper: marks the given peer as components-capable. +func markCapable(p *nbpeer.Peer) { + p.Meta.Capabilities = append(p.Meta.Capabilities, nbpeer.PeerCapabilityComponentNetworkMap) +} + +func TestGetPeerNetworkMapResult_CapablePeerGetsComponents(t *testing.T) { + account, validatedPeers := scalableTestAccount(10, 2) + markCapable(account.Peers["peer-0"]) + + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + + result := account.GetPeerNetworkMapResult( + context.Background(), + "peer-0", + false, // componentsDisabled + nbdns.CustomZone{}, + nil, + validatedPeers, + resourcePolicies, + routers, + nil, + groupIDToUserIDs, + ) + + require.True(t, result.IsComponents(), "capable peer must get the components shape") + assert.Nil(t, result.NetworkMap) + require.NotNil(t, result.Components) + assert.Equal(t, "peer-0", result.Components.PeerID) +} + +func TestGetPeerNetworkMapResult_LegacyPeerGetsNetworkMap(t *testing.T) { + account, validatedPeers := scalableTestAccount(10, 2) + // peer-0 left without the component capability + + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + + result := account.GetPeerNetworkMapResult( + context.Background(), + "peer-0", + false, + nbdns.CustomZone{}, + nil, + validatedPeers, + resourcePolicies, + routers, + nil, + groupIDToUserIDs, + ) + + assert.False(t, result.IsComponents()) + assert.Nil(t, result.Components) + require.NotNil(t, result.NetworkMap, "legacy peer must get a NetworkMap") +} + +func TestGetPeerNetworkMapResult_KillSwitchOverridesCapability(t *testing.T) { + // Capable peer + componentsDisabled=true → falls back to legacy. + account, validatedPeers := scalableTestAccount(10, 2) + markCapable(account.Peers["peer-0"]) + + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + groupIDToUserIDs := account.GetActiveGroupUsers() + + result := account.GetPeerNetworkMapResult( + context.Background(), + "peer-0", + true, // componentsDisabled = true (kill switch) + nbdns.CustomZone{}, + nil, + validatedPeers, + resourcePolicies, + routers, + nil, + groupIDToUserIDs, + ) + + assert.False(t, result.IsComponents(), "kill switch must force legacy NetworkMap path") + assert.Nil(t, result.Components) + require.NotNil(t, result.NetworkMap) +} + +func TestPeerNetworkMapResult_IsComponents(t *testing.T) { + assert.True(t, types.PeerNetworkMapResult{Components: &types.NetworkMapComponents{}}.IsComponents()) + assert.False(t, types.PeerNetworkMapResult{NetworkMap: &types.NetworkMap{}}.IsComponents()) + assert.False(t, types.PeerNetworkMapResult{}.IsComponents()) +} diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index f291c53a894..6755f16353b 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -4651,6 +4651,16 @@ type NetworkMapComponentsFull struct { // Per posture-check id (xid string), the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. PostureFailedPeers map[string]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Account-level DNS forwarder port (mirrors the legacy + // proto.DNSConfig.ForwarderPort). Computed by the controller from peer + // versions; clients fold it into their Calculate() DNS output. + DnsForwarderPort int64 `protobuf:"varint,23,opt,name=dns_forwarder_port,json=dnsForwarderPort,proto3" json:"dns_forwarder_port,omitempty"` + // Pre-expanded NetworkMap fragments injected post-Calculate by external + // controllers (BYOP / port-forwarding proxies). The receiving client + // merges these into its locally-computed NetworkMap the same way the + // legacy server does via NetworkMap.Merge — so downstream consumers see + // a unified merged result regardless of source. + ProxyPatch *ProxyPatch `protobuf:"bytes,24,opt,name=proxy_patch,json=proxyPatch,proto3" json:"proxy_patch,omitempty"` } func (x *NetworkMapComponentsFull) Reset() { @@ -4839,6 +4849,112 @@ func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[string]*PeerIndex return nil } +func (x *NetworkMapComponentsFull) GetDnsForwarderPort() int64 { + if x != nil { + return x.DnsForwarderPort + } + return 0 +} + +func (x *NetworkMapComponentsFull) GetProxyPatch() *ProxyPatch { + if x != nil { + return x.ProxyPatch + } + return nil +} + +// ProxyPatch carries NetworkMap fragments that don't fit the component-graph +// model — they're pre-expanded by external controllers (BYOP / +// port-forwarding proxies) and injected post-Calculate. Fields use the +// legacy wire types because the proxy delivers them pre-formed; there is +// no raw component shape to convert from. Empty when no proxy is active. +type ProxyPatch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Peers []*RemotePeerConfig `protobuf:"bytes,1,rep,name=peers,proto3" json:"peers,omitempty"` + OfflinePeers []*RemotePeerConfig `protobuf:"bytes,2,rep,name=offline_peers,json=offlinePeers,proto3" json:"offline_peers,omitempty"` + FirewallRules []*FirewallRule `protobuf:"bytes,3,rep,name=firewall_rules,json=firewallRules,proto3" json:"firewall_rules,omitempty"` + Routes []*Route `protobuf:"bytes,4,rep,name=routes,proto3" json:"routes,omitempty"` + RouteFirewallRules []*RouteFirewallRule `protobuf:"bytes,5,rep,name=route_firewall_rules,json=routeFirewallRules,proto3" json:"route_firewall_rules,omitempty"` + ForwardingRules []*ForwardingRule `protobuf:"bytes,6,rep,name=forwarding_rules,json=forwardingRules,proto3" json:"forwarding_rules,omitempty"` +} + +func (x *ProxyPatch) Reset() { + *x = ProxyPatch{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProxyPatch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProxyPatch) ProtoMessage() {} + +func (x *ProxyPatch) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[55] + 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 ProxyPatch.ProtoReflect.Descriptor instead. +func (*ProxyPatch) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{55} +} + +func (x *ProxyPatch) GetPeers() []*RemotePeerConfig { + if x != nil { + return x.Peers + } + return nil +} + +func (x *ProxyPatch) GetOfflinePeers() []*RemotePeerConfig { + if x != nil { + return x.OfflinePeers + } + return nil +} + +func (x *ProxyPatch) GetFirewallRules() []*FirewallRule { + if x != nil { + return x.FirewallRules + } + return nil +} + +func (x *ProxyPatch) GetRoutes() []*Route { + if x != nil { + return x.Routes + } + return nil +} + +func (x *ProxyPatch) GetRouteFirewallRules() []*RouteFirewallRule { + if x != nil { + return x.RouteFirewallRules + } + return nil +} + +func (x *ProxyPatch) GetForwardingRules() []*ForwardingRule { + if x != nil { + return x.ForwardingRules + } + return nil +} + // AccountSettingsCompact carries the account-level settings the client needs // to evaluate locally. Mirrors the subset of types.AccountSettingsInfo that // Calculate() actually reads — login-expiration (used to filter expired @@ -4857,7 +4973,7 @@ type AccountSettingsCompact struct { func (x *AccountSettingsCompact) Reset() { *x = AccountSettingsCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[55] + mi := &file_management_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4870,7 +4986,7 @@ func (x *AccountSettingsCompact) String() string { func (*AccountSettingsCompact) ProtoMessage() {} func (x *AccountSettingsCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[55] + mi := &file_management_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4883,7 +4999,7 @@ func (x *AccountSettingsCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use AccountSettingsCompact.ProtoReflect.Descriptor instead. func (*AccountSettingsCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{55} + return file_management_proto_rawDescGZIP(), []int{56} } func (x *AccountSettingsCompact) GetPeerLoginExpirationEnabled() bool { @@ -4920,7 +5036,7 @@ type AccountNetwork struct { func (x *AccountNetwork) Reset() { *x = AccountNetwork{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[56] + mi := &file_management_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4933,7 +5049,7 @@ func (x *AccountNetwork) String() string { func (*AccountNetwork) ProtoMessage() {} func (x *AccountNetwork) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[56] + mi := &file_management_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4946,7 +5062,7 @@ func (x *AccountNetwork) ProtoReflect() protoreflect.Message { // Deprecated: Use AccountNetwork.ProtoReflect.Descriptor instead. func (*AccountNetwork) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{56} + return file_management_proto_rawDescGZIP(), []int{57} } func (x *AccountNetwork) GetIdentifier() string { @@ -4996,7 +5112,7 @@ type NetworkMapComponentsDelta struct { func (x *NetworkMapComponentsDelta) Reset() { *x = NetworkMapComponentsDelta{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[57] + mi := &file_management_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5009,7 +5125,7 @@ func (x *NetworkMapComponentsDelta) String() string { func (*NetworkMapComponentsDelta) ProtoMessage() {} func (x *NetworkMapComponentsDelta) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[57] + mi := &file_management_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5022,7 +5138,7 @@ func (x *NetworkMapComponentsDelta) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkMapComponentsDelta.ProtoReflect.Descriptor instead. func (*NetworkMapComponentsDelta) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{57} + return file_management_proto_rawDescGZIP(), []int{58} } // PeerCompact is the wire-shape of a remote peer used by the component @@ -5069,7 +5185,7 @@ type PeerCompact struct { func (x *PeerCompact) Reset() { *x = PeerCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[58] + mi := &file_management_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5082,7 +5198,7 @@ func (x *PeerCompact) String() string { func (*PeerCompact) ProtoMessage() {} func (x *PeerCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[58] + mi := &file_management_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5095,7 +5211,7 @@ func (x *PeerCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerCompact.ProtoReflect.Descriptor instead. func (*PeerCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{58} + return file_management_proto_rawDescGZIP(), []int{59} } func (x *PeerCompact) GetWgPubKey() []byte { @@ -5188,7 +5304,7 @@ type PolicyCompact struct { func (x *PolicyCompact) Reset() { *x = PolicyCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[59] + mi := &file_management_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5201,7 +5317,7 @@ func (x *PolicyCompact) String() string { func (*PolicyCompact) ProtoMessage() {} func (x *PolicyCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[59] + mi := &file_management_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5214,7 +5330,7 @@ func (x *PolicyCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyCompact.ProtoReflect.Descriptor instead. func (*PolicyCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{59} + return file_management_proto_rawDescGZIP(), []int{60} } func (x *PolicyCompact) GetId() uint32 { @@ -5292,7 +5408,7 @@ type GroupCompact struct { func (x *GroupCompact) Reset() { *x = GroupCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[60] + mi := &file_management_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5305,7 +5421,7 @@ func (x *GroupCompact) String() string { func (*GroupCompact) ProtoMessage() {} func (x *GroupCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[60] + mi := &file_management_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5318,7 +5434,7 @@ func (x *GroupCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use GroupCompact.ProtoReflect.Descriptor instead. func (*GroupCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{60} + return file_management_proto_rawDescGZIP(), []int{61} } func (x *GroupCompact) GetId() uint32 { @@ -5355,7 +5471,7 @@ type DNSSettingsCompact struct { func (x *DNSSettingsCompact) Reset() { *x = DNSSettingsCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[61] + mi := &file_management_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5368,7 +5484,7 @@ func (x *DNSSettingsCompact) String() string { func (*DNSSettingsCompact) ProtoMessage() {} func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[61] + mi := &file_management_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5381,7 +5497,7 @@ func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSSettingsCompact.ProtoReflect.Descriptor instead. func (*DNSSettingsCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{61} + return file_management_proto_rawDescGZIP(), []int{62} } func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []uint32 { @@ -5433,7 +5549,7 @@ type RouteRaw struct { func (x *RouteRaw) Reset() { *x = RouteRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[62] + mi := &file_management_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5446,7 +5562,7 @@ func (x *RouteRaw) String() string { func (*RouteRaw) ProtoMessage() {} func (x *RouteRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[62] + mi := &file_management_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5459,7 +5575,7 @@ func (x *RouteRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteRaw.ProtoReflect.Descriptor instead. func (*RouteRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{62} + return file_management_proto_rawDescGZIP(), []int{63} } func (x *RouteRaw) GetId() uint32 { @@ -5598,7 +5714,7 @@ type NameServerGroupRaw struct { func (x *NameServerGroupRaw) Reset() { *x = NameServerGroupRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[63] + mi := &file_management_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5611,7 +5727,7 @@ func (x *NameServerGroupRaw) String() string { func (*NameServerGroupRaw) ProtoMessage() {} func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[63] + mi := &file_management_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5624,7 +5740,7 @@ func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use NameServerGroupRaw.ProtoReflect.Descriptor instead. func (*NameServerGroupRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{63} + return file_management_proto_rawDescGZIP(), []int{64} } func (x *NameServerGroupRaw) GetId() uint32 { @@ -5711,7 +5827,7 @@ type NetworkResourceRaw struct { func (x *NetworkResourceRaw) Reset() { *x = NetworkResourceRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[64] + mi := &file_management_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5724,7 +5840,7 @@ func (x *NetworkResourceRaw) String() string { func (*NetworkResourceRaw) ProtoMessage() {} func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[64] + mi := &file_management_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5737,7 +5853,7 @@ func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkResourceRaw.ProtoReflect.Descriptor instead. func (*NetworkResourceRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{64} + return file_management_proto_rawDescGZIP(), []int{65} } func (x *NetworkResourceRaw) GetId() uint32 { @@ -5816,7 +5932,7 @@ type NetworkRouterList struct { func (x *NetworkRouterList) Reset() { *x = NetworkRouterList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[65] + mi := &file_management_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5829,7 +5945,7 @@ func (x *NetworkRouterList) String() string { func (*NetworkRouterList) ProtoMessage() {} func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[65] + mi := &file_management_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5842,7 +5958,7 @@ func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkRouterList.ProtoReflect.Descriptor instead. func (*NetworkRouterList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{65} + return file_management_proto_rawDescGZIP(), []int{66} } func (x *NetworkRouterList) GetEntries() []*NetworkRouterEntry { @@ -5871,7 +5987,7 @@ type NetworkRouterEntry struct { func (x *NetworkRouterEntry) Reset() { *x = NetworkRouterEntry{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[66] + mi := &file_management_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5884,7 +6000,7 @@ func (x *NetworkRouterEntry) String() string { func (*NetworkRouterEntry) ProtoMessage() {} func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[66] + mi := &file_management_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5897,7 +6013,7 @@ func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkRouterEntry.ProtoReflect.Descriptor instead. func (*NetworkRouterEntry) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{66} + return file_management_proto_rawDescGZIP(), []int{67} } func (x *NetworkRouterEntry) GetId() uint32 { @@ -5961,7 +6077,7 @@ type PolicyIndexes struct { func (x *PolicyIndexes) Reset() { *x = PolicyIndexes{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[67] + mi := &file_management_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5974,7 +6090,7 @@ func (x *PolicyIndexes) String() string { func (*PolicyIndexes) ProtoMessage() {} func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[67] + mi := &file_management_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5987,7 +6103,7 @@ func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyIndexes.ProtoReflect.Descriptor instead. func (*PolicyIndexes) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{67} + return file_management_proto_rawDescGZIP(), []int{68} } func (x *PolicyIndexes) GetIndexes() []uint32 { @@ -6010,7 +6126,7 @@ type UserIDList struct { func (x *UserIDList) Reset() { *x = UserIDList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[68] + mi := &file_management_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6023,7 +6139,7 @@ func (x *UserIDList) String() string { func (*UserIDList) ProtoMessage() {} func (x *UserIDList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[68] + mi := &file_management_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6036,7 +6152,7 @@ func (x *UserIDList) ProtoReflect() protoreflect.Message { // Deprecated: Use UserIDList.ProtoReflect.Descriptor instead. func (*UserIDList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{68} + return file_management_proto_rawDescGZIP(), []int{69} } func (x *UserIDList) GetUserIds() []string { @@ -6059,7 +6175,7 @@ type PeerIndexSet struct { func (x *PeerIndexSet) Reset() { *x = PeerIndexSet{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[69] + mi := &file_management_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6072,7 +6188,7 @@ func (x *PeerIndexSet) String() string { func (*PeerIndexSet) ProtoMessage() {} func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[69] + mi := &file_management_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6085,7 +6201,7 @@ func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerIndexSet.ProtoReflect.Descriptor instead. func (*PeerIndexSet) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{69} + return file_management_proto_rawDescGZIP(), []int{70} } func (x *PeerIndexSet) GetPeerIndexes() []uint32 { @@ -6107,7 +6223,7 @@ type PortInfo_Range struct { func (x *PortInfo_Range) Reset() { *x = PortInfo_Range{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[71] + mi := &file_management_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6120,7 +6236,7 @@ func (x *PortInfo_Range) String() string { func (*PortInfo_Range) ProtoMessage() {} func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[71] + mi := &file_management_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6792,7 +6908,7 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x22, 0x8b, 0x0e, 0x0a, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, + 0x64, 0x22, 0xf2, 0x0e, 0x0a, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, @@ -6880,289 +6996,320 @@ var file_management_proto_rawDesc = []byte{ 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x61, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x6e, + 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x64, 0x6e, 0x73, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x61, 0x74, 0x63, + 0x68, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, - 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x17, 0x10, 0x33, 0x22, - 0x94, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x65, - 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, - 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x15, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x74, - 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, - 0x43, 0x69, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x5f, 0x76, 0x36, 0x5f, 0x63, - 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x56, 0x36, - 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x21, - 0x0a, 0x19, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, - 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, - 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, - 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, - 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, - 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x78, 0x12, 0x2f, 0x0a, 0x14, - 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x73, 0x6f, 0x5f, 0x6c, - 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x64, 0x64, 0x65, - 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x38, 0x0a, - 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x22, 0xdc, 0x02, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x0b, - 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, - 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x0d, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, - 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x57, - 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x1a, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, - 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, 0x72, - 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, - 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, - 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, - 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, - 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, - 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, - 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, - 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, - 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, - 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, - 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0d, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, - 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, - 0x85, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, - 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, - 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, - 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, - 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x61, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x4a, 0x04, 0x08, 0x19, 0x10, 0x33, 0x22, 0x87, 0x03, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x6f, 0x66, 0x66, + 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, + 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x0e, + 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, + 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, + 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x14, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, + 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, + 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x10, 0x66, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, + 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, + 0x22, 0x94, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, + 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, + 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x5f, 0x76, 0x36, 0x5f, + 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x56, + 0x36, 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, + 0x21, 0x0a, 0x19, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, + 0x10, 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, + 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, + 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x69, 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, + 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x78, 0x12, 0x2f, 0x0a, + 0x14, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x73, 0x6f, 0x5f, + 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x38, + 0x0a, 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x22, 0xdc, 0x02, 0x0a, 0x0d, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x3b, 0x0a, + 0x0b, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, + 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x1a, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, + 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, + 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, + 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, - 0x0a, 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, 0x4c, 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, 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, 0xfd, 0x06, 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, + 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, + 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, + 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, + 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x22, 0x85, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, + 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, + 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, + 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, + 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, + 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, + 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, + 0x31, 0x0a, 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, 0x4c, 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, 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, 0xfd, 0x06, + 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, - 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, + 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, 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, + 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, 0x28, 0x01, 0x30, 0x01, 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, + 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, - 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, 0x42, 0x08, 0x5a, 0x06, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 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, 0x42, 0x08, 0x5a, + 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7178,7 +7325,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 76) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 77) var file_management_proto_goTypes = []interface{}{ (JobStatus)(0), // 0: management.JobStatus (PeerCapability)(0), // 1: management.PeerCapability @@ -7243,29 +7390,30 @@ var file_management_proto_goTypes = []interface{}{ (*StopExposeResponse)(nil), // 60: management.StopExposeResponse (*NetworkMapEnvelope)(nil), // 61: management.NetworkMapEnvelope (*NetworkMapComponentsFull)(nil), // 62: management.NetworkMapComponentsFull - (*AccountSettingsCompact)(nil), // 63: management.AccountSettingsCompact - (*AccountNetwork)(nil), // 64: management.AccountNetwork - (*NetworkMapComponentsDelta)(nil), // 65: management.NetworkMapComponentsDelta - (*PeerCompact)(nil), // 66: management.PeerCompact - (*PolicyCompact)(nil), // 67: management.PolicyCompact - (*GroupCompact)(nil), // 68: management.GroupCompact - (*DNSSettingsCompact)(nil), // 69: management.DNSSettingsCompact - (*RouteRaw)(nil), // 70: management.RouteRaw - (*NameServerGroupRaw)(nil), // 71: management.NameServerGroupRaw - (*NetworkResourceRaw)(nil), // 72: management.NetworkResourceRaw - (*NetworkRouterList)(nil), // 73: management.NetworkRouterList - (*NetworkRouterEntry)(nil), // 74: management.NetworkRouterEntry - (*PolicyIndexes)(nil), // 75: management.PolicyIndexes - (*UserIDList)(nil), // 76: management.UserIDList - (*PeerIndexSet)(nil), // 77: management.PeerIndexSet - nil, // 78: management.SSHAuth.MachineUsersEntry - (*PortInfo_Range)(nil), // 79: management.PortInfo.Range - nil, // 80: management.NetworkMapComponentsFull.RoutersMapEntry - nil, // 81: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - nil, // 82: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - nil, // 83: management.NetworkMapComponentsFull.PostureFailedPeersEntry - (*timestamppb.Timestamp)(nil), // 84: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 85: google.protobuf.Duration + (*ProxyPatch)(nil), // 63: management.ProxyPatch + (*AccountSettingsCompact)(nil), // 64: management.AccountSettingsCompact + (*AccountNetwork)(nil), // 65: management.AccountNetwork + (*NetworkMapComponentsDelta)(nil), // 66: management.NetworkMapComponentsDelta + (*PeerCompact)(nil), // 67: management.PeerCompact + (*PolicyCompact)(nil), // 68: management.PolicyCompact + (*GroupCompact)(nil), // 69: management.GroupCompact + (*DNSSettingsCompact)(nil), // 70: management.DNSSettingsCompact + (*RouteRaw)(nil), // 71: management.RouteRaw + (*NameServerGroupRaw)(nil), // 72: management.NameServerGroupRaw + (*NetworkResourceRaw)(nil), // 73: management.NetworkResourceRaw + (*NetworkRouterList)(nil), // 74: management.NetworkRouterList + (*NetworkRouterEntry)(nil), // 75: management.NetworkRouterEntry + (*PolicyIndexes)(nil), // 76: management.PolicyIndexes + (*UserIDList)(nil), // 77: management.UserIDList + (*PeerIndexSet)(nil), // 78: management.PeerIndexSet + nil, // 79: management.SSHAuth.MachineUsersEntry + (*PortInfo_Range)(nil), // 80: management.PortInfo.Range + nil, // 81: management.NetworkMapComponentsFull.RoutersMapEntry + nil, // 82: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + nil, // 83: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + nil, // 84: management.NetworkMapComponentsFull.PostureFailedPeersEntry + (*timestamppb.Timestamp)(nil), // 85: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 86: google.protobuf.Duration } var file_management_proto_depIdxs = []int32{ 11, // 0: management.JobRequest.bundle:type_name -> management.BundleParameters @@ -7289,14 +7437,14 @@ var file_management_proto_depIdxs = []int32{ 25, // 18: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig 31, // 19: management.LoginResponse.peerConfig:type_name -> management.PeerConfig 51, // 20: management.LoginResponse.Checks:type_name -> management.Checks - 84, // 21: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 85, // 21: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 26, // 22: management.NetbirdConfig.stuns:type_name -> management.HostConfig 30, // 23: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig 26, // 24: management.NetbirdConfig.signal:type_name -> management.HostConfig 27, // 25: management.NetbirdConfig.relay:type_name -> management.RelayConfig 28, // 26: management.NetbirdConfig.flow:type_name -> management.FlowConfig 6, // 27: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol - 85, // 28: management.FlowConfig.interval:type_name -> google.protobuf.Duration + 86, // 28: management.FlowConfig.interval:type_name -> google.protobuf.Duration 26, // 29: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig 37, // 30: management.PeerConfig.sshConfig:type_name -> management.SSHConfig 32, // 31: management.PeerConfig.autoUpdate:type_name -> management.AutoUpdateSettings @@ -7309,7 +7457,7 @@ var file_management_proto_depIdxs = []int32{ 53, // 38: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule 54, // 39: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule 34, // 40: management.NetworkMap.sshAuth:type_name -> management.SSHAuth - 78, // 41: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry + 79, // 41: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry 37, // 42: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig 29, // 43: management.SSHConfig.jwtConfig:type_name -> management.JWTConfig 7, // 44: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider @@ -7323,7 +7471,7 @@ var file_management_proto_depIdxs = []int32{ 4, // 52: management.FirewallRule.Action:type_name -> management.RuleAction 2, // 53: management.FirewallRule.Protocol:type_name -> management.RuleProtocol 52, // 54: management.FirewallRule.PortInfo:type_name -> management.PortInfo - 79, // 55: management.PortInfo.range:type_name -> management.PortInfo.Range + 80, // 55: management.PortInfo.range:type_name -> management.PortInfo.Range 4, // 56: management.RouteFirewallRule.action:type_name -> management.RuleAction 2, // 57: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol 52, // 58: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo @@ -7332,62 +7480,69 @@ var file_management_proto_depIdxs = []int32{ 52, // 61: management.ForwardingRule.translatedPort:type_name -> management.PortInfo 5, // 62: management.ExposeServiceRequest.protocol:type_name -> management.ExposeProtocol 62, // 63: management.NetworkMapEnvelope.full:type_name -> management.NetworkMapComponentsFull - 65, // 64: management.NetworkMapEnvelope.delta:type_name -> management.NetworkMapComponentsDelta + 66, // 64: management.NetworkMapEnvelope.delta:type_name -> management.NetworkMapComponentsDelta 31, // 65: management.NetworkMapComponentsFull.peer_config:type_name -> management.PeerConfig - 64, // 66: management.NetworkMapComponentsFull.network:type_name -> management.AccountNetwork - 63, // 67: management.NetworkMapComponentsFull.account_settings:type_name -> management.AccountSettingsCompact - 69, // 68: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact - 66, // 69: management.NetworkMapComponentsFull.peers:type_name -> management.PeerCompact - 67, // 70: management.NetworkMapComponentsFull.policies:type_name -> management.PolicyCompact - 68, // 71: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact - 70, // 72: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw - 71, // 73: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw + 65, // 66: management.NetworkMapComponentsFull.network:type_name -> management.AccountNetwork + 64, // 67: management.NetworkMapComponentsFull.account_settings:type_name -> management.AccountSettingsCompact + 70, // 68: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact + 67, // 69: management.NetworkMapComponentsFull.peers:type_name -> management.PeerCompact + 68, // 70: management.NetworkMapComponentsFull.policies:type_name -> management.PolicyCompact + 69, // 71: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact + 71, // 72: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw + 72, // 73: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw 46, // 74: management.NetworkMapComponentsFull.all_dns_records:type_name -> management.SimpleRecord 45, // 75: management.NetworkMapComponentsFull.account_zones:type_name -> management.CustomZone - 72, // 76: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw - 80, // 77: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry - 81, // 78: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - 82, // 79: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - 83, // 80: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry - 4, // 81: management.PolicyCompact.action:type_name -> management.RuleAction - 2, // 82: management.PolicyCompact.protocol:type_name -> management.RuleProtocol - 79, // 83: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range - 48, // 84: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer - 74, // 85: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry - 35, // 86: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes - 73, // 87: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList - 75, // 88: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIndexes - 76, // 89: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList - 77, // 90: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet - 8, // 91: management.ManagementService.Login:input_type -> management.EncryptedMessage - 8, // 92: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 24, // 93: management.ManagementService.GetServerKey:input_type -> management.Empty - 24, // 94: management.ManagementService.isHealthy:input_type -> management.Empty - 8, // 95: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 96: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 97: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage - 8, // 98: management.ManagementService.Logout:input_type -> management.EncryptedMessage - 8, // 99: management.ManagementService.Job:input_type -> management.EncryptedMessage - 8, // 100: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage - 8, // 101: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage - 8, // 102: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage - 8, // 103: management.ManagementService.Login:output_type -> management.EncryptedMessage - 8, // 104: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 23, // 105: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 24, // 106: management.ManagementService.isHealthy:output_type -> management.Empty - 8, // 107: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 8, // 108: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage - 24, // 109: management.ManagementService.SyncMeta:output_type -> management.Empty - 24, // 110: management.ManagementService.Logout:output_type -> management.Empty - 8, // 111: management.ManagementService.Job:output_type -> management.EncryptedMessage - 8, // 112: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage - 8, // 113: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage - 8, // 114: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage - 103, // [103:115] is the sub-list for method output_type - 91, // [91:103] is the sub-list for method input_type - 91, // [91:91] is the sub-list for extension type_name - 91, // [91:91] is the sub-list for extension extendee - 0, // [0:91] is the sub-list for field type_name + 73, // 76: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw + 81, // 77: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry + 82, // 78: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + 83, // 79: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + 84, // 80: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry + 63, // 81: management.NetworkMapComponentsFull.proxy_patch:type_name -> management.ProxyPatch + 36, // 82: management.ProxyPatch.peers:type_name -> management.RemotePeerConfig + 36, // 83: management.ProxyPatch.offline_peers:type_name -> management.RemotePeerConfig + 49, // 84: management.ProxyPatch.firewall_rules:type_name -> management.FirewallRule + 43, // 85: management.ProxyPatch.routes:type_name -> management.Route + 53, // 86: management.ProxyPatch.route_firewall_rules:type_name -> management.RouteFirewallRule + 54, // 87: management.ProxyPatch.forwarding_rules:type_name -> management.ForwardingRule + 4, // 88: management.PolicyCompact.action:type_name -> management.RuleAction + 2, // 89: management.PolicyCompact.protocol:type_name -> management.RuleProtocol + 80, // 90: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range + 48, // 91: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer + 75, // 92: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry + 35, // 93: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes + 74, // 94: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList + 76, // 95: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIndexes + 77, // 96: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList + 78, // 97: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet + 8, // 98: management.ManagementService.Login:input_type -> management.EncryptedMessage + 8, // 99: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 24, // 100: management.ManagementService.GetServerKey:input_type -> management.Empty + 24, // 101: management.ManagementService.isHealthy:input_type -> management.Empty + 8, // 102: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 103: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 104: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage + 8, // 105: management.ManagementService.Logout:input_type -> management.EncryptedMessage + 8, // 106: management.ManagementService.Job:input_type -> management.EncryptedMessage + 8, // 107: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage + 8, // 108: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage + 8, // 109: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage + 8, // 110: management.ManagementService.Login:output_type -> management.EncryptedMessage + 8, // 111: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 23, // 112: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 24, // 113: management.ManagementService.isHealthy:output_type -> management.Empty + 8, // 114: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 8, // 115: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage + 24, // 116: management.ManagementService.SyncMeta:output_type -> management.Empty + 24, // 117: management.ManagementService.Logout:output_type -> management.Empty + 8, // 118: management.ManagementService.Job:output_type -> management.EncryptedMessage + 8, // 119: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage + 8, // 120: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage + 8, // 121: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage + 110, // [110:122] is the sub-list for method output_type + 98, // [98:110] is the sub-list for method input_type + 98, // [98:98] is the sub-list for extension type_name + 98, // [98:98] is the sub-list for extension extendee + 0, // [0:98] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -8057,7 +8212,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountSettingsCompact); i { + switch v := v.(*ProxyPatch); i { case 0: return &v.state case 1: @@ -8069,7 +8224,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountNetwork); i { + switch v := v.(*AccountSettingsCompact); i { case 0: return &v.state case 1: @@ -8081,7 +8236,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkMapComponentsDelta); i { + switch v := v.(*AccountNetwork); i { case 0: return &v.state case 1: @@ -8093,7 +8248,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerCompact); i { + switch v := v.(*NetworkMapComponentsDelta); i { case 0: return &v.state case 1: @@ -8105,7 +8260,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyCompact); i { + switch v := v.(*PeerCompact); i { case 0: return &v.state case 1: @@ -8117,7 +8272,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupCompact); i { + switch v := v.(*PolicyCompact); i { case 0: return &v.state case 1: @@ -8129,7 +8284,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DNSSettingsCompact); i { + switch v := v.(*GroupCompact); i { case 0: return &v.state case 1: @@ -8141,7 +8296,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RouteRaw); i { + switch v := v.(*DNSSettingsCompact); i { case 0: return &v.state case 1: @@ -8153,7 +8308,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameServerGroupRaw); i { + switch v := v.(*RouteRaw); i { case 0: return &v.state case 1: @@ -8165,7 +8320,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkResourceRaw); i { + switch v := v.(*NameServerGroupRaw); i { case 0: return &v.state case 1: @@ -8177,7 +8332,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkRouterList); i { + switch v := v.(*NetworkResourceRaw); i { case 0: return &v.state case 1: @@ -8189,7 +8344,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkRouterEntry); i { + switch v := v.(*NetworkRouterList); i { case 0: return &v.state case 1: @@ -8201,7 +8356,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyIndexes); i { + switch v := v.(*NetworkRouterEntry); i { case 0: return &v.state case 1: @@ -8213,7 +8368,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserIDList); i { + switch v := v.(*PolicyIndexes); i { case 0: return &v.state case 1: @@ -8225,6 +8380,18 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserIDList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PeerIndexSet); i { case 0: return &v.state @@ -8236,7 +8403,7 @@ func file_management_proto_init() { return nil } } - file_management_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + file_management_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PortInfo_Range); i { case 0: return &v.state @@ -8269,7 +8436,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 8, - NumMessages: 76, + NumMessages: 77, NumExtensions: 0, NumServices: 1, }, diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index d2bd62e6780..6ba6730b81f 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -820,9 +820,35 @@ message NetworkMapComponentsFull { // the check. Server-side evaluation result; clients do not re-evaluate. map posture_failed_peers = 22; + // Account-level DNS forwarder port (mirrors the legacy + // proto.DNSConfig.ForwarderPort). Computed by the controller from peer + // versions; clients fold it into their Calculate() DNS output. + int64 dns_forwarder_port = 23; + + // Pre-expanded NetworkMap fragments injected post-Calculate by external + // controllers (BYOP / port-forwarding proxies). The receiving client + // merges these into its locally-computed NetworkMap the same way the + // legacy server does via NetworkMap.Merge — so downstream consumers see + // a unified merged result regardless of source. + ProxyPatch proxy_patch = 24; + // Reserved for future component additions (incremental_serial, parent_seq, // etc.) without forcing a renumber. - reserved 23 to 50; + reserved 25 to 50; +} + +// ProxyPatch carries NetworkMap fragments that don't fit the component-graph +// model — they're pre-expanded by external controllers (BYOP / +// port-forwarding proxies) and injected post-Calculate. Fields use the +// legacy wire types because the proxy delivers them pre-formed; there is +// no raw component shape to convert from. Empty when no proxy is active. +message ProxyPatch { + repeated RemotePeerConfig peers = 1; + repeated RemotePeerConfig offline_peers = 2; + repeated FirewallRule firewall_rules = 3; + repeated Route routes = 4; + repeated RouteFirewallRule route_firewall_rules = 5; + repeated ForwardingRule forwarding_rules = 6; } // AccountSettingsCompact carries the account-level settings the client needs From b9a0186200227d3d0b92f7af0d81e7ca8095c950 Mon Sep 17 00:00:00 2001 From: crn4 Date: Mon, 11 May 2026 14:53:19 +0200 Subject: [PATCH 05/42] fix routes filtering in account componnents --- management/server/types/account_components.go | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index c6f651782e2..f9b73888476 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -296,25 +296,49 @@ func (a *Account) getPeersGroupsPoliciesRoutes( relevantPeerIDs[peerID] = a.GetPeer(peerID) + peerGroupSet := make(map[string]struct{}, 8) for groupID, group := range a.Groups { if slices.Contains(group.Peers, peerID) { relevantGroupIDs[groupID] = a.GetGroup(groupID) + peerGroupSet[groupID] = struct{}{} } } routeAccessControlGroups := make(map[string]struct{}) for _, r := range a.Routes { - for _, groupID := range r.Groups { - relevantGroupIDs[groupID] = a.GetGroup(groupID) + if r == nil { + continue + } + relevant := r.Peer == peerID + if !relevant { + for _, groupID := range r.PeerGroups { + if _, ok := peerGroupSet[groupID]; ok { + relevant = true + break + } + } } + if !relevant && r.Enabled { + for _, groupID := range r.Groups { + if _, ok := peerGroupSet[groupID]; ok { + relevant = true + break + } + } + } + if !relevant { + continue + } + for _, groupID := range r.PeerGroups { relevantGroupIDs[groupID] = a.GetGroup(groupID) } - if r.Enabled { - for _, groupID := range r.AccessControlGroups { - relevantGroupIDs[groupID] = a.GetGroup(groupID) - routeAccessControlGroups[groupID] = struct{}{} - } + for _, groupID := range r.Groups { + relevantGroupIDs[groupID] = a.GetGroup(groupID) + } + for _, groupID := range r.AccessControlGroups { + relevantGroupIDs[groupID] = a.GetGroup(groupID) + routeAccessControlGroups[groupID] = struct{}{} } relevantRoutes = append(relevantRoutes, r) } From 672b057aa00bd080ee1749f0a57431e6baac0f37 Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 12 May 2026 11:50:47 +0200 Subject: [PATCH 06/42] fix Group.Copy losing AccountSeqID --- management/server/types/group.go | 1 + 1 file changed, 1 insertion(+) diff --git a/management/server/types/group.go b/management/server/types/group.go index ee5e45ab314..dd1526a743c 100644 --- a/management/server/types/group.go +++ b/management/server/types/group.go @@ -86,6 +86,7 @@ func (g *Group) Copy() *Group { group := &Group{ ID: g.ID, AccountID: g.AccountID, + AccountSeqID: g.AccountSeqID, Name: g.Name, Issued: g.Issued, Peers: make([]string, len(g.Peers)), From 9bbbafaf69cb9e763e943c084785e3626a096221 Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 12 May 2026 12:55:19 +0200 Subject: [PATCH 07/42] int id for networks and posture checks migration --- management/server/networks/manager.go | 32 +++++++-- management/server/networks/manager_test.go | 70 +++++++++++++++++++ management/server/networks/types/network.go | 27 +++++-- management/server/posture/checks.go | 22 ++++-- management/server/posture_checks.go | 12 ++++ management/server/posture_checks_test.go | 58 +++++++++++++++ management/server/store/account_seq_test.go | 41 +++++++++++ management/server/store/sql_store.go | 20 ++++++ management/server/store/store.go | 6 ++ .../server/types/account_seq_counter.go | 2 + 10 files changed, 273 insertions(+), 17 deletions(-) diff --git a/management/server/networks/manager.go b/management/server/networks/manager.go index c96b60bb25a..8da0c594dd3 100644 --- a/management/server/networks/manager.go +++ b/management/server/networks/manager.go @@ -71,9 +71,20 @@ func (m *managerImpl) CreateNetwork(ctx context.Context, userID string, network network.ID = xid.New().String() - err = m.store.SaveNetwork(ctx, network) + err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error { + seq, err := transaction.AllocateAccountSeqID(ctx, network.AccountID, serverTypes.AccountSeqEntityNetwork) + if err != nil { + return fmt.Errorf("failed to allocate network seq id: %w", err) + } + network.AccountSeqID = seq + + if err := transaction.SaveNetwork(ctx, network); err != nil { + return fmt.Errorf("failed to save network: %w", err) + } + return nil + }) if err != nil { - return nil, fmt.Errorf("failed to save network: %w", err) + return nil, err } m.accountManager.StoreEvent(ctx, userID, network.ID, network.AccountID, activity.NetworkCreated, network.EventMeta()) @@ -102,14 +113,25 @@ func (m *managerImpl) UpdateNetwork(ctx context.Context, userID string, network return nil, status.NewPermissionDeniedError() } - _, err = m.store.GetNetworkByID(ctx, store.LockingStrengthUpdate, network.AccountID, network.ID) + err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error { + existing, err := transaction.GetNetworkByID(ctx, store.LockingStrengthUpdate, network.AccountID, network.ID) + if err != nil { + return fmt.Errorf("failed to get network: %w", err) + } + network.AccountSeqID = existing.AccountSeqID + + if err := transaction.SaveNetwork(ctx, network); err != nil { + return fmt.Errorf("failed to save network: %w", err) + } + return nil + }) if err != nil { - return nil, fmt.Errorf("failed to get network: %w", err) + return nil, err } m.accountManager.StoreEvent(ctx, userID, network.ID, network.AccountID, activity.NetworkUpdated, network.EventMeta()) - return network, m.store.SaveNetwork(ctx, network) + return network, nil } func (m *managerImpl) DeleteNetwork(ctx context.Context, accountID, userID, networkID string) error { diff --git a/management/server/networks/manager_test.go b/management/server/networks/manager_test.go index 6fb19d157a7..a86d7e4e652 100644 --- a/management/server/networks/manager_test.go +++ b/management/server/networks/manager_test.go @@ -252,3 +252,73 @@ func Test_UpdateNetworkFailsWithPermissionDenied(t *testing.T) { require.Error(t, err) require.Nil(t, updatedNetwork) } + +// Test_CreateNetworkAllocatesSeqID verifies that CreateNetwork sets a +// non-zero AccountSeqID on the persisted network (allocated through the +// account_seq_counters table). +func Test_CreateNetworkAllocatesSeqID(t *testing.T) { + ctx := context.Background() + const accountID = "testAccountId" + const userID = "testAdminId" + + s, cleanUp, err := store.NewTestStoreFromSQL(ctx, "../testdata/networks.sql", t.TempDir()) + require.NoError(t, err) + t.Cleanup(cleanUp) + + am := mock_server.MockAccountManager{} + permissionsManager := permissions.NewManager(s) + groupsManager := groups.NewManagerMock() + routerManager := routers.NewManagerMock() + resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am, nil) + manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am) + + created, err := manager.CreateNetwork(ctx, userID, &types.Network{ + AccountID: accountID, + Name: "seq-allocation-test", + }) + require.NoError(t, err) + require.NotZero(t, created.AccountSeqID, "CreateNetwork must allocate a non-zero AccountSeqID") +} + +// Test_UpdateNetworkPreservesSeqID verifies UpdateNetwork does not reset +// AccountSeqID even when the caller passes a zero value (the shape REST +// handlers produce because the field is `json:"-"`). +func Test_UpdateNetworkPreservesSeqID(t *testing.T) { + ctx := context.Background() + const accountID = "testAccountId" + const userID = "testAdminId" + + s, cleanUp, err := store.NewTestStoreFromSQL(ctx, "../testdata/networks.sql", t.TempDir()) + require.NoError(t, err) + t.Cleanup(cleanUp) + + am := mock_server.MockAccountManager{} + permissionsManager := permissions.NewManager(s) + groupsManager := groups.NewManagerMock() + routerManager := routers.NewManagerMock() + resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am, nil) + manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am) + + created, err := manager.CreateNetwork(ctx, userID, &types.Network{ + AccountID: accountID, + Name: "seq-preserve-original", + }) + require.NoError(t, err) + originalSeq := created.AccountSeqID + require.NotZero(t, originalSeq) + + update := &types.Network{ + AccountID: accountID, + ID: created.ID, + Name: "seq-preserve-renamed", + } + require.Zero(t, update.AccountSeqID, "incoming struct must mirror an HTTP handler shape") + + _, err = manager.UpdateNetwork(ctx, userID, update) + require.NoError(t, err) + + got, err := manager.GetNetwork(ctx, accountID, userID, created.ID) + require.NoError(t, err) + require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must survive UpdateNetwork") + require.Equal(t, "seq-preserve-renamed", got.Name) +} diff --git a/management/server/networks/types/network.go b/management/server/networks/types/network.go index 69d596f8b8b..0c6920879c0 100644 --- a/management/server/networks/types/network.go +++ b/management/server/networks/types/network.go @@ -7,12 +7,24 @@ import ( ) type Network struct { - ID string `gorm:"primaryKey"` - AccountID string `gorm:"index"` + ID string `gorm:"primaryKey"` + AccountID string `gorm:"index"` + + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_networks_account_seq_id;not null;default:0"` + Name string Description string } +// HasSeqID reports whether the network has been persisted long enough to have +// a per-account sequence id allocated. Wire encoders that key off AccountSeqID +// must skip networks that return false here. +func (n *Network) HasSeqID() bool { + return n != nil && n.AccountSeqID != 0 +} + func NewNetwork(accountId, name, description string) *Network { return &Network{ ID: xid.New().String(), @@ -41,13 +53,14 @@ func (n *Network) FromAPIRequest(req *api.NetworkRequest) { } } -// Copy returns a copy of a posture checks. +// Copy returns a copy of a network. func (n *Network) Copy() *Network { return &Network{ - ID: n.ID, - AccountID: n.AccountID, - Name: n.Name, - Description: n.Description, + ID: n.ID, + AccountID: n.AccountID, + AccountSeqID: n.AccountSeqID, + Name: n.Name, + Description: n.Description, } } diff --git a/management/server/posture/checks.go b/management/server/posture/checks.go index f0bbbc32e10..a79cf6898a3 100644 --- a/management/server/posture/checks.go +++ b/management/server/posture/checks.go @@ -47,10 +47,21 @@ type Checks struct { // AccountID is a reference to the Account that this object belongs AccountID string `json:"-" gorm:"index"` + // AccountSeqID is a per-account monotonically increasing identifier used as the + // compact wire id when sending NetworkMap components to capable peers. + AccountSeqID uint32 `json:"-" gorm:"index:idx_posture_checks_account_seq_id;not null;default:0"` + // Checks is a set of objects that perform the actual checks Checks ChecksDefinition `gorm:"serializer:json"` } +// HasSeqID reports whether the posture check has been persisted long enough +// to have a per-account sequence id allocated. Wire encoders that key off +// AccountSeqID must skip checks that return false here. +func (pc *Checks) HasSeqID() bool { + return pc != nil && pc.AccountSeqID != 0 +} + // ChecksDefinition contains definition of actual check type ChecksDefinition struct { NBVersionCheck *NBVersionCheck `json:",omitempty"` @@ -121,11 +132,12 @@ func (*Checks) TableName() string { // Copy returns a copy of a posture checks. func (pc *Checks) Copy() *Checks { checks := &Checks{ - ID: pc.ID, - Name: pc.Name, - Description: pc.Description, - AccountID: pc.AccountID, - Checks: pc.Checks.Copy(), + ID: pc.ID, + Name: pc.Name, + Description: pc.Description, + AccountID: pc.AccountID, + AccountSeqID: pc.AccountSeqID, + Checks: pc.Checks.Copy(), } return checks } diff --git a/management/server/posture_checks.go b/management/server/posture_checks.go index 1e3ce4b8abc..5f548f2de9d 100644 --- a/management/server/posture_checks.go +++ b/management/server/posture_checks.go @@ -51,12 +51,24 @@ func (am *DefaultAccountManager) SavePostureChecks(ctx context.Context, accountI } if isUpdate { + existing, err := transaction.GetPostureChecksByID(ctx, store.LockingStrengthNone, accountID, postureChecks.ID) + if err != nil { + return err + } + postureChecks.AccountSeqID = existing.AccountSeqID + updateAccountPeers, err = arePostureCheckChangesAffectPeers(ctx, transaction, accountID, postureChecks.ID) if err != nil { return err } action = activity.PostureCheckUpdated + } else { + seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPostureCheck) + if err != nil { + return err + } + postureChecks.AccountSeqID = seq } postureChecks.AccountID = accountID diff --git a/management/server/posture_checks_test.go b/management/server/posture_checks_test.go index 394f0d89654..ca9bef38948 100644 --- a/management/server/posture_checks_test.go +++ b/management/server/posture_checks_test.go @@ -563,3 +563,61 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) { assert.False(t, result) }) } + +// TestSavePostureChecks_AllocatesSeqIDOnCreate verifies that the create path +// (no incoming ID) allocates a non-zero AccountSeqID via the +// account_seq_counters table. +func TestSavePostureChecks_AllocatesSeqIDOnCreate(t *testing.T) { + am, _, err := createManager(t) + require.NoError(t, err) + + account, err := initTestPostureChecksAccount(am) + require.NoError(t, err) + + created, err := am.SavePostureChecks(context.Background(), account.Id, adminUserID, &posture.Checks{ + Name: "seq-allocation-test", + Checks: posture.ChecksDefinition{ + NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.26.0"}, + }, + }, true) + require.NoError(t, err) + require.NotZero(t, created.AccountSeqID, "SavePostureChecks on create must allocate a non-zero AccountSeqID") +} + +// TestSavePostureChecks_PreservesSeqIDOnUpdate verifies the update path does +// not reset AccountSeqID even when the caller passes a zero value (REST +// handler shape, because the field is `json:"-"`). +func TestSavePostureChecks_PreservesSeqIDOnUpdate(t *testing.T) { + am, _, err := createManager(t) + require.NoError(t, err) + + account, err := initTestPostureChecksAccount(am) + require.NoError(t, err) + + created, err := am.SavePostureChecks(context.Background(), account.Id, adminUserID, &posture.Checks{ + Name: "seq-preserve-original", + Checks: posture.ChecksDefinition{ + NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.26.0"}, + }, + }, true) + require.NoError(t, err) + originalSeq := created.AccountSeqID + require.NotZero(t, originalSeq) + + update := &posture.Checks{ + ID: created.ID, + Name: "seq-preserve-renamed", + Checks: posture.ChecksDefinition{ + NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.27.0"}, + }, + } + require.Zero(t, update.AccountSeqID, "incoming struct must mirror an HTTP handler shape") + + _, err = am.SavePostureChecks(context.Background(), account.Id, adminUserID, update, false) + require.NoError(t, err) + + got, err := am.GetPostureChecks(context.Background(), account.Id, created.ID, adminUserID) + require.NoError(t, err) + require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must survive SavePostureChecks update") + require.Equal(t, "seq-preserve-renamed", got.Name) +} diff --git a/management/server/store/account_seq_test.go b/management/server/store/account_seq_test.go index 4a8134559f9..7ee52e4ccec 100644 --- a/management/server/store/account_seq_test.go +++ b/management/server/store/account_seq_test.go @@ -11,6 +11,8 @@ import ( nbdns "github.com/netbirdio/netbird/dns" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" + networkTypes "github.com/netbirdio/netbird/management/server/networks/types" + "github.com/netbirdio/netbird/management/server/posture" "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/route" ) @@ -208,6 +210,7 @@ func TestSaveAccount_PreservesExistingSeqIDs(t *testing.T) { nsgSeqs := make(map[string]uint32) resourceSeqs := make(map[string]uint32) routerSeqs := make(map[string]uint32) + networkSeqs := make(map[string]uint32) for _, g := range account.Groups { require.NotZero(t, g.AccountSeqID, "fixture group must have seq>0 after backfill") @@ -233,6 +236,10 @@ func TestSaveAccount_PreservesExistingSeqIDs(t *testing.T) { require.NotZero(t, nr.AccountSeqID, "fixture network_router must have seq>0") routerSeqs[nr.ID] = nr.AccountSeqID } + for _, n := range account.Networks { + require.NotZero(t, n.AccountSeqID, "fixture network must have seq>0 after backfill") + networkSeqs[n.ID] = n.AccountSeqID + } require.NoError(t, store.SaveAccount(ctx, account)) @@ -256,6 +263,9 @@ func TestSaveAccount_PreservesExistingSeqIDs(t *testing.T) { for _, nr := range after.NetworkRouters { require.Equal(t, routerSeqs[nr.ID], nr.AccountSeqID, "network_router %s seq must be preserved", nr.ID) } + for _, n := range after.Networks { + require.Equal(t, networkSeqs[n.ID], n.AccountSeqID, "network %s seq must be preserved", n.ID) + } } func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { @@ -298,6 +308,15 @@ func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { NetworkRouters: []*routerTypes.NetworkRouter{ {ID: "nrt1", AccountID: accountID, NetworkID: "net1", Peer: "peer1", Enabled: true}, }, + Networks: []*networkTypes.Network{ + {ID: "n1", AccountID: accountID, Name: "n1"}, + }, + PostureChecks: []*posture.Checks{ + {ID: "pc1", AccountID: accountID, Name: "pc1", + Checks: posture.ChecksDefinition{ + NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.26.0"}, + }}, + }, } require.NoError(t, store.SaveAccount(ctx, account)) @@ -311,6 +330,8 @@ func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { require.Len(t, after.NameServerGroups, 1) require.Len(t, after.NetworkResources, 1) require.Len(t, after.NetworkRouters, 1) + require.Len(t, after.Networks, 1) + require.Len(t, after.PostureChecks, 1) for _, g := range after.Groups { require.NotZero(t, g.AccountSeqID, "group seq must be allocated") @@ -330,6 +351,12 @@ func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { for _, nr := range after.NetworkRouters { require.NotZero(t, nr.AccountSeqID, "network_router seq must be allocated") } + for _, n := range after.Networks { + require.NotZero(t, n.AccountSeqID, "network seq must be allocated") + } + for _, pc := range after.PostureChecks { + require.NotZero(t, pc.AccountSeqID, "posture_check seq must be allocated") + } require.NoError(t, store.SaveAccount(ctx, after)) final, err := store.GetAccount(ctx, accountID) @@ -340,6 +367,20 @@ func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { for _, n := range final.NameServerGroups { require.Equal(t, after.NameServerGroups[n.ID].AccountSeqID, n.AccountSeqID, "name_server_group seq preserved on re-save") } + afterByID := map[string]uint32{} + for _, n := range after.Networks { + afterByID[n.ID] = n.AccountSeqID + } + for _, n := range final.Networks { + require.Equal(t, afterByID[n.ID], n.AccountSeqID, "network seq preserved on re-save") + } + afterPCByID := map[string]uint32{} + for _, pc := range after.PostureChecks { + afterPCByID[pc.ID] = pc.AccountSeqID + } + for _, pc := range final.PostureChecks { + require.Equal(t, afterPCByID[pc.ID], pc.AccountSeqID, "posture_check seq preserved on re-save") + } } func TestAllocateAccountSeqID_ConcurrentSameAccountEntity(t *testing.T) { diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index be0e7f2161d..855e54af864 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -3724,6 +3724,26 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account } nr.AccountSeqID = seq } + for _, n := range account.Networks { + if n == nil || n.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetwork) + if err != nil { + return err + } + n.AccountSeqID = seq + } + for _, pc := range account.PostureChecks { + if pc == nil || pc.AccountSeqID != 0 { + continue + } + seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityPostureCheck) + if err != nil { + return err + } + pc.AccountSeqID = seq + } return nil } diff --git a/management/server/store/store.go b/management/server/store/store.go index 28aa2e26452..9b4ecd8b0c3 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -545,6 +545,12 @@ func getMigrationsPostAuto(ctx context.Context) []migrationFunc { func(db *gorm.DB) error { return migration.BackfillAccountSeqIDs[dns.NameServerGroup](ctx, db, types.AccountSeqEntityNameserverGroup, "id") }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[networkTypes.Network](ctx, db, types.AccountSeqEntityNetwork, "id") + }, + func(db *gorm.DB) error { + return migration.BackfillAccountSeqIDs[posture.Checks](ctx, db, types.AccountSeqEntityPostureCheck, "id") + }, } } diff --git a/management/server/types/account_seq_counter.go b/management/server/types/account_seq_counter.go index e21f7331760..80ce74099db 100644 --- a/management/server/types/account_seq_counter.go +++ b/management/server/types/account_seq_counter.go @@ -10,6 +10,8 @@ const ( AccountSeqEntityNetworkResource AccountSeqEntity = "network_resource" AccountSeqEntityNetworkRouter AccountSeqEntity = "network_router" AccountSeqEntityNameserverGroup AccountSeqEntity = "nameserver_group" + AccountSeqEntityNetwork AccountSeqEntity = "network" + AccountSeqEntityPostureCheck AccountSeqEntity = "posture_check" ) // AccountSeqCounter tracks the next per-account integer id for a given component From 582cd700861520ac7d135d053fd1d27f1885a0fa Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 12 May 2026 16:43:32 +0200 Subject: [PATCH 08/42] client side and components on shared folder --- client/internal/engine.go | 58 +- .../shared/grpc/components_encoder.go | 179 ++- .../shared/grpc/components_encoder_test.go | 35 +- .../grpc/components_envelope_response.go | 52 +- .../internals/shared/grpc/conversion.go | 291 +--- .../internals/shared/grpc/conversion_test.go | 11 +- management/server/store/sql_store.go | 6 +- management/server/types/account.go | 9 + management/server/types/account_components.go | 41 +- .../server/types/networkmap_components.go | 11 + shared/management/client/grpc.go | 7 + shared/management/proto/management.pb.go | 1170 +++++++++++------ shared/management/proto/management.proto | 129 +- 13 files changed, 1213 insertions(+), 786 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 3bd0d462125..f63840aa11d 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -61,9 +61,11 @@ import ( cProto "github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/client/system" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/route" mgm "github.com/netbirdio/netbird/shared/management/client" "github.com/netbirdio/netbird/shared/management/domain" + nbnetworkmap "github.com/netbirdio/netbird/shared/management/networkmap" mgmProto "github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/netiputil" auth "github.com/netbirdio/netbird/shared/relay/auth/hmac" @@ -202,6 +204,13 @@ type Engine struct { // networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service networkSerial uint64 + // latestComponents is the most-recent NetworkMapComponents decoded from + // a NetworkMapEnvelope (capability=3 peers only). Held alongside the + // NetworkMap that Calculate() produced from it so Step 3 incremental + // updates have a base to apply changes against. nil for legacy-format + // peers. Guarded by syncMsgMux. + latestComponents *types.NetworkMapComponents + networkMonitor *networkmonitor.NetworkMonitor sshServer sshServer @@ -907,11 +916,45 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { return err } - nm := update.GetNetworkMap() + var ( + nm *mgmProto.NetworkMap + components *types.NetworkMapComponents + ) + if envelope := update.GetNetworkMapEnvelope(); envelope != nil { + // Components-format peer: decode the envelope back to typed + // components, run Calculate() locally, and convert to the wire + // NetworkMap shape the rest of the engine consumes. Components are + // retained so future incremental updates (Step 3) can apply deltas + // instead of doing a full reconstruction. + localKey := e.config.WgPrivateKey.PublicKey().String() + dnsName := "" + if pc := update.GetPeerConfig(); pc != nil { + // PeerConfig.Fqdn = "." — extract the + // shared domain by stripping the peer's own label prefix. Falls + // back to empty if the FQDN doesn't have the expected shape. + dnsName = extractDNSDomainFromFQDN(pc.GetFqdn()) + } + result, err := nbnetworkmap.EnvelopeToNetworkMap(e.ctx, envelope, localKey, dnsName) + if err != nil { + return fmt.Errorf("decode network map envelope: %w", err) + } + nm = result.NetworkMap + components = result.Components + } else { + nm = update.GetNetworkMap() + } if nm == nil { return nil } + // Only retain the components view when the server sent the envelope + // path. A legacy proto.NetworkMap means components == nil; writing it + // here would clobber a previously-cached snapshot, breaking the Step 3 + // incremental-delta base on a future envelope sync. + if components != nil { + e.latestComponents = components + } + // Persist sync response under the dedicated lock (syncRespMux), not under syncMsgMux. // Read the storage-enabled flag under the syncRespMux too. e.syncRespMux.RLock() @@ -937,6 +980,19 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { return nil } +// extractDNSDomainFromFQDN returns the trailing dotted domain part of the +// receiving peer's FQDN — the same value the management server fills as +// dnsName when it builds the legacy NetworkMap. "peer42.netbird.cloud" → +// "netbird.cloud". An empty string is returned for unrecognized formats. +func extractDNSDomainFromFQDN(fqdn string) string { + for i := 0; i < len(fqdn); i++ { + if fqdn[i] == '.' && i+1 < len(fqdn) { + return fqdn[i+1:] + } + } + return "" +} + func (e *Engine) handleRelayUpdate(update *mgmProto.RelayConfig) error { if update != nil { // when we receive token we expect valid address list too diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index fa21a58c122..e9c01d56b7d 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -10,6 +10,7 @@ import ( nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/types" nbroute "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/shared/management/networkmap" "github.com/netbirdio/netbird/shared/management/proto" ) @@ -27,6 +28,10 @@ type ComponentsEnvelopeInput struct { PeerConfig *proto.PeerConfig DNSDomain string DNSForwarderPort int64 + // UserIDClaim is the OIDC claim name the client should embed in + // SshAuth.UserIDClaim when reconstructing the NetworkMap. Empty value + // is OK — client treats empty as "no SshAuth to build". + UserIDClaim string // ProxyPatch carries pre-expanded NetworkMap fragments injected by // external controllers (BYOP/port-forwarding). Nil when no proxy data // is present; encoder skips the field in that case. @@ -64,6 +69,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel PeerConfig: in.PeerConfig, DnsDomain: in.DNSDomain, DnsForwarderPort: in.DNSForwarderPort, + UserIdClaim: in.UserIDClaim, AccountSettings: &proto.AccountSettingsCompact{}, ProxyPatch: in.ProxyPatch, }, @@ -95,6 +101,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel Network: toAccountNetwork(c.Network), AccountSettings: toAccountSettingsCompact(c.AccountSettings), DnsForwarderPort: in.DNSForwarderPort, + UserIdClaim: in.UserIDClaim, ProxyPatch: in.ProxyPatch, DnsSettings: enc.encodeDNSSettings(c.DNSSettings), DnsDomain: in.DNSDomain, @@ -108,9 +115,9 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel NameserverGroups: enc.encodeNameServerGroups(c.NameServerGroups), AllDnsRecords: encodeSimpleRecords(c.AllDNSRecords), AccountZones: encodeCustomZones(c.AccountZones), - NetworkResources: encodeNetworkResources(c.NetworkResources), + NetworkResources: enc.encodeNetworkResources(c.NetworkResources), RoutersMap: enc.encodeRoutersMap(c.RoutersMap), - ResourcePoliciesMap: encodeResourcePoliciesMap(c.ResourcePoliciesMap, policyToIdxs), + ResourcePoliciesMap: enc.encodeResourcePoliciesMap(c.ResourcePoliciesMap, policyToIdxs), GroupIdToUserIds: enc.encodeGroupIDToUserIDs(c.GroupIDToUserIDs), AllowedUserIds: stringSetToSlice(c.AllowedUserIDs), PostureFailedPeers: enc.encodePostureFailedPeers(c.PostureFailedPeers), @@ -146,8 +153,7 @@ func newComponentEncoder(c *types.NetworkMapComponents) *componentEncoder { components: c, peerOrder: make(map[string]uint32, len(c.Peers)), peers: make([]*proto.PeerCompact, 0, len(c.Peers)), - agentVersionOrder: map[string]uint32{"": 0}, - agentVersions: []string{""}, + agentVersionOrder: make(map[string]uint32), } } @@ -174,6 +180,21 @@ func (e *componentEncoder) agentVersionIndex(v string) uint32 { if idx, ok := e.agentVersionOrder[v]; ok { return idx } + // Lazy-initialise the table with "" at index 0 so the empty string + // stays interchangeable with proto3's default uint32=0 — peers without + // a WtVersion don't force the table to materialise. + if v == "" { + idx := uint32(len(e.agentVersions)) + if idx == 0 { + e.agentVersions = append(e.agentVersions, "") + } + e.agentVersionOrder[""] = idx + return idx + } + if len(e.agentVersions) == 0 { + e.agentVersions = append(e.agentVersions, "") + e.agentVersionOrder[""] = 0 + } idx := uint32(len(e.agentVersions)) e.agentVersionOrder[v] = idx e.agentVersions = append(e.agentVersions, v) @@ -244,14 +265,19 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) ([]*proto.Po continue } pc := &proto.PolicyCompact{ - Id: pol.AccountSeqID, - Action: getProtoAction(string(r.Action)), - Protocol: getProtoProtocol(string(r.Protocol)), - Bidirectional: r.Bidirectional, - Ports: portsToUint32(r.Ports), - PortRanges: portRangesToProto(r.PortRanges), - SourceGroupIds: make([]uint32, 0, len(r.Sources)), - DestinationGroupIds: make([]uint32, 0, len(r.Destinations)), + Id: pol.AccountSeqID, + Action: networkmap.GetProtoAction(string(r.Action)), + Protocol: networkmap.GetProtoProtocol(string(r.Protocol)), + Bidirectional: r.Bidirectional, + Ports: portsToUint32(r.Ports), + PortRanges: portRangesToProto(r.PortRanges), + SourceGroupIds: make([]uint32, 0, len(r.Sources)), + DestinationGroupIds: make([]uint32, 0, len(r.Destinations)), + AuthorizedUser: r.AuthorizedUser, + AuthorizedGroups: e.encodeAuthorizedGroups(r.AuthorizedGroups), + SourceResource: e.resourceToProto(r.SourceResource), + DestinationResource: e.resourceToProto(r.DestinationResource), + SourcePostureCheckSeqIds: e.postureCheckSeqs(pol.SourcePostureChecks), } for _, gid := range r.Sources { if seq, ok := e.groupSeq(gid); ok { @@ -277,6 +303,11 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) ([]*proto.Po // from the wire and the client's resource-policy lookup would come back // empty. func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*types.Policy) []*types.Policy { + // Fast path: non-router peers have no resource-only policies, so the + // "union" is identical to `policies`. Skip the dedup map allocation. + if len(resourcePolicies) == 0 { + return policies + } seen := make(map[*types.Policy]struct{}, len(policies)) out := make([]*types.Policy, 0, len(policies)) for _, p := range policies { @@ -304,6 +335,25 @@ func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*type return out } +// encodeAuthorizedGroups translates rule.AuthorizedGroups (map keyed by +// group xid → local-user names) to the wire form (map keyed by group +// account_seq_id → UserNameList). Groups without a seq id are dropped — +// matches how source/destination group references handle the same case. +func (e *componentEncoder) encodeAuthorizedGroups(m map[string][]string) map[uint32]*proto.UserNameList { + if len(m) == 0 { + return nil + } + out := make(map[uint32]*proto.UserNameList, len(m)) + for groupID, names := range m { + seq, ok := e.groupSeq(groupID) + if !ok { + continue + } + out[seq] = &proto.UserNameList{Names: append([]string(nil), names...)} + } + return out +} + func (e *componentEncoder) groupSeq(groupID string) (uint32, bool) { g, ok := e.components.Groups[groupID] if !ok || !g.HasSeqID() { @@ -312,6 +362,56 @@ func (e *componentEncoder) groupSeq(groupID string) (uint32, bool) { return g.AccountSeqID, true } +// resourceToProto translates types.Resource for the wire. For peer-typed +// resources the peer id is converted to a peer index into the envelope's +// peers array. For other resource types only the type string is shipped +// today (Calculate's resource-typed rule path consults SourceResource only +// for "peer" — other types fall through to group-based lookup). +func (e *componentEncoder) resourceToProto(r types.Resource) *proto.ResourceCompact { + if r.ID == "" && r.Type == "" { + return nil + } + out := &proto.ResourceCompact{Type: string(r.Type)} + if r.Type == types.ResourceTypePeer && r.ID != "" { + if idx, ok := e.peerOrder[r.ID]; ok { + out.PeerIndexSet = true + out.PeerIndex = idx + } + } + return out +} + +// postureCheckSeqs translates a slice of posture-check xids to their +// per-account integer ids using the NetworkMapComponents.PostureCheckXIDToSeq +// lookup. Unresolvable xids are silently dropped — matches how group/peer +// references handle the same case. +func (e *componentEncoder) postureCheckSeqs(xids []string) []uint32 { + if len(xids) == 0 || len(e.components.PostureCheckXIDToSeq) == 0 { + return nil + } + out := make([]uint32, 0, len(xids)) + for _, xid := range xids { + if seq, ok := e.components.PostureCheckXIDToSeq[xid]; ok { + out = append(out, seq) + } + } + return out +} + +// networkSeq translates a Network xid to its per-account integer id using +// the NetworkMapComponents.NetworkXIDToSeq lookup. Returns (0,false) when +// the xid isn't known — callers decide whether to skip the parent record. +func (e *componentEncoder) networkSeq(xid string) (uint32, bool) { + if xid == "" { + return 0, false + } + seq, ok := e.components.NetworkXIDToSeq[xid] + if !ok || seq == 0 { + return 0, false + } + return seq, true +} + func (e *componentEncoder) encodeDNSSettings(s *types.DNSSettings) *proto.DNSSettingsCompact { if s == nil || len(s.DisabledManagementGroups) == 0 { return nil @@ -451,7 +551,7 @@ func encodeCustomZones(zones []nbdns.CustomZone) []*proto.CustomZone { return out } -func encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto.NetworkResourceRaw { +func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto.NetworkResourceRaw { if len(resources) == 0 { return nil } @@ -462,7 +562,6 @@ func encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto } entry := &proto.NetworkResourceRaw{ Id: r.AccountSeqID, - NetworkId: r.NetworkID, Name: r.Name, Description: r.Description, Type: string(r.Type), @@ -470,6 +569,9 @@ func encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto DomainValue: r.Domain, Enabled: r.Enabled, } + if seq, ok := e.networkSeq(r.NetworkID); ok { + entry.NetworkSeq = seq + } if r.Prefix.IsValid() { entry.PrefixCidr = r.Prefix.String() } @@ -478,15 +580,19 @@ func encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto return out } -func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[string]*proto.NetworkRouterList { +func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[uint32]*proto.NetworkRouterList { if len(routersMap) == 0 { return nil } - out := make(map[string]*proto.NetworkRouterList, len(routersMap)) - for networkID, routers := range routersMap { + out := make(map[uint32]*proto.NetworkRouterList, len(routersMap)) + for networkXID, routers := range routersMap { if len(routers) == 0 { continue } + netSeq, ok := e.networkSeq(networkXID) + if !ok { + continue + } entries := make([]*proto.NetworkRouterEntry, 0, len(routers)) for peerID, r := range routers { if r == nil { @@ -505,17 +611,30 @@ func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*ro } entries = append(entries, entry) } - out[networkID] = &proto.NetworkRouterList{Entries: entries} + out[netSeq] = &proto.NetworkRouterList{Entries: entries} } return out } -func encodeResourcePoliciesMap(rpm map[string][]*types.Policy, policyToIdxs map[*types.Policy][]uint32) map[string]*proto.PolicyIndexes { +func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*types.Policy, policyToIdxs map[*types.Policy][]uint32) map[uint32]*proto.PolicyIndexes { if len(rpm) == 0 { return nil } - out := make(map[string]*proto.PolicyIndexes, len(rpm)) - for resourceID, policies := range rpm { + // resourceXIDToSeq is local to one encode — built from components.NetworkResources + // (small slice). Network resources without seq id are dropped, matching how + // other components-without-seq are silently filtered. + resourceXIDToSeq := make(map[string]uint32, len(e.components.NetworkResources)) + for _, r := range e.components.NetworkResources { + if r != nil && r.AccountSeqID != 0 { + resourceXIDToSeq[r.ID] = r.AccountSeqID + } + } + out := make(map[uint32]*proto.PolicyIndexes, len(rpm)) + for resourceXID, policies := range rpm { + seq, ok := resourceXIDToSeq[resourceXID] + if !ok { + continue + } idxs := make([]uint32, 0, len(policies)*2) for _, pol := range policies { idxs = append(idxs, policyToIdxs[pol]...) @@ -523,7 +642,7 @@ func encodeResourcePoliciesMap(rpm map[string][]*types.Policy, policyToIdxs map[ if len(idxs) == 0 { continue } - out[resourceID] = &proto.PolicyIndexes{Indexes: idxs} + out[seq] = &proto.PolicyIndexes{Indexes: idxs} } return out } @@ -554,12 +673,16 @@ func stringSetToSlice(s map[string]struct{}) []string { return out } -func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[string]*proto.PeerIndexSet { +func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[uint32]*proto.PeerIndexSet { if len(m) == 0 { return nil } - out := make(map[string]*proto.PeerIndexSet, len(m)) - for checkID, failedPeerIDs := range m { + out := make(map[uint32]*proto.PeerIndexSet, len(m)) + for checkXID, failedPeerIDs := range m { + seq, ok := e.components.PostureCheckXIDToSeq[checkXID] + if !ok || seq == 0 { + continue + } idxs := make([]uint32, 0, len(failedPeerIDs)) for peerID := range failedPeerIDs { if idx, ok := e.peerOrder[peerID]; ok { @@ -569,7 +692,7 @@ func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]stru if len(idxs) == 0 { continue } - out[checkID] = &proto.PeerIndexSet{PeerIndexes: idxs} + out[seq] = &proto.PeerIndexSet{PeerIndexes: idxs} } return out } @@ -614,6 +737,10 @@ func toPeerCompact(p *nbpeer.Peer, agentVersionIdx uint32) *proto.PeerCompact { AgentVersionIdx: agentVersionIdx, AddedWithSsoLogin: p.UserID != "", LoginExpirationEnabled: p.LoginExpirationEnabled, + SshEnabled: p.SSHEnabled, + SupportsIpv6: p.SupportsIPv6(), + SupportsSourcePrefixes: p.SupportsSourcePrefixes(), + ServerSshAllowed: p.Meta.Flags.ServerSSHAllowed, } if p.LastLogin != nil { pc.LastLoginUnixNano = p.LastLogin.UnixNano() diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go index bcb153b541b..de859b100d1 100644 --- a/management/internals/shared/grpc/components_encoder_test.go +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -15,6 +15,7 @@ import ( goproto "google.golang.org/protobuf/proto" nbdns "github.com/netbirdio/netbird/dns" + resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/types" @@ -637,6 +638,11 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing c.ResourcePoliciesMap = map[string][]*types.Policy{ "resource-x": {c.Policies[0], resourceOnlyPolicy}, // shared + resource-only } + // Resource must appear in components.NetworkResources with a seq id — + // encoder uses that to translate the xid map key to uint32. + c.NetworkResources = []*resourceTypes.NetworkResource{ + {ID: "resource-x", AccountSeqID: 77, Name: "res-x", Enabled: true}, + } full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() @@ -651,8 +657,8 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing require.Contains(t, policyByID, uint32(10), "original peer-traffic policy id 10") require.Contains(t, policyByID, uint32(99), "resource-only policy id 99") - require.Contains(t, full.ResourcePoliciesMap, "resource-x") - idxs := full.ResourcePoliciesMap["resource-x"].Indexes + require.Contains(t, full.ResourcePoliciesMap, uint32(77)) + idxs := full.ResourcePoliciesMap[77].Indexes require.Len(t, idxs, 2) assert.ElementsMatch(t, []uint32{policyIdxByID[10], policyIdxByID[99]}, idxs, "resource policies map must reference both wire policy indexes") @@ -665,7 +671,7 @@ func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) { NameServers: []nbdns.NameServer{{ IP: netip.MustParseAddr("8.8.8.8"), NSType: nbdns.UDPNameServerType, Port: 53, }}, - Groups: []string{"group-src", "group-not-persisted"}, + Groups: []string{"group-src", "group-not-persisted"}, Primary: true, Enabled: true, Domains: []string{"corp.example"}, }} @@ -685,6 +691,7 @@ func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) { func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { c := newTestComponents() + c.PostureCheckXIDToSeq = map[string]uint32{"check-1": 33} c.PostureFailedPeers = map[string]map[string]struct{}{ "check-1": { "peer-a": {}, @@ -695,13 +702,14 @@ func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.PostureFailedPeers, "check-1") - idxs := full.PostureFailedPeers["check-1"].PeerIndexes + require.Contains(t, full.PostureFailedPeers, uint32(33)) + idxs := full.PostureFailedPeers[33].PeerIndexes assert.Len(t, idxs, 2, "missing peer is silently dropped (filterPostureFailedPeers guarantees presence in real data)") } func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { c := newTestComponents() + c.NetworkXIDToSeq = map[string]uint32{"net-1": 5} c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ "net-1": { "peer-c": { @@ -713,8 +721,8 @@ func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.RoutersMap, "net-1") - entries := full.RoutersMap["net-1"].Entries + require.Contains(t, full.RoutersMap, uint32(5)) + entries := full.RoutersMap[5].Entries require.Len(t, entries, 1) e := entries[0] assert.EqualValues(t, 200, e.Id) @@ -737,15 +745,16 @@ func TestEncodeNetworkMapEnvelope_RouterPeerNotInComponentsPeers(t *testing.T) { DNSLabel: "peerc", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, } c.RouterPeers = map[string]*nbpeer.Peer{"peer-c": routerPeer} + c.NetworkXIDToSeq = map[string]uint32{"net-1": 5} c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ "net-1": {"peer-c": {ID: "r-1", AccountSeqID: 1, Peer: "peer-c", Enabled: true}}, } full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.RoutersMap, "net-1") - require.Len(t, full.RoutersMap["net-1"].Entries, 1) - e := full.RoutersMap["net-1"].Entries[0] + require.Contains(t, full.RoutersMap, uint32(5)) + require.Len(t, full.RoutersMap[5].Entries, 1) + e := full.RoutersMap[5].Entries[0] assert.True(t, e.PeerIndexSet, "router peer must be indexed even when not in c.Peers") } @@ -766,9 +775,9 @@ func TestEncodeNetworkMapEnvelope_DNSSettingsFiltersUnpersistedGroups(t *testing func TestEncodeNetworkMapEnvelope_GroupIDToUserIDs(t *testing.T) { c := newTestComponents() c.GroupIDToUserIDs = map[string][]string{ - "group-src": {"user-1", "user-2"}, - "group-no-seq": {"user-3"}, // group not persisted → drop - "group-missing": {"user-4"}, // group not in components → drop + "group-src": {"user-1", "user-2"}, + "group-no-seq": {"user-3"}, // group not persisted → drop + "group-missing": {"user-4"}, // group not in components → drop } c.Groups["group-no-seq"] = &types.Group{ID: "group-no-seq", AccountSeqID: 0} diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index 12715357fa2..dfd7b5ad491 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -5,10 +5,12 @@ import ( integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" + "github.com/netbirdio/netbird/client/ssh/auth" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/posture" "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/shared/management/networkmap" "github.com/netbirdio/netbird/shared/management/proto" ) @@ -41,17 +43,23 @@ func ToComponentSyncResponse( dnsFwdPort int64, ) *proto.SyncResponse { network := networkOrZero(components) - enableSSH := computeSSHEnabledForPeer(components, peer.ID) + enableSSH := computeSSHEnabledForPeer(components, peer) peerConfig := toPeerConfig(peer, network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH) includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid() useSourcePrefixes := peer.SupportsSourcePrefixes() + userIDClaim := auth.DefaultUserIDClaim + if httpConfig != nil && httpConfig.AuthUserIDClaim != "" { + userIDClaim = httpConfig.AuthUserIDClaim + } + envelope := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ Components: components, PeerConfig: peerConfig, DNSDomain: dnsName, DNSForwarderPort: dnsFwdPort, + UserIDClaim: userIDClaim, ProxyPatch: toProxyPatch(proxyPatch, dnsName, includeIPv6, useSourcePrefixes), }) @@ -98,11 +106,11 @@ func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePr } patch := &proto.ProxyPatch{ - Peers: appendRemotePeerConfig(nil, nm.Peers, dnsName, includeIPv6), - OfflinePeers: appendRemotePeerConfig(nil, nm.OfflinePeers, dnsName, includeIPv6), - FirewallRules: toProtocolFirewallRules(nm.FirewallRules, includeIPv6, useSourcePrefixes), - Routes: toProtocolRoutes(nm.Routes), - RouteFirewallRules: toProtocolRoutesFirewallRules(nm.RoutesFirewallRules), + Peers: networkmap.AppendRemotePeerConfig(nil, nm.Peers, dnsName, includeIPv6), + OfflinePeers: networkmap.AppendRemotePeerConfig(nil, nm.OfflinePeers, dnsName, includeIPv6), + FirewallRules: networkmap.ToProtocolFirewallRules(nm.FirewallRules, includeIPv6, useSourcePrefixes), + Routes: networkmap.ToProtocolRoutes(nm.Routes), + RouteFirewallRules: networkmap.ToProtocolRoutesFirewallRules(nm.RoutesFirewallRules), } if len(nm.ForwardingRules) > 0 { patch.ForwardingRules = make([]*proto.ForwardingRule, 0, len(nm.ForwardingRules)) @@ -120,12 +128,23 @@ func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePr // that's the destination of an SSH-enabling policy without having // peer.SSHEnabled set locally. // -// Cheaper than running Calculate() because we ignore peer-pair expansion — -// only the "any matched policy with NetbirdSSH protocol" check is needed. +// Mirrors the two activation paths in Calculate() (`networkmap_components.go` +// `getPeerConnectionResources`): +// 1. Explicit: rule.Protocol == NetbirdSSH and peer is in the rule's +// destinations. +// 2. Legacy implicit: rule covers TCP/22 or TCP/22022 (or ALL), peer is in +// destinations, AND the peer has SSHEnabled set locally — this is the +// "allow-all/TCP-22 implies SSH activation for SSH-capable peers" path. +// // The full SSH AuthorizedUsers map is still produced by the client when it // runs Calculate() over the envelope. -func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peerID string) bool { - if c == nil { +func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peer *nbpeer.Peer) bool { + if c == nil || peer == nil { + return false + } + // Mirror Calculate's `getAllPeersFromGroups` invariant: target peer must + // exist in c.Peers, otherwise no rule applies to it. + if _, ok := c.Peers[peer.ID]; !ok { return false } for _, policy := range c.Policies { @@ -136,10 +155,13 @@ func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peerID string) bool if rule == nil || !rule.Enabled { continue } - if rule.Protocol != types.PolicyRuleProtocolNetbirdSSH { + if !peerInDestinations(c, rule, peer.ID) { continue } - if peerInDestinations(c, rule, peerID) { + if rule.Protocol == types.PolicyRuleProtocolNetbirdSSH { + return true + } + if peer.SSHEnabled && types.PolicyRuleImpliesLegacySSH(rule) { return true } } @@ -148,9 +170,11 @@ func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peerID string) bool } // peerInDestinations reports whether peerID is in any of rule.Destinations' -// groups (or matches DestinationResource if used). +// groups (or matches DestinationResource if it's a peer-typed resource — +// for non-peer types Calculate falls through to group lookup, so we mirror +// that exactly to avoid silent divergence). func peerInDestinations(c *types.NetworkMapComponents, rule *types.PolicyRule, peerID string) bool { - if rule.DestinationResource.ID != "" { + if rule.DestinationResource.Type == types.ResourceTypePeer && rule.DestinationResource.ID != "" { return rule.DestinationResource.ID == peerID } for _, groupID := range rule.Destinations { diff --git a/management/internals/shared/grpc/conversion.go b/management/internals/shared/grpc/conversion.go index 12402b420e8..efa08a071f1 100644 --- a/management/internals/shared/grpc/conversion.go +++ b/management/internals/shared/grpc/conversion.go @@ -7,23 +7,18 @@ import ( "net/url" "strings" - log "github.com/sirupsen/logrus" - goproto "google.golang.org/protobuf/proto" - integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" "github.com/netbirdio/netbird/client/ssh/auth" - nbdns "github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/management/internals/controllers/network_map/controller/cache" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/posture" "github.com/netbirdio/netbird/management/server/types" - nbroute "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/shared/management/networkmap" "github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/netiputil" - "github.com/netbirdio/netbird/shared/sshauth" ) func toNetbirdConfig(config *nbconfig.Config, turnCredentials *Token, relayToken *Token, extraSettings *types.ExtraSettings) *proto.NetbirdConfig { @@ -138,8 +133,8 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, settings, httpConfig, deviceFlowConfig, networkMap.EnableSSH), NetworkMap: &proto.NetworkMap{ Serial: networkMap.Network.CurrentSerial(), - Routes: toProtocolRoutes(networkMap.Routes), - DNSConfig: toProtocolDNSConfig(networkMap.DNSConfig, dnsCache, dnsFwdPort), + Routes: networkmap.ToProtocolRoutes(networkMap.Routes), + DNSConfig: networkmap.ToProtocolDNSConfig(networkMap.DNSConfig, dnsCache, dnsFwdPort), PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, settings, httpConfig, deviceFlowConfig, networkMap.EnableSSH), }, Checks: toProtocolChecks(ctx, checks), @@ -152,19 +147,19 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb response.NetworkMap.PeerConfig = response.PeerConfig remotePeers := make([]*proto.RemotePeerConfig, 0, len(networkMap.Peers)+len(networkMap.OfflinePeers)) - remotePeers = appendRemotePeerConfig(remotePeers, networkMap.Peers, dnsName, includeIPv6) + remotePeers = networkmap.AppendRemotePeerConfig(remotePeers, networkMap.Peers, dnsName, includeIPv6) response.RemotePeers = remotePeers response.NetworkMap.RemotePeers = remotePeers response.RemotePeersIsEmpty = len(remotePeers) == 0 response.NetworkMap.RemotePeersIsEmpty = response.RemotePeersIsEmpty - response.NetworkMap.OfflinePeers = appendRemotePeerConfig(nil, networkMap.OfflinePeers, dnsName, includeIPv6) + response.NetworkMap.OfflinePeers = networkmap.AppendRemotePeerConfig(nil, networkMap.OfflinePeers, dnsName, includeIPv6) - firewallRules := toProtocolFirewallRules(networkMap.FirewallRules, includeIPv6, useSourcePrefixes) + firewallRules := networkmap.ToProtocolFirewallRules(networkMap.FirewallRules, includeIPv6, useSourcePrefixes) response.NetworkMap.FirewallRules = firewallRules response.NetworkMap.FirewallRulesIsEmpty = len(firewallRules) == 0 - routesFirewallRules := toProtocolRoutesFirewallRules(networkMap.RoutesFirewallRules) + routesFirewallRules := networkmap.ToProtocolRoutesFirewallRules(networkMap.RoutesFirewallRules) response.NetworkMap.RoutesFirewallRules = routesFirewallRules response.NetworkMap.RoutesFirewallRulesIsEmpty = len(routesFirewallRules) == 0 @@ -177,7 +172,7 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb } if networkMap.AuthorizedUsers != nil { - hashedUsers, machineUsers := buildAuthorizedUsersProto(ctx, networkMap.AuthorizedUsers) + hashedUsers, machineUsers := networkmap.BuildAuthorizedUsersProto(ctx, networkMap.AuthorizedUsers) userIDClaim := auth.DefaultUserIDClaim if httpConfig != nil && httpConfig.AuthUserIDClaim != "" { userIDClaim = httpConfig.AuthUserIDClaim @@ -188,78 +183,6 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb return response } -func buildAuthorizedUsersProto(ctx context.Context, authorizedUsers map[string]map[string]struct{}) ([][]byte, map[string]*proto.MachineUserIndexes) { - userIDToIndex := make(map[string]uint32) - var hashedUsers [][]byte - machineUsers := make(map[string]*proto.MachineUserIndexes, len(authorizedUsers)) - - for machineUser, users := range authorizedUsers { - indexes := make([]uint32, 0, len(users)) - for userID := range users { - idx, exists := userIDToIndex[userID] - if !exists { - hash, err := sshauth.HashUserID(userID) - if err != nil { - log.WithContext(ctx).Errorf("failed to hash user id %s: %v", userID, err) - continue - } - idx = uint32(len(hashedUsers)) - userIDToIndex[userID] = idx - hashedUsers = append(hashedUsers, hash[:]) - } - indexes = append(indexes, idx) - } - machineUsers[machineUser] = &proto.MachineUserIndexes{Indexes: indexes} - } - - return hashedUsers, machineUsers -} - -func appendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*nbpeer.Peer, dnsName string, includeIPv6 bool) []*proto.RemotePeerConfig { - for _, rPeer := range peers { - allowedIPs := []string{rPeer.IP.String() + "/32"} - if includeIPv6 && rPeer.IPv6.IsValid() { - allowedIPs = append(allowedIPs, rPeer.IPv6.String()+"/128") - } - dst = append(dst, &proto.RemotePeerConfig{ - WgPubKey: rPeer.Key, - AllowedIps: allowedIPs, - SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)}, - Fqdn: rPeer.FQDN(dnsName), - AgentVersion: rPeer.Meta.WtVersion, - }) - } - return dst -} - -// toProtocolDNSConfig converts nbdns.Config to proto.DNSConfig using the cache -func toProtocolDNSConfig(update nbdns.Config, cache *cache.DNSConfigCache, forwardPort int64) *proto.DNSConfig { - protoUpdate := &proto.DNSConfig{ - ServiceEnable: update.ServiceEnable, - CustomZones: make([]*proto.CustomZone, 0, len(update.CustomZones)), - NameServerGroups: make([]*proto.NameServerGroup, 0, len(update.NameServerGroups)), - ForwarderPort: forwardPort, - } - - for _, zone := range update.CustomZones { - protoZone := convertToProtoCustomZone(zone) - protoUpdate.CustomZones = append(protoUpdate.CustomZones, protoZone) - } - - for _, nsGroup := range update.NameServerGroups { - cacheKey := nsGroup.ID - if cachedGroup, exists := cache.GetNameServerGroup(cacheKey); exists { - protoUpdate.NameServerGroups = append(protoUpdate.NameServerGroups, cachedGroup) - } else { - protoGroup := convertToProtoNameServerGroup(nsGroup) - cache.SetNameServerGroup(cacheKey, protoGroup) - protoUpdate.NameServerGroups = append(protoUpdate.NameServerGroups, protoGroup) - } - } - - return protoUpdate -} - func ToResponseProto(configProto nbconfig.Protocol) proto.HostConfig_Protocol { switch configProto { case nbconfig.UDP: @@ -277,204 +200,6 @@ func ToResponseProto(configProto nbconfig.Protocol) proto.HostConfig_Protocol { } } -func toProtocolRoutes(routes []*nbroute.Route) []*proto.Route { - protoRoutes := make([]*proto.Route, 0, len(routes)) - for _, r := range routes { - protoRoutes = append(protoRoutes, toProtocolRoute(r)) - } - return protoRoutes -} - -func toProtocolRoute(route *nbroute.Route) *proto.Route { - return &proto.Route{ - ID: string(route.ID), - NetID: string(route.NetID), - Network: route.Network.String(), - Domains: route.Domains.ToPunycodeList(), - NetworkType: int64(route.NetworkType), - Peer: route.Peer, - Metric: int64(route.Metric), - Masquerade: route.Masquerade, - KeepRoute: route.KeepRoute, - SkipAutoApply: route.SkipAutoApply, - } -} - -// toProtocolFirewallRules converts the firewall rules to the protocol firewall rules. -// When useSourcePrefixes is true, the compact SourcePrefixes field is populated -// alongside the deprecated PeerIP for forward compatibility. -// Wildcard rules ("0.0.0.0") are expanded into separate v4 and v6 SourcePrefixes -// when includeIPv6 is true. -func toProtocolFirewallRules(rules []*types.FirewallRule, includeIPv6, useSourcePrefixes bool) []*proto.FirewallRule { - result := make([]*proto.FirewallRule, 0, len(rules)) - for i := range rules { - rule := rules[i] - - fwRule := &proto.FirewallRule{ - PolicyID: []byte(rule.PolicyID), - PeerIP: rule.PeerIP, //nolint:staticcheck // populated for backward compatibility - Direction: getProtoDirection(rule.Direction), - Action: getProtoAction(rule.Action), - Protocol: getProtoProtocol(rule.Protocol), - Port: rule.Port, - } - - if useSourcePrefixes && rule.PeerIP != "" { - result = append(result, populateSourcePrefixes(fwRule, rule, includeIPv6)...) - } - - if shouldUsePortRange(fwRule) { - fwRule.PortInfo = rule.PortRange.ToProto() - } - - result = append(result, fwRule) - } - return result -} - - -// populateSourcePrefixes sets SourcePrefixes on fwRule and returns any -// additional rules needed (e.g. a v6 wildcard clone when the peer IP is unspecified). -func populateSourcePrefixes(fwRule *proto.FirewallRule, rule *types.FirewallRule, includeIPv6 bool) []*proto.FirewallRule { - addr, err := netip.ParseAddr(rule.PeerIP) - if err != nil { - return nil - } - - if !addr.IsUnspecified() { - fwRule.SourcePrefixes = [][]byte{netiputil.EncodeAddr(addr.Unmap())} - return nil - } - - // IPv4Unspecified/0 is always valid, error is impossible. - v4Wildcard, _ := netiputil.EncodePrefix(netip.PrefixFrom(netip.IPv4Unspecified(), 0)) - fwRule.SourcePrefixes = [][]byte{v4Wildcard} - - if !includeIPv6 { - return nil - } - - v6Rule := goproto.Clone(fwRule).(*proto.FirewallRule) - v6Rule.PeerIP = "::" //nolint:staticcheck // populated for backward compatibility - // IPv6Unspecified/0 is always valid, error is impossible. - v6Wildcard, _ := netiputil.EncodePrefix(netip.PrefixFrom(netip.IPv6Unspecified(), 0)) - v6Rule.SourcePrefixes = [][]byte{v6Wildcard} - if shouldUsePortRange(v6Rule) { - v6Rule.PortInfo = rule.PortRange.ToProto() - } - return []*proto.FirewallRule{v6Rule} -} - -// getProtoDirection converts the direction to proto.RuleDirection. -func getProtoDirection(direction int) proto.RuleDirection { - if direction == types.FirewallRuleDirectionOUT { - return proto.RuleDirection_OUT - } - return proto.RuleDirection_IN -} - -func toProtocolRoutesFirewallRules(rules []*types.RouteFirewallRule) []*proto.RouteFirewallRule { - result := make([]*proto.RouteFirewallRule, len(rules)) - for i := range rules { - rule := rules[i] - result[i] = &proto.RouteFirewallRule{ - SourceRanges: rule.SourceRanges, - Action: getProtoAction(rule.Action), - Destination: rule.Destination, - Protocol: getProtoProtocol(rule.Protocol), - PortInfo: getProtoPortInfo(rule), - IsDynamic: rule.IsDynamic, - Domains: rule.Domains.ToPunycodeList(), - PolicyID: []byte(rule.PolicyID), - RouteID: string(rule.RouteID), - } - } - - return result -} - -// getProtoAction converts the action to proto.RuleAction. -func getProtoAction(action string) proto.RuleAction { - if action == string(types.PolicyTrafficActionDrop) { - return proto.RuleAction_DROP - } - return proto.RuleAction_ACCEPT -} - -// getProtoProtocol converts the protocol to proto.RuleProtocol. -func getProtoProtocol(protocol string) proto.RuleProtocol { - switch types.PolicyRuleProtocolType(protocol) { - case types.PolicyRuleProtocolALL: - return proto.RuleProtocol_ALL - case types.PolicyRuleProtocolTCP: - return proto.RuleProtocol_TCP - case types.PolicyRuleProtocolUDP: - return proto.RuleProtocol_UDP - case types.PolicyRuleProtocolICMP: - return proto.RuleProtocol_ICMP - default: - return proto.RuleProtocol_UNKNOWN - } -} - -// getProtoPortInfo converts the port info to proto.PortInfo. -func getProtoPortInfo(rule *types.RouteFirewallRule) *proto.PortInfo { - var portInfo proto.PortInfo - if rule.Port != 0 { - portInfo.PortSelection = &proto.PortInfo_Port{Port: uint32(rule.Port)} - } else if portRange := rule.PortRange; portRange.Start != 0 && portRange.End != 0 { - portInfo.PortSelection = &proto.PortInfo_Range_{ - Range: &proto.PortInfo_Range{ - Start: uint32(portRange.Start), - End: uint32(portRange.End), - }, - } - } - return &portInfo -} - -func shouldUsePortRange(rule *proto.FirewallRule) bool { - return rule.Port == "" && (rule.Protocol == proto.RuleProtocol_UDP || rule.Protocol == proto.RuleProtocol_TCP) -} - -// Helper function to convert nbdns.CustomZone to proto.CustomZone -func convertToProtoCustomZone(zone nbdns.CustomZone) *proto.CustomZone { - protoZone := &proto.CustomZone{ - Domain: zone.Domain, - Records: make([]*proto.SimpleRecord, 0, len(zone.Records)), - SearchDomainDisabled: zone.SearchDomainDisabled, - NonAuthoritative: zone.NonAuthoritative, - } - for _, record := range zone.Records { - protoZone.Records = append(protoZone.Records, &proto.SimpleRecord{ - Name: record.Name, - Type: int64(record.Type), - Class: record.Class, - TTL: int64(record.TTL), - RData: record.RData, - }) - } - return protoZone -} - -// Helper function to convert nbdns.NameServerGroup to proto.NameServerGroup -func convertToProtoNameServerGroup(nsGroup *nbdns.NameServerGroup) *proto.NameServerGroup { - protoGroup := &proto.NameServerGroup{ - Primary: nsGroup.Primary, - Domains: nsGroup.Domains, - SearchDomainsEnabled: nsGroup.SearchDomainsEnabled, - NameServers: make([]*proto.NameServer, 0, len(nsGroup.NameServers)), - } - for _, ns := range nsGroup.NameServers { - protoGroup.NameServers = append(protoGroup.NameServers, &proto.NameServer{ - IP: ns.IP.String(), - Port: int64(ns.Port), - NSType: int64(ns.NSType), - }) - } - return protoGroup -} - // buildJWTConfig constructs JWT configuration for SSH servers from management server config func buildJWTConfig(config *nbconfig.HttpServerConfig, deviceFlowConfig *nbconfig.DeviceAuthorizationFlow) *proto.JWTConfig { if config == nil || config.AuthAudience == "" { diff --git a/management/internals/shared/grpc/conversion_test.go b/management/internals/shared/grpc/conversion_test.go index 1e75caf959a..102e95eac41 100644 --- a/management/internals/shared/grpc/conversion_test.go +++ b/management/internals/shared/grpc/conversion_test.go @@ -12,6 +12,7 @@ import ( "github.com/netbirdio/netbird/management/internals/controllers/network_map" "github.com/netbirdio/netbird/management/internals/controllers/network_map/controller/cache" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" + "github.com/netbirdio/netbird/shared/management/networkmap" ) func TestToProtocolDNSConfigWithCache(t *testing.T) { @@ -61,13 +62,13 @@ func TestToProtocolDNSConfigWithCache(t *testing.T) { } // First run with config1 - result1 := toProtocolDNSConfig(config1, &cache, int64(network_map.DnsForwarderPort)) + result1 := networkmap.ToProtocolDNSConfig(config1, &cache, int64(network_map.DnsForwarderPort)) // Second run with config2 - result2 := toProtocolDNSConfig(config2, &cache, int64(network_map.DnsForwarderPort)) + result2 := networkmap.ToProtocolDNSConfig(config2, &cache, int64(network_map.DnsForwarderPort)) // Third run with config1 again - result3 := toProtocolDNSConfig(config1, &cache, int64(network_map.DnsForwarderPort)) + result3 := networkmap.ToProtocolDNSConfig(config1, &cache, int64(network_map.DnsForwarderPort)) // Verify that result1 and result3 are identical if !reflect.DeepEqual(result1, result3) { @@ -99,7 +100,7 @@ func BenchmarkToProtocolDNSConfig(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - toProtocolDNSConfig(testData, cache, int64(network_map.DnsForwarderPort)) + networkmap.ToProtocolDNSConfig(testData, cache, int64(network_map.DnsForwarderPort)) } }) @@ -107,7 +108,7 @@ func BenchmarkToProtocolDNSConfig(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { cache := &cache.DNSConfigCache{} - toProtocolDNSConfig(testData, cache, int64(network_map.DnsForwarderPort)) + networkmap.ToProtocolDNSConfig(testData, cache, int64(network_map.DnsForwarderPort)) } }) } diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 855e54af864..40cdc7c36ce 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -2177,7 +2177,7 @@ func (s *SqlStore) getNameServerGroups(ctx context.Context, accountID string) ([ } func (s *SqlStore) getPostureChecks(ctx context.Context, accountID string) ([]*posture.Checks, error) { - const query = `SELECT id, account_id, name, description, checks FROM posture_checks WHERE account_id = $1` + const query = `SELECT id, account_id, account_seq_id, name, description, checks FROM posture_checks WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2185,7 +2185,7 @@ func (s *SqlStore) getPostureChecks(ctx context.Context, accountID string) ([]*p checks, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (*posture.Checks, error) { var c posture.Checks var checksDef []byte - err := row.Scan(&c.ID, &c.AccountID, &c.Name, &c.Description, &checksDef) + err := row.Scan(&c.ID, &c.AccountID, &c.AccountSeqID, &c.Name, &c.Description, &checksDef) if err == nil && checksDef != nil { _ = json.Unmarshal(checksDef, &c.Checks) } @@ -2351,7 +2351,7 @@ func (s *SqlStore) getServices(ctx context.Context, accountID string) ([]*rpserv } func (s *SqlStore) getNetworks(ctx context.Context, accountID string) ([]*networkTypes.Network, error) { - const query = `SELECT id, account_id, name, description FROM networks WHERE account_id = $1` + const query = `SELECT id, account_id, account_seq_id, name, description FROM networks WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err diff --git a/management/server/types/account.go b/management/server/types/account.go index 870333a603c..70cf5fe00da 100644 --- a/management/server/types/account.go +++ b/management/server/types/account.go @@ -1006,6 +1006,15 @@ func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer } } +// PolicyRuleImpliesLegacySSH reports whether the rule (without an explicit +// NetbirdSSH protocol) implicitly authorises SSH because it permits TCP/22 or +// TCP/22022 — either by ALL-protocol coverage or by an explicit port/port-range +// containing one of those. Exposed for ToComponentSyncResponse so the +// envelope-format response mirrors the legacy SshConfig.SshEnabled bit. +func PolicyRuleImpliesLegacySSH(rule *PolicyRule) bool { + return policyRuleImpliesLegacySSH(rule) +} + func policyRuleImpliesLegacySSH(rule *PolicyRule) bool { return rule.Protocol == PolicyRuleProtocolALL || (rule.Protocol == PolicyRuleProtocolTCP && (portsIncludesSSH(rule.Ports) || portRangeIncludesSSH(rule.PortRanges))) } diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index f9b73888476..45f7189b0f3 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -125,15 +125,27 @@ func (a *Account) GetPeerNetworkMapComponents( } components := &NetworkMapComponents{ - PeerID: peerID, - Network: a.Network.Copy(), - NameServerGroups: make([]*nbdns.NameServerGroup, 0), - CustomZoneDomain: peersCustomZone.Domain, - ResourcePoliciesMap: make(map[string][]*Policy), - RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), - NetworkResources: make([]*resourceTypes.NetworkResource, 0), - PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)), - RouterPeers: make(map[string]*nbpeer.Peer), + PeerID: peerID, + Network: a.Network.Copy(), + NameServerGroups: make([]*nbdns.NameServerGroup, 0), + CustomZoneDomain: peersCustomZone.Domain, + ResourcePoliciesMap: make(map[string][]*Policy), + RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), + NetworkResources: make([]*resourceTypes.NetworkResource, 0), + PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)), + RouterPeers: make(map[string]*nbpeer.Peer), + NetworkXIDToSeq: make(map[string]uint32, len(a.Networks)), + PostureCheckXIDToSeq: make(map[string]uint32, len(a.PostureChecks)), + } + for _, n := range a.Networks { + if n != nil && n.HasSeqID() { + components.NetworkXIDToSeq[n.ID] = n.AccountSeqID + } + } + for _, pc := range a.PostureChecks { + if pc != nil && pc.HasSeqID() { + components.PostureCheckXIDToSeq[pc.ID] = pc.AccountSeqID + } } components.AccountSettings = &AccountSettingsInfo{ @@ -552,6 +564,13 @@ func (a *Account) getPostureValidPeersSaveFailed(inputPeers []string, postureChe return dest } +// filterGroupPeers trims each group's Peers slice to only those peers that +// also appear in `peers`. Groups whose filtered list is empty are NOT +// deleted from the map — they're kept so the components wire encoder can +// still resolve seq references from routes/policies/access-control groups +// that name them. Calculate() tolerates groups with empty Peers (the inner +// loops simply iterate zero times), so retaining them is behaviourally a +// no-op for the legacy path that consumes the same NetworkMapComponents. func filterGroupPeers(groups *map[string]*Group, peers map[string]*nbpeer.Peer) { for groupID, groupInfo := range *groups { filteredPeers := make([]string, 0, len(groupInfo.Peers)) @@ -561,9 +580,7 @@ func filterGroupPeers(groups *map[string]*Group, peers map[string]*nbpeer.Peer) } } - if len(filteredPeers) == 0 { - delete(*groups, groupID) - } else if len(filteredPeers) != len(groupInfo.Peers) { + if len(filteredPeers) != len(groupInfo.Peers) { ng := groupInfo.Copy() ng.Peers = filteredPeers (*groups)[groupID] = ng diff --git a/management/server/types/networkmap_components.go b/management/server/types/networkmap_components.go index 3a7e20ec527..008c041842f 100644 --- a/management/server/types/networkmap_components.go +++ b/management/server/types/networkmap_components.go @@ -42,6 +42,17 @@ type NetworkMapComponents struct { PostureFailedPeers map[string]map[string]struct{} RouterPeers map[string]*nbpeer.Peer + + // NetworkXIDToSeq maps Network.ID (xid) → AccountSeqID. Populated by the + // account-side component builder; consumed by the envelope encoder to + // translate RoutersMap keys and NetworkResource.NetworkID references + // to compact uint32 ids. Legacy Calculate() doesn't consult it. + NetworkXIDToSeq map[string]uint32 + + // PostureCheckXIDToSeq maps posture.Checks.ID (xid) → AccountSeqID. + // Same role as NetworkXIDToSeq, used for PostureFailedPeers keys and + // policy SourcePostureChecks references. + PostureCheckXIDToSeq map[string]uint32 } type AccountSettingsInfo struct { diff --git a/shared/management/client/grpc.go b/shared/management/client/grpc.go index 58895b7c222..97abc6961a5 100644 --- a/shared/management/client/grpc.go +++ b/shared/management/client/grpc.go @@ -950,6 +950,13 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta { func peerCapabilities(info system.Info) []proto.PeerCapability { caps := []proto.PeerCapability{ proto.PeerCapability_PeerCapabilitySourcePrefixes, + // PeerCapabilityComponentNetworkMap signals that this client can + // decode the components-format SyncResponse.NetworkMapEnvelope and + // run Calculate() locally. Always advertised by Step-4-capable + // builds — there's no opt-out flag because the server-side kill + // switch (NB_NETWORK_MAP_COMPONENTS_DISABLE) covers emergency + // rollback and the client decoder is built in. + proto.PeerCapability_PeerCapabilityComponentNetworkMap, } if !info.DisableIPv6 { caps = append(caps, proto.PeerCapability_PeerCapabilityIPv6Overlay) diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 6755f16353b..11fa041b4da 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -137,6 +137,13 @@ const ( RuleProtocol_UDP RuleProtocol = 3 RuleProtocol_ICMP RuleProtocol = 4 RuleProtocol_CUSTOM RuleProtocol = 5 + // NETBIRD_SSH (types.PolicyRuleProtocolType "netbird-ssh") is the marker + // policy rule that drives SSH-server activation in Calculate(). The legacy + // proto.FirewallRule path doesn't ship this value (Calculate already + // expands SSH rules into TCP/22 before encoding), but the components path + // ships RAW policies — the client must see this protocol to derive + // AuthorizedUsers locally. + RuleProtocol_NETBIRD_SSH RuleProtocol = 6 ) // Enum value maps for RuleProtocol. @@ -148,14 +155,16 @@ var ( 3: "UDP", 4: "ICMP", 5: "CUSTOM", + 6: "NETBIRD_SSH", } RuleProtocol_value = map[string]int32{ - "UNKNOWN": 0, - "ALL": 1, - "TCP": 2, - "UDP": 3, - "ICMP": 4, - "CUSTOM": 5, + "UNKNOWN": 0, + "ALL": 1, + "TCP": 2, + "UDP": 3, + "ICMP": 4, + "CUSTOM": 5, + "NETBIRD_SSH": 6, } ) @@ -4637,20 +4646,20 @@ type NetworkMapComponentsFull struct { AccountZones []*CustomZone `protobuf:"bytes,16,rep,name=account_zones,json=accountZones,proto3" json:"account_zones,omitempty"` // Network resources (mirrors []*resourceTypes.NetworkResource). NetworkResources []*NetworkResourceRaw `protobuf:"bytes,17,rep,name=network_resources,json=networkResources,proto3" json:"network_resources,omitempty"` - // Routers per network. Outer key: network id (xid string). Each entry is + // Routers per network. Outer key: network account_seq_id. Each entry is // the set of routers backing that network for this peer's view. - RoutersMap map[string]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // For each NetworkResource id (xid string), the indexes into policies[] + RoutersMap map[uint32]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // For each NetworkResource account_seq_id, the indexes into policies[] // that apply to it. - ResourcePoliciesMap map[string]*PolicyIndexes `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ResourcePoliciesMap map[uint32]*PolicyIndexes `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Group-id (account_seq_id) → user ids authorized for SSH on members. GroupIdToUserIds map[uint32]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). AllowedUserIds []string `protobuf:"bytes,21,rep,name=allowed_user_ids,json=allowedUserIds,proto3" json:"allowed_user_ids,omitempty"` - // Per posture-check id (xid string), the set of peer indexes that failed + // Per posture-check account_seq_id, the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. - PostureFailedPeers map[string]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + PostureFailedPeers map[uint32]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level DNS forwarder port (mirrors the legacy // proto.DNSConfig.ForwarderPort). Computed by the controller from peer // versions; clients fold it into their Calculate() DNS output. @@ -4661,6 +4670,11 @@ type NetworkMapComponentsFull struct { // legacy server does via NetworkMap.Merge — so downstream consumers see // a unified merged result regardless of source. ProxyPatch *ProxyPatch `protobuf:"bytes,24,opt,name=proxy_patch,json=proxyPatch,proto3" json:"proxy_patch,omitempty"` + // SSH UserIDClaim — server-side HttpServerConfig.AuthUserIDClaim, or + // "sub" by default. Populated in proto.SSHAuth.UserIDClaim when the + // client rebuilds the NetworkMap from this envelope. Empty when the + // account has no AuthorizedUsers (and thus no SshAuth to populate). + UserIdClaim string `protobuf:"bytes,25,opt,name=user_id_claim,json=userIdClaim,proto3" json:"user_id_claim,omitempty"` } func (x *NetworkMapComponentsFull) Reset() { @@ -4814,14 +4828,14 @@ func (x *NetworkMapComponentsFull) GetNetworkResources() []*NetworkResourceRaw { return nil } -func (x *NetworkMapComponentsFull) GetRoutersMap() map[string]*NetworkRouterList { +func (x *NetworkMapComponentsFull) GetRoutersMap() map[uint32]*NetworkRouterList { if x != nil { return x.RoutersMap } return nil } -func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[string]*PolicyIndexes { +func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[uint32]*PolicyIndexes { if x != nil { return x.ResourcePoliciesMap } @@ -4842,7 +4856,7 @@ func (x *NetworkMapComponentsFull) GetAllowedUserIds() []string { return nil } -func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[string]*PeerIndexSet { +func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[uint32]*PeerIndexSet { if x != nil { return x.PostureFailedPeers } @@ -4863,6 +4877,13 @@ func (x *NetworkMapComponentsFull) GetProxyPatch() *ProxyPatch { return nil } +func (x *NetworkMapComponentsFull) GetUserIdClaim() string { + if x != nil { + return x.UserIdClaim + } + return "" +} + // ProxyPatch carries NetworkMap fragments that don't fit the component-graph // model — they're pre-expanded by external controllers (BYOP / // port-forwarding proxies) and injected post-Calculate. Fields use the @@ -5180,6 +5201,26 @@ type PeerCompact struct { // makes a fresh peer immediately expired iff login_expiration_enabled is // true — the same semantics as types.Peer.GetLastLogin). LastLoginUnixNano int64 `protobuf:"varint,9,opt,name=last_login_unix_nano,json=lastLoginUnixNano,proto3" json:"last_login_unix_nano,omitempty"` + // True when the peer has an SSH server enabled locally. Used by the + // legacy SSH path in Calculate() (`policyRuleImpliesLegacySSH`): a rule + // with protocol ALL/TCP-with-SSH-ports activates SSH for the receiving + // peer when this bit is set, even without an explicit NetbirdSSH rule. + SshEnabled bool `protobuf:"varint,10,opt,name=ssh_enabled,json=sshEnabled,proto3" json:"ssh_enabled,omitempty"` + // Mirror of types.Peer.SupportsIPv6() — !Meta.Flags.DisableIPv6 && + // HasCapability(PeerCapabilityIPv6Overlay). Used by the local peer's + // Calculate() when deciding whether to emit IPv6 firewall rules + // (appendIPv6FirewallRule) against this peer's IPv6 address. + SupportsIpv6 bool `protobuf:"varint,12,opt,name=supports_ipv6,json=supportsIpv6,proto3" json:"supports_ipv6,omitempty"` + // Mirror of types.Peer.SupportsSourcePrefixes() — + // HasCapability(PeerCapabilitySourcePrefixes). Determines whether the + // local peer's Calculate() emits SourcePrefixes alongside legacy PeerIP + // fields in proto.FirewallRule. + SupportsSourcePrefixes bool `protobuf:"varint,13,opt,name=supports_source_prefixes,json=supportsSourcePrefixes,proto3" json:"supports_source_prefixes,omitempty"` + // Mirror of types.Peer.Meta.Flags.ServerSSHAllowed. Read by Calculate() + // when expanding TCP port-22 firewall rules — the native SSH companion + // (port 22022) is only added when this flag is set and the peer agent + // version supports it. + ServerSshAllowed bool `protobuf:"varint,14,opt,name=server_ssh_allowed,json=serverSshAllowed,proto3" json:"server_ssh_allowed,omitempty"` } func (x *PeerCompact) Reset() { @@ -5277,6 +5318,34 @@ func (x *PeerCompact) GetLastLoginUnixNano() int64 { return 0 } +func (x *PeerCompact) GetSshEnabled() bool { + if x != nil { + return x.SshEnabled + } + return false +} + +func (x *PeerCompact) GetSupportsIpv6() bool { + if x != nil { + return x.SupportsIpv6 + } + return false +} + +func (x *PeerCompact) GetSupportsSourcePrefixes() bool { + if x != nil { + return x.SupportsSourcePrefixes + } + return false +} + +func (x *PeerCompact) GetServerSshAllowed() bool { + if x != nil { + return x.ServerSshAllowed + } + return false +} + // PolicyCompact is the compact form of a policy rule. Group references use // the per-account integer ids from account_seq_counters; the client resolves // them against NetworkMapComponentsFull.groups. Direction is derived per-peer @@ -5287,7 +5356,9 @@ type PolicyCompact struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Per-account integer id (matches policies.account_seq_id). + // Per-account integer id (matches policies.account_seq_id). Used as a + // stable reference for ResourcePoliciesMap.indexes and future delta + // updates (Step 3). Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Action RuleAction `protobuf:"varint,2,opt,name=action,proto3,enum=management.RuleAction" json:"action,omitempty"` Protocol RuleProtocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` @@ -5299,6 +5370,30 @@ type PolicyCompact struct { // Group ids (account_seq_id) of source / destination groups. SourceGroupIds []uint32 `protobuf:"varint,7,rep,packed,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` DestinationGroupIds []uint32 `protobuf:"varint,8,rep,packed,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` + // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's + // applicable group ids (account_seq_id) to a list of local-user names — + // when a peer in one of those groups is the SSH destination, the named + // local users gain access. AuthorizedUser is the single-user form + // (legacy: rule scopes SSH to one specific user id). + // + // Both fields are only consumed by Calculate() when the rule's protocol + // is NetbirdSSH (or the legacy implicit-SSH heuristic). + AuthorizedGroups map[uint32]*UserNameList `protobuf:"bytes,10,rep,name=authorized_groups,json=authorizedGroups,proto3" json:"authorized_groups,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + AuthorizedUser string `protobuf:"bytes,11,opt,name=authorized_user,json=authorizedUser,proto3" json:"authorized_user,omitempty"` + // Resource-typed rule sources/destinations. When a rule targets a specific + // peer (rather than groups), Calculate() reads SourceResource / + // DestinationResource — without these the rule's connection resources + // can't be produced on the client. ResourceCompact's peer_index refers to + // NetworkMapComponentsFull.peers; type is the raw ResourceType string + // ("peer", "host", "subnet", "domain"). Only "peer" is meaningful for + // Calculate's resource-typed rule path today. + SourceResource *ResourceCompact `protobuf:"bytes,12,opt,name=source_resource,json=sourceResource,proto3" json:"source_resource,omitempty"` + DestinationResource *ResourceCompact `protobuf:"bytes,13,opt,name=destination_resource,json=destinationResource,proto3" json:"destination_resource,omitempty"` + // Posture-check seq ids gating this policy's source peers. Calculate() + // reads them when filtering rule peers (peers that fail any listed check + // are dropped from sourcePeers). Match keys in + // NetworkMapComponentsFull.posture_failed_peers. + SourcePostureCheckSeqIds []uint32 `protobuf:"varint,15,rep,packed,name=source_posture_check_seq_ids,json=sourcePostureCheckSeqIds,proto3" json:"source_posture_check_seq_ids,omitempty"` } func (x *PolicyCompact) Reset() { @@ -5389,6 +5484,158 @@ func (x *PolicyCompact) GetDestinationGroupIds() []uint32 { return nil } +func (x *PolicyCompact) GetAuthorizedGroups() map[uint32]*UserNameList { + if x != nil { + return x.AuthorizedGroups + } + return nil +} + +func (x *PolicyCompact) GetAuthorizedUser() string { + if x != nil { + return x.AuthorizedUser + } + return "" +} + +func (x *PolicyCompact) GetSourceResource() *ResourceCompact { + if x != nil { + return x.SourceResource + } + return nil +} + +func (x *PolicyCompact) GetDestinationResource() *ResourceCompact { + if x != nil { + return x.DestinationResource + } + return nil +} + +func (x *PolicyCompact) GetSourcePostureCheckSeqIds() []uint32 { + if x != nil { + return x.SourcePostureCheckSeqIds + } + return nil +} + +// ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry +// rule.SourceResource / rule.DestinationResource when the rule targets a +// specific resource (typically a peer) rather than groups. +// peer_index_set tells whether peer_index is valid (proto3 uint32 cannot +// disambiguate "0" from "unset"); set only when type == "peer". +type ResourceCompact struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + PeerIndexSet bool `protobuf:"varint,2,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerIndex uint32 `protobuf:"varint,3,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` +} + +func (x *ResourceCompact) Reset() { + *x = ResourceCompact{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceCompact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceCompact) ProtoMessage() {} + +func (x *ResourceCompact) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[61] + 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 ResourceCompact.ProtoReflect.Descriptor instead. +func (*ResourceCompact) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{61} +} + +func (x *ResourceCompact) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ResourceCompact) GetPeerIndexSet() bool { + if x != nil { + return x.PeerIndexSet + } + return false +} + +func (x *ResourceCompact) GetPeerIndex() uint32 { + if x != nil { + return x.PeerIndex + } + return 0 +} + +// UserNameList is a list of local-user names — used as the value type in +// PolicyCompact.authorized_groups. +type UserNameList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` +} + +func (x *UserNameList) Reset() { + *x = UserNameList{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserNameList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserNameList) ProtoMessage() {} + +func (x *UserNameList) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[62] + 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 UserNameList.ProtoReflect.Descriptor instead. +func (*UserNameList) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{62} +} + +func (x *UserNameList) GetNames() []string { + if x != nil { + return x.Names + } + return nil +} + // GroupCompact is the wire-shape of a group: per-account integer id, optional // name, and indexes into NetworkMapComponentsFull.peers identifying members. type GroupCompact struct { @@ -5408,7 +5655,7 @@ type GroupCompact struct { func (x *GroupCompact) Reset() { *x = GroupCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[61] + mi := &file_management_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5421,7 +5668,7 @@ func (x *GroupCompact) String() string { func (*GroupCompact) ProtoMessage() {} func (x *GroupCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[61] + mi := &file_management_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5434,7 +5681,7 @@ func (x *GroupCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use GroupCompact.ProtoReflect.Descriptor instead. func (*GroupCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{61} + return file_management_proto_rawDescGZIP(), []int{63} } func (x *GroupCompact) GetId() uint32 { @@ -5471,7 +5718,7 @@ type DNSSettingsCompact struct { func (x *DNSSettingsCompact) Reset() { *x = DNSSettingsCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[62] + mi := &file_management_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5484,7 +5731,7 @@ func (x *DNSSettingsCompact) String() string { func (*DNSSettingsCompact) ProtoMessage() {} func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[62] + mi := &file_management_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5497,7 +5744,7 @@ func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSSettingsCompact.ProtoReflect.Descriptor instead. func (*DNSSettingsCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{62} + return file_management_proto_rawDescGZIP(), []int{64} } func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []uint32 { @@ -5549,7 +5796,7 @@ type RouteRaw struct { func (x *RouteRaw) Reset() { *x = RouteRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[63] + mi := &file_management_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5562,7 +5809,7 @@ func (x *RouteRaw) String() string { func (*RouteRaw) ProtoMessage() {} func (x *RouteRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[63] + mi := &file_management_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5575,7 +5822,7 @@ func (x *RouteRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteRaw.ProtoReflect.Descriptor instead. func (*RouteRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{63} + return file_management_proto_rawDescGZIP(), []int{65} } func (x *RouteRaw) GetId() uint32 { @@ -5714,7 +5961,7 @@ type NameServerGroupRaw struct { func (x *NameServerGroupRaw) Reset() { *x = NameServerGroupRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[64] + mi := &file_management_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5727,7 +5974,7 @@ func (x *NameServerGroupRaw) String() string { func (*NameServerGroupRaw) ProtoMessage() {} func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[64] + mi := &file_management_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5740,7 +5987,7 @@ func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use NameServerGroupRaw.ProtoReflect.Descriptor instead. func (*NameServerGroupRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{64} + return file_management_proto_rawDescGZIP(), []int{66} } func (x *NameServerGroupRaw) GetId() uint32 { @@ -5812,8 +6059,8 @@ type NetworkResourceRaw struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id - NetworkId string `protobuf:"bytes,2,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` // xid string — networks have no seq id today + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id + NetworkSeq uint32 `protobuf:"varint,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` // networks.account_seq_id (replaces xid) Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // Resource type: "host" / "subnet" / "domain". @@ -5827,7 +6074,7 @@ type NetworkResourceRaw struct { func (x *NetworkResourceRaw) Reset() { *x = NetworkResourceRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[65] + mi := &file_management_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5840,7 +6087,7 @@ func (x *NetworkResourceRaw) String() string { func (*NetworkResourceRaw) ProtoMessage() {} func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[65] + mi := &file_management_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5853,7 +6100,7 @@ func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkResourceRaw.ProtoReflect.Descriptor instead. func (*NetworkResourceRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{65} + return file_management_proto_rawDescGZIP(), []int{67} } func (x *NetworkResourceRaw) GetId() uint32 { @@ -5863,11 +6110,11 @@ func (x *NetworkResourceRaw) GetId() uint32 { return 0 } -func (x *NetworkResourceRaw) GetNetworkId() string { +func (x *NetworkResourceRaw) GetNetworkSeq() uint32 { if x != nil { - return x.NetworkId + return x.NetworkSeq } - return "" + return 0 } func (x *NetworkResourceRaw) GetName() string { @@ -5932,7 +6179,7 @@ type NetworkRouterList struct { func (x *NetworkRouterList) Reset() { *x = NetworkRouterList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[66] + mi := &file_management_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5945,7 +6192,7 @@ func (x *NetworkRouterList) String() string { func (*NetworkRouterList) ProtoMessage() {} func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[66] + mi := &file_management_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5958,7 +6205,7 @@ func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkRouterList.ProtoReflect.Descriptor instead. func (*NetworkRouterList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{66} + return file_management_proto_rawDescGZIP(), []int{68} } func (x *NetworkRouterList) GetEntries() []*NetworkRouterEntry { @@ -5987,7 +6234,7 @@ type NetworkRouterEntry struct { func (x *NetworkRouterEntry) Reset() { *x = NetworkRouterEntry{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[67] + mi := &file_management_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6000,7 +6247,7 @@ func (x *NetworkRouterEntry) String() string { func (*NetworkRouterEntry) ProtoMessage() {} func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[67] + mi := &file_management_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6013,7 +6260,7 @@ func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkRouterEntry.ProtoReflect.Descriptor instead. func (*NetworkRouterEntry) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{67} + return file_management_proto_rawDescGZIP(), []int{69} } func (x *NetworkRouterEntry) GetId() uint32 { @@ -6077,7 +6324,7 @@ type PolicyIndexes struct { func (x *PolicyIndexes) Reset() { *x = PolicyIndexes{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[68] + mi := &file_management_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6090,7 +6337,7 @@ func (x *PolicyIndexes) String() string { func (*PolicyIndexes) ProtoMessage() {} func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[68] + mi := &file_management_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6103,7 +6350,7 @@ func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyIndexes.ProtoReflect.Descriptor instead. func (*PolicyIndexes) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{68} + return file_management_proto_rawDescGZIP(), []int{70} } func (x *PolicyIndexes) GetIndexes() []uint32 { @@ -6126,7 +6373,7 @@ type UserIDList struct { func (x *UserIDList) Reset() { *x = UserIDList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[69] + mi := &file_management_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6139,7 +6386,7 @@ func (x *UserIDList) String() string { func (*UserIDList) ProtoMessage() {} func (x *UserIDList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[69] + mi := &file_management_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6152,7 +6399,7 @@ func (x *UserIDList) ProtoReflect() protoreflect.Message { // Deprecated: Use UserIDList.ProtoReflect.Descriptor instead. func (*UserIDList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{69} + return file_management_proto_rawDescGZIP(), []int{71} } func (x *UserIDList) GetUserIds() []string { @@ -6175,7 +6422,7 @@ type PeerIndexSet struct { func (x *PeerIndexSet) Reset() { *x = PeerIndexSet{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[70] + mi := &file_management_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6188,7 +6435,7 @@ func (x *PeerIndexSet) String() string { func (*PeerIndexSet) ProtoMessage() {} func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[70] + mi := &file_management_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6201,7 +6448,7 @@ func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerIndexSet.ProtoReflect.Descriptor instead. func (*PeerIndexSet) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{70} + return file_management_proto_rawDescGZIP(), []int{72} } func (x *PeerIndexSet) GetPeerIndexes() []uint32 { @@ -6223,7 +6470,7 @@ type PortInfo_Range struct { func (x *PortInfo_Range) Reset() { *x = PortInfo_Range{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[72] + mi := &file_management_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6236,7 +6483,7 @@ func (x *PortInfo_Range) String() string { func (*PortInfo_Range) ProtoMessage() {} func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[72] + mi := &file_management_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6908,7 +7155,7 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x22, 0xf2, 0x0e, 0x0a, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, + 0x64, 0x22, 0x96, 0x0f, 0x0a, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, @@ -7003,188 +7250,240 @@ var file_management_proto_rawDesc = []byte{ 0x79, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x61, 0x74, 0x63, - 0x68, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x61, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x4a, 0x04, 0x08, 0x19, 0x10, 0x33, 0x22, 0x87, 0x03, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, + 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x61, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, + 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, + 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x1a, 0x10, 0x33, 0x22, 0x87, 0x03, 0x0a, 0x0a, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x65, 0x65, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, + 0x0d, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x6f, 0x66, 0x66, - 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, - 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x0e, - 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, - 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, - 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x14, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, - 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, - 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x10, 0x66, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, - 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, - 0x22, 0x94, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x70, - 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, - 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, - 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, - 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x5f, 0x76, 0x36, 0x5f, - 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x56, - 0x36, 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, - 0x21, 0x0a, 0x19, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, - 0x10, 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, - 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x69, 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, - 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, - 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x78, 0x12, 0x2f, 0x0a, - 0x14, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x73, 0x6f, 0x5f, - 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x38, - 0x0a, 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x22, 0xdc, 0x02, 0x0a, 0x0d, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x3b, 0x0a, - 0x0b, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, - 0x03, 0x28, 0x0d, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, - 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x1a, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, - 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, - 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, - 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, - 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, - 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, - 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, - 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, - 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, - 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, - 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, - 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, - 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, - 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, - 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x22, 0x85, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, + 0x12, 0x3f, 0x0a, 0x0e, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, + 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, + 0x73, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x14, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, + 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, + 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x45, 0x0a, + 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, + 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, + 0x75, 0x6c, 0x65, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, + 0x41, 0x0a, 0x1d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, + 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, + 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, + 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, + 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6e, 0x65, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, + 0x5f, 0x76, 0x36, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6e, 0x65, 0x74, 0x56, 0x36, 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x22, 0x21, 0x0a, 0x19, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, + 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, + 0x4a, 0x04, 0x08, 0x01, 0x10, 0x65, 0x22, 0x88, 0x04, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, + 0x62, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, + 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x78, 0x12, 0x2f, 0x0a, 0x14, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, + 0x73, 0x73, 0x6f, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x61, 0x64, 0x64, 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x14, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, + 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x23, + 0x0a, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x49, + 0x70, 0x76, 0x36, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x53, 0x73, 0x68, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x0b, 0x10, + 0x0c, 0x22, 0xa4, 0x06, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, + 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, + 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x13, 0x64, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, + 0x12, 0x5c, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, + 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x0e, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4e, 0x0a, + 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3e, 0x0a, + 0x1c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x74, 0x75, + 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x71, 0x49, 0x64, 0x73, 0x1a, 0x5d, 0x0a, + 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x09, + 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x22, 0x70, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x24, 0x0a, 0x0c, 0x55, 0x73, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, + 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, + 0x22, 0x93, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, + 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, + 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, + 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x0e, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, + 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, + 0x0d, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, + 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, + 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, + 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8d, + 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x5f, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, @@ -7196,120 +7495,122 @@ var file_management_proto_rawDesc = []byte{ 0x0b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, - 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, - 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, - 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, - 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, - 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, - 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, - 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, - 0x31, 0x0a, 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, 0x4c, 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, 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, 0xfd, 0x06, - 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, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x22, 0x4d, + 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, + 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, + 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, + 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x07, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x0a, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, 0x0a, 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, 0xfd, 0x06, 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, 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, + 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, 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, + 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, 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, 0x4c, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 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, 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, 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, 0x42, 0x08, 0x5a, - 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7325,7 +7626,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 77) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 80) var file_management_proto_goTypes = []interface{}{ (JobStatus)(0), // 0: management.JobStatus (PeerCapability)(0), // 1: management.PeerCapability @@ -7396,24 +7697,27 @@ var file_management_proto_goTypes = []interface{}{ (*NetworkMapComponentsDelta)(nil), // 66: management.NetworkMapComponentsDelta (*PeerCompact)(nil), // 67: management.PeerCompact (*PolicyCompact)(nil), // 68: management.PolicyCompact - (*GroupCompact)(nil), // 69: management.GroupCompact - (*DNSSettingsCompact)(nil), // 70: management.DNSSettingsCompact - (*RouteRaw)(nil), // 71: management.RouteRaw - (*NameServerGroupRaw)(nil), // 72: management.NameServerGroupRaw - (*NetworkResourceRaw)(nil), // 73: management.NetworkResourceRaw - (*NetworkRouterList)(nil), // 74: management.NetworkRouterList - (*NetworkRouterEntry)(nil), // 75: management.NetworkRouterEntry - (*PolicyIndexes)(nil), // 76: management.PolicyIndexes - (*UserIDList)(nil), // 77: management.UserIDList - (*PeerIndexSet)(nil), // 78: management.PeerIndexSet - nil, // 79: management.SSHAuth.MachineUsersEntry - (*PortInfo_Range)(nil), // 80: management.PortInfo.Range - nil, // 81: management.NetworkMapComponentsFull.RoutersMapEntry - nil, // 82: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - nil, // 83: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - nil, // 84: management.NetworkMapComponentsFull.PostureFailedPeersEntry - (*timestamppb.Timestamp)(nil), // 85: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 86: google.protobuf.Duration + (*ResourceCompact)(nil), // 69: management.ResourceCompact + (*UserNameList)(nil), // 70: management.UserNameList + (*GroupCompact)(nil), // 71: management.GroupCompact + (*DNSSettingsCompact)(nil), // 72: management.DNSSettingsCompact + (*RouteRaw)(nil), // 73: management.RouteRaw + (*NameServerGroupRaw)(nil), // 74: management.NameServerGroupRaw + (*NetworkResourceRaw)(nil), // 75: management.NetworkResourceRaw + (*NetworkRouterList)(nil), // 76: management.NetworkRouterList + (*NetworkRouterEntry)(nil), // 77: management.NetworkRouterEntry + (*PolicyIndexes)(nil), // 78: management.PolicyIndexes + (*UserIDList)(nil), // 79: management.UserIDList + (*PeerIndexSet)(nil), // 80: management.PeerIndexSet + nil, // 81: management.SSHAuth.MachineUsersEntry + (*PortInfo_Range)(nil), // 82: management.PortInfo.Range + nil, // 83: management.NetworkMapComponentsFull.RoutersMapEntry + nil, // 84: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + nil, // 85: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + nil, // 86: management.NetworkMapComponentsFull.PostureFailedPeersEntry + nil, // 87: management.PolicyCompact.AuthorizedGroupsEntry + (*timestamppb.Timestamp)(nil), // 88: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 89: google.protobuf.Duration } var file_management_proto_depIdxs = []int32{ 11, // 0: management.JobRequest.bundle:type_name -> management.BundleParameters @@ -7437,14 +7741,14 @@ var file_management_proto_depIdxs = []int32{ 25, // 18: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig 31, // 19: management.LoginResponse.peerConfig:type_name -> management.PeerConfig 51, // 20: management.LoginResponse.Checks:type_name -> management.Checks - 85, // 21: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 88, // 21: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 26, // 22: management.NetbirdConfig.stuns:type_name -> management.HostConfig 30, // 23: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig 26, // 24: management.NetbirdConfig.signal:type_name -> management.HostConfig 27, // 25: management.NetbirdConfig.relay:type_name -> management.RelayConfig 28, // 26: management.NetbirdConfig.flow:type_name -> management.FlowConfig 6, // 27: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol - 86, // 28: management.FlowConfig.interval:type_name -> google.protobuf.Duration + 89, // 28: management.FlowConfig.interval:type_name -> google.protobuf.Duration 26, // 29: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig 37, // 30: management.PeerConfig.sshConfig:type_name -> management.SSHConfig 32, // 31: management.PeerConfig.autoUpdate:type_name -> management.AutoUpdateSettings @@ -7457,7 +7761,7 @@ var file_management_proto_depIdxs = []int32{ 53, // 38: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule 54, // 39: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule 34, // 40: management.NetworkMap.sshAuth:type_name -> management.SSHAuth - 79, // 41: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry + 81, // 41: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry 37, // 42: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig 29, // 43: management.SSHConfig.jwtConfig:type_name -> management.JWTConfig 7, // 44: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider @@ -7471,7 +7775,7 @@ var file_management_proto_depIdxs = []int32{ 4, // 52: management.FirewallRule.Action:type_name -> management.RuleAction 2, // 53: management.FirewallRule.Protocol:type_name -> management.RuleProtocol 52, // 54: management.FirewallRule.PortInfo:type_name -> management.PortInfo - 80, // 55: management.PortInfo.range:type_name -> management.PortInfo.Range + 82, // 55: management.PortInfo.range:type_name -> management.PortInfo.Range 4, // 56: management.RouteFirewallRule.action:type_name -> management.RuleAction 2, // 57: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol 52, // 58: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo @@ -7484,19 +7788,19 @@ var file_management_proto_depIdxs = []int32{ 31, // 65: management.NetworkMapComponentsFull.peer_config:type_name -> management.PeerConfig 65, // 66: management.NetworkMapComponentsFull.network:type_name -> management.AccountNetwork 64, // 67: management.NetworkMapComponentsFull.account_settings:type_name -> management.AccountSettingsCompact - 70, // 68: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact + 72, // 68: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact 67, // 69: management.NetworkMapComponentsFull.peers:type_name -> management.PeerCompact 68, // 70: management.NetworkMapComponentsFull.policies:type_name -> management.PolicyCompact - 69, // 71: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact - 71, // 72: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw - 72, // 73: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw + 71, // 71: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact + 73, // 72: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw + 74, // 73: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw 46, // 74: management.NetworkMapComponentsFull.all_dns_records:type_name -> management.SimpleRecord 45, // 75: management.NetworkMapComponentsFull.account_zones:type_name -> management.CustomZone - 73, // 76: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw - 81, // 77: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry - 82, // 78: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - 83, // 79: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - 84, // 80: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry + 75, // 76: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw + 83, // 77: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry + 84, // 78: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry + 85, // 79: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry + 86, // 80: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry 63, // 81: management.NetworkMapComponentsFull.proxy_patch:type_name -> management.ProxyPatch 36, // 82: management.ProxyPatch.peers:type_name -> management.RemotePeerConfig 36, // 83: management.ProxyPatch.offline_peers:type_name -> management.RemotePeerConfig @@ -7506,43 +7810,47 @@ var file_management_proto_depIdxs = []int32{ 54, // 87: management.ProxyPatch.forwarding_rules:type_name -> management.ForwardingRule 4, // 88: management.PolicyCompact.action:type_name -> management.RuleAction 2, // 89: management.PolicyCompact.protocol:type_name -> management.RuleProtocol - 80, // 90: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range - 48, // 91: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer - 75, // 92: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry - 35, // 93: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes - 74, // 94: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList - 76, // 95: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIndexes - 77, // 96: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList - 78, // 97: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet - 8, // 98: management.ManagementService.Login:input_type -> management.EncryptedMessage - 8, // 99: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 24, // 100: management.ManagementService.GetServerKey:input_type -> management.Empty - 24, // 101: management.ManagementService.isHealthy:input_type -> management.Empty - 8, // 102: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 103: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 104: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage - 8, // 105: management.ManagementService.Logout:input_type -> management.EncryptedMessage - 8, // 106: management.ManagementService.Job:input_type -> management.EncryptedMessage - 8, // 107: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage - 8, // 108: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage - 8, // 109: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage - 8, // 110: management.ManagementService.Login:output_type -> management.EncryptedMessage - 8, // 111: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 23, // 112: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 24, // 113: management.ManagementService.isHealthy:output_type -> management.Empty - 8, // 114: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 8, // 115: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage - 24, // 116: management.ManagementService.SyncMeta:output_type -> management.Empty - 24, // 117: management.ManagementService.Logout:output_type -> management.Empty - 8, // 118: management.ManagementService.Job:output_type -> management.EncryptedMessage - 8, // 119: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage - 8, // 120: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage - 8, // 121: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage - 110, // [110:122] is the sub-list for method output_type - 98, // [98:110] is the sub-list for method input_type - 98, // [98:98] is the sub-list for extension type_name - 98, // [98:98] is the sub-list for extension extendee - 0, // [0:98] is the sub-list for field type_name + 82, // 90: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range + 87, // 91: management.PolicyCompact.authorized_groups:type_name -> management.PolicyCompact.AuthorizedGroupsEntry + 69, // 92: management.PolicyCompact.source_resource:type_name -> management.ResourceCompact + 69, // 93: management.PolicyCompact.destination_resource:type_name -> management.ResourceCompact + 48, // 94: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer + 77, // 95: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry + 35, // 96: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes + 76, // 97: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList + 78, // 98: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIndexes + 79, // 99: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList + 80, // 100: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet + 70, // 101: management.PolicyCompact.AuthorizedGroupsEntry.value:type_name -> management.UserNameList + 8, // 102: management.ManagementService.Login:input_type -> management.EncryptedMessage + 8, // 103: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 24, // 104: management.ManagementService.GetServerKey:input_type -> management.Empty + 24, // 105: management.ManagementService.isHealthy:input_type -> management.Empty + 8, // 106: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 107: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage + 8, // 108: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage + 8, // 109: management.ManagementService.Logout:input_type -> management.EncryptedMessage + 8, // 110: management.ManagementService.Job:input_type -> management.EncryptedMessage + 8, // 111: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage + 8, // 112: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage + 8, // 113: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage + 8, // 114: management.ManagementService.Login:output_type -> management.EncryptedMessage + 8, // 115: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 23, // 116: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 24, // 117: management.ManagementService.isHealthy:output_type -> management.Empty + 8, // 118: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 8, // 119: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage + 24, // 120: management.ManagementService.SyncMeta:output_type -> management.Empty + 24, // 121: management.ManagementService.Logout:output_type -> management.Empty + 8, // 122: management.ManagementService.Job:output_type -> management.EncryptedMessage + 8, // 123: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage + 8, // 124: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage + 8, // 125: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage + 114, // [114:126] is the sub-list for method output_type + 102, // [102:114] is the sub-list for method input_type + 102, // [102:102] is the sub-list for extension type_name + 102, // [102:102] is the sub-list for extension extendee + 0, // [0:102] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -8284,7 +8592,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupCompact); i { + switch v := v.(*ResourceCompact); i { case 0: return &v.state case 1: @@ -8296,7 +8604,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DNSSettingsCompact); i { + switch v := v.(*UserNameList); i { case 0: return &v.state case 1: @@ -8308,7 +8616,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RouteRaw); i { + switch v := v.(*GroupCompact); i { case 0: return &v.state case 1: @@ -8320,7 +8628,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameServerGroupRaw); i { + switch v := v.(*DNSSettingsCompact); i { case 0: return &v.state case 1: @@ -8332,7 +8640,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkResourceRaw); i { + switch v := v.(*RouteRaw); i { case 0: return &v.state case 1: @@ -8344,7 +8652,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkRouterList); i { + switch v := v.(*NameServerGroupRaw); i { case 0: return &v.state case 1: @@ -8356,7 +8664,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkRouterEntry); i { + switch v := v.(*NetworkResourceRaw); i { case 0: return &v.state case 1: @@ -8368,7 +8676,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyIndexes); i { + switch v := v.(*NetworkRouterList); i { case 0: return &v.state case 1: @@ -8380,7 +8688,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserIDList); i { + switch v := v.(*NetworkRouterEntry); i { case 0: return &v.state case 1: @@ -8392,7 +8700,19 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerIndexSet); i { + switch v := v.(*PolicyIndexes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserIDList); i { case 0: return &v.state case 1: @@ -8404,6 +8724,18 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeerIndexSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PortInfo_Range); i { case 0: return &v.state @@ -8436,7 +8768,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 8, - NumMessages: 77, + NumMessages: 80, NumExtensions: 0, NumServices: 1, }, diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 6ba6730b81f..4031f755a70 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -577,6 +577,13 @@ enum RuleProtocol { UDP = 3; ICMP = 4; CUSTOM = 5; + // NETBIRD_SSH (types.PolicyRuleProtocolType "netbird-ssh") is the marker + // policy rule that drives SSH-server activation in Calculate(). The legacy + // proto.FirewallRule path doesn't ship this value (Calculate already + // expands SSH rules into TCP/22 before encoding), but the components path + // ships RAW policies — the client must see this protocol to derive + // AuthorizedUsers locally. + NETBIRD_SSH = 6; } enum RuleDirection { @@ -801,13 +808,21 @@ message NetworkMapComponentsFull { // Network resources (mirrors []*resourceTypes.NetworkResource). repeated NetworkResourceRaw network_resources = 17; - // Routers per network. Outer key: network id (xid string). Each entry is + // Routers per network. Outer key: network account_seq_id. Each entry is // the set of routers backing that network for this peer's view. - map routers_map = 18; - - // For each NetworkResource id (xid string), the indexes into policies[] + // + // INCOMPATIBLE WIRE CHANGE: the map key changed from string (network xid) + // to uint32 (account_seq_id). Field 18 was reused without a `reserved` + // entry because capability=3 has never been released — every cap=3 + // producer and consumer carries the same regenerated descriptor. Do NOT + // reuse this pattern for any further wire change once cap=3 ships. + map routers_map = 18; + + // For each NetworkResource account_seq_id, the indexes into policies[] // that apply to it. - map resource_policies_map = 19; + // + // INCOMPATIBLE WIRE CHANGE: see routers_map note above. + map resource_policies_map = 19; // Group-id (account_seq_id) → user ids authorized for SSH on members. map group_id_to_user_ids = 20; @@ -816,9 +831,11 @@ message NetworkMapComponentsFull { // authorized users for the receiving peer). repeated string allowed_user_ids = 21; - // Per posture-check id (xid string), the set of peer indexes that failed + // Per posture-check account_seq_id, the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. - map posture_failed_peers = 22; + // + // INCOMPATIBLE WIRE CHANGE: see routers_map note above. + map posture_failed_peers = 22; // Account-level DNS forwarder port (mirrors the legacy // proto.DNSConfig.ForwarderPort). Computed by the controller from peer @@ -832,9 +849,15 @@ message NetworkMapComponentsFull { // a unified merged result regardless of source. ProxyPatch proxy_patch = 24; + // SSH UserIDClaim — server-side HttpServerConfig.AuthUserIDClaim, or + // "sub" by default. Populated in proto.SSHAuth.UserIDClaim when the + // client rebuilds the NetworkMap from this envelope. Empty when the + // account has no AuthorizedUsers (and thus no SshAuth to populate). + string user_id_claim = 25; + // Reserved for future component additions (incremental_serial, parent_seq, // etc.) without forcing a renumber. - reserved 25 to 50; + reserved 26 to 50; } // ProxyPatch carries NetworkMap fragments that don't fit the component-graph @@ -925,6 +948,32 @@ message PeerCompact { // makes a fresh peer immediately expired iff login_expiration_enabled is // true — the same semantics as types.Peer.GetLastLogin). int64 last_login_unix_nano = 9; + + // True when the peer has an SSH server enabled locally. Used by the + // legacy SSH path in Calculate() (`policyRuleImpliesLegacySSH`): a rule + // with protocol ALL/TCP-with-SSH-ports activates SSH for the receiving + // peer when this bit is set, even without an explicit NetbirdSSH rule. + bool ssh_enabled = 10; + + reserved 11; // was: id (string xid) + + // Mirror of types.Peer.SupportsIPv6() — !Meta.Flags.DisableIPv6 && + // HasCapability(PeerCapabilityIPv6Overlay). Used by the local peer's + // Calculate() when deciding whether to emit IPv6 firewall rules + // (appendIPv6FirewallRule) against this peer's IPv6 address. + bool supports_ipv6 = 12; + + // Mirror of types.Peer.SupportsSourcePrefixes() — + // HasCapability(PeerCapabilitySourcePrefixes). Determines whether the + // local peer's Calculate() emits SourcePrefixes alongside legacy PeerIP + // fields in proto.FirewallRule. + bool supports_source_prefixes = 13; + + // Mirror of types.Peer.Meta.Flags.ServerSSHAllowed. Read by Calculate() + // when expanding TCP port-22 firewall rules — the native SSH companion + // (port 22022) is only added when this flag is set and the peer agent + // version supports it. + bool server_ssh_allowed = 14; } // PolicyCompact is the compact form of a policy rule. Group references use @@ -933,7 +982,9 @@ message PeerCompact { // on the client (ingress when the peer is in destination_group_ids, egress // when in source_group_ids; both when bidirectional). message PolicyCompact { - // Per-account integer id (matches policies.account_seq_id). + // Per-account integer id (matches policies.account_seq_id). Used as a + // stable reference for ResourcePoliciesMap.indexes and future delta + // updates (Step 3). uint32 id = 1; RuleAction action = 2; @@ -949,6 +1000,55 @@ message PolicyCompact { // Group ids (account_seq_id) of source / destination groups. repeated uint32 source_group_ids = 7; repeated uint32 destination_group_ids = 8; + + reserved 9; // was: xid (string) + + // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's + // applicable group ids (account_seq_id) to a list of local-user names — + // when a peer in one of those groups is the SSH destination, the named + // local users gain access. AuthorizedUser is the single-user form + // (legacy: rule scopes SSH to one specific user id). + // + // Both fields are only consumed by Calculate() when the rule's protocol + // is NetbirdSSH (or the legacy implicit-SSH heuristic). + map authorized_groups = 10; + string authorized_user = 11; + + // Resource-typed rule sources/destinations. When a rule targets a specific + // peer (rather than groups), Calculate() reads SourceResource / + // DestinationResource — without these the rule's connection resources + // can't be produced on the client. ResourceCompact's peer_index refers to + // NetworkMapComponentsFull.peers; type is the raw ResourceType string + // ("peer", "host", "subnet", "domain"). Only "peer" is meaningful for + // Calculate's resource-typed rule path today. + ResourceCompact source_resource = 12; + ResourceCompact destination_resource = 13; + + // Posture-check seq ids gating this policy's source peers. Calculate() + // reads them when filtering rule peers (peers that fail any listed check + // are dropped from sourcePeers). Match keys in + // NetworkMapComponentsFull.posture_failed_peers. + repeated uint32 source_posture_check_seq_ids = 15; + + reserved 14; // was: source_posture_check_ids (repeated string xid) +} + +// ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry +// rule.SourceResource / rule.DestinationResource when the rule targets a +// specific resource (typically a peer) rather than groups. +// peer_index_set tells whether peer_index is valid (proto3 uint32 cannot +// disambiguate "0" from "unset"); set only when type == "peer". +message ResourceCompact { + string type = 1; + bool peer_index_set = 2; + uint32 peer_index = 3; + reserved 4; // future: host/subnet/domain references when needed +} + +// UserNameList is a list of local-user names — used as the value type in +// PolicyCompact.authorized_groups. +message UserNameList { + repeated string names = 1; } // GroupCompact is the wire-shape of a group: per-account integer id, optional @@ -1007,6 +1107,8 @@ message RouteRaw { repeated uint32 group_ids = 14; repeated uint32 access_control_group_ids = 15; bool skip_auto_apply = 16; + + reserved 17; // was: xid (string) } // NameServerGroupRaw mirrors *nbdns.NameServerGroup. Distinct from the @@ -1027,9 +1129,15 @@ message NameServerGroupRaw { } // NetworkResourceRaw mirrors *resourceTypes.NetworkResource. +// +// INCOMPATIBLE WIRE CHANGE: field 2 changed from `string network_id` (xid) +// to `uint32 network_seq` without a `reserved` entry. Safe only because +// capability=3 has never been released — every cap=3 producer and consumer +// carries the same regenerated descriptor. Do NOT reuse this pattern once +// cap=3 ships. message NetworkResourceRaw { uint32 id = 1; // network_resources.account_seq_id - string network_id = 2; // xid string — networks have no seq id today + uint32 network_seq = 2; // networks.account_seq_id (replaces xid) string name = 3; string description = 4; // Resource type: "host" / "subnet" / "domain". @@ -1038,6 +1146,7 @@ message NetworkResourceRaw { string domain_value = 7; // resource.Domain string prefix_cidr = 8; bool enabled = 9; + reserved 10; // was: xid (string) } // NetworkRouterList carries the routers backing one network. From 728057ef15f5a34e22ea03028846c10529213aac Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 12 May 2026 16:54:56 +0200 Subject: [PATCH 09/42] missed files for client side and shared files --- .../grpc/components_envelope_response_test.go | 186 ++++++ shared/management/networkmap/decode.go | 586 ++++++++++++++++++ shared/management/networkmap/encode.go | 323 ++++++++++ shared/management/networkmap/envelope.go | 204 ++++++ shared/management/networkmap/envelope_test.go | 173 ++++++ 5 files changed, 1472 insertions(+) create mode 100644 management/internals/shared/grpc/components_envelope_response_test.go create mode 100644 shared/management/networkmap/decode.go create mode 100644 shared/management/networkmap/encode.go create mode 100644 shared/management/networkmap/envelope.go create mode 100644 shared/management/networkmap/envelope_test.go diff --git a/management/internals/shared/grpc/components_envelope_response_test.go b/management/internals/shared/grpc/components_envelope_response_test.go new file mode 100644 index 00000000000..dfb6b573424 --- /dev/null +++ b/management/internals/shared/grpc/components_envelope_response_test.go @@ -0,0 +1,186 @@ +package grpc + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" +) + +// TestComputeSSHEnabledForPeer covers both Calculate-mirroring branches: +// explicit NetbirdSSH protocol, and the legacy implicit case where a +// TCP/22 (or 22022 / ALL / port-range-covering-22) rule activates SSH when +// the destination peer has SSHEnabled=true locally. Belt-and-suspenders for +// the B1 fix that the prod-DB equivalence test alone wouldn't have caught +// if no account had this combination. +func TestComputeSSHEnabledForPeer(t *testing.T) { + const targetPeerID = "target" + const targetGroupID = "g_dst" + + mkComponents := func(rule *types.PolicyRule, sshEnabled bool) (*types.NetworkMapComponents, *nbpeer.Peer) { + peer := &nbpeer.Peer{ID: targetPeerID, SSHEnabled: sshEnabled} + group := &types.Group{ID: targetGroupID, Name: "dst", Peers: []string{targetPeerID}} + return &types.NetworkMapComponents{ + Peers: map[string]*nbpeer.Peer{targetPeerID: peer}, + Groups: map[string]*types.Group{targetGroupID: group}, + Policies: []*types.Policy{{ + ID: "p", + Enabled: true, + Rules: []*types.PolicyRule{rule}, + }}, + }, peer + } + + cases := []struct { + name string + peerSSH bool + rule types.PolicyRule + wantEnabled bool + }{ + { + name: "explicit-netbird-ssh-activates-regardless-of-peer-ssh", + peerSSH: false, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolNetbirdSSH, + Destinations: []string{targetGroupID}, + }, + wantEnabled: true, + }, + { + name: "implicit-tcp-22-with-peer-ssh", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"22"}, + Destinations: []string{targetGroupID}, + }, + wantEnabled: true, + }, + { + name: "implicit-tcp-22-without-peer-ssh-disabled", + peerSSH: false, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"22"}, + Destinations: []string{targetGroupID}, + }, + wantEnabled: false, + }, + { + name: "implicit-tcp-22022-with-peer-ssh", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"22022"}, + Destinations: []string{targetGroupID}, + }, + wantEnabled: true, + }, + { + name: "implicit-all-protocol-with-peer-ssh", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolALL, + Destinations: []string{targetGroupID}, + }, + wantEnabled: true, + }, + { + name: "implicit-port-range-covers-22", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: true, + Protocol: types.PolicyRuleProtocolTCP, + PortRanges: []types.RulePortRange{{Start: 20, End: 30}}, + Destinations: []string{targetGroupID}, + }, + wantEnabled: true, + }, + { + name: "tcp-80-no-ssh", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"80"}, + Destinations: []string{targetGroupID}, + }, + wantEnabled: false, + }, + { + name: "disabled-rule-skipped", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: false, Protocol: types.PolicyRuleProtocolNetbirdSSH, + Destinations: []string{targetGroupID}, + }, + wantEnabled: false, + }, + { + name: "peer-not-in-destinations", + peerSSH: true, + rule: types.PolicyRule{ + Enabled: true, Protocol: types.PolicyRuleProtocolNetbirdSSH, + Destinations: []string{"g_other"}, // target not in this group + }, + wantEnabled: false, + }, + { + name: "peer-typed-destination-resource-matches", + peerSSH: false, + rule: types.PolicyRule{ + Enabled: true, + Protocol: types.PolicyRuleProtocolNetbirdSSH, + DestinationResource: types.Resource{ID: targetPeerID, Type: types.ResourceTypePeer}, + }, + wantEnabled: true, + }, + { + name: "non-peer-destination-resource-falls-through-to-groups", + peerSSH: false, + rule: types.PolicyRule{ + Enabled: true, + Protocol: types.PolicyRuleProtocolNetbirdSSH, + DestinationResource: types.Resource{ID: targetPeerID, Type: "host"}, // wrong type + Destinations: []string{targetGroupID}, // saved by group fallback + }, + wantEnabled: true, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + c, peer := mkComponents(&tc.rule, tc.peerSSH) + got := computeSSHEnabledForPeer(c, peer) + assert.Equal(t, tc.wantEnabled, got) + }) + } +} + +// TestComputeSSHEnabledForPeer_TargetMissingFromComponents covers the +// belt-and-suspenders presence guard mirroring Calculate's +// getAllPeersFromGroups invariant. +func TestComputeSSHEnabledForPeer_TargetMissingFromComponents(t *testing.T) { + peer := &nbpeer.Peer{ID: "missing", SSHEnabled: true} + c := &types.NetworkMapComponents{ + Peers: map[string]*nbpeer.Peer{}, // target peer NOT present + Groups: map[string]*types.Group{ + "g": {ID: "g", Peers: []string{"missing"}}, + }, + Policies: []*types.Policy{{ + ID: "p", Enabled: true, + Rules: []*types.PolicyRule{{ + Enabled: true, Protocol: types.PolicyRuleProtocolNetbirdSSH, + Destinations: []string{"g"}, + }}, + }}, + } + assert.False(t, computeSSHEnabledForPeer(c, peer), + "missing target peer must short-circuit to false, not consult policies") +} + +// TestComputeSSHEnabledForPeer_NilInputs guards the cheap nil-checks at +// function entry — Calculate doesn't accept nil either, but the helper is +// exported indirectly via ToComponentSyncResponse and may receive nil +// components on graceful-degrade paths. +func TestComputeSSHEnabledForPeer_NilInputs(t *testing.T) { + assert.False(t, computeSSHEnabledForPeer(nil, &nbpeer.Peer{ID: "x"})) + assert.False(t, computeSSHEnabledForPeer(&types.NetworkMapComponents{}, nil)) +} diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go new file mode 100644 index 00000000000..84b10498612 --- /dev/null +++ b/shared/management/networkmap/decode.go @@ -0,0 +1,586 @@ +package networkmap + +import ( + "encoding/base64" + "fmt" + "net" + "net/netip" + "strconv" + "time" + + nbdns "github.com/netbirdio/netbird/dns" + resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" + routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" + nbroute "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/shared/management/domain" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// DecodeEnvelope converts a NetworkMapEnvelope into a NetworkMapComponents +// the client can run Calculate() over. Every ID-reference on the wire is a +// uint32 (peer index or account_seq_id) — no xid strings travel. The decoder +// synthesises consistent string IDs from the uint32s so the reconstructed +// components struct round-trips through Calculate exactly the way the +// server-side typed components would. +// +// Synthetic ID scheme (underscore-separated, visually distinct from the xid +// format Calculate would put in log lines under the legacy path): +// +// Peers "p_" // envelope.peers is index-addressed +// Groups "g_" +// Policies "pol_" // 1 rule per policy +// Routes "r_" +// Network resources "nres_" +// Posture checks "pc_" +// Networks "net_" +// Nameserver groups "nsg_" +func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, error) { + if env == nil { + return nil, fmt.Errorf("nil envelope") + } + full := env.GetFull() + if full == nil { + return nil, fmt.Errorf("envelope has no Full payload") + } + + c := &types.NetworkMapComponents{ + PeerID: "", // engine fills its own peer id from PeerConfig + Network: decodeAccountNetwork(full.Network), + AccountSettings: decodeAccountSettings(full.AccountSettings), + CustomZoneDomain: full.CustomZoneDomain, + Peers: make(map[string]*nbpeer.Peer, len(full.Peers)), + Groups: make(map[string]*types.Group, len(full.Groups)), + Policies: make([]*types.Policy, 0, len(full.Policies)), + Routes: make([]*nbroute.Route, 0, len(full.Routes)), + NameServerGroups: make([]*nbdns.NameServerGroup, 0, len(full.NameserverGroups)), + AllDNSRecords: decodeSimpleRecords(full.AllDnsRecords), + AccountZones: decodeCustomZones(full.AccountZones), + ResourcePoliciesMap: make(map[string][]*types.Policy), + RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), + NetworkResources: make([]*resourceTypes.NetworkResource, 0, len(full.NetworkResources)), + RouterPeers: make(map[string]*nbpeer.Peer), + AllowedUserIDs: stringSliceToSet(full.AllowedUserIds), + PostureFailedPeers: make(map[string]map[string]struct{}, len(full.PostureFailedPeers)), + GroupIDToUserIDs: make(map[string][]string, len(full.GroupIdToUserIds)), + } + + if full.DnsSettings != nil { + c.DNSSettings = &types.DNSSettings{ + DisabledManagementGroups: groupIDsFromSeqs(full.DnsSettings.DisabledManagementGroupIds), + } + } else { + c.DNSSettings = &types.DNSSettings{} + } + + // Phase 1: peers. The envelope's peers slice is index-addressed; we + // build a peerOrder lookup for downstream references. Peer.ID is + // synthesized from the peer's wire index — wire format ships no xid + // for peers (and never has). + peerIDByIndex := make([]string, len(full.Peers)) + for idx, pc := range full.Peers { + peerID := synthPeerID(uint32(idx)) + peer := decodePeerCompact(pc, peerID, full.AgentVersions) + c.Peers[peerID] = peer + peerIDByIndex[idx] = peerID + } + + // Phase 2: groups. AccountSeqID becomes both the synthesized string ID + // and the GroupCompact.id wire value. + for _, gc := range full.Groups { + groupID := synthGroupID(gc.Id) + peerIDs := make([]string, 0, len(gc.PeerIndexes)) + for _, idx := range gc.PeerIndexes { + if int(idx) < len(peerIDByIndex) { + peerIDs = append(peerIDs, peerIDByIndex[idx]) + } + } + c.Groups[groupID] = &types.Group{ + ID: groupID, + AccountSeqID: gc.Id, + Name: gc.Name, + Peers: peerIDs, + } + } + + // Phase 3: policies (PolicyCompact = one rule per entry; current data + // model is 1 rule per policy). Policy.ID is synthesized from the + // per-account seq id; proto.FirewallRule.PolicyID downstream carries + // the same synth string (no xid on the wire). + for _, pc := range full.Policies { + policyID := synthPolicyID(pc.Id) + c.Policies = append(c.Policies, decodePolicyCompact(pc, policyID, peerIDByIndex)) + } + + // Phase 4: routes. + for _, rr := range full.Routes { + c.Routes = append(c.Routes, decodeRouteRaw(rr, peerIDByIndex)) + } + + // Phase 5: NSGs. + for _, nsg := range full.NameserverGroups { + c.NameServerGroups = append(c.NameServerGroups, decodeNameServerGroupRaw(nsg)) + } + + // Phase 6: network resources. + for _, nr := range full.NetworkResources { + c.NetworkResources = append(c.NetworkResources, decodeNetworkResource(nr)) + } + + // Phase 7: routers_map (outer key = network seq id, inner key = peer-id + // reconstructed from peer_index). Synthesized network id is "net_". + for networkSeq, list := range full.RoutersMap { + networkID := synthNetworkID(networkSeq) + inner := make(map[string]*routerTypes.NetworkRouter, len(list.Entries)) + for _, entry := range list.Entries { + if !entry.PeerIndexSet { + continue + } + if int(entry.PeerIndex) >= len(peerIDByIndex) { + continue + } + peerID := peerIDByIndex[entry.PeerIndex] + inner[peerID] = &routerTypes.NetworkRouter{ + ID: "", + NetworkID: networkID, + AccountSeqID: entry.Id, + Peer: peerID, + PeerGroups: groupIDsFromSeqs(entry.PeerGroupIds), + Masquerade: entry.Masquerade, + Metric: int(entry.Metric), + Enabled: entry.Enabled, + } + } + if len(inner) > 0 { + c.RoutersMap[networkID] = inner + } + } + + // Phase 8: resource_policies_map (resource seq id → list of *types.Policy + // pointers from the decoded policies slice). Resource ID is synthesized + // the same way as in decodeNetworkResource. + for resourceSeq, idxs := range full.ResourcePoliciesMap { + if len(idxs.Indexes) == 0 { + continue + } + resourceID := synthNetworkResourceID(resourceSeq) + policies := make([]*types.Policy, 0, len(idxs.Indexes)) + for _, i := range idxs.Indexes { + if int(i) < len(c.Policies) { + policies = append(policies, c.Policies[i]) + } + } + if len(policies) > 0 { + c.ResourcePoliciesMap[resourceID] = policies + } + } + + // Phase 9: group_id_to_user_ids — wire keys are seq ids, synth to strings. + for groupSeq, list := range full.GroupIdToUserIds { + c.GroupIDToUserIDs[synthGroupID(groupSeq)] = append([]string(nil), list.UserIds...) + } + + // Phase 10: posture_failed_peers — wire keys are posture-check seq ids, + // values are peer indexes that need to be turned into peer ids. PolicyRule + // SourcePostureChecks (also synth ids) reference the same key space. + for checkSeq, set := range full.PostureFailedPeers { + checkID := synthPostureCheckID(checkSeq) + failed := make(map[string]struct{}, len(set.PeerIndexes)) + for _, idx := range set.PeerIndexes { + if int(idx) < len(peerIDByIndex) { + failed[peerIDByIndex[idx]] = struct{}{} + } + } + if len(failed) > 0 { + c.PostureFailedPeers[checkID] = failed + } + } + + // Phase 11: router_peer_indexes — peers that act as routers. They're + // already in c.Peers (router peers are appended to the global peers + // list by the encoder); RouterPeers is the subset. + for _, idx := range full.RouterPeerIndexes { + if int(idx) < len(peerIDByIndex) { + peerID := peerIDByIndex[idx] + c.RouterPeers[peerID] = c.Peers[peerID] + } + } + + return c, nil +} + +func decodeAccountNetwork(an *proto.AccountNetwork) *types.Network { + if an == nil { + return nil + } + n := &types.Network{ + Identifier: an.Identifier, + Dns: an.Dns, + Serial: an.Serial, + } + if an.NetCidr != "" { + if _, ipnet, err := net.ParseCIDR(an.NetCidr); err == nil && ipnet != nil { + n.Net = *ipnet + } + } + if an.NetV6Cidr != "" { + if _, ipnet, err := net.ParseCIDR(an.NetV6Cidr); err == nil && ipnet != nil { + n.NetV6 = *ipnet + } + } + return n +} + +func decodeAccountSettings(as *proto.AccountSettingsCompact) *types.AccountSettingsInfo { + if as == nil { + return &types.AccountSettingsInfo{} + } + return &types.AccountSettingsInfo{ + PeerLoginExpirationEnabled: as.PeerLoginExpirationEnabled, + PeerLoginExpiration: time.Duration(as.PeerLoginExpirationNs), + } +} + +func decodePeerCompact(pc *proto.PeerCompact, peerID string, agentVersions []string) *nbpeer.Peer { + var caps []int32 + if pc.SupportsSourcePrefixes { + caps = append(caps, nbpeer.PeerCapabilitySourcePrefixes) + } + if pc.SupportsIpv6 { + caps = append(caps, nbpeer.PeerCapabilityIPv6Overlay) + } + peer := &nbpeer.Peer{ + ID: peerID, + Key: encodeWgKeyBase64(pc.WgPubKey), + SSHKey: string(pc.SshPubKey), + SSHEnabled: pc.SshEnabled, + DNSLabel: pc.DnsLabel, + LoginExpirationEnabled: pc.LoginExpirationEnabled, + Meta: nbpeer.PeerSystemMeta{ + WtVersion: lookupAgentVersion(agentVersions, pc.AgentVersionIdx), + Capabilities: caps, + Flags: nbpeer.Flags{ + ServerSSHAllowed: pc.ServerSshAllowed, + }, + }, + } + if pc.AddedWithSsoLogin { + // Set a non-empty UserID so (*Peer).AddedWithSSOLogin() returns true. + // The original UserID isn't on the wire; the value is intentionally + // visibly synthetic so any future consumer that mistakes UserID for a + // real account user xid won't silently match (or worse, write the + // sentinel into a downstream record). + peer.UserID = "" + } + if pc.LastLoginUnixNano != 0 { + t := time.Unix(0, pc.LastLoginUnixNano) + peer.LastLogin = &t + } + switch len(pc.Ip) { + case 4: + peer.IP = netip.AddrFrom4([4]byte{pc.Ip[0], pc.Ip[1], pc.Ip[2], pc.Ip[3]}) + case 16: + var a [16]byte + copy(a[:], pc.Ip) + peer.IP = netip.AddrFrom16(a) + } + if len(pc.Ipv6) == 16 { + var a [16]byte + copy(a[:], pc.Ipv6) + peer.IPv6 = netip.AddrFrom16(a) + } + return peer +} + +func decodePolicyCompact(pc *proto.PolicyCompact, policyID string, peerIDByIndex []string) *types.Policy { + rule := &types.PolicyRule{ + ID: policyID, // 1 rule per policy → reuse synthesized id + PolicyID: policyID, + Enabled: true, + Action: actionFromProto(pc.Action), + Protocol: protocolFromProto(pc.Protocol), + Bidirectional: pc.Bidirectional, + Ports: uint32SliceToStrings(pc.Ports), + PortRanges: portRangesFromProto(pc.PortRanges), + Sources: groupIDsFromSeqs(pc.SourceGroupIds), + Destinations: groupIDsFromSeqs(pc.DestinationGroupIds), + AuthorizedUser: pc.AuthorizedUser, + AuthorizedGroups: authorizedGroupsFromProto(pc.AuthorizedGroups), + SourceResource: resourceFromProto(pc.SourceResource, peerIDByIndex), + DestinationResource: resourceFromProto(pc.DestinationResource, peerIDByIndex), + } + return &types.Policy{ + ID: policyID, + AccountSeqID: pc.Id, + Enabled: true, + Rules: []*types.PolicyRule{rule}, + SourcePostureChecks: postureCheckIDsFromSeqs(pc.SourcePostureCheckSeqIds), + } +} + +// resourceFromProto rebuilds types.Resource. For peer-typed resources the +// peer reference is reconstructed from the envelope's peer index — wire +// format ships no xid for peers, so we use the synthesized peer id. +func resourceFromProto(r *proto.ResourceCompact, peerIDByIndex []string) types.Resource { + if r == nil { + return types.Resource{} + } + out := types.Resource{Type: types.ResourceType(r.Type)} + if r.PeerIndexSet && int(r.PeerIndex) < len(peerIDByIndex) { + out.ID = peerIDByIndex[r.PeerIndex] + } + return out +} + +// postureCheckIDsFromSeqs synths posture-check ids from per-account seq ids. +// Mirrors groupIDsFromSeqs. +func postureCheckIDsFromSeqs(seqs []uint32) []string { + if len(seqs) == 0 { + return nil + } + out := make([]string, len(seqs)) + for i, s := range seqs { + out[i] = synthPostureCheckID(s) + } + return out +} + +// authorizedGroupsFromProto inverts encodeAuthorizedGroups: the wire form +// keys by group account_seq_id, the typed PolicyRule field keys by group +// xid string. We rebuild using the same synthetic scheme the rest of the +// decoder uses ("g"). +func authorizedGroupsFromProto(m map[uint32]*proto.UserNameList) map[string][]string { + if len(m) == 0 { + return nil + } + out := make(map[string][]string, len(m)) + for seq, list := range m { + if list == nil { + continue + } + out[synthGroupID(seq)] = append([]string(nil), list.Names...) + } + return out +} + +func decodeRouteRaw(rr *proto.RouteRaw, peerIDByIndex []string) *nbroute.Route { + r := &nbroute.Route{ + ID: nbroute.ID(synthRouteID(rr.Id)), + AccountSeqID: rr.Id, + NetID: nbroute.NetID(rr.NetId), + Description: rr.Description, + Domains: domainsFromPunycode(rr.Domains), + KeepRoute: rr.KeepRoute, + NetworkType: nbroute.NetworkType(rr.NetworkType), + Masquerade: rr.Masquerade, + Metric: int(rr.Metric), + Enabled: rr.Enabled, + Groups: groupIDsFromSeqs(rr.GroupIds), + AccessControlGroups: groupIDsFromSeqs(rr.AccessControlGroupIds), + PeerGroups: groupIDsFromSeqs(rr.PeerGroupIds), + SkipAutoApply: rr.SkipAutoApply, + } + if rr.NetworkCidr != "" { + if p, err := netip.ParsePrefix(rr.NetworkCidr); err == nil { + r.Network = p + } + } + if rr.PeerIndexSet && int(rr.PeerIndex) < len(peerIDByIndex) { + r.Peer = peerIDByIndex[rr.PeerIndex] + } + return r +} + +func decodeNameServerGroupRaw(nsg *proto.NameServerGroupRaw) *nbdns.NameServerGroup { + out := &nbdns.NameServerGroup{ + ID: synthNameServerGroupID(nsg.Id), + AccountSeqID: nsg.Id, + Name: nsg.Name, + Description: nsg.Description, + Groups: groupIDsFromSeqs(nsg.GroupIds), + Primary: nsg.Primary, + Domains: nsg.Domains, + Enabled: nsg.Enabled, + SearchDomainsEnabled: nsg.SearchDomainsEnabled, + NameServers: make([]nbdns.NameServer, 0, len(nsg.Nameservers)), + } + for _, ns := range nsg.Nameservers { + if addr, err := netip.ParseAddr(ns.IP); err == nil { + out.NameServers = append(out.NameServers, nbdns.NameServer{ + IP: addr, + NSType: nbdns.NameServerType(ns.NSType), + Port: int(ns.Port), + }) + } + } + return out +} + +func decodeNetworkResource(nr *proto.NetworkResourceRaw) *resourceTypes.NetworkResource { + out := &resourceTypes.NetworkResource{ + ID: synthNetworkResourceID(nr.Id), + AccountSeqID: nr.Id, + NetworkID: synthNetworkID(nr.NetworkSeq), + Name: nr.Name, + Description: nr.Description, + Type: resourceTypes.NetworkResourceType(nr.Type), + Address: nr.Address, + Domain: nr.DomainValue, + Enabled: nr.Enabled, + } + if nr.PrefixCidr != "" { + if p, err := netip.ParsePrefix(nr.PrefixCidr); err == nil { + out.Prefix = p + } + } + return out +} + +func decodeSimpleRecords(records []*proto.SimpleRecord) []nbdns.SimpleRecord { + out := make([]nbdns.SimpleRecord, 0, len(records)) + for _, r := range records { + out = append(out, nbdns.SimpleRecord{ + Name: r.Name, + Type: int(r.Type), + Class: r.Class, + TTL: int(r.TTL), + RData: r.RData, + }) + } + return out +} + +func decodeCustomZones(zones []*proto.CustomZone) []nbdns.CustomZone { + out := make([]nbdns.CustomZone, 0, len(zones)) + for _, z := range zones { + out = append(out, nbdns.CustomZone{ + Domain: z.Domain, + Records: decodeSimpleRecords(z.Records), + SearchDomainDisabled: z.SearchDomainDisabled, + NonAuthoritative: z.NonAuthoritative, + }) + } + return out +} + +// Synthetic ID generators — deterministic given the same wire input. +// Underscore-separated ("p_", "pol_", ...) so they're visually +// distinct in operator logs. fmt.Sprintf would dominate the decode hot path +// on large accounts (a 10k-peer envelope produces ~50k synth calls); the +// strconv.AppendUint builder keeps it allocation-light. +func synthID(prefix string, n uint32) string { + buf := make([]byte, 0, len(prefix)+10) + buf = append(buf, prefix...) + buf = strconv.AppendUint(buf, uint64(n), 10) + return string(buf) +} + +func synthPeerID(idx uint32) string { return synthID("p_", idx) } +func synthGroupID(seq uint32) string { return synthID("g_", seq) } +func synthPolicyID(seq uint32) string { return synthID("pol_", seq) } +func synthRouteID(seq uint32) string { return synthID("r_", seq) } +func synthNetworkResourceID(seq uint32) string { return synthID("nres_", seq) } +func synthPostureCheckID(seq uint32) string { return synthID("pc_", seq) } +func synthNetworkID(seq uint32) string { return synthID("net_", seq) } +func synthNameServerGroupID(seq uint32) string { return synthID("nsg_", seq) } + +func groupIDsFromSeqs(seqs []uint32) []string { + if len(seqs) == 0 { + return nil + } + out := make([]string, len(seqs)) + for i, s := range seqs { + out[i] = synthGroupID(s) + } + return out +} + +func uint32SliceToStrings(ports []uint32) []string { + if len(ports) == 0 { + return nil + } + out := make([]string, len(ports)) + for i, p := range ports { + out[i] = strconv.FormatUint(uint64(p), 10) + } + return out +} + +func portRangesFromProto(ranges []*proto.PortInfo_Range) []types.RulePortRange { + if len(ranges) == 0 { + return nil + } + out := make([]types.RulePortRange, 0, len(ranges)) + for _, r := range ranges { + out = append(out, types.RulePortRange{ + Start: uint16(r.Start), + End: uint16(r.End), + }) + } + return out +} + +func actionFromProto(a proto.RuleAction) types.PolicyTrafficActionType { + if a == proto.RuleAction_DROP { + return types.PolicyTrafficActionDrop + } + return types.PolicyTrafficActionAccept +} + +func protocolFromProto(p proto.RuleProtocol) types.PolicyRuleProtocolType { + switch p { + case proto.RuleProtocol_TCP: + return types.PolicyRuleProtocolTCP + case proto.RuleProtocol_UDP: + return types.PolicyRuleProtocolUDP + case proto.RuleProtocol_ICMP: + return types.PolicyRuleProtocolICMP + case proto.RuleProtocol_ALL: + return types.PolicyRuleProtocolALL + case proto.RuleProtocol_NETBIRD_SSH: + return types.PolicyRuleProtocolNetbirdSSH + default: + return types.PolicyRuleProtocolALL + } +} + +func encodeWgKeyBase64(raw []byte) string { + if len(raw) != 32 { + return "" + } + return base64.StdEncoding.EncodeToString(raw) +} + +func lookupAgentVersion(table []string, idx uint32) string { + if int(idx) < len(table) { + return table[idx] + } + return "" +} + +func stringSliceToSet(s []string) map[string]struct{} { + if len(s) == 0 { + return nil + } + out := make(map[string]struct{}, len(s)) + for _, v := range s { + out[v] = struct{}{} + } + return out +} + +// domainsFromPunycode is a thin wrapper that converts a punycode list back to +// the domain.List type the route.Route struct expects. It accepts the +// punycode strings as-is (no extra decoding) — symmetric with +// route.Domains.ToPunycodeList() used in the encoder. +func domainsFromPunycode(punycoded []string) domain.List { + if len(punycoded) == 0 { + return nil + } + out := make(domain.List, 0, len(punycoded)) + for _, d := range punycoded { + out = append(out, domain.Domain(d)) + } + return out +} diff --git a/shared/management/networkmap/encode.go b/shared/management/networkmap/encode.go new file mode 100644 index 00000000000..ebaede64aef --- /dev/null +++ b/shared/management/networkmap/encode.go @@ -0,0 +1,323 @@ +// Package networkmap contains the shared NetworkMap helpers that both the +// management server and the client agent need. +// +// The proto-conversion helpers (types.NetworkMap → proto.NetworkMap) live +// here so the client can run the same conversion locally after deriving its +// NetworkMap from a NetworkMapEnvelope, without taking a dependency on the +// server-side conversion package (which pulls in cloud integrations and is +// otherwise an unwanted internal import on the client). +// +// The helpers are pure functions over inputs — no caches, no IO, no logging +// beyond a context-aware error log when an individual user-id hash fails. +package networkmap + +import ( + "context" + + log "github.com/sirupsen/logrus" + goproto "google.golang.org/protobuf/proto" + + nbdns "github.com/netbirdio/netbird/dns" + "net/netip" + + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" + nbroute "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/shared/management/proto" + "github.com/netbirdio/netbird/shared/netiputil" + "github.com/netbirdio/netbird/shared/sshauth" +) + +// ToProtocolRoutes converts a slice of typed routes to their proto form. +func ToProtocolRoutes(routes []*nbroute.Route) []*proto.Route { + protoRoutes := make([]*proto.Route, 0, len(routes)) + for _, r := range routes { + protoRoutes = append(protoRoutes, ToProtocolRoute(r)) + } + return protoRoutes +} + +// ToProtocolRoute converts one typed route to its proto form. +func ToProtocolRoute(route *nbroute.Route) *proto.Route { + return &proto.Route{ + ID: string(route.ID), + NetID: string(route.NetID), + Network: route.Network.String(), + Domains: route.Domains.ToPunycodeList(), + NetworkType: int64(route.NetworkType), + Peer: route.Peer, + Metric: int64(route.Metric), + Masquerade: route.Masquerade, + KeepRoute: route.KeepRoute, + SkipAutoApply: route.SkipAutoApply, + } +} + +// ToProtocolFirewallRules converts the firewall rules to the protocol form. +// When useSourcePrefixes is true, the compact SourcePrefixes field is +// populated alongside the deprecated PeerIP for forward compatibility. +// Wildcard rules ("0.0.0.0") are expanded into separate v4/v6 SourcePrefixes +// when includeIPv6 is true. +func ToProtocolFirewallRules(rules []*types.FirewallRule, includeIPv6, useSourcePrefixes bool) []*proto.FirewallRule { + result := make([]*proto.FirewallRule, 0, len(rules)) + for i := range rules { + rule := rules[i] + + fwRule := &proto.FirewallRule{ + PolicyID: []byte(rule.PolicyID), + PeerIP: rule.PeerIP, //nolint:staticcheck // populated for backward compatibility + Direction: GetProtoDirection(rule.Direction), + Action: GetProtoAction(rule.Action), + Protocol: GetProtoProtocol(rule.Protocol), + Port: rule.Port, + } + + if useSourcePrefixes && rule.PeerIP != "" { + result = append(result, populateSourcePrefixes(fwRule, rule, includeIPv6)...) + } + + if ShouldUsePortRange(fwRule) { + fwRule.PortInfo = rule.PortRange.ToProto() + } + + result = append(result, fwRule) + } + return result +} + +// populateSourcePrefixes sets SourcePrefixes on fwRule and returns any +// additional rules needed (e.g. a v6 wildcard clone when the peer IP is +// unspecified). +func populateSourcePrefixes(fwRule *proto.FirewallRule, rule *types.FirewallRule, includeIPv6 bool) []*proto.FirewallRule { + addr, err := netip.ParseAddr(rule.PeerIP) + if err != nil { + return nil + } + + if !addr.IsUnspecified() { + fwRule.SourcePrefixes = [][]byte{netiputil.EncodeAddr(addr.Unmap())} + return nil + } + + v4Wildcard, _ := netiputil.EncodePrefix(netip.PrefixFrom(netip.IPv4Unspecified(), 0)) + fwRule.SourcePrefixes = [][]byte{v4Wildcard} + + if !includeIPv6 { + return nil + } + + v6Rule := goproto.Clone(fwRule).(*proto.FirewallRule) + v6Rule.PeerIP = "::" //nolint:staticcheck // populated for backward compatibility + v6Wildcard, _ := netiputil.EncodePrefix(netip.PrefixFrom(netip.IPv6Unspecified(), 0)) + v6Rule.SourcePrefixes = [][]byte{v6Wildcard} + if ShouldUsePortRange(v6Rule) { + v6Rule.PortInfo = rule.PortRange.ToProto() + } + return []*proto.FirewallRule{v6Rule} +} + +// GetProtoDirection converts the direction to proto.RuleDirection. +func GetProtoDirection(direction int) proto.RuleDirection { + if direction == types.FirewallRuleDirectionOUT { + return proto.RuleDirection_OUT + } + return proto.RuleDirection_IN +} + +// GetProtoAction converts the action to proto.RuleAction. +func GetProtoAction(action string) proto.RuleAction { + if action == string(types.PolicyTrafficActionDrop) { + return proto.RuleAction_DROP + } + return proto.RuleAction_ACCEPT +} + +// GetProtoProtocol converts the protocol to proto.RuleProtocol. +func GetProtoProtocol(protocol string) proto.RuleProtocol { + switch types.PolicyRuleProtocolType(protocol) { + case types.PolicyRuleProtocolALL: + return proto.RuleProtocol_ALL + case types.PolicyRuleProtocolTCP: + return proto.RuleProtocol_TCP + case types.PolicyRuleProtocolUDP: + return proto.RuleProtocol_UDP + case types.PolicyRuleProtocolICMP: + return proto.RuleProtocol_ICMP + case types.PolicyRuleProtocolNetbirdSSH: + return proto.RuleProtocol_NETBIRD_SSH + default: + return proto.RuleProtocol_UNKNOWN + } +} + +// GetProtoPortInfo converts route-firewall-rule port info to proto.PortInfo. +func GetProtoPortInfo(rule *types.RouteFirewallRule) *proto.PortInfo { + var portInfo proto.PortInfo + if rule.Port != 0 { + portInfo.PortSelection = &proto.PortInfo_Port{Port: uint32(rule.Port)} + } else if portRange := rule.PortRange; portRange.Start != 0 && portRange.End != 0 { + portInfo.PortSelection = &proto.PortInfo_Range_{ + Range: &proto.PortInfo_Range{ + Start: uint32(portRange.Start), + End: uint32(portRange.End), + }, + } + } + return &portInfo +} + +// ShouldUsePortRange reports whether the firewall rule should use a port +// range rather than a single port (TCP/UDP without a single port). +func ShouldUsePortRange(rule *proto.FirewallRule) bool { + return rule.Port == "" && (rule.Protocol == proto.RuleProtocol_UDP || rule.Protocol == proto.RuleProtocol_TCP) +} + +// ToProtocolRoutesFirewallRules converts a slice of typed route-firewall +// rules to proto. +func ToProtocolRoutesFirewallRules(rules []*types.RouteFirewallRule) []*proto.RouteFirewallRule { + result := make([]*proto.RouteFirewallRule, len(rules)) + for i := range rules { + rule := rules[i] + result[i] = &proto.RouteFirewallRule{ + SourceRanges: rule.SourceRanges, + Action: GetProtoAction(rule.Action), + Destination: rule.Destination, + Protocol: GetProtoProtocol(rule.Protocol), + PortInfo: GetProtoPortInfo(rule), + IsDynamic: rule.IsDynamic, + Domains: rule.Domains.ToPunycodeList(), + PolicyID: []byte(rule.PolicyID), + RouteID: string(rule.RouteID), + } + } + return result +} + +// ConvertToProtoCustomZone converts an nbdns.CustomZone to its proto form. +func ConvertToProtoCustomZone(zone nbdns.CustomZone) *proto.CustomZone { + protoZone := &proto.CustomZone{ + Domain: zone.Domain, + Records: make([]*proto.SimpleRecord, 0, len(zone.Records)), + SearchDomainDisabled: zone.SearchDomainDisabled, + NonAuthoritative: zone.NonAuthoritative, + } + for _, record := range zone.Records { + protoZone.Records = append(protoZone.Records, &proto.SimpleRecord{ + Name: record.Name, + Type: int64(record.Type), + Class: record.Class, + TTL: int64(record.TTL), + RData: record.RData, + }) + } + return protoZone +} + +// ConvertToProtoNameServerGroup converts a NameServerGroup to its proto form. +func ConvertToProtoNameServerGroup(nsGroup *nbdns.NameServerGroup) *proto.NameServerGroup { + protoGroup := &proto.NameServerGroup{ + Primary: nsGroup.Primary, + Domains: nsGroup.Domains, + SearchDomainsEnabled: nsGroup.SearchDomainsEnabled, + NameServers: make([]*proto.NameServer, 0, len(nsGroup.NameServers)), + } + for _, ns := range nsGroup.NameServers { + protoGroup.NameServers = append(protoGroup.NameServers, &proto.NameServer{ + IP: ns.IP.String(), + Port: int64(ns.Port), + NSType: int64(ns.NSType), + }) + } + return protoGroup +} + +// DNSConfigCache is the cache contract for amortising NameServerGroup +// proto-conversion across peers in the same account. Server uses a concrete +// implementation; client passes nil (no cross-peer caching needed when +// rebuilding a single NetworkMap from an envelope). +type DNSConfigCache interface { + GetNameServerGroup(key string) (*proto.NameServerGroup, bool) + SetNameServerGroup(key string, value *proto.NameServerGroup) +} + +// ToProtocolDNSConfig converts nbdns.Config to proto.DNSConfig. If cache is +// non-nil, NameServerGroup proto values are cached by NSG.ID across calls — +// the server amortises this across peers, the client passes nil. +func ToProtocolDNSConfig(update nbdns.Config, cache DNSConfigCache, forwardPort int64) *proto.DNSConfig { + protoUpdate := &proto.DNSConfig{ + ServiceEnable: update.ServiceEnable, + CustomZones: make([]*proto.CustomZone, 0, len(update.CustomZones)), + NameServerGroups: make([]*proto.NameServerGroup, 0, len(update.NameServerGroups)), + ForwarderPort: forwardPort, + } + + for _, zone := range update.CustomZones { + protoUpdate.CustomZones = append(protoUpdate.CustomZones, ConvertToProtoCustomZone(zone)) + } + + for _, nsGroup := range update.NameServerGroups { + if cache != nil { + if cachedGroup, exists := cache.GetNameServerGroup(nsGroup.ID); exists { + protoUpdate.NameServerGroups = append(protoUpdate.NameServerGroups, cachedGroup) + continue + } + } + protoGroup := ConvertToProtoNameServerGroup(nsGroup) + if cache != nil { + cache.SetNameServerGroup(nsGroup.ID, protoGroup) + } + protoUpdate.NameServerGroups = append(protoUpdate.NameServerGroups, protoGroup) + } + + return protoUpdate +} + +// AppendRemotePeerConfig appends typed peers as proto.RemotePeerConfig +// entries to dst and returns the result. +func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*nbpeer.Peer, dnsName string, includeIPv6 bool) []*proto.RemotePeerConfig { + for _, rPeer := range peers { + allowedIPs := []string{rPeer.IP.String() + "/32"} + if includeIPv6 && rPeer.IPv6.IsValid() { + allowedIPs = append(allowedIPs, rPeer.IPv6.String()+"/128") + } + dst = append(dst, &proto.RemotePeerConfig{ + WgPubKey: rPeer.Key, + AllowedIps: allowedIPs, + SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)}, + Fqdn: rPeer.FQDN(dnsName), + AgentVersion: rPeer.Meta.WtVersion, + }) + } + return dst +} + +// BuildAuthorizedUsersProto deduplicates user-IDs into a hashed list and +// builds per-machine-user index maps. Returns (hashedUsers, machineUsers). +// Errors from individual hash failures are logged via the provided context; +// they leave the offending user out of the result but don't abort the build. +func BuildAuthorizedUsersProto(ctx context.Context, authorizedUsers map[string]map[string]struct{}) ([][]byte, map[string]*proto.MachineUserIndexes) { + userIDToIndex := make(map[string]uint32) + var hashedUsers [][]byte + machineUsers := make(map[string]*proto.MachineUserIndexes, len(authorizedUsers)) + + for machineUser, users := range authorizedUsers { + indexes := make([]uint32, 0, len(users)) + for userID := range users { + idx, exists := userIDToIndex[userID] + if !exists { + hash, err := sshauth.HashUserID(userID) + if err != nil { + log.WithContext(ctx).Errorf("failed to hash user id %s: %v", userID, err) + continue + } + idx = uint32(len(hashedUsers)) + userIDToIndex[userID] = idx + hashedUsers = append(hashedUsers, hash[:]) + } + indexes = append(indexes, idx) + } + machineUsers[machineUser] = &proto.MachineUserIndexes{Indexes: indexes} + } + + return hashedUsers, machineUsers +} diff --git a/shared/management/networkmap/envelope.go b/shared/management/networkmap/envelope.go new file mode 100644 index 00000000000..295c26b7a82 --- /dev/null +++ b/shared/management/networkmap/envelope.go @@ -0,0 +1,204 @@ +package networkmap + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// EnvelopeResult is what the client engine consumes after receiving a +// component-format NetworkMap. Both fields are populated: +// +// - NetworkMap is the *proto.NetworkMap shape the engine reads today via +// update.GetNetworkMap() — built from the envelope's components by +// running Calculate() locally + converting back through the shared +// proto helpers + merging the optional ProxyPatch. +// - Components is the *types.NetworkMapComponents the engine retains so +// future incremental delta updates (Step 3) have a base to apply +// changes against. The client keeps it under its sync lock. +type EnvelopeResult struct { + NetworkMap *proto.NetworkMap + Components *types.NetworkMapComponents +} + +// EnvelopeToNetworkMap is the full client-side pipeline: decode the +// component envelope back to a typed NetworkMapComponents, run Calculate() +// locally to produce the typed NetworkMap, convert it to the wire form the +// engine consumes, and fold in any ProxyPatch the server attached. +// +// localPeerKey is the receiving peer's WG pub key (used to derive +// includeIPv6 / useSourcePrefixes from the receiving peer's own record in +// the components struct, mirroring legacy ToSyncResponse behaviour). +// +// dnsName is the account's DNS domain ("netbird.cloud" etc.); used when +// rebuilding the per-peer FQDNs that proto.RemotePeerConfig carries. +func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, localPeerKey, dnsName string) (*EnvelopeResult, error) { + components, err := DecodeEnvelope(env) + if err != nil { + return nil, fmt.Errorf("decode envelope: %w", err) + } + + // Find the receiving peer in the decoded components by WG key so we can + // derive its capabilities and set components.PeerID for Calculate(). The + // envelope.peers list is index-addressed; we synthesized IDs as "p". + localPeerID, localPeer := findPeerByWgKey(components, localPeerKey) + if localPeer == nil { + return nil, fmt.Errorf("receiving peer (wg_key prefix %q) not found among %d decoded peers — components have no PeerID, Calculate would return empty", trimKey(localPeerKey), len(components.Peers)) + } + components.PeerID = localPeerID + + includeIPv6 := localPeer != nil && localPeer.SupportsIPv6() && localPeer.IPv6.IsValid() + useSourcePrefixes := localPeer != nil && localPeer.SupportsSourcePrefixes() + + typedNM := components.Calculate(ctx) + + full := env.GetFull() + dnsFwdPort := int64(0) + if full != nil { + dnsFwdPort = full.DnsForwarderPort + } + + protoNM := &proto.NetworkMap{ + Serial: typedNM.Network.CurrentSerial(), + } + if full != nil { + protoNM.PeerConfig = full.PeerConfig + } + protoNM.Routes = ToProtocolRoutes(typedNM.Routes) + protoNM.DNSConfig = ToProtocolDNSConfig(typedNM.DNSConfig, nil, dnsFwdPort) + + remotePeers := AppendRemotePeerConfig(nil, typedNM.Peers, dnsName, includeIPv6) + protoNM.RemotePeers = remotePeers + protoNM.RemotePeersIsEmpty = len(remotePeers) == 0 + + protoNM.OfflinePeers = AppendRemotePeerConfig(nil, typedNM.OfflinePeers, dnsName, includeIPv6) + + firewallRules := ToProtocolFirewallRules(typedNM.FirewallRules, includeIPv6, useSourcePrefixes) + protoNM.FirewallRules = firewallRules + protoNM.FirewallRulesIsEmpty = len(firewallRules) == 0 + + routesFirewallRules := ToProtocolRoutesFirewallRules(typedNM.RoutesFirewallRules) + protoNM.RoutesFirewallRules = routesFirewallRules + protoNM.RoutesFirewallRulesIsEmpty = len(routesFirewallRules) == 0 + + if typedNM.AuthorizedUsers != nil { + hashedUsers, machineUsers := BuildAuthorizedUsersProto(ctx, typedNM.AuthorizedUsers) + userIDClaim := "" + if full != nil { + userIDClaim = full.UserIdClaim + } + protoNM.SshAuth = &proto.SSHAuth{ + AuthorizedUsers: hashedUsers, + MachineUsers: machineUsers, + UserIDClaim: userIDClaim, + } + } + + if typedNM.ForwardingRules != nil { + forwardingRules := make([]*proto.ForwardingRule, 0, len(typedNM.ForwardingRules)) + for _, rule := range typedNM.ForwardingRules { + forwardingRules = append(forwardingRules, rule.ToProto()) + } + protoNM.ForwardingRules = forwardingRules + } + + // Merge the proxy patch the server attached. Mirrors the legacy + // NetworkMap.Merge step that the server runs after Calculate(). + if full != nil && full.ProxyPatch != nil { + mergeProxyPatch(protoNM, full.ProxyPatch) + } + + return &EnvelopeResult{ + NetworkMap: protoNM, + Components: components, + }, nil +} + +// mergeProxyPatch folds a ProxyPatch's pre-expanded fragments into the +// proto.NetworkMap that Calculate() produced. Mirrors types.NetworkMap.Merge +// — same six collections, deduplicated where the legacy merge dedupes. +func mergeProxyPatch(nm *proto.NetworkMap, patch *proto.ProxyPatch) { + nm.RemotePeers = appendUniquePeers(nm.RemotePeers, patch.Peers) + nm.OfflinePeers = appendUniquePeers(nm.OfflinePeers, patch.OfflinePeers) + nm.FirewallRules = append(nm.FirewallRules, patch.FirewallRules...) + nm.Routes = append(nm.Routes, patch.Routes...) + nm.RoutesFirewallRules = append(nm.RoutesFirewallRules, patch.RouteFirewallRules...) + nm.ForwardingRules = append(nm.ForwardingRules, patch.ForwardingRules...) + if len(nm.RemotePeers) > 0 { + nm.RemotePeersIsEmpty = false + } + if len(nm.FirewallRules) > 0 { + nm.FirewallRulesIsEmpty = false + } + if len(nm.RoutesFirewallRules) > 0 { + nm.RoutesFirewallRulesIsEmpty = false + } +} + +// appendUniquePeers dedupes by WgPubKey — mirrors legacy +// mergeUniquePeersByID's intent (legacy keyed off Peer.ID; in proto form the +// closest stable identifier is WgPubKey). +func appendUniquePeers(dst, extra []*proto.RemotePeerConfig) []*proto.RemotePeerConfig { + if len(extra) == 0 { + return dst + } + seen := make(map[string]struct{}, len(dst)) + for _, p := range dst { + seen[p.WgPubKey] = struct{}{} + } + for _, p := range extra { + if _, ok := seen[p.WgPubKey]; ok { + continue + } + seen[p.WgPubKey] = struct{}{} + dst = append(dst, p) + } + return dst +} + +func trimKey(s string) string { + if len(s) > 12 { + return s[:12] + } + return s +} + +// findPeerByWgKey locates the receiving peer in the decoded components by +// matching its WireGuard public key. Compares raw 32-byte decode output — +// not the base64 string — because production data has occasional non-canonical +// padding bits that round-trip through the envelope's `bytes wg_pub_key` +// field, canonicalising the encoding (semantically equivalent key, different +// string). Decodes `wgKey` once up front and reuses a stack buffer in the +// loop so an N-peer search is ~zero-alloc. +func findPeerByWgKey(c *types.NetworkMapComponents, wgKey string) (string, *nbpeer.Peer) { + const wgKeyRawLen = 32 + var ( + targetRaw [wgKeyRawLen]byte + haveRaw bool + ) + if n, err := base64.StdEncoding.Decode(targetRaw[:], []byte(wgKey)); err == nil && n == wgKeyRawLen { + haveRaw = true + } + var peerRaw [wgKeyRawLen]byte + for id, p := range c.Peers { + if p == nil { + continue + } + if p.Key == wgKey { + return id, p + } + if !haveRaw { + continue + } + n, err := base64.StdEncoding.Decode(peerRaw[:], []byte(p.Key)) + if err == nil && n == wgKeyRawLen && bytes.Equal(peerRaw[:], targetRaw[:]) { + return id, p + } + } + return "", nil +} diff --git a/shared/management/networkmap/envelope_test.go b/shared/management/networkmap/envelope_test.go new file mode 100644 index 00000000000..70e70573708 --- /dev/null +++ b/shared/management/networkmap/envelope_test.go @@ -0,0 +1,173 @@ +package networkmap_test + +import ( + "context" + "crypto/rand" + "encoding/base64" + "net" + "net/netip" + "testing" + + "github.com/stretchr/testify/require" + goproto "google.golang.org/protobuf/proto" + + mgmtgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/types" + nbnetworkmap "github.com/netbirdio/netbird/shared/management/networkmap" + "github.com/netbirdio/netbird/shared/management/proto" +) + +// TestEnvelopeToNetworkMap_RoundTrip exercises the full client-side pipeline: +// build a small components struct, encode an envelope, marshal/unmarshal the +// wire bytes, decode back via EnvelopeToNetworkMap, and verify the result is +// non-empty and consistent. Deeper per-field semantic equivalence with the +// legacy server path is covered by the prod-DB equivalence test in +// management/server/store/networkmap_envelope_equivalence_test.go. +func TestEnvelopeToNetworkMap_RoundTrip(t *testing.T) { + c, localPeerKey := buildSmokeComponents(t) + + envelope := mgmtgrpc.EncodeNetworkMapEnvelope(mgmtgrpc.ComponentsEnvelopeInput{ + Components: c, + DNSDomain: "netbird.cloud", + }) + + wire, err := goproto.Marshal(envelope) + require.NoError(t, err, "marshal envelope") + + var decoded proto.NetworkMapEnvelope + require.NoError(t, goproto.Unmarshal(wire, &decoded), "unmarshal envelope") + + result, err := nbnetworkmap.EnvelopeToNetworkMap(context.Background(), &decoded, localPeerKey, "netbird.cloud") + require.NoError(t, err, "EnvelopeToNetworkMap") + require.NotNil(t, result) + require.NotNil(t, result.NetworkMap, "decoded NetworkMap must be non-nil") + require.NotNil(t, result.Components, "Components must be retained for future delta updates") + require.NotNil(t, result.Components.AccountSettings) + require.NotEmpty(t, result.NetworkMap.RemotePeers, "two-peer allow policy should produce one remote peer") + require.NotEmpty(t, result.NetworkMap.FirewallRules, "two-peer allow policy should produce firewall rules") +} + +// TestCalculate_FirewallRuleProtocol_NeverNetbirdSSH guards against the +// scenario where a rule with Protocol=NetbirdSSH leaks the enum value into +// proto.FirewallRule.Protocol. Calculate() must rewrite NetbirdSSH → TCP +// before forming firewall rules (see networkmap_components.go:282 and +// account.go:868). Without that rewrite, agents fall into UNKNOWN-protocol +// handling, which on some platforms downgrades to allow-all — a real +// security regression. +func TestCalculate_FirewallRuleProtocol_NeverNetbirdSSH(t *testing.T) { + c, localPeerKey := buildSmokeComponents(t) + // Replace the smoke policy with a NetbirdSSH-protocol allow. + c.Policies = []*types.Policy{{ + ID: "pol-ssh", AccountSeqID: 2, Enabled: true, + Rules: []*types.PolicyRule{{ + ID: "rule-ssh", + Enabled: true, + Action: types.PolicyTrafficActionAccept, + Protocol: types.PolicyRuleProtocolNetbirdSSH, + Bidirectional: true, + Sources: []string{"group-all"}, + Destinations: []string{"group-all"}, + }}, + }} + + envelope := mgmtgrpc.EncodeNetworkMapEnvelope(mgmtgrpc.ComponentsEnvelopeInput{ + Components: c, + DNSDomain: "netbird.cloud", + }) + wire, err := goproto.Marshal(envelope) + require.NoError(t, err) + var decoded proto.NetworkMapEnvelope + require.NoError(t, goproto.Unmarshal(wire, &decoded)) + + result, err := nbnetworkmap.EnvelopeToNetworkMap(context.Background(), &decoded, localPeerKey, "netbird.cloud") + require.NoError(t, err) + require.NotEmpty(t, result.NetworkMap.FirewallRules, "ssh policy should produce firewall rules") + for i, fr := range result.NetworkMap.FirewallRules { + require.NotEqualf(t, proto.RuleProtocol_NETBIRD_SSH, fr.Protocol, + "FirewallRules[%d].Protocol must be the rewritten TCP, not NETBIRD_SSH", i) + } +} + +func TestEnvelopeToNetworkMap_NilEnvelope(t *testing.T) { + _, err := nbnetworkmap.EnvelopeToNetworkMap(context.Background(), nil, "key", "netbird.cloud") + require.Error(t, err, "nil envelope must produce an error rather than panic") +} + +func TestEnvelopeToNetworkMap_FullPayloadMissing(t *testing.T) { + env := &proto.NetworkMapEnvelope{} + _, err := nbnetworkmap.EnvelopeToNetworkMap(context.Background(), env, "key", "netbird.cloud") + require.Error(t, err, "envelope with no Full payload must produce an error") +} + +// buildSmokeComponents returns a minimal NetworkMapComponents (2 peers, 1 +// group, 1 allow policy) plus the receiving peer's WG public key. Sufficient +// to validate the encode → marshal → decode → Calculate pipeline produces +// non-empty output. +func buildSmokeComponents(t *testing.T) (*types.NetworkMapComponents, string) { + t.Helper() + + peerAKey := randomWgKey(t) + peerBKey := randomWgKey(t) + + peerA := &nbpeer.Peer{ + ID: "peer-A", + Key: peerAKey, + IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}), + DNSLabel: "peerA", + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + peerB := &nbpeer.Peer{ + ID: "peer-B", + Key: peerBKey, + IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}), + DNSLabel: "peerB", + Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, + } + + group := &types.Group{ + ID: "group-all", AccountSeqID: 1, Name: "All", + Peers: []string{"peer-A", "peer-B"}, + } + + policy := &types.Policy{ + ID: "pol-allow", AccountSeqID: 1, Enabled: true, + Rules: []*types.PolicyRule{{ + ID: "rule-allow", + Enabled: true, + Action: types.PolicyTrafficActionAccept, + Protocol: types.PolicyRuleProtocolALL, + Bidirectional: true, + Sources: []string{"group-all"}, + Destinations: []string{"group-all"}, + }}, + } + + c := &types.NetworkMapComponents{ + PeerID: "peer-A", + Network: &types.Network{ + Identifier: "net-smoke", + Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}, + Serial: 1, + }, + AccountSettings: &types.AccountSettingsInfo{}, + DNSSettings: &types.DNSSettings{}, + Peers: map[string]*nbpeer.Peer{ + "peer-A": peerA, + "peer-B": peerB, + }, + Groups: map[string]*types.Group{ + "group-all": group, + }, + Policies: []*types.Policy{policy}, + } + return c, peerAKey +} + +func randomWgKey(t *testing.T) string { + t.Helper() + var raw [32]byte + _, err := rand.Read(raw[:]) + require.NoError(t, err) + return base64.StdEncoding.EncodeToString(raw[:]) +} From 3a1bbeba906ed913cd8c382b4004ce069812131f Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 19 May 2026 20:27:50 +0200 Subject: [PATCH 10/42] review comments --- client/internal/engine.go | 8 +- .../network_map/controller/controller.go | 2 +- .../shared/grpc/components_encoder.go | 61 ++++++------ .../grpc/components_envelope_response.go | 27 ++++-- .../networks/resources/types/resource.go | 23 ++--- management/server/networks/routers/manager.go | 14 ++- .../server/networks/routers/types/router.go | 17 ++-- management/server/store/sql_store.go | 93 +++++++++++++++++-- management/server/types/account_components.go | 8 +- .../types/networkmap_wire_benchmark_test.go | 12 ++- .../types/networkmap_wire_breakdown_test.go | 7 ++ management/server/types/policy.go | 1 + route/route.go | 1 + shared/management/client/client_test.go | 59 ++++++++++-- shared/management/networkmap/decode.go | 31 ++++++- shared/management/networkmap/encode.go | 2 +- shared/management/networkmap/envelope.go | 10 +- 17 files changed, 289 insertions(+), 87 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index f63840aa11d..acbe94f227c 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -874,8 +874,12 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { return e.ctx.Err() } - if update.NetworkMap != nil && update.NetworkMap.PeerConfig != nil { - e.handleAutoUpdateVersion(update.NetworkMap.PeerConfig.AutoUpdate) + // Envelope sync responses carry PeerConfig at the top level; legacy + // NetworkMap syncs carry it under NetworkMap.PeerConfig. + if pc := update.GetPeerConfig(); pc != nil { + e.handleAutoUpdateVersion(pc.GetAutoUpdate()) + } else if nm := update.GetNetworkMap(); nm != nil && nm.GetPeerConfig() != nil { + e.handleAutoUpdateVersion(nm.GetPeerConfig().GetAutoUpdate()) } if update.GetNetbirdConfig() != nil { diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 2f9274a069b..938df5539ac 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -223,7 +223,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start)) - proxyNetworkMap := proxyNetworkMaps[peer.ID] + proxyNetworkMap := proxyNetworkMaps[p.ID] if result.NetworkMap != nil && proxyNetworkMap != nil { result.NetworkMap.Merge(proxyNetworkMap) } diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index e9c01d56b7d..af91b008c44 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -264,38 +264,47 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) ([]*proto.Po if r == nil || !r.Enabled { continue } - pc := &proto.PolicyCompact{ - Id: pol.AccountSeqID, - Action: networkmap.GetProtoAction(string(r.Action)), - Protocol: networkmap.GetProtoProtocol(string(r.Protocol)), - Bidirectional: r.Bidirectional, - Ports: portsToUint32(r.Ports), - PortRanges: portRangesToProto(r.PortRanges), - SourceGroupIds: make([]uint32, 0, len(r.Sources)), - DestinationGroupIds: make([]uint32, 0, len(r.Destinations)), - AuthorizedUser: r.AuthorizedUser, - AuthorizedGroups: e.encodeAuthorizedGroups(r.AuthorizedGroups), - SourceResource: e.resourceToProto(r.SourceResource), - DestinationResource: e.resourceToProto(r.DestinationResource), - SourcePostureCheckSeqIds: e.postureCheckSeqs(pol.SourcePostureChecks), - } - for _, gid := range r.Sources { - if seq, ok := e.groupSeq(gid); ok { - pc.SourceGroupIds = append(pc.SourceGroupIds, seq) - } - } - for _, gid := range r.Destinations { - if seq, ok := e.groupSeq(gid); ok { - pc.DestinationGroupIds = append(pc.DestinationGroupIds, seq) - } - } idxByPolicy[pol] = append(idxByPolicy[pol], uint32(len(out))) - out = append(out, pc) + out = append(out, e.encodePolicyRule(pol, r)) } } return out, idxByPolicy } +// encodePolicyRule maps a single PolicyRule under pol to a PolicyCompact entry. +func (e *componentEncoder) encodePolicyRule(pol *types.Policy, r *types.PolicyRule) *proto.PolicyCompact { + return &proto.PolicyCompact{ + Id: pol.AccountSeqID, + Action: networkmap.GetProtoAction(string(r.Action)), + Protocol: networkmap.GetProtoProtocol(string(r.Protocol)), + Bidirectional: r.Bidirectional, + Ports: portsToUint32(r.Ports), + PortRanges: portRangesToProto(r.PortRanges), + SourceGroupIds: e.groupSeqIDs(r.Sources), + DestinationGroupIds: e.groupSeqIDs(r.Destinations), + AuthorizedUser: r.AuthorizedUser, + AuthorizedGroups: e.encodeAuthorizedGroups(r.AuthorizedGroups), + SourceResource: e.resourceToProto(r.SourceResource), + DestinationResource: e.resourceToProto(r.DestinationResource), + SourcePostureCheckSeqIds: e.postureCheckSeqs(pol.SourcePostureChecks), + } +} + +// groupSeqIDs maps the xid group IDs in src to their per-account seq ids, +// dropping any group that has no seq id assigned. +func (e *componentEncoder) groupSeqIDs(src []string) []uint32 { + if len(src) == 0 { + return nil + } + out := make([]uint32, 0, len(src)) + for _, gid := range src { + if seq, ok := e.groupSeq(gid); ok { + out = append(out, seq) + } + } + return out +} + // unionPolicies merges c.Policies with every policy referenced by // c.ResourcePoliciesMap, deduplicating by pointer identity. Resource-only // policies (relevant to a NetworkResource but not to peer-pair traffic) diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index dfd7b5ad491..5a6f8d06615 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -152,16 +152,7 @@ func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peer *nbpeer.Peer) continue } for _, rule := range policy.Rules { - if rule == nil || !rule.Enabled { - continue - } - if !peerInDestinations(c, rule, peer.ID) { - continue - } - if rule.Protocol == types.PolicyRuleProtocolNetbirdSSH { - return true - } - if peer.SSHEnabled && types.PolicyRuleImpliesLegacySSH(rule) { + if ruleEnablesSSHForPeer(c, rule, peer) { return true } } @@ -169,6 +160,22 @@ func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peer *nbpeer.Peer) return false } +// ruleEnablesSSHForPeer returns true when rule is active, targets peer, and +// either explicitly authorises SSH or covers the legacy TCP/22 path while the +// peer itself has SSH enabled locally. +func ruleEnablesSSHForPeer(c *types.NetworkMapComponents, rule *types.PolicyRule, peer *nbpeer.Peer) bool { + if rule == nil || !rule.Enabled { + return false + } + if !peerInDestinations(c, rule, peer.ID) { + return false + } + if rule.Protocol == types.PolicyRuleProtocolNetbirdSSH { + return true + } + return peer.SSHEnabled && types.PolicyRuleImpliesLegacySSH(rule) +} + // peerInDestinations reports whether peerID is in any of rule.Destinations' // groups (or matches DestinationResource if it's a peer-typed resource — // for non-peer types Calculate falls through to group lookup, so we mirror diff --git a/management/server/networks/resources/types/resource.go b/management/server/networks/resources/types/resource.go index 1ced3ab918c..454ca4162fb 100644 --- a/management/server/networks/resources/types/resource.go +++ b/management/server/networks/resources/types/resource.go @@ -96,17 +96,18 @@ func (n *NetworkResource) FromAPIRequest(req *api.NetworkResourceRequest) { func (n *NetworkResource) Copy() *NetworkResource { return &NetworkResource{ - ID: n.ID, - AccountID: n.AccountID, - NetworkID: n.NetworkID, - Name: n.Name, - Description: n.Description, - Type: n.Type, - Address: n.Address, - Domain: n.Domain, - Prefix: n.Prefix, - GroupIDs: n.GroupIDs, - Enabled: n.Enabled, + ID: n.ID, + AccountID: n.AccountID, + NetworkID: n.NetworkID, + AccountSeqID: n.AccountSeqID, + Name: n.Name, + Description: n.Description, + Type: n.Type, + Address: n.Address, + Domain: n.Domain, + Prefix: n.Prefix, + GroupIDs: n.GroupIDs, + Enabled: n.Enabled, } } diff --git a/management/server/networks/routers/manager.go b/management/server/networks/routers/manager.go index 3a985a5b0d6..4f60b82ce16 100644 --- a/management/server/networks/routers/manager.go +++ b/management/server/networks/routers/manager.go @@ -173,10 +173,20 @@ func (m *managerImpl) UpdateRouter(ctx context.Context, userID string, router *t } oldRouter, err := transaction.GetNetworkRouterByID(ctx, store.LockingStrengthNone, router.AccountID, router.ID) - if err != nil { + if err == nil { + router.AccountSeqID = oldRouter.AccountSeqID + } else if e, ok := status.FromError(err); ok && e.Type() == status.NotFound { + // PUT-as-upsert: caller may target a brand-new router id (used by + // the dashboard's "save" flow). Allocate a fresh account_seq_id so + // the upsert behaves the same as Create(). + seq, allocErr := transaction.AllocateAccountSeqID(ctx, router.AccountID, serverTypes.AccountSeqEntityNetworkRouter) + if allocErr != nil { + return fmt.Errorf("failed to allocate network router seq id: %w", allocErr) + } + router.AccountSeqID = seq + } else { return fmt.Errorf("failed to get existing network router: %w", err) } - router.AccountSeqID = oldRouter.AccountSeqID err = transaction.SaveNetworkRouter(ctx, router) if err != nil { diff --git a/management/server/networks/routers/types/router.go b/management/server/networks/routers/types/router.go index 7325599b215..d4c55eaf3de 100644 --- a/management/server/networks/routers/types/router.go +++ b/management/server/networks/routers/types/router.go @@ -81,14 +81,15 @@ func (n *NetworkRouter) FromAPIRequest(req *api.NetworkRouterRequest) { func (n *NetworkRouter) Copy() *NetworkRouter { return &NetworkRouter{ - ID: n.ID, - NetworkID: n.NetworkID, - AccountID: n.AccountID, - Peer: n.Peer, - PeerGroups: n.PeerGroups, - Masquerade: n.Masquerade, - Metric: n.Metric, - Enabled: n.Enabled, + ID: n.ID, + NetworkID: n.NetworkID, + AccountID: n.AccountID, + AccountSeqID: n.AccountSeqID, + Peer: n.Peer, + PeerGroups: n.PeerGroups, + Masquerade: n.Masquerade, + Metric: n.Metric, + Enabled: n.Enabled, } } diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 40cdc7c36ce..05ba3fd71b7 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -3659,11 +3659,24 @@ func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.Accou // the in-memory account whose AccountSeqID is zero. Called from SaveAccount so // the canonical "save the whole account" path produces the same persisted seq // ids that the manager-level Create paths produce. Update flows that go -// through SaveAccount preserve existing non-zero values. +// through SaveAccount preserve existing non-zero values; for those, the +// per-entity counter is bumped so subsequent AllocateAccountSeqID calls don't +// hand out a colliding id. func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account *types.Account) error { + maxByEntity := make(map[types.AccountSeqEntity]uint32, 8) + bump := func(entity types.AccountSeqEntity, seq uint32) { + if seq > maxByEntity[entity] { + maxByEntity[entity] = seq + } + } + for i := range account.GroupsG { g := account.GroupsG[i] - if g == nil || g.AccountSeqID != 0 { + if g == nil { + continue + } + if g.AccountSeqID != 0 { + bump(types.AccountSeqEntityGroup, g.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityGroup) @@ -3673,7 +3686,11 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account g.AccountSeqID = seq } for _, p := range account.Policies { - if p == nil || p.AccountSeqID != 0 { + if p == nil { + continue + } + if p.AccountSeqID != 0 { + bump(types.AccountSeqEntityPolicy, p.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityPolicy) @@ -3685,6 +3702,7 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account for i := range account.RoutesG { r := &account.RoutesG[i] if r.AccountSeqID != 0 { + bump(types.AccountSeqEntityRoute, r.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityRoute) @@ -3696,6 +3714,7 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account for i := range account.NameServerGroupsG { ng := &account.NameServerGroupsG[i] if ng.AccountSeqID != 0 { + bump(types.AccountSeqEntityNameserverGroup, ng.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNameserverGroup) @@ -3705,7 +3724,11 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account ng.AccountSeqID = seq } for _, nr := range account.NetworkResources { - if nr == nil || nr.AccountSeqID != 0 { + if nr == nil { + continue + } + if nr.AccountSeqID != 0 { + bump(types.AccountSeqEntityNetworkResource, nr.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetworkResource) @@ -3715,7 +3738,11 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account nr.AccountSeqID = seq } for _, nr := range account.NetworkRouters { - if nr == nil || nr.AccountSeqID != 0 { + if nr == nil { + continue + } + if nr.AccountSeqID != 0 { + bump(types.AccountSeqEntityNetworkRouter, nr.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetworkRouter) @@ -3725,7 +3752,11 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account nr.AccountSeqID = seq } for _, n := range account.Networks { - if n == nil || n.AccountSeqID != 0 { + if n == nil { + continue + } + if n.AccountSeqID != 0 { + bump(types.AccountSeqEntityNetwork, n.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetwork) @@ -3735,7 +3766,11 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account n.AccountSeqID = seq } for _, pc := range account.PostureChecks { - if pc == nil || pc.AccountSeqID != 0 { + if pc == nil { + continue + } + if pc.AccountSeqID != 0 { + bump(types.AccountSeqEntityPostureCheck, pc.AccountSeqID) continue } seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityPostureCheck) @@ -3744,9 +3779,53 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account } pc.AccountSeqID = seq } + for entity, maxSeq := range maxByEntity { + if err := ensureAccountSeqCounter(tx, s.storeEngine, account.Id, entity, maxSeq+1); err != nil { + return fmt.Errorf("seed counter for %s: %w", entity, err) + } + } return nil } +// ensureAccountSeqCounter raises the per-account counter for entity to at +// least target. Used when SaveAccount persists components that already carry +// AccountSeqIDs (e.g. test bulk-load from sqlite to postgres, or migrations +// running before component data lands) so that the next AllocateAccountSeqID +// call returns a fresh id beyond what was just written. +func ensureAccountSeqCounter(db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity, target uint32) error { + switch engine { + case types.PostgresStoreEngine, types.SqliteStoreEngine: + const sqlStr = ` + INSERT INTO account_seq_counters (account_id, entity, next_id) + VALUES (?, ?, ?) + ON CONFLICT (account_id, entity) DO UPDATE + SET next_id = GREATEST(account_seq_counters.next_id, EXCLUDED.next_id) + ` + // sqlite's UPSERT understands max() but the migration uses GREATEST + // for postgres and max() for sqlite. We collapse to dialect-specific + // statements only when needed. + if engine == types.SqliteStoreEngine { + const sqliteSQL = ` + INSERT INTO account_seq_counters (account_id, entity, next_id) + VALUES (?, ?, ?) + ON CONFLICT (account_id, entity) DO UPDATE + SET next_id = max(account_seq_counters.next_id, excluded.next_id) + ` + return db.Exec(sqliteSQL, accountID, string(entity), target).Error + } + return db.Exec(sqlStr, accountID, string(entity), target).Error + case types.MysqlStoreEngine: + const sqlStr = ` + INSERT INTO account_seq_counters (account_id, entity, next_id) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE next_id = GREATEST(next_id, VALUES(next_id)) + ` + return db.Exec(sqlStr, accountID, string(entity), target).Error + default: + return fmt.Errorf("unsupported store engine for account_seq counter: %v", engine) + } +} + // transaction wraps a GORM transaction with MySQL-specific FK checks handling // Use this instead of db.Transaction() directly to avoid deadlocks on MySQL/Aurora func (s *SqlStore) transaction(fn func(*gorm.DB) error) error { diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 45f7189b0f3..8f6ffb6bafc 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -348,9 +348,11 @@ func (a *Account) getPeersGroupsPoliciesRoutes( for _, groupID := range r.Groups { relevantGroupIDs[groupID] = a.GetGroup(groupID) } - for _, groupID := range r.AccessControlGroups { - relevantGroupIDs[groupID] = a.GetGroup(groupID) - routeAccessControlGroups[groupID] = struct{}{} + if r.Enabled { + for _, groupID := range r.AccessControlGroups { + relevantGroupIDs[groupID] = a.GetGroup(groupID) + routeAccessControlGroups[groupID] = struct{}{} + } } relevantRoutes = append(relevantRoutes, r) } diff --git a/management/server/types/networkmap_wire_benchmark_test.go b/management/server/types/networkmap_wire_benchmark_test.go index eb18dd04e8c..43c9e1fbf11 100644 --- a/management/server/types/networkmap_wire_benchmark_test.go +++ b/management/server/types/networkmap_wire_benchmark_test.go @@ -43,7 +43,7 @@ func populateAccountSeqIDs(account *types.Account) { } // assignValidWgKeys overwrites every peer's Key with a valid base64-encoded -// 32-byte string. The default scalableTestAccount uses unparseable strings +// 32-byte string. The default scalableTestAccount uses unparsable strings // like "key-peer-0", which makes the components encoder emit a nil WgPubKey // and the legacy encoder ship 10-char placeholders — both shrink the wire // size in unrealistic ways. Production peers always have valid 44-char base64 @@ -154,14 +154,20 @@ func BenchmarkNetworkMapWireSize(b *testing.B) { settings := &types.Settings{} legacyResp := mgmtgrpc.ToSyncResponse(ctx, nil, nil, nil, peer, nil, nil, networkMap, "netbird.cloud", nil, dnsCache, settings, nil, nil, 0) - legacyBytes, _ := goproto.Marshal(legacyResp.NetworkMap) + legacyBytes, err := goproto.Marshal(legacyResp.NetworkMap) + if err != nil { + b.Fatalf("marshal legacy networkmap: %v", err) + } env := mgmtgrpc.EncodeNetworkMapEnvelope(mgmtgrpc.ComponentsEnvelopeInput{ Components: components, PeerConfig: legacyResp.NetworkMap.PeerConfig, DNSDomain: "netbird.cloud", }) - envBytes, _ := goproto.Marshal(env) + envBytes, err := goproto.Marshal(env) + if err != nil { + b.Fatalf("marshal envelope: %v", err) + } b.Run(fmt.Sprintf("size/%s", scale.name), func(b *testing.B) { b.ReportMetric(float64(len(legacyBytes)), "legacy_bytes") diff --git a/management/server/types/networkmap_wire_breakdown_test.go b/management/server/types/networkmap_wire_breakdown_test.go index d76e73fb9f7..39b0b81c4b7 100644 --- a/management/server/types/networkmap_wire_breakdown_test.go +++ b/management/server/types/networkmap_wire_breakdown_test.go @@ -3,6 +3,7 @@ package types_test import ( "context" "fmt" + "os" "testing" goproto "google.golang.org/protobuf/proto" @@ -23,6 +24,9 @@ func TestNetworkMapWireBreakdown(t *testing.T) { if testing.Short() { t.Skip("size diagnostic, skipped with -short") } + if os.Getenv("NB_RUN_WIRE_BREAKDOWN") != "1" { + t.Skip("set NB_RUN_WIRE_BREAKDOWN=1 to run wire breakdown diagnostic") + } const peerCount, groupCount = 5000, 100 account, validatedPeers := scalableTestAccount(peerCount, groupCount) @@ -74,6 +78,9 @@ func TestNetworkMapWireBreakdown(t *testing.T) { } full := envelope.GetFull() + if full == nil { + t.Fatalf("expected full network map envelope payload, got nil") + } t.Logf("\n=== COMPONENTS NetworkMapEnvelope (%d peers, %d groups) ===", peerCount, groupCount) t.Logf(" Total: %d bytes (%.1f%% of legacy)\n", componentsTotal, pct(componentsTotal, legacyTotal)) diff --git a/management/server/types/policy.go b/management/server/types/policy.go index 69c7c97623f..fdda30b6675 100644 --- a/management/server/types/policy.go +++ b/management/server/types/policy.go @@ -91,6 +91,7 @@ func (p *Policy) Copy() *Policy { c := &Policy{ ID: p.ID, AccountID: p.AccountID, + AccountSeqID: p.AccountSeqID, Name: p.Name, Description: p.Description, Enabled: p.Enabled, diff --git a/route/route.go b/route/route.go index 4a8c342b2f1..8a26cc3bbfa 100644 --- a/route/route.go +++ b/route/route.go @@ -131,6 +131,7 @@ func (r *Route) Copy() *Route { route := &Route{ ID: r.ID, AccountID: r.AccountID, + AccountSeqID: r.AccountSeqID, Description: r.Description, NetID: r.NetID, Network: r.Network, diff --git a/shared/management/client/client_test.go b/shared/management/client/client_test.go index a8e8172dc88..37daabde5dc 100644 --- a/shared/management/client/client_test.go +++ b/shared/management/client/client_test.go @@ -316,27 +316,74 @@ func TestClient_Sync(t *testing.T) { select { case resp := <-ch: - if resp.GetPeerConfig() == nil { + if resp.GetPeerConfig() == nil && resp.GetNetworkMap().GetPeerConfig() == nil { t.Error("expecting non nil PeerConfig got nil") } if resp.GetNetbirdConfig() == nil { t.Error("expecting non nil NetbirdConfig got nil") } - if len(resp.GetRemotePeers()) != 1 { - t.Errorf("expecting RemotePeers size %d got %d", 1, len(resp.GetRemotePeers())) + // Component-capable clients receive a NetworkMapEnvelope; the + // remote-peers list is encoded inside it. Decode it and check the + // envelope's peers slice. Legacy peers populate the top-level + // RemotePeers; both shapes must surface exactly one remote peer. + remotePeerKeys := remotePeerKeysFromSync(resp, testKey.PublicKey().String()) + if len(remotePeerKeys) != 1 { + t.Errorf("expecting RemotePeers size %d got %d", 1, len(remotePeerKeys)) return } - if resp.GetRemotePeersIsEmpty() == true { + if resp.GetNetworkMap() != nil && resp.GetRemotePeersIsEmpty() { t.Error("expecting RemotePeers property to be false, got true") } - if resp.GetRemotePeers()[0].GetWgPubKey() != remoteKey.PublicKey().String() { - t.Errorf("expecting RemotePeer public key %s got %s", remoteKey.PublicKey().String(), resp.GetRemotePeers()[0].GetWgPubKey()) + if remotePeerKeys[0] != remoteKey.PublicKey().String() { + t.Errorf("expecting RemotePeer public key %s got %s", remoteKey.PublicKey().String(), remotePeerKeys[0]) } case <-time.After(3 * time.Second): t.Error("timeout waiting for test to finish") } } +// remotePeerKeysFromSync extracts the remote-peer WG keys from either the +// legacy NetworkMap.RemotePeers list or the components NetworkMapEnvelope's +// inner peers slice (filtering out the local receiving peer identified by +// localKey, since the envelope's peers list is index-addressed and includes +// the local peer alongside remotes). +func remotePeerKeysFromSync(resp *mgmtProto.SyncResponse, localKey string) []string { + if rp := resp.GetRemotePeers(); len(rp) > 0 { + out := make([]string, 0, len(rp)) + for _, p := range rp { + out = append(out, p.GetWgPubKey()) + } + return out + } + env := resp.GetNetworkMapEnvelope().GetFull() + if env == nil { + return nil + } + out := make([]string, 0, len(env.GetPeers())) + for _, p := range env.GetPeers() { + key := wgKeyFromBytes(p.GetWgPubKey()) + if key == "" || key == localKey { + continue + } + out = append(out, key) + } + return out +} + +// wgKeyFromBytes mirrors the client-side decoder: the envelope ships raw 32 +// bytes; reconstruct the standard base64 key the test compares against. +func wgKeyFromBytes(raw []byte) string { + if len(raw) == 0 { + return "" + } + var k wgtypes.Key + if len(raw) != len(k) { + return "" + } + copy(k[:], raw) + return k.String() +} + func Test_SystemMetaDataFromClient(t *testing.T) { s, lis, mgmtMockServer, serverKey := startMockManagement(t) defer s.GracefulStop() diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go index 84b10498612..4d4a41c8822 100644 --- a/shared/management/networkmap/decode.go +++ b/shared/management/networkmap/decode.go @@ -80,6 +80,9 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, // for peers (and never has). peerIDByIndex := make([]string, len(full.Peers)) for idx, pc := range full.Peers { + if pc == nil { + return nil, fmt.Errorf("invalid envelope: peers[%d] is nil", idx) + } peerID := synthPeerID(uint32(idx)) peer := decodePeerCompact(pc, peerID, full.AgentVersions) c.Peers[peerID] = peer @@ -88,7 +91,10 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, // Phase 2: groups. AccountSeqID becomes both the synthesized string ID // and the GroupCompact.id wire value. - for _, gc := range full.Groups { + for i, gc := range full.Groups { + if gc == nil { + return nil, fmt.Errorf("invalid envelope: groups[%d] is nil", i) + } groupID := synthGroupID(gc.Id) peerIDs := make([]string, 0, len(gc.PeerIndexes)) for _, idx := range gc.PeerIndexes { @@ -108,23 +114,35 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, // model is 1 rule per policy). Policy.ID is synthesized from the // per-account seq id; proto.FirewallRule.PolicyID downstream carries // the same synth string (no xid on the wire). - for _, pc := range full.Policies { + for i, pc := range full.Policies { + if pc == nil { + return nil, fmt.Errorf("invalid envelope: policies[%d] is nil", i) + } policyID := synthPolicyID(pc.Id) c.Policies = append(c.Policies, decodePolicyCompact(pc, policyID, peerIDByIndex)) } // Phase 4: routes. - for _, rr := range full.Routes { + for i, rr := range full.Routes { + if rr == nil { + return nil, fmt.Errorf("invalid envelope: routes[%d] is nil", i) + } c.Routes = append(c.Routes, decodeRouteRaw(rr, peerIDByIndex)) } // Phase 5: NSGs. - for _, nsg := range full.NameserverGroups { + for i, nsg := range full.NameserverGroups { + if nsg == nil { + return nil, fmt.Errorf("invalid envelope: nameserver_groups[%d] is nil", i) + } c.NameServerGroups = append(c.NameServerGroups, decodeNameServerGroupRaw(nsg)) } // Phase 6: network resources. - for _, nr := range full.NetworkResources { + for i, nr := range full.NetworkResources { + if nr == nil { + return nil, fmt.Errorf("invalid envelope: network_resources[%d] is nil", i) + } c.NetworkResources = append(c.NetworkResources, decodeNetworkResource(nr)) } @@ -513,6 +531,9 @@ func portRangesFromProto(ranges []*proto.PortInfo_Range) []types.RulePortRange { } out := make([]types.RulePortRange, 0, len(ranges)) for _, r := range ranges { + if r == nil || r.Start > 65535 || r.End > 65535 { + continue + } out = append(out, types.RulePortRange{ Start: uint16(r.Start), End: uint16(r.End), diff --git a/shared/management/networkmap/encode.go b/shared/management/networkmap/encode.go index ebaede64aef..1b1c3380ec6 100644 --- a/shared/management/networkmap/encode.go +++ b/shared/management/networkmap/encode.go @@ -307,7 +307,7 @@ func BuildAuthorizedUsersProto(ctx context.Context, authorizedUsers map[string]m if !exists { hash, err := sshauth.HashUserID(userID) if err != nil { - log.WithContext(ctx).Errorf("failed to hash user id %s: %v", userID, err) + log.WithContext(ctx).WithError(err).Error("failed to hash user id") continue } idx = uint32(len(hashedUsers)) diff --git a/shared/management/networkmap/envelope.go b/shared/management/networkmap/envelope.go index 295c26b7a82..e90cb59820e 100644 --- a/shared/management/networkmap/envelope.go +++ b/shared/management/networkmap/envelope.go @@ -52,8 +52,8 @@ func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, lo } components.PeerID = localPeerID - includeIPv6 := localPeer != nil && localPeer.SupportsIPv6() && localPeer.IPv6.IsValid() - useSourcePrefixes := localPeer != nil && localPeer.SupportsSourcePrefixes() + includeIPv6 := localPeer.SupportsIPv6() && localPeer.IPv6.IsValid() + useSourcePrefixes := localPeer.SupportsSourcePrefixes() typedNM := components.Calculate(ctx) @@ -149,9 +149,15 @@ func appendUniquePeers(dst, extra []*proto.RemotePeerConfig) []*proto.RemotePeer } seen := make(map[string]struct{}, len(dst)) for _, p := range dst { + if p == nil { + continue + } seen[p.WgPubKey] = struct{}{} } for _, p := range extra { + if p == nil { + continue + } if _, ok := seen[p.WgPubKey]; ok { continue } From 5fbcdeceac63b94a6d4c0ced784dc2f2971dc0b0 Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 19 May 2026 21:41:08 +0200 Subject: [PATCH 11/42] more comments --- management/server/store/sql_store.go | 18 ++++++++++ management/server/types/user.go | 54 ---------------------------- 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 05ba3fd71b7..5215af54570 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -3684,6 +3684,14 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account return err } g.AccountSeqID = seq + // Defensive: generateAccountSQLTypes currently aliases the same + // *Group pointer into GroupsG and Groups[id] (so this is a no-op + // today), but mirror the seq anyway so any future divergence in + // how the two collections are populated doesn't silently leave + // the canonical map view stale. + if original, ok := account.Groups[g.ID]; ok && original != nil && original != g { + original.AccountSeqID = seq + } } for _, p := range account.Policies { if p == nil { @@ -3710,6 +3718,13 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account return err } r.AccountSeqID = seq + // Mirror the new seq onto the canonical map view so callers that + // hold the same in-memory account post-Save read a consistent + // AccountSeqID — without this, components/encoder code would see + // 0 for routes saved this transaction until the account is reloaded. + if original, ok := account.Routes[r.ID]; ok && original != nil { + original.AccountSeqID = seq + } } for i := range account.NameServerGroupsG { ng := &account.NameServerGroupsG[i] @@ -3722,6 +3737,9 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account return err } ng.AccountSeqID = seq + if original, ok := account.NameServerGroups[ng.ID]; ok && original != nil { + original.AccountSeqID = seq + } } for _, nr := range account.NetworkResources { if nr == nil { diff --git a/management/server/types/user.go b/management/server/types/user.go index dc601e15b6e..0b89b84991f 100644 --- a/management/server/types/user.go +++ b/management/server/types/user.go @@ -5,7 +5,6 @@ import ( "strings" "time" - "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/integration_reference" "github.com/netbirdio/netbird/util/crypt" ) @@ -143,59 +142,6 @@ func (u *User) IsRestrictable() bool { return u.Role == UserRoleUser || u.Role == UserRoleBillingAdmin } -// ToUserInfo converts a User object to a UserInfo object. -func (u *User) ToUserInfo(userData *idp.UserData) (*UserInfo, error) { - autoGroups := u.AutoGroups - if autoGroups == nil { - autoGroups = []string{} - } - - if userData == nil { - - name := u.Name - if u.IsServiceUser { - name = u.ServiceUserName - } - - return &UserInfo{ - ID: u.Id, - Email: u.Email, - Name: name, - Role: string(u.Role), - AutoGroups: u.AutoGroups, - Status: string(UserStatusActive), - IsServiceUser: u.IsServiceUser, - IsBlocked: u.Blocked, - LastLogin: u.GetLastLogin(), - Issued: u.Issued, - PendingApproval: u.PendingApproval, - }, nil - } - if userData.ID != u.Id { - return nil, fmt.Errorf("wrong UserData provided for user %s", u.Id) - } - - userStatus := UserStatusActive - if userData.AppMetadata.WTPendingInvite != nil && *userData.AppMetadata.WTPendingInvite { - userStatus = UserStatusInvited - } - - return &UserInfo{ - ID: u.Id, - Email: userData.Email, - Name: userData.Name, - Role: string(u.Role), - AutoGroups: autoGroups, - Status: string(userStatus), - IsServiceUser: u.IsServiceUser, - IsBlocked: u.Blocked, - LastLogin: u.GetLastLogin(), - Issued: u.Issued, - PendingApproval: u.PendingApproval, - Password: userData.Password, - }, nil -} - // Copy the user func (u *User) Copy() *User { autoGroups := make([]string, len(u.AutoGroups)) From efa6a3f502fe909abcaad7ebd40c7cc2f10d6199 Mon Sep 17 00:00:00 2001 From: crn4 Date: Wed, 20 May 2026 12:41:05 +0200 Subject: [PATCH 12/42] missed file --- management/server/types/user.go | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/management/server/types/user.go b/management/server/types/user.go index 0b89b84991f..dc601e15b6e 100644 --- a/management/server/types/user.go +++ b/management/server/types/user.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/integration_reference" "github.com/netbirdio/netbird/util/crypt" ) @@ -142,6 +143,59 @@ func (u *User) IsRestrictable() bool { return u.Role == UserRoleUser || u.Role == UserRoleBillingAdmin } +// ToUserInfo converts a User object to a UserInfo object. +func (u *User) ToUserInfo(userData *idp.UserData) (*UserInfo, error) { + autoGroups := u.AutoGroups + if autoGroups == nil { + autoGroups = []string{} + } + + if userData == nil { + + name := u.Name + if u.IsServiceUser { + name = u.ServiceUserName + } + + return &UserInfo{ + ID: u.Id, + Email: u.Email, + Name: name, + Role: string(u.Role), + AutoGroups: u.AutoGroups, + Status: string(UserStatusActive), + IsServiceUser: u.IsServiceUser, + IsBlocked: u.Blocked, + LastLogin: u.GetLastLogin(), + Issued: u.Issued, + PendingApproval: u.PendingApproval, + }, nil + } + if userData.ID != u.Id { + return nil, fmt.Errorf("wrong UserData provided for user %s", u.Id) + } + + userStatus := UserStatusActive + if userData.AppMetadata.WTPendingInvite != nil && *userData.AppMetadata.WTPendingInvite { + userStatus = UserStatusInvited + } + + return &UserInfo{ + ID: u.Id, + Email: userData.Email, + Name: userData.Name, + Role: string(u.Role), + AutoGroups: autoGroups, + Status: string(userStatus), + IsServiceUser: u.IsServiceUser, + IsBlocked: u.Blocked, + LastLogin: u.GetLastLogin(), + Issued: u.Issued, + PendingApproval: u.PendingApproval, + Password: userData.Password, + }, nil +} + // Copy the user func (u *User) Copy() *User { autoGroups := make([]string, len(u.AutoGroups)) From 13e41e432c4fb36b9fd18c5323e7fb97c2013f2a Mon Sep 17 00:00:00 2001 From: crn4 Date: Thu, 21 May 2026 15:21:28 +0200 Subject: [PATCH 13/42] idp dex fix --- idp/dex/config.go | 2 +- idp/dex/provider.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/idp/dex/config.go b/idp/dex/config.go index 56ed998c25c..bbe33ae5bc4 100644 --- a/idp/dex/config.go +++ b/idp/dex/config.go @@ -308,7 +308,7 @@ func (s *Storage) OpenStorage(logger *slog.Logger) (storage.Storage, error) { if file == "" { return nil, fmt.Errorf("sqlite3 storage requires 'file' config") } - return (&sql.SQLite3{File: file}).Open(logger) + return newSQLite3(file).Open(logger) case "postgres": dsn, _ := s.Config["dsn"].(string) if dsn == "" { diff --git a/idp/dex/provider.go b/idp/dex/provider.go index 526d6a17a18..854382a2f17 100644 --- a/idp/dex/provider.go +++ b/idp/dex/provider.go @@ -20,7 +20,6 @@ import ( "github.com/dexidp/dex/server" "github.com/dexidp/dex/server/signer" "github.com/dexidp/dex/storage" - "github.com/dexidp/dex/storage/sql" jose "github.com/go-jose/go-jose/v4" "github.com/google/uuid" "github.com/prometheus/client_golang/prometheus" @@ -77,7 +76,7 @@ func NewProvider(ctx context.Context, config *Config) (*Provider, error) { // Initialize SQLite storage dbPath := filepath.Join(config.DataDir, "oidc.db") - sqliteConfig := &sql.SQLite3{File: dbPath} + sqliteConfig := newSQLite3(dbPath) stor, err := sqliteConfig.Open(logger) if err != nil { return nil, fmt.Errorf("failed to open storage: %w", err) From 5d5c2d9f9516c2e8f5e6724376e688bc90437d0b Mon Sep 17 00:00:00 2001 From: crn4 Date: Tue, 26 May 2026 11:33:21 +0200 Subject: [PATCH 14/42] filtering fix --- management/server/types/account_components.go | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 8f6ffb6bafc..cc8c72b3cb2 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -263,21 +263,26 @@ func (a *Account) GetPeerNetworkMapComponents( components.ResourcePoliciesMap[resource.ID] = policies } - components.RoutersMap[resource.NetworkID] = networkRoutingPeers - for peerIDKey := range networkRoutingPeers { - if p := a.Peers[peerIDKey]; p != nil { - if _, exists := components.RouterPeers[peerIDKey]; !exists { - components.RouterPeers[peerIDKey] = p - } - if _, exists := components.Peers[peerIDKey]; !exists { - if _, validated := validatedPeersMap[peerIDKey]; validated { - components.Peers[peerIDKey] = p + // Only expose router peers and the per-network routers_map when this + // target peer actually has access to the resource (either as a router + // itself or via a policy that includes it as a source). Without this + // gate, every peer's envelope was leaking router peers of every + // network in the account — accounts with many tenants/networks + // shipped tens of unrelated peers in `peers[]` and `routers_map`. + if addSourcePeers { + components.RoutersMap[resource.NetworkID] = networkRoutingPeers + for peerIDKey := range networkRoutingPeers { + if p := a.Peers[peerIDKey]; p != nil { + if _, exists := components.RouterPeers[peerIDKey]; !exists { + components.RouterPeers[peerIDKey] = p + } + if _, exists := components.Peers[peerIDKey]; !exists { + if _, validated := validatedPeersMap[peerIDKey]; validated { + components.Peers[peerIDKey] = p + } } } } - } - - if addSourcePeers { components.NetworkResources = append(components.NetworkResources, resource) } } @@ -354,6 +359,44 @@ func (a *Account) getPeersGroupsPoliciesRoutes( routeAccessControlGroups[groupID] = struct{}{} } } + + // Include route advertisers in relevantPeerIDs. The envelope + // encoder writes route.peer_index by looking up r.Peer in the + // shipped peers list; if the advertiser is policy-isolated from + // the target peer (no rule edge between them), it would otherwise + // be omitted and the decoder would fail to resolve r.Peer, leaving + // the client without a WG tunnel target for this route. Legacy + // NetworkMap.Routes shipped the WG public key inline, so the + // equivalence path doesn't surface this — but the dependency is + // real once a client actually tries to use the route. + // Gate by validatedPeersMap so non-validated advertisers stay out + // (matches the network-resource router behaviour at the bottom of + // this loop, and the legacy invariant that only validated peers + // reach a client's view). + if r.Peer != "" { + if _, ok := validatedPeersMap[r.Peer]; ok { + if p := a.GetPeer(r.Peer); p != nil { + relevantPeerIDs[r.Peer] = p + } + } + } + for _, groupID := range r.PeerGroups { + g := a.GetGroup(groupID) + if g == nil { + continue + } + for _, pid := range g.Peers { + if _, exists := relevantPeerIDs[pid]; exists { + continue + } + if _, ok := validatedPeersMap[pid]; !ok { + continue + } + if p := a.GetPeer(pid); p != nil { + relevantPeerIDs[pid] = p + } + } + } relevantRoutes = append(relevantRoutes, r) } From 21cfec93d4f35a5d2d3013a0393d84c71fc3da87 Mon Sep 17 00:00:00 2001 From: crn4 Date: Wed, 27 May 2026 16:51:55 +0200 Subject: [PATCH 15/42] comments cleanup --- .github/workflows/golangci-lint.yml | 4 +-- client/internal/engine.go | 8 +++--- idp/dex/sqlite_cgo.go | 11 +++----- idp/dex/sqlite_nocgo.go | 9 +++---- .../network_map/controller/controller.go | 23 ++++++---------- .../shared/grpc/components_encoder.go | 26 +++++++++---------- .../shared/grpc/components_encoder_test.go | 6 ++--- .../grpc/components_envelope_response.go | 3 +-- .../grpc/components_envelope_response_test.go | 4 +-- management/internals/shared/grpc/server.go | 10 +++---- .../types/networkmap_wire_benchmark_test.go | 5 ++-- shared/management/client/grpc.go | 5 +--- shared/management/networkmap/envelope.go | 4 +-- shared/management/networkmap/envelope_test.go | 11 +++----- shared/management/proto/management.pb.go | 12 ++++----- shared/management/proto/management.proto | 12 ++++----- 16 files changed, 63 insertions(+), 90 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 7b7b32ec056..7842530cebd 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -35,7 +35,7 @@ jobs: display_name: Linux name: ${{ matrix.display_name }} runs-on: ${{ matrix.os }} - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: Checkout code uses: actions/checkout@v4 @@ -58,4 +58,4 @@ jobs: skip-cache: true skip-save-cache: true cache-invalidation-interval: 0 - args: --timeout=12m + args: --timeout=20m diff --git a/client/internal/engine.go b/client/internal/engine.go index cf77d6cf9c0..d44e16619df 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -206,7 +206,7 @@ type Engine struct { // latestComponents is the most-recent NetworkMapComponents decoded from // a NetworkMapEnvelope (capability=3 peers only). Held alongside the - // NetworkMap that Calculate() produced from it so Step 3 incremental + // NetworkMap that Calculate() produced from it so future incremental // updates have a base to apply changes against. nil for legacy-format // peers. Guarded by syncMsgMux. latestComponents *types.NetworkMapComponents @@ -928,8 +928,8 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { // Components-format peer: decode the envelope back to typed // components, run Calculate() locally, and convert to the wire // NetworkMap shape the rest of the engine consumes. Components are - // retained so future incremental updates (Step 3) can apply deltas - // instead of doing a full reconstruction. + // retained so future incremental updates can apply deltas instead + // of doing a full reconstruction. localKey := e.config.WgPrivateKey.PublicKey().String() dnsName := "" if pc := update.GetPeerConfig(); pc != nil { @@ -953,7 +953,7 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { // Only retain the components view when the server sent the envelope // path. A legacy proto.NetworkMap means components == nil; writing it - // here would clobber a previously-cached snapshot, breaking the Step 3 + // here would clobber a previously-cached snapshot, breaking the // incremental-delta base on a future envelope sync. if components != nil { e.latestComponents = components diff --git a/idp/dex/sqlite_cgo.go b/idp/dex/sqlite_cgo.go index 6a598610252..5de66f647ed 100644 --- a/idp/dex/sqlite_cgo.go +++ b/idp/dex/sqlite_cgo.go @@ -7,14 +7,9 @@ import ( ) // newSQLite3 builds the dex SQLite3 config. CGO builds use the upstream -// struct that takes a File path. Non-CGO builds (see sqlite_nocgo.go) build -// the empty stub — `Open()` then returns the dex "SQLite not available" -// error, which is correct behaviour for binaries that can't link sqlite3. -// -// Wrapping the construction this way keeps the call sites in config.go / -// provider.go compilable under both build modes — the upstream stub for -// !cgo has no File field, so a bare `sql.SQLite3{File: file}` literal fails -// the cross-compile for targets like linux/arm/v6 in the release pipeline. +// struct that takes a File path. Non-CGO builds get an empty stub whose +// Open() returns the dex "SQLite not available" error — correct behaviour +// for binaries that can't link sqlite3 (e.g. cross-compiled ARM targets). func newSQLite3(file string) *sql.SQLite3 { return &sql.SQLite3{File: file} } diff --git a/idp/dex/sqlite_nocgo.go b/idp/dex/sqlite_nocgo.go index 1820a8e40a6..4def12143cd 100644 --- a/idp/dex/sqlite_nocgo.go +++ b/idp/dex/sqlite_nocgo.go @@ -7,12 +7,9 @@ import ( ) // newSQLite3 for non-CGO builds. The dex SQLite3 stub has no fields and its -// Open() returns an error documenting the missing CGO support, which is the -// right behaviour for the cross-compiled artefacts (linux/arm, wasm, etc.) -// — those targets never actually run the embedded IdP. -// -// The `file` argument is ignored intentionally; keeping the signature -// matched with sqlite_cgo.go lets the call sites stay identical. +// Open() returns an error documenting the missing CGO support — correct +// behaviour for cross-compiled artefacts that never actually run the +// embedded IdP. The `file` argument is ignored. func newSQLite3(_ string) *sql.SQLite3 { return &sql.SQLite3{} } diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 938df5539ac..1eae5f11192 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -56,13 +56,10 @@ type Controller struct { integratedPeerValidator integrated_validator.IntegratedValidator - // componentsDisabled is the kill switch for the component-based wire - // format. When true the controller emits legacy proto.NetworkMap to every - // peer regardless of capability — used to roll back instantly via a - // management restart from a bad components encoder. - // - // Set once in NewController from NB_NETWORK_MAP_COMPONENTS_DISABLE and - // never written after — readers race-free without a mutex. + // componentsDisabled, when true, forces the controller to emit legacy + // proto.NetworkMap to every peer regardless of capability. Set once at + // construction and never written after — readers race-free without a + // mutex. componentsDisabled bool } @@ -98,17 +95,14 @@ func NewController(ctx context.Context, store store.Store, metrics telemetry.App } // PeerNeedsComponents reports whether the gRPC layer should emit the -// component-based wire format for this peer. Combines the peer's advertised -// capability with the controller-level kill switch — callers ask exactly -// this question, so encapsulating it removes accidental double-checks. +// component-based wire format for this peer. func (c *Controller) PeerNeedsComponents(p *nbpeer.Peer) bool { return p != nil && p.SupportsComponentNetworkMap() && !c.componentsDisabled } // parseBoolEnv reads an env var via strconv.ParseBool so callers accept the // usual "1/t/T/TRUE/true/True" set instead of being strict about a single -// literal — matches the convention used elsewhere in the codebase -// (e.g. event.go's NB_TRAFFIC_EVENT_*) and reduces operator surprises. +// literal. func parseBoolEnv(key string) bool { v, _ := strconv.ParseBool(os.Getenv(key)) return v @@ -420,9 +414,8 @@ func (c *Controller) BufferUpdateAccountPeers(ctx context.Context, accountID str // GetValidatedPeerWithMap. It returns raw NetworkMapComponents for capable // peers along with the proxy NetworkMap fragment (BYOP / port-forwarding // data the legacy server folds in via NetworkMap.Merge). The gRPC layer -// encodes both into the wire envelope. The caller is responsible for -// checking peer capability + componentsDisabled before dispatching here — -// this method does NOT branch on capability itself. +// encodes both into the wire envelope. Callers must gate on capability +// themselves before dispatching here — this method does NOT branch on it. func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequiresApproval bool, accountID string, peer *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMapComponents, *types.NetworkMap, []*posture.Checks, int64, error) { if isRequiresApproval { network, err := c.repo.GetAccountNetwork(ctx, accountID) diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index af91b008c44..2164586f671 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -18,9 +18,9 @@ import ( const wgKeyRawLen = 32 // ComponentsEnvelopeInput bundles the data the component-format encoder needs. -// In Step 2 the envelope is fully self-contained — every field needed by the -// client's local Calculate() comes from the components struct itself. The -// only externally-supplied data is the receiving peer's PeerConfig (which is +// The envelope is fully self-contained — every field needed by the client's +// local Calculate() comes from the components struct itself. The only +// externally-supplied data is the receiving peer's PeerConfig (which is // computed alongside the components in the network_map controller and reused // from the legacy proto path) and the dns_domain string. type ComponentsEnvelopeInput struct { @@ -48,21 +48,19 @@ type ComponentsEnvelopeInput struct { // not byte-equal. // // Callers must NOT concatenate or merge envelopes from different encodes — -// index spaces are local to a single envelope. Delta sync (Step 3+) will -// use a different shape for the same reason. +// index spaces are local to a single envelope. func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvelope { c := in.Components // Graceful degrade when components is nil — matches the legacy path's - // account_components.go:43 behaviour for missing/unvalidated peers - // (return a NetworkMap with only Network populated). The receiver gets - // an envelope it can decode without crashing; AccountSettings stays - // non-nil so client-side dereferences are safe. + // behaviour for missing/unvalidated peers (return a NetworkMap with only + // Network populated). The receiver gets an envelope it can decode + // without crashing; AccountSettings stays non-nil so client-side + // dereferences are safe. if c == nil { // Match legacy missing-peer minimum: a NetworkMap with only Network - // populated (account_components.go:43). The receiver gets enough to - // bootstrap (Network identifier, dns_domain, account_settings) and - // nothing else. + // populated. The receiver gets enough to bootstrap (Network + // identifier, dns_domain, account_settings) and nothing else. return &proto.NetworkMapEnvelope{ Payload: &proto.NetworkMapEnvelope_Full{ Full: &proto.NetworkMapComponentsFull{ @@ -129,8 +127,8 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel } // networkSerial returns c.Network.CurrentSerial() with a nil guard. The -// production path always populates c.Network (account_components.go:86), but -// the encoder is exported and a hand-built components struct may omit it. +// production path always populates c.Network, but the encoder is exported +// and a hand-built components struct may omit it. func networkSerial(n *types.Network) uint64 { if n == nil { return 0 diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go index de859b100d1..e42675b0339 100644 --- a/management/internals/shared/grpc/components_encoder_test.go +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -814,8 +814,8 @@ func TestToProxyPatch_PopulatesAllFields(t *testing.T) { // TestEncodeNetworkMapEnvelope_ProxyPatchPropagated covers the ProxyPatch // pass-through in both encoder branches (normal path + nil-Components -// graceful-degrade). Without this test a regression that drops `ProxyPatch:` -// from one of the struct literals in components_encoder.go would slip past CI. +// graceful-degrade). Guards against a regression that drops `ProxyPatch:` +// from one of the envelope struct literals. func TestEncodeNetworkMapEnvelope_ProxyPatchPropagated(t *testing.T) { patch := &proto.ProxyPatch{ ForwardingRules: []*proto.ForwardingRule{{ @@ -850,7 +850,7 @@ func TestEncodeNetworkMapEnvelope_ProxyPatchPropagated(t *testing.T) { func TestEncodeNetworkMapEnvelope_NilComponentsGracefulDegrade(t *testing.T) { // nil Components → minimal envelope, no crash. Matches the legacy - // account_components.go:43 behaviour for missing/unvalidated peers. + // behaviour for missing/unvalidated peers. env := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ Components: nil, DNSDomain: "netbird.cloud", diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index 5a6f8d06615..edb236e7e8e 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -128,8 +128,7 @@ func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePr // that's the destination of an SSH-enabling policy without having // peer.SSHEnabled set locally. // -// Mirrors the two activation paths in Calculate() (`networkmap_components.go` -// `getPeerConnectionResources`): +// Mirrors the two activation paths Calculate() uses: // 1. Explicit: rule.Protocol == NetbirdSSH and peer is in the rule's // destinations. // 2. Legacy implicit: rule covers TCP/22 or TCP/22022 (or ALL), peer is in diff --git a/management/internals/shared/grpc/components_envelope_response_test.go b/management/internals/shared/grpc/components_envelope_response_test.go index dfb6b573424..bf35bb7b94d 100644 --- a/management/internals/shared/grpc/components_envelope_response_test.go +++ b/management/internals/shared/grpc/components_envelope_response_test.go @@ -12,9 +12,7 @@ import ( // TestComputeSSHEnabledForPeer covers both Calculate-mirroring branches: // explicit NetbirdSSH protocol, and the legacy implicit case where a // TCP/22 (or 22022 / ALL / port-range-covering-22) rule activates SSH when -// the destination peer has SSHEnabled=true locally. Belt-and-suspenders for -// the B1 fix that the prod-DB equivalence test alone wouldn't have caught -// if no account had this combination. +// the destination peer has SSHEnabled=true locally. func TestComputeSSHEnabledForPeer(t *testing.T) { const targetPeerID = "target" const targetGroupID = "g_dst" diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index 27d77beb4ad..a58c83497f2 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -943,11 +943,11 @@ func (s *Server) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer // (network_map.Controller.UpdateAccountPeers) skips this duplication // because it dispatches by capability before computing. // - // TODO(step-4-sync): refactor SyncPeer / SyncAndMarkPeer / their - // mocks + manager interfaces to return PeerNetworkMapResult so the - // initial-sync path stops doing duplicate work. ~13 files of churn, - // deferred until the client-side decoder lands and there's a real - // deployment of capability=3 peers worth optimizing for. + // TODO: refactor SyncPeer / SyncAndMarkPeer / their mocks + manager + // interfaces to return PeerNetworkMapResult so the initial-sync path + // stops doing duplicate work. Deferred until the client-side + // decoder lands and there's a real deployment of capability=3 peers + // worth optimizing for. _, components, proxyPatch, _, _, err := s.networkMapController.GetValidatedPeerWithComponents(ctx, false, peer.AccountID, peer) if err != nil { log.WithContext(ctx).Errorf("failed to build components for peer %s on initial sync: %v", peer.ID, err) diff --git a/management/server/types/networkmap_wire_benchmark_test.go b/management/server/types/networkmap_wire_benchmark_test.go index 43c9e1fbf11..087b1cbfa4b 100644 --- a/management/server/types/networkmap_wire_benchmark_test.go +++ b/management/server/types/networkmap_wire_benchmark_test.go @@ -15,9 +15,8 @@ import ( "github.com/netbirdio/netbird/management/server/types" ) -// wireBenchScales mirrors the scales used by networkmap_benchmark_test.go but -// trimmed: encoding+marshal are linear, so we don't need the 30k peer extreme -// to see the trend. +// wireBenchScales — trimmed scale set for wire-size measurements. Encoding +// and marshalling are linear, so the largest extremes don't add signal. var wireBenchScales = []benchmarkScale{ {"100peers_5groups", 100, 5}, {"500peers_20groups", 500, 20}, diff --git a/shared/management/client/grpc.go b/shared/management/client/grpc.go index 97abc6961a5..5c8114a54c3 100644 --- a/shared/management/client/grpc.go +++ b/shared/management/client/grpc.go @@ -952,10 +952,7 @@ func peerCapabilities(info system.Info) []proto.PeerCapability { proto.PeerCapability_PeerCapabilitySourcePrefixes, // PeerCapabilityComponentNetworkMap signals that this client can // decode the components-format SyncResponse.NetworkMapEnvelope and - // run Calculate() locally. Always advertised by Step-4-capable - // builds — there's no opt-out flag because the server-side kill - // switch (NB_NETWORK_MAP_COMPONENTS_DISABLE) covers emergency - // rollback and the client decoder is built in. + // run Calculate() locally. proto.PeerCapability_PeerCapabilityComponentNetworkMap, } if !info.DisableIPv6 { diff --git a/shared/management/networkmap/envelope.go b/shared/management/networkmap/envelope.go index e90cb59820e..e2564706b51 100644 --- a/shared/management/networkmap/envelope.go +++ b/shared/management/networkmap/envelope.go @@ -19,8 +19,8 @@ import ( // running Calculate() locally + converting back through the shared // proto helpers + merging the optional ProxyPatch. // - Components is the *types.NetworkMapComponents the engine retains so -// future incremental delta updates (Step 3) have a base to apply -// changes against. The client keeps it under its sync lock. +// future incremental delta updates have a base to apply changes +// against. The client keeps it under its sync lock. type EnvelopeResult struct { NetworkMap *proto.NetworkMap Components *types.NetworkMapComponents diff --git a/shared/management/networkmap/envelope_test.go b/shared/management/networkmap/envelope_test.go index 70e70573708..f0c40d67d6f 100644 --- a/shared/management/networkmap/envelope_test.go +++ b/shared/management/networkmap/envelope_test.go @@ -21,9 +21,7 @@ import ( // TestEnvelopeToNetworkMap_RoundTrip exercises the full client-side pipeline: // build a small components struct, encode an envelope, marshal/unmarshal the // wire bytes, decode back via EnvelopeToNetworkMap, and verify the result is -// non-empty and consistent. Deeper per-field semantic equivalence with the -// legacy server path is covered by the prod-DB equivalence test in -// management/server/store/networkmap_envelope_equivalence_test.go. +// non-empty and consistent. func TestEnvelopeToNetworkMap_RoundTrip(t *testing.T) { c, localPeerKey := buildSmokeComponents(t) @@ -51,10 +49,9 @@ func TestEnvelopeToNetworkMap_RoundTrip(t *testing.T) { // TestCalculate_FirewallRuleProtocol_NeverNetbirdSSH guards against the // scenario where a rule with Protocol=NetbirdSSH leaks the enum value into // proto.FirewallRule.Protocol. Calculate() must rewrite NetbirdSSH → TCP -// before forming firewall rules (see networkmap_components.go:282 and -// account.go:868). Without that rewrite, agents fall into UNKNOWN-protocol -// handling, which on some platforms downgrades to allow-all — a real -// security regression. +// before forming firewall rules. Without that rewrite, agents fall into +// UNKNOWN-protocol handling, which on some platforms downgrades to +// allow-all — a real security regression. func TestCalculate_FirewallRuleProtocol_NeverNetbirdSSH(t *testing.T) { c, localPeerKey := buildSmokeComponents(t) // Replace the smoke policy with a NetbirdSSH-protocol allow. diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 11fa041b4da..538b0346099 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -4511,8 +4511,8 @@ func (*StopExposeResponse) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{52} } -// NetworkMapEnvelope wraps either a full snapshot or a delta. Step 2 ships -// only Full; Delta is reserved for the incremental-update work. +// NetworkMapEnvelope wraps either a full snapshot or a delta. Only Full is +// emitted today; Delta is reserved for the incremental-update work. type NetworkMapEnvelope struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5121,9 +5121,9 @@ func (x *AccountNetwork) GetSerial() uint64 { return 0 } -// NetworkMapComponentsDelta is reserved for the incremental update protocol -// (Step 3 of the migration plan). Field numbers 1–100 are pre-allocated to -// keep room for the planned event types without needing a renumber. +// NetworkMapComponentsDelta is reserved for the incremental update +// protocol. Field numbers 1–100 are pre-allocated to keep room for the +// planned event types without needing a renumber. type NetworkMapComponentsDelta struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5358,7 +5358,7 @@ type PolicyCompact struct { // Per-account integer id (matches policies.account_seq_id). Used as a // stable reference for ResourcePoliciesMap.indexes and future delta - // updates (Step 3). + // updates. Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Action RuleAction `protobuf:"varint,2,opt,name=action,proto3,enum=management.RuleAction" json:"action,omitempty"` Protocol RuleProtocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 4031f755a70..27ea0e498ee 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -734,8 +734,8 @@ message StopExposeResponse {} // NetworkMap from the server. // ===================================================================== -// NetworkMapEnvelope wraps either a full snapshot or a delta. Step 2 ships -// only Full; Delta is reserved for the incremental-update work. +// NetworkMapEnvelope wraps either a full snapshot or a delta. Only Full is +// emitted today; Delta is reserved for the incremental-update work. message NetworkMapEnvelope { oneof payload { NetworkMapComponentsFull full = 1; @@ -898,9 +898,9 @@ message AccountNetwork { uint64 serial = 5; } -// NetworkMapComponentsDelta is reserved for the incremental update protocol -// (Step 3 of the migration plan). Field numbers 1–100 are pre-allocated to -// keep room for the planned event types without needing a renumber. +// NetworkMapComponentsDelta is reserved for the incremental update +// protocol. Field numbers 1–100 are pre-allocated to keep room for the +// planned event types without needing a renumber. message NetworkMapComponentsDelta { reserved 1 to 100; } @@ -984,7 +984,7 @@ message PeerCompact { message PolicyCompact { // Per-account integer id (matches policies.account_seq_id). Used as a // stable reference for ResourcePoliciesMap.indexes and future delta - // updates (Step 3). + // updates. uint32 id = 1; RuleAction action = 2; From 596952265d3b249fcb8c9395e70c1f088890815a Mon Sep 17 00:00:00 2001 From: crn4 Date: Thu, 28 May 2026 12:52:15 +0200 Subject: [PATCH 16/42] wgkey as peer id --- shared/management/networkmap/decode.go | 20 ++++---- shared/management/networkmap/envelope.go | 58 ++++++++---------------- 2 files changed, 30 insertions(+), 48 deletions(-) diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go index 4d4a41c8822..96d71f0db59 100644 --- a/shared/management/networkmap/decode.go +++ b/shared/management/networkmap/decode.go @@ -25,10 +25,9 @@ import ( // components struct round-trips through Calculate exactly the way the // server-side typed components would. // -// Synthetic ID scheme (underscore-separated, visually distinct from the xid -// format Calculate would put in log lines under the legacy path): +// ID scheme on the client side: // -// Peers "p_" // envelope.peers is index-addressed +// Peers base64(wg_pub_key) // stable across snapshots // Groups "g_" // Policies "pol_" // 1 rule per policy // Routes "r_" @@ -74,16 +73,20 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, c.DNSSettings = &types.DNSSettings{} } - // Phase 1: peers. The envelope's peers slice is index-addressed; we - // build a peerOrder lookup for downstream references. Peer.ID is - // synthesized from the peer's wire index — wire format ships no xid - // for peers (and never has). + // Phase 1: peers. The envelope's peers slice is index-addressed on the + // wire; we re-key by the peer's WireGuard public key (base64) so the + // in-memory components struct uses a stable identifier across + // snapshots. peerIDByIndex lets downstream phases resolve wire indexes + // back to that key. peerIDByIndex := make([]string, len(full.Peers)) for idx, pc := range full.Peers { if pc == nil { return nil, fmt.Errorf("invalid envelope: peers[%d] is nil", idx) } - peerID := synthPeerID(uint32(idx)) + if len(pc.WgPubKey) == 0 { + return nil, fmt.Errorf("invalid envelope: peers[%d] missing wg_pub_key", idx) + } + peerID := encodeWgKeyBase64(pc.WgPubKey) peer := decodePeerCompact(pc, peerID, full.AgentVersions) c.Peers[peerID] = peer peerIDByIndex[idx] = peerID @@ -494,7 +497,6 @@ func synthID(prefix string, n uint32) string { return string(buf) } -func synthPeerID(idx uint32) string { return synthID("p_", idx) } func synthGroupID(seq uint32) string { return synthID("g_", seq) } func synthPolicyID(seq uint32) string { return synthID("pol_", seq) } func synthRouteID(seq uint32) string { return synthID("r_", seq) } diff --git a/shared/management/networkmap/envelope.go b/shared/management/networkmap/envelope.go index e2564706b51..b61d85bbeb2 100644 --- a/shared/management/networkmap/envelope.go +++ b/shared/management/networkmap/envelope.go @@ -1,12 +1,10 @@ package networkmap import ( - "bytes" "context" "encoding/base64" "fmt" - nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/shared/management/proto" ) @@ -43,14 +41,18 @@ func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, lo return nil, fmt.Errorf("decode envelope: %w", err) } - // Find the receiving peer in the decoded components by WG key so we can - // derive its capabilities and set components.PeerID for Calculate(). The - // envelope.peers list is index-addressed; we synthesized IDs as "p". - localPeerID, localPeer := findPeerByWgKey(components, localPeerKey) + // Find the receiving peer in the decoded components by WG key. + // c.Peers is keyed by canonical base64 of the raw 32-byte pub key + // (decoder re-encodes the bytes off the wire). The caller may pass a + // non-canonical encoding (some persisted production keys carry + // non-zero trailing padding bits that survived a legacy import), so + // round-trip through raw bytes once to canonicalize before lookup. + canonicalKey := canonicalizeWgKey(localPeerKey) + localPeer := components.Peers[canonicalKey] if localPeer == nil { return nil, fmt.Errorf("receiving peer (wg_key prefix %q) not found among %d decoded peers — components have no PeerID, Calculate would return empty", trimKey(localPeerKey), len(components.Peers)) } - components.PeerID = localPeerID + components.PeerID = canonicalKey includeIPv6 := localPeer.SupportsIPv6() && localPeer.IPv6.IsValid() useSourcePrefixes := localPeer.SupportsSourcePrefixes() @@ -174,37 +176,15 @@ func trimKey(s string) string { return s } -// findPeerByWgKey locates the receiving peer in the decoded components by -// matching its WireGuard public key. Compares raw 32-byte decode output — -// not the base64 string — because production data has occasional non-canonical -// padding bits that round-trip through the envelope's `bytes wg_pub_key` -// field, canonicalising the encoding (semantically equivalent key, different -// string). Decodes `wgKey` once up front and reuses a stack buffer in the -// loop so an N-peer search is ~zero-alloc. -func findPeerByWgKey(c *types.NetworkMapComponents, wgKey string) (string, *nbpeer.Peer) { - const wgKeyRawLen = 32 - var ( - targetRaw [wgKeyRawLen]byte - haveRaw bool - ) - if n, err := base64.StdEncoding.Decode(targetRaw[:], []byte(wgKey)); err == nil && n == wgKeyRawLen { - haveRaw = true - } - var peerRaw [wgKeyRawLen]byte - for id, p := range c.Peers { - if p == nil { - continue - } - if p.Key == wgKey { - return id, p - } - if !haveRaw { - continue - } - n, err := base64.StdEncoding.Decode(peerRaw[:], []byte(p.Key)) - if err == nil && n == wgKeyRawLen && bytes.Equal(peerRaw[:], targetRaw[:]) { - return id, p - } +// canonicalizeWgKey normalises a base64-encoded WireGuard public key so it +// matches the canonical encoding emitted by the envelope decoder. Returns +// the input unchanged when it does not decode to 32 raw bytes (caller will +// hit a miss in the peer map and surface the error). +func canonicalizeWgKey(s string) string { + raw, err := base64.StdEncoding.DecodeString(s) + if err != nil || len(raw) != 32 { + return s } - return "", nil + return base64.StdEncoding.EncodeToString(raw) } + From 7048b8793196b78961800617f113b518644a1e43 Mon Sep 17 00:00:00 2001 From: crn4 Date: Fri, 29 May 2026 13:50:06 +0200 Subject: [PATCH 17/42] fix wasm bin filesize - type aliases --- client/internal/engine.go | 2 +- management/server/types/account.go | 156 +----------------- management/server/types/account_components.go | 2 +- management/server/types/account_test.go | 2 +- management/server/types/aliases.go | 142 ++++++++++++++++ shared/management/networkmap/decode.go | 29 ++-- shared/management/networkmap/encode.go | 2 +- shared/management/networkmap/envelope.go | 2 +- shared/management/networkmap/envelope_test.go | 31 ++++ .../management}/types/dns_settings.go | 0 shared/management/types/firewall_helpers.go | 131 +++++++++++++++ .../management}/types/firewall_rule.go | 4 +- .../management}/types/firewall_rule_test.go | 12 +- .../management}/types/group.go | 0 .../management}/types/network.go | 0 .../management}/types/network_test.go | 0 .../types/networkmap_components.go | 42 ++--- .../types/networkmap_components_compact.go | 0 .../management}/types/policy.go | 0 .../management}/types/policyrule.go | 0 .../management}/types/resource.go | 0 .../management}/types/route_firewall_rule.go | 0 22 files changed, 362 insertions(+), 195 deletions(-) create mode 100644 management/server/types/aliases.go rename {management/server => shared/management}/types/dns_settings.go (100%) create mode 100644 shared/management/types/firewall_helpers.go rename {management/server => shared/management}/types/firewall_rule.go (97%) rename {management/server => shared/management}/types/firewall_rule_test.go (92%) rename {management/server => shared/management}/types/group.go (100%) rename {management/server => shared/management}/types/network.go (100%) rename {management/server => shared/management}/types/network_test.go (100%) rename {management/server => shared/management}/types/networkmap_components.go (96%) rename {management/server => shared/management}/types/networkmap_components_compact.go (100%) rename {management/server => shared/management}/types/policy.go (100%) rename {management/server => shared/management}/types/policyrule.go (100%) rename {management/server => shared/management}/types/resource.go (100%) rename {management/server => shared/management}/types/route_firewall_rule.go (100%) diff --git a/client/internal/engine.go b/client/internal/engine.go index d44e16619df..fa7f3730d8b 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -61,7 +61,7 @@ import ( cProto "github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/client/system" nbdns "github.com/netbirdio/netbird/dns" - "github.com/netbirdio/netbird/management/server/types" + types "github.com/netbirdio/netbird/shared/management/types" "github.com/netbirdio/netbird/route" mgm "github.com/netbirdio/netbird/shared/management/client" "github.com/netbirdio/netbird/shared/management/domain" diff --git a/management/server/types/account.go b/management/server/types/account.go index 5328d34ae22..ed42a4872c9 100644 --- a/management/server/types/account.go +++ b/management/server/types/account.go @@ -42,26 +42,8 @@ const ( PrivateCategory = "private" UnknownCategory = "unknown" - // firewallRuleMinPortRangesVer defines the minimum peer version that supports port range rules. - firewallRuleMinPortRangesVer = "0.48.0" - // firewallRuleMinNativeSSHVer defines the minimum peer version that supports native SSH features in the firewall rules. - firewallRuleMinNativeSSHVer = "0.60.0" - - // nativeSSHPortString defines the default port number as a string used for native SSH connections; this port is used by clients when hijacking ssh connections. - nativeSSHPortString = "22022" - nativeSSHPortNumber = 22022 - // defaultSSHPortString defines the standard SSH port number as a string, commonly used for default SSH connections. - defaultSSHPortString = "22" - defaultSSHPortNumber = 22 ) -type supportedFeatures struct { - nativeSSH bool - portRanges bool -} - -type LookupMap map[string]struct{} - // AccountMeta is a struct that contains a stripped down version of the Account object. // It doesn't carry any peers, groups, policies, or routes, etc. Just some metadata (e.g. ID, created by, created at, etc). type AccountMeta struct { @@ -1037,7 +1019,7 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peer *nbpeer.P default: authorizedUsers[auth.Wildcard] = a.getAllowedUserIDs() } - } else if peerInDestinations && policyRuleImpliesLegacySSH(rule) && peer.SSHEnabled { + } else if peerInDestinations && PolicyRuleImpliesLegacySSH(rule) && peer.SSHEnabled { sshEnabled = true authorizedUsers[auth.Wildcard] = a.getAllowedUserIDs() } @@ -1103,15 +1085,15 @@ func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer if len(rule.Ports) == 0 && len(rule.PortRanges) == 0 { rules = append(rules, &fr) } else { - rules = append(rules, expandPortsAndRanges(fr, rule, targetPeer)...) + rules = append(rules, ExpandPortsAndRanges(fr, rule, targetPeer)...) } - rules = appendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, firewallRuleContext{ - direction: direction, - dirStr: strconv.Itoa(direction), - protocolStr: string(protocol), - actionStr: string(rule.Action), - portsJoined: strings.Join(rule.Ports, ","), + rules = AppendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, FirewallRuleContext{ + Direction: direction, + DirStr: strconv.Itoa(direction), + ProtocolStr: string(protocol), + ActionStr: string(rule.Action), + PortsJoined: strings.Join(rule.Ports, ","), }) } }, func() ([]*nbpeer.Peer, []*FirewallRule) { @@ -1119,37 +1101,6 @@ func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer } } -// PolicyRuleImpliesLegacySSH reports whether the rule (without an explicit -// NetbirdSSH protocol) implicitly authorises SSH because it permits TCP/22 or -// TCP/22022 — either by ALL-protocol coverage or by an explicit port/port-range -// containing one of those. Exposed for ToComponentSyncResponse so the -// envelope-format response mirrors the legacy SshConfig.SshEnabled bit. -func PolicyRuleImpliesLegacySSH(rule *PolicyRule) bool { - return policyRuleImpliesLegacySSH(rule) -} - -func policyRuleImpliesLegacySSH(rule *PolicyRule) bool { - return rule.Protocol == PolicyRuleProtocolALL || (rule.Protocol == PolicyRuleProtocolTCP && (portsIncludesSSH(rule.Ports) || portRangeIncludesSSH(rule.PortRanges))) -} - -func portRangeIncludesSSH(portRanges []RulePortRange) bool { - for _, pr := range portRanges { - if (pr.Start <= defaultSSHPortNumber && pr.End >= defaultSSHPortNumber) || (pr.Start <= nativeSSHPortNumber && pr.End >= nativeSSHPortNumber) { - return true - } - } - return false -} - -func portsIncludesSSH(ports []string) bool { - for _, port := range ports { - if port == defaultSSHPortString || port == nativeSSHPortString { - return true - } - } - return false -} - // getAllPeersFromGroups for given peer ID and list of groups // // Returns a list of peers from specified groups that pass specified posture checks @@ -1249,7 +1200,7 @@ func (a *Account) getRouteFirewallRules(ctx context.Context, peerID string, poli } rulePeers := a.getRulePeers(rule, policy.SourcePostureChecks, peerID, distributionPeers, validatedPeersMap) - rules := generateRouteFirewallRules(ctx, route, rule, rulePeers, FirewallRuleDirectionIN, includeIPv6) + rules := GenerateRouteFirewallRules(ctx, route, rule, rulePeers, FirewallRuleDirectionIN, includeIPv6) fwRules = append(fwRules, rules...) } } @@ -1742,95 +1693,6 @@ func (a *Account) createProxyPolicy(svc *service.Service, target *service.Target } } -// expandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules -func expandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule { - features := peerSupportedFirewallFeatures(peer.Meta.WtVersion) - - var expanded []*FirewallRule - - for _, port := range rule.Ports { - fr := base - fr.Port = port - expanded = append(expanded, &fr) - } - - for _, portRange := range rule.PortRanges { - // prefer PolicyRule.Ports - if len(rule.Ports) > 0 { - break - } - fr := base - - if features.portRanges { - fr.PortRange = portRange - } else { - // Peer doesn't support port ranges, only allow single-port ranges - if portRange.Start != portRange.End { - continue - } - fr.Port = strconv.FormatUint(uint64(portRange.Start), 10) - } - expanded = append(expanded, &fr) - } - - if shouldCheckRulesForNativeSSH(features.nativeSSH, rule, peer) || rule.Protocol == PolicyRuleProtocolNetbirdSSH { - expanded = addNativeSSHRule(base, expanded) - } - - return expanded -} - -// addNativeSSHRule adds a native SSH rule (port 22022) to the expanded rules if the base rule has port 22 configured. -func addNativeSSHRule(base FirewallRule, expanded []*FirewallRule) []*FirewallRule { - shouldAdd := false - for _, fr := range expanded { - if isPortInRule(nativeSSHPortString, 22022, fr) { - return expanded - } - if isPortInRule(defaultSSHPortString, 22, fr) { - shouldAdd = true - } - } - if !shouldAdd { - return expanded - } - - fr := base - fr.Port = nativeSSHPortString - return append(expanded, &fr) -} - -func isPortInRule(portString string, portInt uint16, rule *FirewallRule) bool { - return rule.Port == portString || (rule.PortRange.Start <= portInt && portInt <= rule.PortRange.End) -} - -// shouldCheckRulesForNativeSSH determines whether specific policy rules should be checked for native SSH support. -// While users can add the nativeSSHPortString, we look for cases when they used port 22 and based on SSH enabled -// in both management and client, we indicate to add the native port. -func shouldCheckRulesForNativeSSH(supportsNative bool, rule *PolicyRule, peer *nbpeer.Peer) bool { - return supportsNative && peer.SSHEnabled && peer.Meta.Flags.ServerSSHAllowed && rule.Protocol == PolicyRuleProtocolTCP -} - -// peerSupportedFirewallFeatures checks if the peer version supports port ranges. -func peerSupportedFirewallFeatures(peerVer string) supportedFeatures { - if strings.Contains(peerVer, "dev") { - return supportedFeatures{true, true} - } - - var features supportedFeatures - - meetMinVer, err := posture.MeetsMinVersion(firewallRuleMinNativeSSHVer, peerVer) - features.nativeSSH = err == nil && meetMinVer - - if features.nativeSSH { - features.portRanges = true - } else { - meetMinVer, err = posture.MeetsMinVersion(firewallRuleMinPortRangesVer, peerVer) - features.portRanges = err == nil && meetMinVer - } - - return features -} // filterZoneRecordsForPeers filters DNS records to only include peers to connect. // AAAA records are excluded when the requesting peer lacks IPv6 capability. diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index a2bab49d421..4dd785273ca 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -477,7 +477,7 @@ func (a *Account) getPeersGroupsPoliciesRoutes( default: sshReqs.needAllowedUserIDs = true } - } else if policyRuleImpliesLegacySSH(rule) && peerSSHEnabled { + } else if PolicyRuleImpliesLegacySSH(rule) && peerSSHEnabled { sshReqs.needAllowedUserIDs = true } } diff --git a/management/server/types/account_test.go b/management/server/types/account_test.go index b55b41638f2..52fa3829eb2 100644 --- a/management/server/types/account_test.go +++ b/management/server/types/account_test.go @@ -700,7 +700,7 @@ func Test_ExpandPortsAndRanges_SSHRuleExpansion(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := expandPortsAndRanges(tt.base, tt.rule, tt.peer) + result := ExpandPortsAndRanges(tt.base, tt.rule, tt.peer) var ports []string for _, fr := range result { diff --git a/management/server/types/aliases.go b/management/server/types/aliases.go new file mode 100644 index 00000000000..949bd743263 --- /dev/null +++ b/management/server/types/aliases.go @@ -0,0 +1,142 @@ +package types + +import ( + "context" + "math/rand" + "net" + "net/netip" + + nbpeer "github.com/netbirdio/netbird/management/server/peer" + nbroute "github.com/netbirdio/netbird/route" + sharedtypes "github.com/netbirdio/netbird/shared/management/types" +) + +// Type aliases for types relocated to shared/management/types so that the +// client-side compute path can depend on them + +type DNSSettings = sharedtypes.DNSSettings + +type FirewallRule = sharedtypes.FirewallRule + +type Group = sharedtypes.Group +type GroupPeer = sharedtypes.GroupPeer + +type Network = sharedtypes.Network +type NetworkMap = sharedtypes.NetworkMap +type ForwardingRule = sharedtypes.ForwardingRule + +type Policy = sharedtypes.Policy +type PolicyUpdateOperation = sharedtypes.PolicyUpdateOperation + +type PolicyRule = sharedtypes.PolicyRule +type PolicyUpdateOperationType = sharedtypes.PolicyUpdateOperationType +type PolicyTrafficActionType = sharedtypes.PolicyTrafficActionType +type PolicyRuleProtocolType = sharedtypes.PolicyRuleProtocolType +type PolicyRuleDirection = sharedtypes.PolicyRuleDirection +type RulePortRange = sharedtypes.RulePortRange + +type Resource = sharedtypes.Resource +type ResourceType = sharedtypes.ResourceType + +type RouteFirewallRule = sharedtypes.RouteFirewallRule + +type NetworkMapComponents = sharedtypes.NetworkMapComponents +type AccountSettingsInfo = sharedtypes.AccountSettingsInfo + +type GroupCompact = sharedtypes.GroupCompact +type NetworkMapComponentsCompact = sharedtypes.NetworkMapComponentsCompact + +type LookupMap = sharedtypes.LookupMap +type FirewallRuleContext = sharedtypes.FirewallRuleContext + +const ( + GroupIssuedAPI = sharedtypes.GroupIssuedAPI + GroupIssuedJWT = sharedtypes.GroupIssuedJWT + GroupIssuedIntegration = sharedtypes.GroupIssuedIntegration + GroupAllName = sharedtypes.GroupAllName +) + +// Function forwarders preserve types.X(...) call sites that previously +// resolved to package-local funcs. Plain forwarders (not var aliases) keep +// the symbol immutable and allow the inliner to flatten the call. + +func PolicyRuleImpliesLegacySSH(rule *PolicyRule) bool { + return sharedtypes.PolicyRuleImpliesLegacySSH(rule) +} + +func ExpandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule { + return sharedtypes.ExpandPortsAndRanges(base, rule, peer) +} + +func AppendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *nbpeer.Peer, rule *PolicyRule, rc FirewallRuleContext) []*FirewallRule { + return sharedtypes.AppendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, rc) +} + +func CalculateNetworkMapFromComponents(ctx context.Context, components *NetworkMapComponents) *NetworkMap { + return sharedtypes.CalculateNetworkMapFromComponents(ctx, components) +} + +func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int, includeIPv6 bool) []*RouteFirewallRule { + return sharedtypes.GenerateRouteFirewallRules(ctx, route, rule, groupPeers, direction, includeIPv6) +} + +func AllocateIPv6Subnet(r *rand.Rand) net.IPNet { + return sharedtypes.AllocateIPv6Subnet(r) +} + +func NewNetwork() *Network { + return sharedtypes.NewNetwork() +} + +func AllocatePeerIP(prefix netip.Prefix, takenIps []netip.Addr) (netip.Addr, error) { + return sharedtypes.AllocatePeerIP(prefix, takenIps) +} + +func AllocateRandomPeerIP(prefix netip.Prefix) (netip.Addr, error) { + return sharedtypes.AllocateRandomPeerIP(prefix) +} + +func AllocateRandomPeerIPv6(prefix netip.Prefix) (netip.Addr, error) { + return sharedtypes.AllocateRandomPeerIPv6(prefix) +} + +func ParseRuleString(rule string) (PolicyRuleProtocolType, RulePortRange, error) { + return sharedtypes.ParseRuleString(rule) +} + +const ( + FirewallRuleDirectionIN = sharedtypes.FirewallRuleDirectionIN + FirewallRuleDirectionOUT = sharedtypes.FirewallRuleDirectionOUT +) + +const ( + ResourceTypePeer = sharedtypes.ResourceTypePeer + ResourceTypeDomain = sharedtypes.ResourceTypeDomain + ResourceTypeHost = sharedtypes.ResourceTypeHost + ResourceTypeSubnet = sharedtypes.ResourceTypeSubnet +) + +const ( + PolicyTrafficActionAccept = sharedtypes.PolicyTrafficActionAccept + PolicyTrafficActionDrop = sharedtypes.PolicyTrafficActionDrop +) + +const ( + PolicyRuleProtocolALL = sharedtypes.PolicyRuleProtocolALL + PolicyRuleProtocolTCP = sharedtypes.PolicyRuleProtocolTCP + PolicyRuleProtocolUDP = sharedtypes.PolicyRuleProtocolUDP + PolicyRuleProtocolICMP = sharedtypes.PolicyRuleProtocolICMP + PolicyRuleProtocolNetbirdSSH = sharedtypes.PolicyRuleProtocolNetbirdSSH +) + +const ( + PolicyRuleFlowDirect = sharedtypes.PolicyRuleFlowDirect + PolicyRuleFlowBidirect = sharedtypes.PolicyRuleFlowBidirect +) + +const ( + DefaultRuleName = sharedtypes.DefaultRuleName + DefaultRuleDescription = sharedtypes.DefaultRuleDescription + DefaultPolicyName = sharedtypes.DefaultPolicyName + DefaultPolicyDescription = sharedtypes.DefaultPolicyDescription +) diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go index 96d71f0db59..73ff308099e 100644 --- a/shared/management/networkmap/decode.go +++ b/shared/management/networkmap/decode.go @@ -8,14 +8,16 @@ import ( "strconv" "time" + log "github.com/sirupsen/logrus" + nbdns "github.com/netbirdio/netbird/dns" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" nbpeer "github.com/netbirdio/netbird/management/server/peer" - "github.com/netbirdio/netbird/management/server/types" nbroute "github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/proto" + "github.com/netbirdio/netbird/shared/management/types" ) // DecodeEnvelope converts a NetworkMapEnvelope into a NetworkMapComponents @@ -77,16 +79,22 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, // wire; we re-key by the peer's WireGuard public key (base64) so the // in-memory components struct uses a stable identifier across // snapshots. peerIDByIndex lets downstream phases resolve wire indexes - // back to that key. + // back to that key. A peer with a missing or malformed wg_pub_key is + // skipped (and its index keeps "" so any cross-reference falls into the + // same missing-peer branch downstream) — matches legacy behaviour, which + // degrades gracefully rather than aborting the whole sync on a single + // bad row. peerIDByIndex := make([]string, len(full.Peers)) for idx, pc := range full.Peers { if pc == nil { - return nil, fmt.Errorf("invalid envelope: peers[%d] is nil", idx) + log.Warnf("envelope: peers[%d] is nil, skipping", idx) + continue } - if len(pc.WgPubKey) == 0 { - return nil, fmt.Errorf("invalid envelope: peers[%d] missing wg_pub_key", idx) + if len(pc.WgPubKey) != 32 { + log.Warnf("envelope: peers[%d] wg_pub_key length %d (want 32), skipping", idx, len(pc.WgPubKey)) + continue } - peerID := encodeWgKeyBase64(pc.WgPubKey) + peerID := base64.StdEncoding.EncodeToString(pc.WgPubKey) peer := decodePeerCompact(pc, peerID, full.AgentVersions) c.Peers[peerID] = peer peerIDByIndex[idx] = peerID @@ -273,7 +281,7 @@ func decodePeerCompact(pc *proto.PeerCompact, peerID string, agentVersions []str } peer := &nbpeer.Peer{ ID: peerID, - Key: encodeWgKeyBase64(pc.WgPubKey), + Key: peerID, SSHKey: string(pc.SshPubKey), SSHEnabled: pc.SshEnabled, DNSLabel: pc.DnsLabel, @@ -568,13 +576,6 @@ func protocolFromProto(p proto.RuleProtocol) types.PolicyRuleProtocolType { } } -func encodeWgKeyBase64(raw []byte) string { - if len(raw) != 32 { - return "" - } - return base64.StdEncoding.EncodeToString(raw) -} - func lookupAgentVersion(table []string, idx uint32) string { if int(idx) < len(table) { return table[idx] diff --git a/shared/management/networkmap/encode.go b/shared/management/networkmap/encode.go index 1b1c3380ec6..e808480ead2 100644 --- a/shared/management/networkmap/encode.go +++ b/shared/management/networkmap/encode.go @@ -21,7 +21,7 @@ import ( "net/netip" nbpeer "github.com/netbirdio/netbird/management/server/peer" - "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/shared/management/types" nbroute "github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/netiputil" diff --git a/shared/management/networkmap/envelope.go b/shared/management/networkmap/envelope.go index b61d85bbeb2..642391e7bf5 100644 --- a/shared/management/networkmap/envelope.go +++ b/shared/management/networkmap/envelope.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "fmt" - "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/shared/management/types" "github.com/netbirdio/netbird/shared/management/proto" ) diff --git a/shared/management/networkmap/envelope_test.go b/shared/management/networkmap/envelope_test.go index f0c40d67d6f..666e86cee59 100644 --- a/shared/management/networkmap/envelope_test.go +++ b/shared/management/networkmap/envelope_test.go @@ -97,6 +97,37 @@ func TestEnvelopeToNetworkMap_FullPayloadMissing(t *testing.T) { require.Error(t, err, "envelope with no Full payload must produce an error") } +// TestDecodeEnvelope_MalformedWgKeyPeerSkipped feeds an envelope where one +// peer has a wg_pub_key that is not 32 bytes long. The decoder must skip +// that peer (keeping the rest of the snapshot usable) instead of aborting +// the whole sync — mirrors legacy behaviour that tolerates an occasional +// bad row. +func TestDecodeEnvelope_MalformedWgKeyPeerSkipped(t *testing.T) { + c, localPeerKey := buildSmokeComponents(t) + envelope := mgmtgrpc.EncodeNetworkMapEnvelope(mgmtgrpc.ComponentsEnvelopeInput{ + Components: c, + DNSDomain: "netbird.cloud", + }) + require.NotNil(t, envelope.GetFull()) + + full := envelope.GetFull() + require.Len(t, full.Peers, 2, "smoke fixture should have two peers") + + // Truncate the second peer's wg_pub_key so it fails the length gate. + full.Peers[1].WgPubKey = full.Peers[1].WgPubKey[:31] + + wire, err := goproto.Marshal(envelope) + require.NoError(t, err, "marshal envelope") + var decoded proto.NetworkMapEnvelope + require.NoError(t, goproto.Unmarshal(wire, &decoded), "unmarshal envelope") + + result, err := nbnetworkmap.EnvelopeToNetworkMap(context.Background(), &decoded, localPeerKey, "netbird.cloud") + require.NoError(t, err, "EnvelopeToNetworkMap must tolerate one bad peer key") + require.NotNil(t, result) + require.NotNil(t, result.Components) + require.Len(t, result.Components.Peers, 1, "the well-formed peer survives, the malformed one is dropped") +} + // buildSmokeComponents returns a minimal NetworkMapComponents (2 peers, 1 // group, 1 allow policy) plus the receiving peer's WG public key. Sufficient // to validate the encode → marshal → decode → Calculate pipeline produces diff --git a/management/server/types/dns_settings.go b/shared/management/types/dns_settings.go similarity index 100% rename from management/server/types/dns_settings.go rename to shared/management/types/dns_settings.go diff --git a/shared/management/types/firewall_helpers.go b/shared/management/types/firewall_helpers.go new file mode 100644 index 00000000000..df2bf14ea50 --- /dev/null +++ b/shared/management/types/firewall_helpers.go @@ -0,0 +1,131 @@ +package types + +import ( + "strconv" + "strings" + + "github.com/netbirdio/netbird/management/server/posture" + nbpeer "github.com/netbirdio/netbird/management/server/peer" +) + +const ( + firewallRuleMinPortRangesVer = "0.48.0" + firewallRuleMinNativeSSHVer = "0.60.0" + + nativeSSHPortString = "22022" + nativeSSHPortNumber = 22022 + defaultSSHPortString = "22" + defaultSSHPortNumber = 22 +) + +type supportedFeatures struct { + nativeSSH bool + portRanges bool +} + +type LookupMap map[string]struct{} + +func PolicyRuleImpliesLegacySSH(rule *PolicyRule) bool { + return rule.Protocol == PolicyRuleProtocolALL || (rule.Protocol == PolicyRuleProtocolTCP && (portsIncludesSSH(rule.Ports) || portRangeIncludesSSH(rule.PortRanges))) +} + +func portRangeIncludesSSH(portRanges []RulePortRange) bool { + for _, pr := range portRanges { + if (pr.Start <= defaultSSHPortNumber && pr.End >= defaultSSHPortNumber) || (pr.Start <= nativeSSHPortNumber && pr.End >= nativeSSHPortNumber) { + return true + } + } + return false +} + +func portsIncludesSSH(ports []string) bool { + for _, port := range ports { + if port == defaultSSHPortString || port == nativeSSHPortString { + return true + } + } + return false +} + +// ExpandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules. +func ExpandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule { + features := peerSupportedFirewallFeatures(peer.Meta.WtVersion) + + var expanded []*FirewallRule + + for _, port := range rule.Ports { + fr := base + fr.Port = port + expanded = append(expanded, &fr) + } + + for _, portRange := range rule.PortRanges { + if len(rule.Ports) > 0 { + break + } + fr := base + + if features.portRanges { + fr.PortRange = portRange + } else { + if portRange.Start != portRange.End { + continue + } + fr.Port = strconv.FormatUint(uint64(portRange.Start), 10) + } + expanded = append(expanded, &fr) + } + + if shouldCheckRulesForNativeSSH(features.nativeSSH, rule, peer) || rule.Protocol == PolicyRuleProtocolNetbirdSSH { + expanded = addNativeSSHRule(base, expanded) + } + + return expanded +} + +func addNativeSSHRule(base FirewallRule, expanded []*FirewallRule) []*FirewallRule { + shouldAdd := false + for _, fr := range expanded { + if isPortInRule(nativeSSHPortString, 22022, fr) { + return expanded + } + if isPortInRule(defaultSSHPortString, 22, fr) { + shouldAdd = true + } + } + if !shouldAdd { + return expanded + } + + fr := base + fr.Port = nativeSSHPortString + return append(expanded, &fr) +} + +func isPortInRule(portString string, portInt uint16, rule *FirewallRule) bool { + return rule.Port == portString || (rule.PortRange.Start <= portInt && portInt <= rule.PortRange.End) +} + +func shouldCheckRulesForNativeSSH(supportsNative bool, rule *PolicyRule, peer *nbpeer.Peer) bool { + return supportsNative && peer.SSHEnabled && peer.Meta.Flags.ServerSSHAllowed && rule.Protocol == PolicyRuleProtocolTCP +} + +func peerSupportedFirewallFeatures(peerVer string) supportedFeatures { + if strings.Contains(peerVer, "dev") { + return supportedFeatures{true, true} + } + + var features supportedFeatures + + meetMinVer, err := posture.MeetsMinVersion(firewallRuleMinNativeSSHVer, peerVer) + features.nativeSSH = err == nil && meetMinVer + + if features.nativeSSH { + features.portRanges = true + } else { + meetMinVer, err = posture.MeetsMinVersion(firewallRuleMinPortRangesVer, peerVer) + features.portRanges = err == nil && meetMinVer + } + + return features +} diff --git a/management/server/types/firewall_rule.go b/shared/management/types/firewall_rule.go similarity index 97% rename from management/server/types/firewall_rule.go rename to shared/management/types/firewall_rule.go index b76a94290af..87dcfe30780 100644 --- a/management/server/types/firewall_rule.go +++ b/shared/management/types/firewall_rule.go @@ -47,11 +47,11 @@ func (r *FirewallRule) Equal(other *FirewallRule) bool { return reflect.DeepEqual(r, other) } -// generateRouteFirewallRules generates a list of firewall rules for a given route. +// GenerateRouteFirewallRules generates a list of firewall rules for a given route. // For static routes, source ranges match the destination family (v4 or v6). // For dynamic routes (domain-based), separate v4 and v6 rules are generated // so the routing peer's forwarding chain allows both address families. -func generateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int, includeIPv6 bool) []*RouteFirewallRule { +func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int, includeIPv6 bool) []*RouteFirewallRule { rulesExists := make(map[string]struct{}) rules := make([]*RouteFirewallRule, 0) diff --git a/management/server/types/firewall_rule_test.go b/shared/management/types/firewall_rule_test.go similarity index 92% rename from management/server/types/firewall_rule_test.go rename to shared/management/types/firewall_rule_test.go index 8d97a46bc60..9de4ca04a10 100644 --- a/management/server/types/firewall_rule_test.go +++ b/shared/management/types/firewall_rule_test.go @@ -57,7 +57,7 @@ func TestGenerateRouteFirewallRules_V4Route(t *testing.T) { Protocol: PolicyRuleProtocolALL, } - rules := generateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) + rules := GenerateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) require.Len(t, rules, 1) assert.Equal(t, []string{"100.64.0.1/32", "100.64.0.2/32"}, rules[0].SourceRanges, "v4 route should only have v4 sources") @@ -86,7 +86,7 @@ func TestGenerateRouteFirewallRules_V6Route(t *testing.T) { Protocol: PolicyRuleProtocolALL, } - rules := generateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) + rules := GenerateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) require.Len(t, rules, 1) assert.Equal(t, []string{"fd00::1/128"}, rules[0].SourceRanges, "v6 route should only have v6 sources") @@ -115,7 +115,7 @@ func TestGenerateRouteFirewallRules_DynamicRoute_DualStack(t *testing.T) { Protocol: PolicyRuleProtocolALL, } - rules := generateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) + rules := GenerateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) require.Len(t, rules, 2, "dynamic route should produce both v4 and v6 rules") assert.Equal(t, []string{"100.64.0.1/32", "100.64.0.2/32"}, rules[0].SourceRanges) @@ -143,7 +143,7 @@ func TestGenerateRouteFirewallRules_DynamicRoute_NoV6Peers(t *testing.T) { Protocol: PolicyRuleProtocolALL, } - rules := generateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) + rules := GenerateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, true) require.Len(t, rules, 1, "no v6 peers means only v4 rule") assert.Equal(t, []string{"100.64.0.1/32", "100.64.0.2/32"}, rules[0].SourceRanges) @@ -173,7 +173,7 @@ func TestGenerateRouteFirewallRules_IncludeIPv6False(t *testing.T) { Protocol: PolicyRuleProtocolALL, } - rules := generateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, false) + rules := GenerateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, false) assert.Empty(t, rules, "v6 route should produce no rules when includeIPv6 is false") }) @@ -190,7 +190,7 @@ func TestGenerateRouteFirewallRules_IncludeIPv6False(t *testing.T) { Protocol: PolicyRuleProtocolALL, } - rules := generateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, false) + rules := GenerateRouteFirewallRules(context.Background(), r, rule, peers, FirewallRuleDirectionIN, false) require.Len(t, rules, 1, "dynamic route with includeIPv6=false should produce only v4 rule") assert.Equal(t, []string{"100.64.0.1/32", "100.64.0.2/32"}, rules[0].SourceRanges) }) diff --git a/management/server/types/group.go b/shared/management/types/group.go similarity index 100% rename from management/server/types/group.go rename to shared/management/types/group.go diff --git a/management/server/types/network.go b/shared/management/types/network.go similarity index 100% rename from management/server/types/network.go rename to shared/management/types/network.go diff --git a/management/server/types/network_test.go b/shared/management/types/network_test.go similarity index 100% rename from management/server/types/network_test.go rename to shared/management/types/network_test.go diff --git a/management/server/types/networkmap_components.go b/shared/management/types/networkmap_components.go similarity index 96% rename from management/server/types/networkmap_components.go rename to shared/management/types/networkmap_components.go index 008c041842f..f341e907129 100644 --- a/management/server/types/networkmap_components.go +++ b/shared/management/types/networkmap_components.go @@ -263,7 +263,7 @@ func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) ( default: authorizedUsers[auth.Wildcard] = c.getAllowedUserIDs() } - } else if peerInDestinations && policyRuleImpliesLegacySSH(rule) && targetPeer.SSHEnabled { + } else if peerInDestinations && PolicyRuleImpliesLegacySSH(rule) && targetPeer.SSHEnabled { sshEnabled = true authorizedUsers[auth.Wildcard] = c.getAllowedUserIDs() } @@ -330,15 +330,15 @@ func (c *NetworkMapComponents) connResourcesGenerator(targetPeer *nbpeer.Peer) ( if len(rule.Ports) == 0 && len(rule.PortRanges) == 0 { rules = append(rules, &fr) } else { - rules = append(rules, expandPortsAndRanges(fr, rule, targetPeer)...) + rules = append(rules, ExpandPortsAndRanges(fr, rule, targetPeer)...) } - rules = appendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, firewallRuleContext{ - direction: direction, - dirStr: dirStr, - protocolStr: protocolStr, - actionStr: actionStr, - portsJoined: portsJoined, + rules = AppendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, FirewallRuleContext{ + Direction: direction, + DirStr: dirStr, + ProtocolStr: protocolStr, + ActionStr: actionStr, + PortsJoined: portsJoined, }) } }, func() ([]*nbpeer.Peer, []*FirewallRule) { @@ -691,7 +691,7 @@ func (c *NetworkMapComponents) getRouteFirewallRules(ctx context.Context, peerID } rulePeers := c.getRulePeers(rule, policy.SourcePostureChecks, peerID, distributionPeers) - rules := generateRouteFirewallRules(ctx, route, rule, rulePeers, FirewallRuleDirectionIN, includeIPv6) + rules := GenerateRouteFirewallRules(ctx, route, rule, rulePeers, FirewallRuleDirectionIN, includeIPv6) fwRules = append(fwRules, rules...) } } @@ -960,21 +960,21 @@ func (c *NetworkMapComponents) addNetworksRoutingPeers( return peersToConnect } -type firewallRuleContext struct { - direction int - dirStr string - protocolStr string - actionStr string - portsJoined string +type FirewallRuleContext struct { + Direction int + DirStr string + ProtocolStr string + ActionStr string + PortsJoined string } -func appendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *nbpeer.Peer, rule *PolicyRule, rc firewallRuleContext) []*FirewallRule { +func AppendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *nbpeer.Peer, rule *PolicyRule, rc FirewallRuleContext) []*FirewallRule { if !peer.IPv6.IsValid() || !targetPeer.SupportsIPv6() || !targetPeer.IPv6.IsValid() { return rules } v6IP := peer.IPv6.String() - v6RuleID := rule.ID + v6IP + rc.dirStr + rc.protocolStr + rc.actionStr + rc.portsJoined + v6RuleID := rule.ID + v6IP + rc.DirStr + rc.ProtocolStr + rc.ActionStr + rc.PortsJoined if _, ok := rulesExists[v6RuleID]; ok { return rules } @@ -983,12 +983,12 @@ func appendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct v6fr := FirewallRule{ PolicyID: rule.ID, PeerIP: v6IP, - Direction: rc.direction, - Action: rc.actionStr, - Protocol: rc.protocolStr, + Direction: rc.Direction, + Action: rc.ActionStr, + Protocol: rc.ProtocolStr, } if len(rule.Ports) == 0 && len(rule.PortRanges) == 0 { return append(rules, &v6fr) } - return append(rules, expandPortsAndRanges(v6fr, rule, targetPeer)...) + return append(rules, ExpandPortsAndRanges(v6fr, rule, targetPeer)...) } diff --git a/management/server/types/networkmap_components_compact.go b/shared/management/types/networkmap_components_compact.go similarity index 100% rename from management/server/types/networkmap_components_compact.go rename to shared/management/types/networkmap_components_compact.go diff --git a/management/server/types/policy.go b/shared/management/types/policy.go similarity index 100% rename from management/server/types/policy.go rename to shared/management/types/policy.go diff --git a/management/server/types/policyrule.go b/shared/management/types/policyrule.go similarity index 100% rename from management/server/types/policyrule.go rename to shared/management/types/policyrule.go diff --git a/management/server/types/resource.go b/shared/management/types/resource.go similarity index 100% rename from management/server/types/resource.go rename to shared/management/types/resource.go diff --git a/management/server/types/route_firewall_rule.go b/shared/management/types/route_firewall_rule.go similarity index 100% rename from management/server/types/route_firewall_rule.go rename to shared/management/types/route_firewall_rule.go From 63dd4df3f3e0d5d8bbb53d315fe32ab26de83dd9 Mon Sep 17 00:00:00 2001 From: pascal Date: Wed, 1 Jul 2026 16:49:24 +0200 Subject: [PATCH 18/42] run proto gen --- shared/management/proto/management.pb.go | 949 ++++++++++++----------- 1 file changed, 512 insertions(+), 437 deletions(-) diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 26f6ce97c8b..06e4738d8b0 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -437,7 +437,7 @@ func (x DeviceAuthorizationFlowProvider) Number() protoreflect.EnumNumber { // Deprecated: Use DeviceAuthorizationFlowProvider.Descriptor instead. func (DeviceAuthorizationFlowProvider) EnumDescriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{33, 0} + return file_management_proto_rawDescGZIP(), []int{34, 0} } type EncryptedMessage struct { @@ -1932,9 +1932,10 @@ type NetbirdConfig struct { // a list of TURN servers Turns []*ProtectedHostConfig `protobuf:"bytes,2,rep,name=turns,proto3" json:"turns,omitempty"` // a Signal server config - Signal *HostConfig `protobuf:"bytes,3,opt,name=signal,proto3" json:"signal,omitempty"` - Relay *RelayConfig `protobuf:"bytes,4,opt,name=relay,proto3" json:"relay,omitempty"` - Flow *FlowConfig `protobuf:"bytes,5,opt,name=flow,proto3" json:"flow,omitempty"` + Signal *HostConfig `protobuf:"bytes,3,opt,name=signal,proto3" json:"signal,omitempty"` + Relay *RelayConfig `protobuf:"bytes,4,opt,name=relay,proto3" json:"relay,omitempty"` + Flow *FlowConfig `protobuf:"bytes,5,opt,name=flow,proto3" json:"flow,omitempty"` + Metrics *MetricsConfig `protobuf:"bytes,6,opt,name=metrics,proto3" json:"metrics,omitempty"` } func (x *NetbirdConfig) Reset() { @@ -2004,6 +2005,13 @@ func (x *NetbirdConfig) GetFlow() *FlowConfig { return nil } +func (x *NetbirdConfig) GetMetrics() *MetricsConfig { + if x != nil { + return x.Metrics + } + return nil +} + // HostConfig describes connection properties of some server (e.g. STUN, Signal, Management) type HostConfig struct { state protoimpl.MessageState @@ -2230,6 +2238,53 @@ func (x *FlowConfig) GetDnsCollection() bool { return false } +type MetricsConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *MetricsConfig) Reset() { + *x = MetricsConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MetricsConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MetricsConfig) ProtoMessage() {} + +func (x *MetricsConfig) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[23] + 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 MetricsConfig.ProtoReflect.Descriptor instead. +func (*MetricsConfig) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{23} +} + +func (x *MetricsConfig) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + // JWTConfig represents JWT authentication configuration for validating tokens. type JWTConfig struct { state protoimpl.MessageState @@ -2249,7 +2304,7 @@ type JWTConfig struct { func (x *JWTConfig) Reset() { *x = JWTConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[23] + mi := &file_management_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2262,7 +2317,7 @@ func (x *JWTConfig) String() string { func (*JWTConfig) ProtoMessage() {} func (x *JWTConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[23] + mi := &file_management_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2275,7 +2330,7 @@ func (x *JWTConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use JWTConfig.ProtoReflect.Descriptor instead. func (*JWTConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{23} + return file_management_proto_rawDescGZIP(), []int{24} } func (x *JWTConfig) GetIssuer() string { @@ -2328,7 +2383,7 @@ type ProtectedHostConfig struct { func (x *ProtectedHostConfig) Reset() { *x = ProtectedHostConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[24] + mi := &file_management_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2341,7 +2396,7 @@ func (x *ProtectedHostConfig) String() string { func (*ProtectedHostConfig) ProtoMessage() {} func (x *ProtectedHostConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[24] + mi := &file_management_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2354,7 +2409,7 @@ func (x *ProtectedHostConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ProtectedHostConfig.ProtoReflect.Descriptor instead. func (*ProtectedHostConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{24} + return file_management_proto_rawDescGZIP(), []int{25} } func (x *ProtectedHostConfig) GetHostConfig() *HostConfig { @@ -2405,7 +2460,7 @@ type PeerConfig struct { func (x *PeerConfig) Reset() { *x = PeerConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[25] + mi := &file_management_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2418,7 +2473,7 @@ func (x *PeerConfig) String() string { func (*PeerConfig) ProtoMessage() {} func (x *PeerConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[25] + mi := &file_management_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2431,7 +2486,7 @@ func (x *PeerConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerConfig.ProtoReflect.Descriptor instead. func (*PeerConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{25} + return file_management_proto_rawDescGZIP(), []int{26} } func (x *PeerConfig) GetAddress() string { @@ -2511,7 +2566,7 @@ type AutoUpdateSettings struct { func (x *AutoUpdateSettings) Reset() { *x = AutoUpdateSettings{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[26] + mi := &file_management_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2524,7 +2579,7 @@ func (x *AutoUpdateSettings) String() string { func (*AutoUpdateSettings) ProtoMessage() {} func (x *AutoUpdateSettings) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[26] + mi := &file_management_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2537,7 +2592,7 @@ func (x *AutoUpdateSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateSettings.ProtoReflect.Descriptor instead. func (*AutoUpdateSettings) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{26} + return file_management_proto_rawDescGZIP(), []int{27} } func (x *AutoUpdateSettings) GetVersion() string { @@ -2592,7 +2647,7 @@ type NetworkMap struct { func (x *NetworkMap) Reset() { *x = NetworkMap{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[27] + mi := &file_management_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2605,7 +2660,7 @@ func (x *NetworkMap) String() string { func (*NetworkMap) ProtoMessage() {} func (x *NetworkMap) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[27] + mi := &file_management_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2618,7 +2673,7 @@ func (x *NetworkMap) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkMap.ProtoReflect.Descriptor instead. func (*NetworkMap) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{27} + return file_management_proto_rawDescGZIP(), []int{28} } func (x *NetworkMap) GetSerial() uint64 { @@ -2728,7 +2783,7 @@ type SSHAuth struct { func (x *SSHAuth) Reset() { *x = SSHAuth{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[28] + mi := &file_management_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2741,7 +2796,7 @@ func (x *SSHAuth) String() string { func (*SSHAuth) ProtoMessage() {} func (x *SSHAuth) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[28] + mi := &file_management_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2754,7 +2809,7 @@ func (x *SSHAuth) ProtoReflect() protoreflect.Message { // Deprecated: Use SSHAuth.ProtoReflect.Descriptor instead. func (*SSHAuth) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{28} + return file_management_proto_rawDescGZIP(), []int{29} } func (x *SSHAuth) GetUserIDClaim() string { @@ -2789,7 +2844,7 @@ type MachineUserIndexes struct { func (x *MachineUserIndexes) Reset() { *x = MachineUserIndexes{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[29] + mi := &file_management_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2802,7 +2857,7 @@ func (x *MachineUserIndexes) String() string { func (*MachineUserIndexes) ProtoMessage() {} func (x *MachineUserIndexes) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[29] + mi := &file_management_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2815,7 +2870,7 @@ func (x *MachineUserIndexes) ProtoReflect() protoreflect.Message { // Deprecated: Use MachineUserIndexes.ProtoReflect.Descriptor instead. func (*MachineUserIndexes) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{29} + return file_management_proto_rawDescGZIP(), []int{30} } func (x *MachineUserIndexes) GetIndexes() []uint32 { @@ -2846,7 +2901,7 @@ type RemotePeerConfig struct { func (x *RemotePeerConfig) Reset() { *x = RemotePeerConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[30] + mi := &file_management_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2859,7 +2914,7 @@ func (x *RemotePeerConfig) String() string { func (*RemotePeerConfig) ProtoMessage() {} func (x *RemotePeerConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[30] + mi := &file_management_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2872,7 +2927,7 @@ func (x *RemotePeerConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use RemotePeerConfig.ProtoReflect.Descriptor instead. func (*RemotePeerConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{30} + return file_management_proto_rawDescGZIP(), []int{31} } func (x *RemotePeerConfig) GetWgPubKey() string { @@ -2927,7 +2982,7 @@ type SSHConfig struct { func (x *SSHConfig) Reset() { *x = SSHConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[31] + mi := &file_management_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2940,7 +2995,7 @@ func (x *SSHConfig) String() string { func (*SSHConfig) ProtoMessage() {} func (x *SSHConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[31] + mi := &file_management_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2953,7 +3008,7 @@ func (x *SSHConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SSHConfig.ProtoReflect.Descriptor instead. func (*SSHConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{31} + return file_management_proto_rawDescGZIP(), []int{32} } func (x *SSHConfig) GetSshEnabled() bool { @@ -2987,7 +3042,7 @@ type DeviceAuthorizationFlowRequest struct { func (x *DeviceAuthorizationFlowRequest) Reset() { *x = DeviceAuthorizationFlowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[32] + mi := &file_management_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3000,7 +3055,7 @@ func (x *DeviceAuthorizationFlowRequest) String() string { func (*DeviceAuthorizationFlowRequest) ProtoMessage() {} func (x *DeviceAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[32] + mi := &file_management_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3013,7 +3068,7 @@ func (x *DeviceAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceAuthorizationFlowRequest.ProtoReflect.Descriptor instead. func (*DeviceAuthorizationFlowRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{32} + return file_management_proto_rawDescGZIP(), []int{33} } // DeviceAuthorizationFlow represents Device Authorization Flow information @@ -3032,7 +3087,7 @@ type DeviceAuthorizationFlow struct { func (x *DeviceAuthorizationFlow) Reset() { *x = DeviceAuthorizationFlow{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[33] + mi := &file_management_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3045,7 +3100,7 @@ func (x *DeviceAuthorizationFlow) String() string { func (*DeviceAuthorizationFlow) ProtoMessage() {} func (x *DeviceAuthorizationFlow) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[33] + mi := &file_management_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3058,7 +3113,7 @@ func (x *DeviceAuthorizationFlow) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceAuthorizationFlow.ProtoReflect.Descriptor instead. func (*DeviceAuthorizationFlow) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{33} + return file_management_proto_rawDescGZIP(), []int{34} } func (x *DeviceAuthorizationFlow) GetProvider() DeviceAuthorizationFlowProvider { @@ -3085,7 +3140,7 @@ type PKCEAuthorizationFlowRequest struct { func (x *PKCEAuthorizationFlowRequest) Reset() { *x = PKCEAuthorizationFlowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[34] + mi := &file_management_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3098,7 +3153,7 @@ func (x *PKCEAuthorizationFlowRequest) String() string { func (*PKCEAuthorizationFlowRequest) ProtoMessage() {} func (x *PKCEAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[34] + mi := &file_management_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3111,7 +3166,7 @@ func (x *PKCEAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PKCEAuthorizationFlowRequest.ProtoReflect.Descriptor instead. func (*PKCEAuthorizationFlowRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{34} + return file_management_proto_rawDescGZIP(), []int{35} } // PKCEAuthorizationFlow represents Authorization Code Flow information @@ -3128,7 +3183,7 @@ type PKCEAuthorizationFlow struct { func (x *PKCEAuthorizationFlow) Reset() { *x = PKCEAuthorizationFlow{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[35] + mi := &file_management_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3141,7 +3196,7 @@ func (x *PKCEAuthorizationFlow) String() string { func (*PKCEAuthorizationFlow) ProtoMessage() {} func (x *PKCEAuthorizationFlow) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[35] + mi := &file_management_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3154,7 +3209,7 @@ func (x *PKCEAuthorizationFlow) ProtoReflect() protoreflect.Message { // Deprecated: Use PKCEAuthorizationFlow.ProtoReflect.Descriptor instead. func (*PKCEAuthorizationFlow) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{35} + return file_management_proto_rawDescGZIP(), []int{36} } func (x *PKCEAuthorizationFlow) GetProviderConfig() *ProviderConfig { @@ -3202,7 +3257,7 @@ type ProviderConfig struct { func (x *ProviderConfig) Reset() { *x = ProviderConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[36] + mi := &file_management_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3215,7 +3270,7 @@ func (x *ProviderConfig) String() string { func (*ProviderConfig) ProtoMessage() {} func (x *ProviderConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[36] + mi := &file_management_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3228,7 +3283,7 @@ func (x *ProviderConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderConfig.ProtoReflect.Descriptor instead. func (*ProviderConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{36} + return file_management_proto_rawDescGZIP(), []int{37} } func (x *ProviderConfig) GetClientID() string { @@ -3337,7 +3392,7 @@ type Route struct { func (x *Route) Reset() { *x = Route{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[37] + mi := &file_management_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3350,7 +3405,7 @@ func (x *Route) String() string { func (*Route) ProtoMessage() {} func (x *Route) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[37] + mi := &file_management_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3363,7 +3418,7 @@ func (x *Route) ProtoReflect() protoreflect.Message { // Deprecated: Use Route.ProtoReflect.Descriptor instead. func (*Route) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{37} + return file_management_proto_rawDescGZIP(), []int{38} } func (x *Route) GetID() string { @@ -3452,7 +3507,7 @@ type DNSConfig struct { func (x *DNSConfig) Reset() { *x = DNSConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[38] + mi := &file_management_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3465,7 +3520,7 @@ func (x *DNSConfig) String() string { func (*DNSConfig) ProtoMessage() {} func (x *DNSConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[38] + mi := &file_management_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3478,7 +3533,7 @@ func (x *DNSConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSConfig.ProtoReflect.Descriptor instead. func (*DNSConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{38} + return file_management_proto_rawDescGZIP(), []int{39} } func (x *DNSConfig) GetServiceEnable() bool { @@ -3527,7 +3582,7 @@ type CustomZone struct { func (x *CustomZone) Reset() { *x = CustomZone{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[39] + mi := &file_management_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3540,7 +3595,7 @@ func (x *CustomZone) String() string { func (*CustomZone) ProtoMessage() {} func (x *CustomZone) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[39] + mi := &file_management_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3553,7 +3608,7 @@ func (x *CustomZone) ProtoReflect() protoreflect.Message { // Deprecated: Use CustomZone.ProtoReflect.Descriptor instead. func (*CustomZone) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{39} + return file_management_proto_rawDescGZIP(), []int{40} } func (x *CustomZone) GetDomain() string { @@ -3600,7 +3655,7 @@ type SimpleRecord struct { func (x *SimpleRecord) Reset() { *x = SimpleRecord{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[40] + mi := &file_management_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3613,7 +3668,7 @@ func (x *SimpleRecord) String() string { func (*SimpleRecord) ProtoMessage() {} func (x *SimpleRecord) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[40] + mi := &file_management_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3626,7 +3681,7 @@ func (x *SimpleRecord) ProtoReflect() protoreflect.Message { // Deprecated: Use SimpleRecord.ProtoReflect.Descriptor instead. func (*SimpleRecord) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{40} + return file_management_proto_rawDescGZIP(), []int{41} } func (x *SimpleRecord) GetName() string { @@ -3679,7 +3734,7 @@ type NameServerGroup struct { func (x *NameServerGroup) Reset() { *x = NameServerGroup{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[41] + mi := &file_management_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3692,7 +3747,7 @@ func (x *NameServerGroup) String() string { func (*NameServerGroup) ProtoMessage() {} func (x *NameServerGroup) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[41] + mi := &file_management_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3705,7 +3760,7 @@ func (x *NameServerGroup) ProtoReflect() protoreflect.Message { // Deprecated: Use NameServerGroup.ProtoReflect.Descriptor instead. func (*NameServerGroup) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{41} + return file_management_proto_rawDescGZIP(), []int{42} } func (x *NameServerGroup) GetNameServers() []*NameServer { @@ -3750,7 +3805,7 @@ type NameServer struct { func (x *NameServer) Reset() { *x = NameServer{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[42] + mi := &file_management_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3763,7 +3818,7 @@ func (x *NameServer) String() string { func (*NameServer) ProtoMessage() {} func (x *NameServer) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[42] + mi := &file_management_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3776,7 +3831,7 @@ func (x *NameServer) ProtoReflect() protoreflect.Message { // Deprecated: Use NameServer.ProtoReflect.Descriptor instead. func (*NameServer) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{42} + return file_management_proto_rawDescGZIP(), []int{43} } func (x *NameServer) GetIP() string { @@ -3827,7 +3882,7 @@ type FirewallRule struct { func (x *FirewallRule) Reset() { *x = FirewallRule{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[43] + mi := &file_management_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3840,7 +3895,7 @@ func (x *FirewallRule) String() string { func (*FirewallRule) ProtoMessage() {} func (x *FirewallRule) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[43] + mi := &file_management_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3853,7 +3908,7 @@ func (x *FirewallRule) ProtoReflect() protoreflect.Message { // Deprecated: Use FirewallRule.ProtoReflect.Descriptor instead. func (*FirewallRule) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{43} + return file_management_proto_rawDescGZIP(), []int{44} } // Deprecated: Do not use. @@ -3932,7 +3987,7 @@ type NetworkAddress struct { func (x *NetworkAddress) Reset() { *x = NetworkAddress{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[44] + mi := &file_management_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3945,7 +4000,7 @@ func (x *NetworkAddress) String() string { func (*NetworkAddress) ProtoMessage() {} func (x *NetworkAddress) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[44] + mi := &file_management_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3958,7 +4013,7 @@ func (x *NetworkAddress) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkAddress.ProtoReflect.Descriptor instead. func (*NetworkAddress) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{44} + return file_management_proto_rawDescGZIP(), []int{45} } func (x *NetworkAddress) GetNetIP() string { @@ -3986,7 +4041,7 @@ type Checks struct { func (x *Checks) Reset() { *x = Checks{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[45] + mi := &file_management_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3999,7 +4054,7 @@ func (x *Checks) String() string { func (*Checks) ProtoMessage() {} func (x *Checks) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[45] + mi := &file_management_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4012,7 +4067,7 @@ func (x *Checks) ProtoReflect() protoreflect.Message { // Deprecated: Use Checks.ProtoReflect.Descriptor instead. func (*Checks) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{45} + return file_management_proto_rawDescGZIP(), []int{46} } func (x *Checks) GetFiles() []string { @@ -4037,7 +4092,7 @@ type PortInfo struct { func (x *PortInfo) Reset() { *x = PortInfo{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[46] + mi := &file_management_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4050,7 +4105,7 @@ func (x *PortInfo) String() string { func (*PortInfo) ProtoMessage() {} func (x *PortInfo) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[46] + mi := &file_management_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4063,7 +4118,7 @@ func (x *PortInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use PortInfo.ProtoReflect.Descriptor instead. func (*PortInfo) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{46} + return file_management_proto_rawDescGZIP(), []int{47} } func (m *PortInfo) GetPortSelection() isPortInfo_PortSelection { @@ -4134,7 +4189,7 @@ type RouteFirewallRule struct { func (x *RouteFirewallRule) Reset() { *x = RouteFirewallRule{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[47] + mi := &file_management_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4147,7 +4202,7 @@ func (x *RouteFirewallRule) String() string { func (*RouteFirewallRule) ProtoMessage() {} func (x *RouteFirewallRule) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[47] + mi := &file_management_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4160,7 +4215,7 @@ func (x *RouteFirewallRule) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteFirewallRule.ProtoReflect.Descriptor instead. func (*RouteFirewallRule) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{47} + return file_management_proto_rawDescGZIP(), []int{48} } func (x *RouteFirewallRule) GetSourceRanges() []string { @@ -4251,7 +4306,7 @@ type ForwardingRule struct { func (x *ForwardingRule) Reset() { *x = ForwardingRule{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[48] + mi := &file_management_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4264,7 +4319,7 @@ func (x *ForwardingRule) String() string { func (*ForwardingRule) ProtoMessage() {} func (x *ForwardingRule) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[48] + mi := &file_management_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4277,7 +4332,7 @@ func (x *ForwardingRule) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingRule.ProtoReflect.Descriptor instead. func (*ForwardingRule) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{48} + return file_management_proto_rawDescGZIP(), []int{49} } func (x *ForwardingRule) GetProtocol() RuleProtocol { @@ -4326,7 +4381,7 @@ type ExposeServiceRequest struct { func (x *ExposeServiceRequest) Reset() { *x = ExposeServiceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[49] + mi := &file_management_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4339,7 +4394,7 @@ func (x *ExposeServiceRequest) String() string { func (*ExposeServiceRequest) ProtoMessage() {} func (x *ExposeServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[49] + mi := &file_management_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4352,7 +4407,7 @@ func (x *ExposeServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExposeServiceRequest.ProtoReflect.Descriptor instead. func (*ExposeServiceRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{49} + return file_management_proto_rawDescGZIP(), []int{50} } func (x *ExposeServiceRequest) GetPort() uint32 { @@ -4425,7 +4480,7 @@ type ExposeServiceResponse struct { func (x *ExposeServiceResponse) Reset() { *x = ExposeServiceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[50] + mi := &file_management_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4438,7 +4493,7 @@ func (x *ExposeServiceResponse) String() string { func (*ExposeServiceResponse) ProtoMessage() {} func (x *ExposeServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[50] + mi := &file_management_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4451,7 +4506,7 @@ func (x *ExposeServiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ExposeServiceResponse.ProtoReflect.Descriptor instead. func (*ExposeServiceResponse) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{50} + return file_management_proto_rawDescGZIP(), []int{51} } func (x *ExposeServiceResponse) GetServiceName() string { @@ -4493,7 +4548,7 @@ type RenewExposeRequest struct { func (x *RenewExposeRequest) Reset() { *x = RenewExposeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[51] + mi := &file_management_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4506,7 +4561,7 @@ func (x *RenewExposeRequest) String() string { func (*RenewExposeRequest) ProtoMessage() {} func (x *RenewExposeRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[51] + mi := &file_management_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4519,7 +4574,7 @@ func (x *RenewExposeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RenewExposeRequest.ProtoReflect.Descriptor instead. func (*RenewExposeRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{51} + return file_management_proto_rawDescGZIP(), []int{52} } func (x *RenewExposeRequest) GetDomain() string { @@ -4538,7 +4593,7 @@ type RenewExposeResponse struct { func (x *RenewExposeResponse) Reset() { *x = RenewExposeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[52] + mi := &file_management_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4551,7 +4606,7 @@ func (x *RenewExposeResponse) String() string { func (*RenewExposeResponse) ProtoMessage() {} func (x *RenewExposeResponse) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[52] + mi := &file_management_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4564,7 +4619,7 @@ func (x *RenewExposeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RenewExposeResponse.ProtoReflect.Descriptor instead. func (*RenewExposeResponse) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{52} + return file_management_proto_rawDescGZIP(), []int{53} } type StopExposeRequest struct { @@ -4578,7 +4633,7 @@ type StopExposeRequest struct { func (x *StopExposeRequest) Reset() { *x = StopExposeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[53] + mi := &file_management_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4591,7 +4646,7 @@ func (x *StopExposeRequest) String() string { func (*StopExposeRequest) ProtoMessage() {} func (x *StopExposeRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[53] + mi := &file_management_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4604,7 +4659,7 @@ func (x *StopExposeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StopExposeRequest.ProtoReflect.Descriptor instead. func (*StopExposeRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{53} + return file_management_proto_rawDescGZIP(), []int{54} } func (x *StopExposeRequest) GetDomain() string { @@ -4623,7 +4678,7 @@ type StopExposeResponse struct { func (x *StopExposeResponse) Reset() { *x = StopExposeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[54] + mi := &file_management_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4636,7 +4691,7 @@ func (x *StopExposeResponse) String() string { func (*StopExposeResponse) ProtoMessage() {} func (x *StopExposeResponse) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[54] + mi := &file_management_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4649,7 +4704,7 @@ func (x *StopExposeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StopExposeResponse.ProtoReflect.Descriptor instead. func (*StopExposeResponse) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{54} + return file_management_proto_rawDescGZIP(), []int{55} } // NetworkMapEnvelope wraps either a full snapshot or a delta. Only Full is @@ -4669,7 +4724,7 @@ type NetworkMapEnvelope struct { func (x *NetworkMapEnvelope) Reset() { *x = NetworkMapEnvelope{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[55] + mi := &file_management_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4682,7 +4737,7 @@ func (x *NetworkMapEnvelope) String() string { func (*NetworkMapEnvelope) ProtoMessage() {} func (x *NetworkMapEnvelope) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[55] + mi := &file_management_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4695,7 +4750,7 @@ func (x *NetworkMapEnvelope) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkMapEnvelope.ProtoReflect.Descriptor instead. func (*NetworkMapEnvelope) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{55} + return file_management_proto_rawDescGZIP(), []int{56} } func (m *NetworkMapEnvelope) GetPayload() isNetworkMapEnvelope_Payload { @@ -4831,7 +4886,7 @@ type NetworkMapComponentsFull struct { func (x *NetworkMapComponentsFull) Reset() { *x = NetworkMapComponentsFull{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[56] + mi := &file_management_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4844,7 +4899,7 @@ func (x *NetworkMapComponentsFull) String() string { func (*NetworkMapComponentsFull) ProtoMessage() {} func (x *NetworkMapComponentsFull) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[56] + mi := &file_management_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4857,7 +4912,7 @@ func (x *NetworkMapComponentsFull) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkMapComponentsFull.ProtoReflect.Descriptor instead. func (*NetworkMapComponentsFull) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{56} + return file_management_proto_rawDescGZIP(), []int{57} } func (x *NetworkMapComponentsFull) GetSerial() uint64 { @@ -5056,7 +5111,7 @@ type ProxyPatch struct { func (x *ProxyPatch) Reset() { *x = ProxyPatch{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[57] + mi := &file_management_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5069,7 +5124,7 @@ func (x *ProxyPatch) String() string { func (*ProxyPatch) ProtoMessage() {} func (x *ProxyPatch) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[57] + mi := &file_management_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5082,7 +5137,7 @@ func (x *ProxyPatch) ProtoReflect() protoreflect.Message { // Deprecated: Use ProxyPatch.ProtoReflect.Descriptor instead. func (*ProxyPatch) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{57} + return file_management_proto_rawDescGZIP(), []int{58} } func (x *ProxyPatch) GetPeers() []*RemotePeerConfig { @@ -5145,7 +5200,7 @@ type AccountSettingsCompact struct { func (x *AccountSettingsCompact) Reset() { *x = AccountSettingsCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[58] + mi := &file_management_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5158,7 +5213,7 @@ func (x *AccountSettingsCompact) String() string { func (*AccountSettingsCompact) ProtoMessage() {} func (x *AccountSettingsCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[58] + mi := &file_management_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5171,7 +5226,7 @@ func (x *AccountSettingsCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use AccountSettingsCompact.ProtoReflect.Descriptor instead. func (*AccountSettingsCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{58} + return file_management_proto_rawDescGZIP(), []int{59} } func (x *AccountSettingsCompact) GetPeerLoginExpirationEnabled() bool { @@ -5208,7 +5263,7 @@ type AccountNetwork struct { func (x *AccountNetwork) Reset() { *x = AccountNetwork{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[59] + mi := &file_management_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5221,7 +5276,7 @@ func (x *AccountNetwork) String() string { func (*AccountNetwork) ProtoMessage() {} func (x *AccountNetwork) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[59] + mi := &file_management_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5234,7 +5289,7 @@ func (x *AccountNetwork) ProtoReflect() protoreflect.Message { // Deprecated: Use AccountNetwork.ProtoReflect.Descriptor instead. func (*AccountNetwork) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{59} + return file_management_proto_rawDescGZIP(), []int{60} } func (x *AccountNetwork) GetIdentifier() string { @@ -5284,7 +5339,7 @@ type NetworkMapComponentsDelta struct { func (x *NetworkMapComponentsDelta) Reset() { *x = NetworkMapComponentsDelta{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[60] + mi := &file_management_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5297,7 +5352,7 @@ func (x *NetworkMapComponentsDelta) String() string { func (*NetworkMapComponentsDelta) ProtoMessage() {} func (x *NetworkMapComponentsDelta) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[60] + mi := &file_management_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5310,7 +5365,7 @@ func (x *NetworkMapComponentsDelta) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkMapComponentsDelta.ProtoReflect.Descriptor instead. func (*NetworkMapComponentsDelta) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{60} + return file_management_proto_rawDescGZIP(), []int{61} } // PeerCompact is the wire-shape of a remote peer used by the component @@ -5377,7 +5432,7 @@ type PeerCompact struct { func (x *PeerCompact) Reset() { *x = PeerCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[61] + mi := &file_management_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5390,7 +5445,7 @@ func (x *PeerCompact) String() string { func (*PeerCompact) ProtoMessage() {} func (x *PeerCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[61] + mi := &file_management_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5403,7 +5458,7 @@ func (x *PeerCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerCompact.ProtoReflect.Descriptor instead. func (*PeerCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{61} + return file_management_proto_rawDescGZIP(), []int{62} } func (x *PeerCompact) GetWgPubKey() []byte { @@ -5550,7 +5605,7 @@ type PolicyCompact struct { func (x *PolicyCompact) Reset() { *x = PolicyCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[62] + mi := &file_management_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5563,7 +5618,7 @@ func (x *PolicyCompact) String() string { func (*PolicyCompact) ProtoMessage() {} func (x *PolicyCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[62] + mi := &file_management_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5576,7 +5631,7 @@ func (x *PolicyCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyCompact.ProtoReflect.Descriptor instead. func (*PolicyCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{62} + return file_management_proto_rawDescGZIP(), []int{63} } func (x *PolicyCompact) GetId() uint32 { @@ -5688,7 +5743,7 @@ type ResourceCompact struct { func (x *ResourceCompact) Reset() { *x = ResourceCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[63] + mi := &file_management_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5701,7 +5756,7 @@ func (x *ResourceCompact) String() string { func (*ResourceCompact) ProtoMessage() {} func (x *ResourceCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[63] + mi := &file_management_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5714,7 +5769,7 @@ func (x *ResourceCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceCompact.ProtoReflect.Descriptor instead. func (*ResourceCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{63} + return file_management_proto_rawDescGZIP(), []int{64} } func (x *ResourceCompact) GetType() string { @@ -5751,7 +5806,7 @@ type UserNameList struct { func (x *UserNameList) Reset() { *x = UserNameList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[64] + mi := &file_management_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5764,7 +5819,7 @@ func (x *UserNameList) String() string { func (*UserNameList) ProtoMessage() {} func (x *UserNameList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[64] + mi := &file_management_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5777,7 +5832,7 @@ func (x *UserNameList) ProtoReflect() protoreflect.Message { // Deprecated: Use UserNameList.ProtoReflect.Descriptor instead. func (*UserNameList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{64} + return file_management_proto_rawDescGZIP(), []int{65} } func (x *UserNameList) GetNames() []string { @@ -5806,7 +5861,7 @@ type GroupCompact struct { func (x *GroupCompact) Reset() { *x = GroupCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[65] + mi := &file_management_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5819,7 +5874,7 @@ func (x *GroupCompact) String() string { func (*GroupCompact) ProtoMessage() {} func (x *GroupCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[65] + mi := &file_management_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5832,7 +5887,7 @@ func (x *GroupCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use GroupCompact.ProtoReflect.Descriptor instead. func (*GroupCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{65} + return file_management_proto_rawDescGZIP(), []int{66} } func (x *GroupCompact) GetId() uint32 { @@ -5869,7 +5924,7 @@ type DNSSettingsCompact struct { func (x *DNSSettingsCompact) Reset() { *x = DNSSettingsCompact{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[66] + mi := &file_management_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5882,7 +5937,7 @@ func (x *DNSSettingsCompact) String() string { func (*DNSSettingsCompact) ProtoMessage() {} func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[66] + mi := &file_management_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5895,7 +5950,7 @@ func (x *DNSSettingsCompact) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSSettingsCompact.ProtoReflect.Descriptor instead. func (*DNSSettingsCompact) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{66} + return file_management_proto_rawDescGZIP(), []int{67} } func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []uint32 { @@ -5947,7 +6002,7 @@ type RouteRaw struct { func (x *RouteRaw) Reset() { *x = RouteRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[67] + mi := &file_management_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5960,7 +6015,7 @@ func (x *RouteRaw) String() string { func (*RouteRaw) ProtoMessage() {} func (x *RouteRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[67] + mi := &file_management_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5973,7 +6028,7 @@ func (x *RouteRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteRaw.ProtoReflect.Descriptor instead. func (*RouteRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{67} + return file_management_proto_rawDescGZIP(), []int{68} } func (x *RouteRaw) GetId() uint32 { @@ -6112,7 +6167,7 @@ type NameServerGroupRaw struct { func (x *NameServerGroupRaw) Reset() { *x = NameServerGroupRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[68] + mi := &file_management_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6125,7 +6180,7 @@ func (x *NameServerGroupRaw) String() string { func (*NameServerGroupRaw) ProtoMessage() {} func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[68] + mi := &file_management_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6138,7 +6193,7 @@ func (x *NameServerGroupRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use NameServerGroupRaw.ProtoReflect.Descriptor instead. func (*NameServerGroupRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{68} + return file_management_proto_rawDescGZIP(), []int{69} } func (x *NameServerGroupRaw) GetId() uint32 { @@ -6231,7 +6286,7 @@ type NetworkResourceRaw struct { func (x *NetworkResourceRaw) Reset() { *x = NetworkResourceRaw{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[69] + mi := &file_management_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6244,7 +6299,7 @@ func (x *NetworkResourceRaw) String() string { func (*NetworkResourceRaw) ProtoMessage() {} func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[69] + mi := &file_management_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6257,7 +6312,7 @@ func (x *NetworkResourceRaw) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkResourceRaw.ProtoReflect.Descriptor instead. func (*NetworkResourceRaw) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{69} + return file_management_proto_rawDescGZIP(), []int{70} } func (x *NetworkResourceRaw) GetId() uint32 { @@ -6336,7 +6391,7 @@ type NetworkRouterList struct { func (x *NetworkRouterList) Reset() { *x = NetworkRouterList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[70] + mi := &file_management_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6349,7 +6404,7 @@ func (x *NetworkRouterList) String() string { func (*NetworkRouterList) ProtoMessage() {} func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[70] + mi := &file_management_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6362,7 +6417,7 @@ func (x *NetworkRouterList) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkRouterList.ProtoReflect.Descriptor instead. func (*NetworkRouterList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{70} + return file_management_proto_rawDescGZIP(), []int{71} } func (x *NetworkRouterList) GetEntries() []*NetworkRouterEntry { @@ -6391,7 +6446,7 @@ type NetworkRouterEntry struct { func (x *NetworkRouterEntry) Reset() { *x = NetworkRouterEntry{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[71] + mi := &file_management_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6404,7 +6459,7 @@ func (x *NetworkRouterEntry) String() string { func (*NetworkRouterEntry) ProtoMessage() {} func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[71] + mi := &file_management_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6417,7 +6472,7 @@ func (x *NetworkRouterEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkRouterEntry.ProtoReflect.Descriptor instead. func (*NetworkRouterEntry) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{71} + return file_management_proto_rawDescGZIP(), []int{72} } func (x *NetworkRouterEntry) GetId() uint32 { @@ -6481,7 +6536,7 @@ type PolicyIndexes struct { func (x *PolicyIndexes) Reset() { *x = PolicyIndexes{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[72] + mi := &file_management_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6494,7 +6549,7 @@ func (x *PolicyIndexes) String() string { func (*PolicyIndexes) ProtoMessage() {} func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[72] + mi := &file_management_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6507,7 +6562,7 @@ func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyIndexes.ProtoReflect.Descriptor instead. func (*PolicyIndexes) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{72} + return file_management_proto_rawDescGZIP(), []int{73} } func (x *PolicyIndexes) GetIndexes() []uint32 { @@ -6530,7 +6585,7 @@ type UserIDList struct { func (x *UserIDList) Reset() { *x = UserIDList{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[73] + mi := &file_management_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6543,7 +6598,7 @@ func (x *UserIDList) String() string { func (*UserIDList) ProtoMessage() {} func (x *UserIDList) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[73] + mi := &file_management_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6556,7 +6611,7 @@ func (x *UserIDList) ProtoReflect() protoreflect.Message { // Deprecated: Use UserIDList.ProtoReflect.Descriptor instead. func (*UserIDList) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{73} + return file_management_proto_rawDescGZIP(), []int{74} } func (x *UserIDList) GetUserIds() []string { @@ -6579,7 +6634,7 @@ type PeerIndexSet struct { func (x *PeerIndexSet) Reset() { *x = PeerIndexSet{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[74] + mi := &file_management_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6592,7 +6647,7 @@ func (x *PeerIndexSet) String() string { func (*PeerIndexSet) ProtoMessage() {} func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[74] + mi := &file_management_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6605,7 +6660,7 @@ func (x *PeerIndexSet) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerIndexSet.ProtoReflect.Descriptor instead. func (*PeerIndexSet) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{74} + return file_management_proto_rawDescGZIP(), []int{75} } func (x *PeerIndexSet) GetPeerIndexes() []uint32 { @@ -6627,7 +6682,7 @@ type PortInfo_Range struct { func (x *PortInfo_Range) Reset() { *x = PortInfo_Range{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[76] + mi := &file_management_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6640,7 +6695,7 @@ func (x *PortInfo_Range) String() string { func (*PortInfo_Range) ProtoMessage() {} func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[76] + mi := &file_management_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6653,7 +6708,7 @@ func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { // Deprecated: Use PortInfo_Range.ProtoReflect.Descriptor instead. func (*PortInfo_Range) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{46, 0} + return file_management_proto_rawDescGZIP(), []int{47, 0} } func (x *PortInfo_Range) GetStart() uint32 { @@ -6908,7 +6963,7 @@ var file_management_proto_rawDesc = []byte{ 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, - 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xff, 0x01, 0x0a, 0x0d, 0x4e, 0x65, 0x74, 0x62, + 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xb4, 0x02, 0x0a, 0x0d, 0x4e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, @@ -6924,43 +6979,49 @@ var file_management_proto_rawDesc = []byte{ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2a, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, - 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, - 0x4c, 0x53, 0x10, 0x04, 0x22, 0x6d, 0x0a, 0x0b, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0a, 0x46, 0x6c, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x75, 0x72, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x12, 0x35, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, - 0x12, 0x65, 0x78, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x78, 0x69, 0x74, 0x4e, - 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, - 0x0d, 0x64, 0x6e, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x64, 0x6e, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xa3, 0x01, 0x0a, 0x09, 0x4a, 0x57, 0x54, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x66, 0x69, 0x67, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x33, 0x0a, 0x07, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x98, + 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, + 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, + 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, + 0x54, 0x50, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, + 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x6d, 0x0a, 0x0b, 0x52, 0x65, 0x6c, + 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0c, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0a, 0x46, 0x6c, 0x6f, + 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x26, 0x0a, + 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x65, 0x78, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x65, 0x78, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x6e, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x64, 0x6e, 0x73, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x22, 0xa3, 0x01, 0x0a, 0x09, 0x4a, 0x57, 0x54, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, @@ -7810,7 +7871,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 82) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 83) var file_management_proto_goTypes = []interface{}{ (JobStatus)(0), // 0: management.JobStatus (PeerCapability)(0), // 1: management.PeerCapability @@ -7843,67 +7904,68 @@ var file_management_proto_goTypes = []interface{}{ (*HostConfig)(nil), // 28: management.HostConfig (*RelayConfig)(nil), // 29: management.RelayConfig (*FlowConfig)(nil), // 30: management.FlowConfig - (*JWTConfig)(nil), // 31: management.JWTConfig - (*ProtectedHostConfig)(nil), // 32: management.ProtectedHostConfig - (*PeerConfig)(nil), // 33: management.PeerConfig - (*AutoUpdateSettings)(nil), // 34: management.AutoUpdateSettings - (*NetworkMap)(nil), // 35: management.NetworkMap - (*SSHAuth)(nil), // 36: management.SSHAuth - (*MachineUserIndexes)(nil), // 37: management.MachineUserIndexes - (*RemotePeerConfig)(nil), // 38: management.RemotePeerConfig - (*SSHConfig)(nil), // 39: management.SSHConfig - (*DeviceAuthorizationFlowRequest)(nil), // 40: management.DeviceAuthorizationFlowRequest - (*DeviceAuthorizationFlow)(nil), // 41: management.DeviceAuthorizationFlow - (*PKCEAuthorizationFlowRequest)(nil), // 42: management.PKCEAuthorizationFlowRequest - (*PKCEAuthorizationFlow)(nil), // 43: management.PKCEAuthorizationFlow - (*ProviderConfig)(nil), // 44: management.ProviderConfig - (*Route)(nil), // 45: management.Route - (*DNSConfig)(nil), // 46: management.DNSConfig - (*CustomZone)(nil), // 47: management.CustomZone - (*SimpleRecord)(nil), // 48: management.SimpleRecord - (*NameServerGroup)(nil), // 49: management.NameServerGroup - (*NameServer)(nil), // 50: management.NameServer - (*FirewallRule)(nil), // 51: management.FirewallRule - (*NetworkAddress)(nil), // 52: management.NetworkAddress - (*Checks)(nil), // 53: management.Checks - (*PortInfo)(nil), // 54: management.PortInfo - (*RouteFirewallRule)(nil), // 55: management.RouteFirewallRule - (*ForwardingRule)(nil), // 56: management.ForwardingRule - (*ExposeServiceRequest)(nil), // 57: management.ExposeServiceRequest - (*ExposeServiceResponse)(nil), // 58: management.ExposeServiceResponse - (*RenewExposeRequest)(nil), // 59: management.RenewExposeRequest - (*RenewExposeResponse)(nil), // 60: management.RenewExposeResponse - (*StopExposeRequest)(nil), // 61: management.StopExposeRequest - (*StopExposeResponse)(nil), // 62: management.StopExposeResponse - (*NetworkMapEnvelope)(nil), // 63: management.NetworkMapEnvelope - (*NetworkMapComponentsFull)(nil), // 64: management.NetworkMapComponentsFull - (*ProxyPatch)(nil), // 65: management.ProxyPatch - (*AccountSettingsCompact)(nil), // 66: management.AccountSettingsCompact - (*AccountNetwork)(nil), // 67: management.AccountNetwork - (*NetworkMapComponentsDelta)(nil), // 68: management.NetworkMapComponentsDelta - (*PeerCompact)(nil), // 69: management.PeerCompact - (*PolicyCompact)(nil), // 70: management.PolicyCompact - (*ResourceCompact)(nil), // 71: management.ResourceCompact - (*UserNameList)(nil), // 72: management.UserNameList - (*GroupCompact)(nil), // 73: management.GroupCompact - (*DNSSettingsCompact)(nil), // 74: management.DNSSettingsCompact - (*RouteRaw)(nil), // 75: management.RouteRaw - (*NameServerGroupRaw)(nil), // 76: management.NameServerGroupRaw - (*NetworkResourceRaw)(nil), // 77: management.NetworkResourceRaw - (*NetworkRouterList)(nil), // 78: management.NetworkRouterList - (*NetworkRouterEntry)(nil), // 79: management.NetworkRouterEntry - (*PolicyIndexes)(nil), // 80: management.PolicyIndexes - (*UserIDList)(nil), // 81: management.UserIDList - (*PeerIndexSet)(nil), // 82: management.PeerIndexSet - nil, // 83: management.SSHAuth.MachineUsersEntry - (*PortInfo_Range)(nil), // 84: management.PortInfo.Range - nil, // 85: management.NetworkMapComponentsFull.RoutersMapEntry - nil, // 86: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - nil, // 87: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - nil, // 88: management.NetworkMapComponentsFull.PostureFailedPeersEntry - nil, // 89: management.PolicyCompact.AuthorizedGroupsEntry - (*timestamppb.Timestamp)(nil), // 90: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 91: google.protobuf.Duration + (*MetricsConfig)(nil), // 31: management.MetricsConfig + (*JWTConfig)(nil), // 32: management.JWTConfig + (*ProtectedHostConfig)(nil), // 33: management.ProtectedHostConfig + (*PeerConfig)(nil), // 34: management.PeerConfig + (*AutoUpdateSettings)(nil), // 35: management.AutoUpdateSettings + (*NetworkMap)(nil), // 36: management.NetworkMap + (*SSHAuth)(nil), // 37: management.SSHAuth + (*MachineUserIndexes)(nil), // 38: management.MachineUserIndexes + (*RemotePeerConfig)(nil), // 39: management.RemotePeerConfig + (*SSHConfig)(nil), // 40: management.SSHConfig + (*DeviceAuthorizationFlowRequest)(nil), // 41: management.DeviceAuthorizationFlowRequest + (*DeviceAuthorizationFlow)(nil), // 42: management.DeviceAuthorizationFlow + (*PKCEAuthorizationFlowRequest)(nil), // 43: management.PKCEAuthorizationFlowRequest + (*PKCEAuthorizationFlow)(nil), // 44: management.PKCEAuthorizationFlow + (*ProviderConfig)(nil), // 45: management.ProviderConfig + (*Route)(nil), // 46: management.Route + (*DNSConfig)(nil), // 47: management.DNSConfig + (*CustomZone)(nil), // 48: management.CustomZone + (*SimpleRecord)(nil), // 49: management.SimpleRecord + (*NameServerGroup)(nil), // 50: management.NameServerGroup + (*NameServer)(nil), // 51: management.NameServer + (*FirewallRule)(nil), // 52: management.FirewallRule + (*NetworkAddress)(nil), // 53: management.NetworkAddress + (*Checks)(nil), // 54: management.Checks + (*PortInfo)(nil), // 55: management.PortInfo + (*RouteFirewallRule)(nil), // 56: management.RouteFirewallRule + (*ForwardingRule)(nil), // 57: management.ForwardingRule + (*ExposeServiceRequest)(nil), // 58: management.ExposeServiceRequest + (*ExposeServiceResponse)(nil), // 59: management.ExposeServiceResponse + (*RenewExposeRequest)(nil), // 60: management.RenewExposeRequest + (*RenewExposeResponse)(nil), // 61: management.RenewExposeResponse + (*StopExposeRequest)(nil), // 62: management.StopExposeRequest + (*StopExposeResponse)(nil), // 63: management.StopExposeResponse + (*NetworkMapEnvelope)(nil), // 64: management.NetworkMapEnvelope + (*NetworkMapComponentsFull)(nil), // 65: management.NetworkMapComponentsFull + (*ProxyPatch)(nil), // 66: management.ProxyPatch + (*AccountSettingsCompact)(nil), // 67: management.AccountSettingsCompact + (*AccountNetwork)(nil), // 68: management.AccountNetwork + (*NetworkMapComponentsDelta)(nil), // 69: management.NetworkMapComponentsDelta + (*PeerCompact)(nil), // 70: management.PeerCompact + (*PolicyCompact)(nil), // 71: management.PolicyCompact + (*ResourceCompact)(nil), // 72: management.ResourceCompact + (*UserNameList)(nil), // 73: management.UserNameList + (*GroupCompact)(nil), // 74: management.GroupCompact + (*DNSSettingsCompact)(nil), // 75: management.DNSSettingsCompact + (*RouteRaw)(nil), // 76: management.RouteRaw + (*NameServerGroupRaw)(nil), // 77: management.NameServerGroupRaw + (*NetworkResourceRaw)(nil), // 78: management.NetworkResourceRaw + (*NetworkRouterList)(nil), // 79: management.NetworkRouterList + (*NetworkRouterEntry)(nil), // 80: management.NetworkRouterEntry + (*PolicyIndexes)(nil), // 81: management.PolicyIndexes + (*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 } var file_management_proto_depIdxs = []int32{ 11, // 0: management.JobRequest.bundle:type_name -> management.BundleParameters @@ -7911,138 +7973,139 @@ var file_management_proto_depIdxs = []int32{ 12, // 2: management.JobResponse.bundle:type_name -> management.BundleResult 21, // 3: management.SyncRequest.meta:type_name -> management.PeerSystemMeta 27, // 4: management.SyncResponse.netbirdConfig:type_name -> management.NetbirdConfig - 33, // 5: management.SyncResponse.peerConfig:type_name -> management.PeerConfig - 38, // 6: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig - 35, // 7: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap - 53, // 8: management.SyncResponse.Checks:type_name -> management.Checks - 90, // 9: management.SyncResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp - 63, // 10: management.SyncResponse.NetworkMapEnvelope:type_name -> management.NetworkMapEnvelope + 34, // 5: management.SyncResponse.peerConfig:type_name -> management.PeerConfig + 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 + 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 17, // 13: management.LoginRequest.peerKeys:type_name -> management.PeerKeys - 52, // 14: management.PeerSystemMeta.networkAddresses:type_name -> management.NetworkAddress + 53, // 14: management.PeerSystemMeta.networkAddresses:type_name -> management.NetworkAddress 18, // 15: management.PeerSystemMeta.environment:type_name -> management.Environment 19, // 16: management.PeerSystemMeta.files:type_name -> management.File 20, // 17: management.PeerSystemMeta.flags:type_name -> management.Flags 1, // 18: management.PeerSystemMeta.capabilities:type_name -> management.PeerCapability 27, // 19: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig - 33, // 20: management.LoginResponse.peerConfig:type_name -> management.PeerConfig - 53, // 21: management.LoginResponse.Checks:type_name -> management.Checks - 90, // 22: management.LoginResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp + 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 21, // 23: management.ExtendAuthSessionRequest.meta:type_name -> management.PeerSystemMeta - 90, // 24: management.ExtendAuthSessionResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp - 90, // 25: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 91, // 24: management.ExtendAuthSessionResponse.sessionExpiresAt:type_name -> google.protobuf.Timestamp + 91, // 25: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 28, // 26: management.NetbirdConfig.stuns:type_name -> management.HostConfig - 32, // 27: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig + 33, // 27: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig 28, // 28: management.NetbirdConfig.signal:type_name -> management.HostConfig 29, // 29: management.NetbirdConfig.relay:type_name -> management.RelayConfig 30, // 30: management.NetbirdConfig.flow:type_name -> management.FlowConfig - 6, // 31: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol - 91, // 32: management.FlowConfig.interval:type_name -> google.protobuf.Duration - 28, // 33: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig - 39, // 34: management.PeerConfig.sshConfig:type_name -> management.SSHConfig - 34, // 35: management.PeerConfig.autoUpdate:type_name -> management.AutoUpdateSettings - 33, // 36: management.NetworkMap.peerConfig:type_name -> management.PeerConfig - 38, // 37: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig - 45, // 38: management.NetworkMap.Routes:type_name -> management.Route - 46, // 39: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig - 38, // 40: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig - 51, // 41: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule - 55, // 42: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule - 56, // 43: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule - 36, // 44: management.NetworkMap.sshAuth:type_name -> management.SSHAuth - 83, // 45: management.SSHAuth.machine_users:type_name -> management.SSHAuth.MachineUsersEntry - 39, // 46: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig - 31, // 47: management.SSHConfig.jwtConfig:type_name -> management.JWTConfig - 7, // 48: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider - 44, // 49: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 44, // 50: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 49, // 51: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup - 47, // 52: management.DNSConfig.CustomZones:type_name -> management.CustomZone - 48, // 53: management.CustomZone.Records:type_name -> management.SimpleRecord - 50, // 54: management.NameServerGroup.NameServers:type_name -> management.NameServer - 3, // 55: management.FirewallRule.Direction:type_name -> management.RuleDirection - 4, // 56: management.FirewallRule.Action:type_name -> management.RuleAction - 2, // 57: management.FirewallRule.Protocol:type_name -> management.RuleProtocol - 54, // 58: management.FirewallRule.PortInfo:type_name -> management.PortInfo - 84, // 59: management.PortInfo.range:type_name -> management.PortInfo.Range - 4, // 60: management.RouteFirewallRule.action:type_name -> management.RuleAction - 2, // 61: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol - 54, // 62: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo - 2, // 63: management.ForwardingRule.protocol:type_name -> management.RuleProtocol - 54, // 64: management.ForwardingRule.destinationPort:type_name -> management.PortInfo - 54, // 65: management.ForwardingRule.translatedPort:type_name -> management.PortInfo - 5, // 66: management.ExposeServiceRequest.protocol:type_name -> management.ExposeProtocol - 64, // 67: management.NetworkMapEnvelope.full:type_name -> management.NetworkMapComponentsFull - 68, // 68: management.NetworkMapEnvelope.delta:type_name -> management.NetworkMapComponentsDelta - 33, // 69: management.NetworkMapComponentsFull.peer_config:type_name -> management.PeerConfig - 67, // 70: management.NetworkMapComponentsFull.network:type_name -> management.AccountNetwork - 66, // 71: management.NetworkMapComponentsFull.account_settings:type_name -> management.AccountSettingsCompact - 74, // 72: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact - 69, // 73: management.NetworkMapComponentsFull.peers:type_name -> management.PeerCompact - 70, // 74: management.NetworkMapComponentsFull.policies:type_name -> management.PolicyCompact - 73, // 75: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact - 75, // 76: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw - 76, // 77: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw - 48, // 78: management.NetworkMapComponentsFull.all_dns_records:type_name -> management.SimpleRecord - 47, // 79: management.NetworkMapComponentsFull.account_zones:type_name -> management.CustomZone - 77, // 80: management.NetworkMapComponentsFull.network_resources:type_name -> management.NetworkResourceRaw - 85, // 81: management.NetworkMapComponentsFull.routers_map:type_name -> management.NetworkMapComponentsFull.RoutersMapEntry - 86, // 82: management.NetworkMapComponentsFull.resource_policies_map:type_name -> management.NetworkMapComponentsFull.ResourcePoliciesMapEntry - 87, // 83: management.NetworkMapComponentsFull.group_id_to_user_ids:type_name -> management.NetworkMapComponentsFull.GroupIdToUserIdsEntry - 88, // 84: management.NetworkMapComponentsFull.posture_failed_peers:type_name -> management.NetworkMapComponentsFull.PostureFailedPeersEntry - 65, // 85: management.NetworkMapComponentsFull.proxy_patch:type_name -> management.ProxyPatch - 38, // 86: management.ProxyPatch.peers:type_name -> management.RemotePeerConfig - 38, // 87: management.ProxyPatch.offline_peers:type_name -> management.RemotePeerConfig - 51, // 88: management.ProxyPatch.firewall_rules:type_name -> management.FirewallRule - 45, // 89: management.ProxyPatch.routes:type_name -> management.Route - 55, // 90: management.ProxyPatch.route_firewall_rules:type_name -> management.RouteFirewallRule - 56, // 91: management.ProxyPatch.forwarding_rules:type_name -> management.ForwardingRule - 4, // 92: management.PolicyCompact.action:type_name -> management.RuleAction - 2, // 93: management.PolicyCompact.protocol:type_name -> management.RuleProtocol - 84, // 94: management.PolicyCompact.port_ranges:type_name -> management.PortInfo.Range - 89, // 95: management.PolicyCompact.authorized_groups:type_name -> management.PolicyCompact.AuthorizedGroupsEntry - 71, // 96: management.PolicyCompact.source_resource:type_name -> management.ResourceCompact - 71, // 97: management.PolicyCompact.destination_resource:type_name -> management.ResourceCompact - 50, // 98: management.NameServerGroupRaw.nameservers:type_name -> management.NameServer - 79, // 99: management.NetworkRouterList.entries:type_name -> management.NetworkRouterEntry - 37, // 100: management.SSHAuth.MachineUsersEntry.value:type_name -> management.MachineUserIndexes - 78, // 101: management.NetworkMapComponentsFull.RoutersMapEntry.value:type_name -> management.NetworkRouterList - 80, // 102: management.NetworkMapComponentsFull.ResourcePoliciesMapEntry.value:type_name -> management.PolicyIndexes - 81, // 103: management.NetworkMapComponentsFull.GroupIdToUserIdsEntry.value:type_name -> management.UserIDList - 82, // 104: management.NetworkMapComponentsFull.PostureFailedPeersEntry.value:type_name -> management.PeerIndexSet - 72, // 105: management.PolicyCompact.AuthorizedGroupsEntry.value:type_name -> management.UserNameList - 8, // 106: management.ManagementService.Login:input_type -> management.EncryptedMessage - 8, // 107: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 26, // 108: management.ManagementService.GetServerKey:input_type -> management.Empty - 26, // 109: management.ManagementService.isHealthy:input_type -> management.Empty - 8, // 110: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 111: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage - 8, // 112: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage - 8, // 113: management.ManagementService.Logout:input_type -> management.EncryptedMessage - 8, // 114: management.ManagementService.Job:input_type -> management.EncryptedMessage - 8, // 115: management.ManagementService.ExtendAuthSession:input_type -> management.EncryptedMessage - 8, // 116: management.ManagementService.CreateExpose:input_type -> management.EncryptedMessage - 8, // 117: management.ManagementService.RenewExpose:input_type -> management.EncryptedMessage - 8, // 118: management.ManagementService.StopExpose:input_type -> management.EncryptedMessage - 8, // 119: management.ManagementService.Login:output_type -> management.EncryptedMessage - 8, // 120: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 25, // 121: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 26, // 122: management.ManagementService.isHealthy:output_type -> management.Empty - 8, // 123: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 8, // 124: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage - 26, // 125: management.ManagementService.SyncMeta:output_type -> management.Empty - 26, // 126: management.ManagementService.Logout:output_type -> management.Empty - 8, // 127: management.ManagementService.Job:output_type -> management.EncryptedMessage - 8, // 128: management.ManagementService.ExtendAuthSession:output_type -> management.EncryptedMessage - 8, // 129: management.ManagementService.CreateExpose:output_type -> management.EncryptedMessage - 8, // 130: management.ManagementService.RenewExpose:output_type -> management.EncryptedMessage - 8, // 131: management.ManagementService.StopExpose:output_type -> management.EncryptedMessage - 119, // [119:132] is the sub-list for method output_type - 106, // [106:119] is the sub-list for method input_type - 106, // [106:106] is the sub-list for extension type_name - 106, // [106:106] is the sub-list for extension extendee - 0, // [0:106] is the sub-list for field type_name + 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 + 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 + 34, // 37: management.NetworkMap.peerConfig:type_name -> management.PeerConfig + 39, // 38: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig + 46, // 39: management.NetworkMap.Routes:type_name -> management.Route + 47, // 40: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig + 39, // 41: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig + 52, // 42: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule + 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 + 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 + 45, // 50: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 45, // 51: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 50, // 52: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup + 48, // 53: management.DNSConfig.CustomZones:type_name -> management.CustomZone + 49, // 54: management.CustomZone.Records:type_name -> management.SimpleRecord + 51, // 55: management.NameServerGroup.NameServers:type_name -> management.NameServer + 3, // 56: management.FirewallRule.Direction:type_name -> management.RuleDirection + 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 + 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 + 2, // 64: management.ForwardingRule.protocol:type_name -> management.RuleProtocol + 55, // 65: management.ForwardingRule.destinationPort:type_name -> management.PortInfo + 55, // 66: management.ForwardingRule.translatedPort:type_name -> management.PortInfo + 5, // 67: management.ExposeServiceRequest.protocol:type_name -> management.ExposeProtocol + 65, // 68: management.NetworkMapEnvelope.full:type_name -> management.NetworkMapComponentsFull + 69, // 69: management.NetworkMapEnvelope.delta:type_name -> management.NetworkMapComponentsDelta + 34, // 70: management.NetworkMapComponentsFull.peer_config:type_name -> management.PeerConfig + 68, // 71: management.NetworkMapComponentsFull.network:type_name -> management.AccountNetwork + 67, // 72: management.NetworkMapComponentsFull.account_settings:type_name -> management.AccountSettingsCompact + 75, // 73: management.NetworkMapComponentsFull.dns_settings:type_name -> management.DNSSettingsCompact + 70, // 74: management.NetworkMapComponentsFull.peers:type_name -> management.PeerCompact + 71, // 75: management.NetworkMapComponentsFull.policies:type_name -> management.PolicyCompact + 74, // 76: management.NetworkMapComponentsFull.groups:type_name -> management.GroupCompact + 76, // 77: management.NetworkMapComponentsFull.routes:type_name -> management.RouteRaw + 77, // 78: management.NetworkMapComponentsFull.nameserver_groups:type_name -> management.NameServerGroupRaw + 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 + 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 + 52, // 89: management.ProxyPatch.firewall_rules:type_name -> management.FirewallRule + 46, // 90: management.ProxyPatch.routes:type_name -> management.Route + 56, // 91: management.ProxyPatch.route_firewall_rules:type_name -> management.RouteFirewallRule + 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 + 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.PolicyIndexes + 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 } func init() { file_management_proto_init() } @@ -8328,7 +8391,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JWTConfig); i { + switch v := v.(*MetricsConfig); i { case 0: return &v.state case 1: @@ -8340,7 +8403,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtectedHostConfig); i { + switch v := v.(*JWTConfig); i { case 0: return &v.state case 1: @@ -8352,7 +8415,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerConfig); i { + switch v := v.(*ProtectedHostConfig); i { case 0: return &v.state case 1: @@ -8364,7 +8427,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AutoUpdateSettings); i { + switch v := v.(*PeerConfig); i { case 0: return &v.state case 1: @@ -8376,7 +8439,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkMap); i { + switch v := v.(*AutoUpdateSettings); i { case 0: return &v.state case 1: @@ -8388,7 +8451,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHAuth); i { + switch v := v.(*NetworkMap); i { case 0: return &v.state case 1: @@ -8400,7 +8463,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MachineUserIndexes); i { + switch v := v.(*SSHAuth); i { case 0: return &v.state case 1: @@ -8412,7 +8475,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemotePeerConfig); i { + switch v := v.(*MachineUserIndexes); i { case 0: return &v.state case 1: @@ -8424,7 +8487,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHConfig); i { + switch v := v.(*RemotePeerConfig); i { case 0: return &v.state case 1: @@ -8436,7 +8499,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceAuthorizationFlowRequest); i { + switch v := v.(*SSHConfig); i { case 0: return &v.state case 1: @@ -8448,7 +8511,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceAuthorizationFlow); i { + switch v := v.(*DeviceAuthorizationFlowRequest); i { case 0: return &v.state case 1: @@ -8460,7 +8523,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PKCEAuthorizationFlowRequest); i { + switch v := v.(*DeviceAuthorizationFlow); i { case 0: return &v.state case 1: @@ -8472,7 +8535,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PKCEAuthorizationFlow); i { + switch v := v.(*PKCEAuthorizationFlowRequest); i { case 0: return &v.state case 1: @@ -8484,7 +8547,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProviderConfig); i { + switch v := v.(*PKCEAuthorizationFlow); i { case 0: return &v.state case 1: @@ -8496,7 +8559,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Route); i { + switch v := v.(*ProviderConfig); i { case 0: return &v.state case 1: @@ -8508,7 +8571,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DNSConfig); i { + switch v := v.(*Route); i { case 0: return &v.state case 1: @@ -8520,7 +8583,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CustomZone); i { + switch v := v.(*DNSConfig); i { case 0: return &v.state case 1: @@ -8532,7 +8595,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SimpleRecord); i { + switch v := v.(*CustomZone); i { case 0: return &v.state case 1: @@ -8544,7 +8607,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameServerGroup); i { + switch v := v.(*SimpleRecord); i { case 0: return &v.state case 1: @@ -8556,7 +8619,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameServer); i { + switch v := v.(*NameServerGroup); i { case 0: return &v.state case 1: @@ -8568,7 +8631,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FirewallRule); i { + switch v := v.(*NameServer); i { case 0: return &v.state case 1: @@ -8580,7 +8643,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkAddress); i { + switch v := v.(*FirewallRule); i { case 0: return &v.state case 1: @@ -8592,7 +8655,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Checks); i { + switch v := v.(*NetworkAddress); i { case 0: return &v.state case 1: @@ -8604,7 +8667,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PortInfo); i { + switch v := v.(*Checks); i { case 0: return &v.state case 1: @@ -8616,7 +8679,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RouteFirewallRule); i { + switch v := v.(*PortInfo); i { case 0: return &v.state case 1: @@ -8628,7 +8691,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingRule); i { + switch v := v.(*RouteFirewallRule); i { case 0: return &v.state case 1: @@ -8640,7 +8703,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExposeServiceRequest); i { + switch v := v.(*ForwardingRule); i { case 0: return &v.state case 1: @@ -8652,7 +8715,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExposeServiceResponse); i { + switch v := v.(*ExposeServiceRequest); i { case 0: return &v.state case 1: @@ -8664,7 +8727,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RenewExposeRequest); i { + switch v := v.(*ExposeServiceResponse); i { case 0: return &v.state case 1: @@ -8676,7 +8739,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RenewExposeResponse); i { + switch v := v.(*RenewExposeRequest); i { case 0: return &v.state case 1: @@ -8688,7 +8751,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StopExposeRequest); i { + switch v := v.(*RenewExposeResponse); i { case 0: return &v.state case 1: @@ -8700,7 +8763,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StopExposeResponse); i { + switch v := v.(*StopExposeRequest); i { case 0: return &v.state case 1: @@ -8712,7 +8775,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkMapEnvelope); i { + switch v := v.(*StopExposeResponse); i { case 0: return &v.state case 1: @@ -8724,7 +8787,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkMapComponentsFull); i { + switch v := v.(*NetworkMapEnvelope); i { case 0: return &v.state case 1: @@ -8736,7 +8799,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProxyPatch); i { + switch v := v.(*NetworkMapComponentsFull); i { case 0: return &v.state case 1: @@ -8748,7 +8811,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountSettingsCompact); i { + switch v := v.(*ProxyPatch); i { case 0: return &v.state case 1: @@ -8760,7 +8823,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountNetwork); i { + switch v := v.(*AccountSettingsCompact); i { case 0: return &v.state case 1: @@ -8772,7 +8835,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkMapComponentsDelta); i { + switch v := v.(*AccountNetwork); i { case 0: return &v.state case 1: @@ -8784,7 +8847,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerCompact); i { + switch v := v.(*NetworkMapComponentsDelta); i { case 0: return &v.state case 1: @@ -8796,7 +8859,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyCompact); i { + switch v := v.(*PeerCompact); i { case 0: return &v.state case 1: @@ -8808,7 +8871,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourceCompact); i { + switch v := v.(*PolicyCompact); i { case 0: return &v.state case 1: @@ -8820,7 +8883,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserNameList); i { + switch v := v.(*ResourceCompact); i { case 0: return &v.state case 1: @@ -8832,7 +8895,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupCompact); i { + switch v := v.(*UserNameList); i { case 0: return &v.state case 1: @@ -8844,7 +8907,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DNSSettingsCompact); i { + switch v := v.(*GroupCompact); i { case 0: return &v.state case 1: @@ -8856,7 +8919,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RouteRaw); i { + switch v := v.(*DNSSettingsCompact); i { case 0: return &v.state case 1: @@ -8868,7 +8931,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameServerGroupRaw); i { + switch v := v.(*RouteRaw); i { case 0: return &v.state case 1: @@ -8880,7 +8943,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkResourceRaw); i { + switch v := v.(*NameServerGroupRaw); i { case 0: return &v.state case 1: @@ -8892,7 +8955,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkRouterList); i { + switch v := v.(*NetworkResourceRaw); i { case 0: return &v.state case 1: @@ -8904,7 +8967,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkRouterEntry); i { + switch v := v.(*NetworkRouterList); i { case 0: return &v.state case 1: @@ -8916,7 +8979,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyIndexes); i { + switch v := v.(*NetworkRouterEntry); i { case 0: return &v.state case 1: @@ -8928,7 +8991,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserIDList); i { + switch v := v.(*PolicyIndexes); i { case 0: return &v.state case 1: @@ -8940,6 +9003,18 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserIDList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PeerIndexSet); i { case 0: return &v.state @@ -8951,7 +9026,7 @@ func file_management_proto_init() { return nil } } - file_management_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { + file_management_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PortInfo_Range); i { case 0: return &v.state @@ -8970,11 +9045,11 @@ func file_management_proto_init() { file_management_proto_msgTypes[2].OneofWrappers = []interface{}{ (*JobResponse_Bundle)(nil), } - file_management_proto_msgTypes[46].OneofWrappers = []interface{}{ + file_management_proto_msgTypes[47].OneofWrappers = []interface{}{ (*PortInfo_Port)(nil), (*PortInfo_Range_)(nil), } - file_management_proto_msgTypes[55].OneofWrappers = []interface{}{ + file_management_proto_msgTypes[56].OneofWrappers = []interface{}{ (*NetworkMapEnvelope_Full)(nil), (*NetworkMapEnvelope_Delta)(nil), } @@ -8984,7 +9059,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 8, - NumMessages: 82, + NumMessages: 83, NumExtensions: 0, NumServices: 1, }, From 7c7dfd7f1d118ed261fea33b21a48f0308ec0dc0 Mon Sep 17 00:00:00 2001 From: pascal Date: Thu, 2 Jul 2026 15:07:37 +0200 Subject: [PATCH 19/42] fix merge artifacts --- .../internals/shared/grpc/components_envelope_response.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index edb236e7e8e..b48b9b3dc4c 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -69,7 +69,7 @@ func ToComponentSyncResponse( Checks: toProtocolChecks(ctx, checks), } - nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings) + nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings, settings) resp.NetbirdConfig = integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings) return resp From a36a1a3c263cecf652d1e88a737ce17d5d35dfa0 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Wed, 8 Jul 2026 21:29:14 +0200 Subject: [PATCH 20/42] cleaned up wire representation of components Signed-off-by: Dmitri Dolguikh --- dns/nameserver.go | 2 +- .../shared/grpc/components_encoder.go | 66 +- .../networks/resources/types/resource.go | 24 +- .../server/networks/routers/types/router.go | 18 +- management/server/networks/types/network.go | 2 +- management/server/posture/checks.go | 2 +- management/server/store/sql_store.go | 18 +- management/server/store/store.go | 2 +- management/server/types/account_components.go | 4 +- route/route.go | 2 +- shared/management/proto/management.pb.go | 847 +++++++++--------- shared/management/proto/management.proto | 71 +- shared/management/types/group.go | 2 +- .../management/types/networkmap_components.go | 4 +- shared/management/types/policy.go | 2 +- 15 files changed, 524 insertions(+), 542 deletions(-) diff --git a/dns/nameserver.go b/dns/nameserver.go index c05c0971588..24faf9be105 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -55,7 +55,7 @@ type NameServerGroup struct { AccountID string `gorm:"index"` // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_nameserver_groups_account_seq_id;not null;default:0"` + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` // Name group name Name string // Description group description diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index 2164586f671..9fb85729ca1 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -88,7 +88,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel // policies + resource-only policies) so encodeResourcePoliciesMap can // translate every *Policy pointer to a wire index. allPolicies := unionPolicies(c.Policies, c.ResourcePoliciesMap) - policies, policyToIdxs := enc.encodePolicies(allPolicies) + policies := enc.encodePolicies(allPolicies) // Phase 3: emit. Order of struct field expressions no longer matters: // every encoder either reads from the dedup tables or works on @@ -115,7 +115,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel AccountZones: encodeCustomZones(c.AccountZones), NetworkResources: enc.encodeNetworkResources(c.NetworkResources), RoutersMap: enc.encodeRoutersMap(c.RoutersMap), - ResourcePoliciesMap: enc.encodeResourcePoliciesMap(c.ResourcePoliciesMap, policyToIdxs), + ResourcePoliciesMap: enc.encodeResourcePoliciesMap(c.ResourcePoliciesMap), GroupIdToUserIds: enc.encodeGroupIDToUserIDs(c.GroupIDToUserIDs), AllowedUserIds: stringSetToSlice(c.AllowedUserIDs), PostureFailedPeers: enc.encodePostureFailedPeers(c.PostureFailedPeers), @@ -170,7 +170,7 @@ func (e *componentEncoder) appendPeer(p *nbpeer.Peer) uint32 { } idx := uint32(len(e.peers)) e.peerOrder[p.ID] = idx - e.peers = append(e.peers, toPeerCompact(p, e.agentVersionIndex(p.Meta.WtVersion))) + e.peers = append(e.peers, toPeerCompact(p)) return idx } @@ -246,13 +246,12 @@ func (e *componentEncoder) encodeGroups() []*proto.GroupCompact { // list and a map from policy pointer to the indexes of its emitted rules in // that list — used by encodeResourcePoliciesMap to translate // ResourcePoliciesMap[resourceID][]*Policy into wire-side indexes. -func (e *componentEncoder) encodePolicies(policies []*types.Policy) ([]*proto.PolicyCompact, map[*types.Policy][]uint32) { +func (e *componentEncoder) encodePolicies(policies []*types.Policy) []*proto.PolicyCompact { if len(policies) == 0 { - return nil, nil + return nil } out := make([]*proto.PolicyCompact, 0, len(policies)) - idxByPolicy := make(map[*types.Policy][]uint32, len(policies)) for _, pol := range policies { if !pol.HasSeqID() || !pol.Enabled { @@ -262,11 +261,10 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) ([]*proto.Po if r == nil || !r.Enabled { continue } - idxByPolicy[pol] = append(idxByPolicy[pol], uint32(len(out))) out = append(out, e.encodePolicyRule(pol, r)) } } - return out, idxByPolicy + return out } // encodePolicyRule maps a single PolicyRule under pol to a PolicyCompact entry. @@ -290,11 +288,11 @@ func (e *componentEncoder) encodePolicyRule(pol *types.Policy, r *types.PolicyRu // groupSeqIDs maps the xid group IDs in src to their per-account seq ids, // dropping any group that has no seq id assigned. -func (e *componentEncoder) groupSeqIDs(src []string) []uint32 { +func (e *componentEncoder) groupSeqIDs(src []string) []int32 { if len(src) == 0 { return nil } - out := make([]uint32, 0, len(src)) + out := make([]int32, 0, len(src)) for _, gid := range src { if seq, ok := e.groupSeq(gid); ok { out = append(out, seq) @@ -346,11 +344,11 @@ func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*type // group xid → local-user names) to the wire form (map keyed by group // account_seq_id → UserNameList). Groups without a seq id are dropped — // matches how source/destination group references handle the same case. -func (e *componentEncoder) encodeAuthorizedGroups(m map[string][]string) map[uint32]*proto.UserNameList { +func (e *componentEncoder) encodeAuthorizedGroups(m map[string][]string) map[int32]*proto.UserNameList { if len(m) == 0 { return nil } - out := make(map[uint32]*proto.UserNameList, len(m)) + out := make(map[int32]*proto.UserNameList, len(m)) for groupID, names := range m { seq, ok := e.groupSeq(groupID) if !ok { @@ -361,7 +359,7 @@ func (e *componentEncoder) encodeAuthorizedGroups(m map[string][]string) map[uin return out } -func (e *componentEncoder) groupSeq(groupID string) (uint32, bool) { +func (e *componentEncoder) groupSeq(groupID string) (int32, bool) { g, ok := e.components.Groups[groupID] if !ok || !g.HasSeqID() { return 0, false @@ -392,11 +390,11 @@ func (e *componentEncoder) resourceToProto(r types.Resource) *proto.ResourceComp // per-account integer ids using the NetworkMapComponents.PostureCheckXIDToSeq // lookup. Unresolvable xids are silently dropped — matches how group/peer // references handle the same case. -func (e *componentEncoder) postureCheckSeqs(xids []string) []uint32 { +func (e *componentEncoder) postureCheckSeqs(xids []string) []int32 { if len(xids) == 0 || len(e.components.PostureCheckXIDToSeq) == 0 { return nil } - out := make([]uint32, 0, len(xids)) + out := make([]int32, 0, len(xids)) for _, xid := range xids { if seq, ok := e.components.PostureCheckXIDToSeq[xid]; ok { out = append(out, seq) @@ -408,7 +406,7 @@ func (e *componentEncoder) postureCheckSeqs(xids []string) []uint32 { // networkSeq translates a Network xid to its per-account integer id using // the NetworkMapComponents.NetworkXIDToSeq lookup. Returns (0,false) when // the xid isn't known — callers decide whether to skip the parent record. -func (e *componentEncoder) networkSeq(xid string) (uint32, bool) { +func (e *componentEncoder) networkSeq(xid string) (int32, bool) { if xid == "" { return 0, false } @@ -424,7 +422,7 @@ func (e *componentEncoder) encodeDNSSettings(s *types.DNSSettings) *proto.DNSSet return nil } out := &proto.DNSSettingsCompact{ - DisabledManagementGroupIds: make([]uint32, 0, len(s.DisabledManagementGroups)), + DisabledManagementGroupIds: make([]int32, 0, len(s.DisabledManagementGroups)), } for _, gid := range s.DisabledManagementGroups { if seq, ok := e.groupSeq(gid); ok { @@ -472,11 +470,11 @@ func (e *componentEncoder) encodeRoutes(routes []*nbroute.Route) []*proto.RouteR return out } -func (e *componentEncoder) groupIDsToSeq(groupIDs []string) []uint32 { +func (e *componentEncoder) groupIDsToSeq(groupIDs []string) []int32 { if len(groupIDs) == 0 { return nil } - out := make([]uint32, 0, len(groupIDs)) + out := make([]int32, 0, len(groupIDs)) for _, gid := range groupIDs { if seq, ok := e.groupSeq(gid); ok { out = append(out, seq) @@ -587,11 +585,11 @@ func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.Net return out } -func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[uint32]*proto.NetworkRouterList { +func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[int32]*proto.NetworkRouterList { if len(routersMap) == 0 { return nil } - out := make(map[uint32]*proto.NetworkRouterList, len(routersMap)) + out := make(map[int32]*proto.NetworkRouterList, len(routersMap)) for networkXID, routers := range routersMap { if len(routers) == 0 { continue @@ -623,42 +621,42 @@ func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*ro return out } -func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*types.Policy, policyToIdxs map[*types.Policy][]uint32) map[uint32]*proto.PolicyIndexes { +func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*types.Policy) map[int32]*proto.PolicyIds { if len(rpm) == 0 { return nil } // resourceXIDToSeq is local to one encode — built from components.NetworkResources // (small slice). Network resources without seq id are dropped, matching how // other components-without-seq are silently filtered. - resourceXIDToSeq := make(map[string]uint32, len(e.components.NetworkResources)) + resourceXIDToSeq := make(map[string]int32, len(e.components.NetworkResources)) for _, r := range e.components.NetworkResources { if r != nil && r.AccountSeqID != 0 { resourceXIDToSeq[r.ID] = r.AccountSeqID } } - out := make(map[uint32]*proto.PolicyIndexes, len(rpm)) + out := make(map[int32]*proto.PolicyIds, len(rpm)) for resourceXID, policies := range rpm { seq, ok := resourceXIDToSeq[resourceXID] if !ok { continue } - idxs := make([]uint32, 0, len(policies)*2) + ids := make([]int32, 0, len(policies)) for _, pol := range policies { - idxs = append(idxs, policyToIdxs[pol]...) + ids = append(ids, pol.AccountSeqID) } - if len(idxs) == 0 { + if len(ids) == 0 { continue } - out[seq] = &proto.PolicyIndexes{Indexes: idxs} + out[seq] = &proto.PolicyIds{Ids: ids} } return out } -func (e *componentEncoder) encodeGroupIDToUserIDs(m map[string][]string) map[uint32]*proto.UserIDList { +func (e *componentEncoder) encodeGroupIDToUserIDs(m map[string][]string) map[int32]*proto.UserIDList { if len(m) == 0 { return nil } - out := make(map[uint32]*proto.UserIDList, len(m)) + out := make(map[int32]*proto.UserIDList, len(m)) for groupID, userIDs := range m { seq, ok := e.groupSeq(groupID) if !ok || len(userIDs) == 0 { @@ -680,11 +678,11 @@ func stringSetToSlice(s map[string]struct{}) []string { return out } -func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[uint32]*proto.PeerIndexSet { +func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[int32]*proto.PeerIndexSet { if len(m) == 0 { return nil } - out := make(map[uint32]*proto.PeerIndexSet, len(m)) + out := make(map[int32]*proto.PeerIndexSet, len(m)) for checkXID, failedPeerIDs := range m { seq, ok := e.components.PostureCheckXIDToSeq[checkXID] if !ok || seq == 0 { @@ -736,12 +734,12 @@ func toAccountNetwork(n *types.Network) *proto.AccountNetwork { return out } -func toPeerCompact(p *nbpeer.Peer, agentVersionIdx uint32) *proto.PeerCompact { +func toPeerCompact(p *nbpeer.Peer) *proto.PeerCompact { pc := &proto.PeerCompact{ WgPubKey: decodeWgKey(p.Key), SshPubKey: []byte(p.SSHKey), DnsLabel: p.DNSLabel, - AgentVersionIdx: agentVersionIdx, + AgentVersion: p.Meta.WtVersion, AddedWithSsoLogin: p.UserID != "", LoginExpirationEnabled: p.LoginExpirationEnabled, SshEnabled: p.SSHEnabled, diff --git a/management/server/networks/resources/types/resource.go b/management/server/networks/resources/types/resource.go index 454ca4162fb..6db991e8f93 100644 --- a/management/server/networks/resources/types/resource.go +++ b/management/server/networks/resources/types/resource.go @@ -29,20 +29,20 @@ func (p NetworkResourceType) String() string { } type NetworkResource struct { - ID string `gorm:"primaryKey"` - NetworkID string `gorm:"index"` - AccountID string `gorm:"index"` + ID string `gorm:"primaryKey"` + NetworkID string `gorm:"index"` + AccountID string `gorm:"index"` // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_network_resources_account_seq_id;not null;default:0"` - Name string - Description string - Type NetworkResourceType - Address string `gorm:"-"` - GroupIDs []string `gorm:"-"` - Domain string - Prefix netip.Prefix `gorm:"serializer:json"` - Enabled bool + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + Name string + Description string + Type NetworkResourceType + Address string `gorm:"-"` + GroupIDs []string `gorm:"-"` + Domain string + Prefix netip.Prefix `gorm:"serializer:json"` + Enabled bool } func NewNetworkResource(accountID, networkID, name, description, address string, groupIDs []string, enabled bool) (*NetworkResource, error) { diff --git a/management/server/networks/routers/types/router.go b/management/server/networks/routers/types/router.go index d4c55eaf3de..48dfff3aab4 100644 --- a/management/server/networks/routers/types/router.go +++ b/management/server/networks/routers/types/router.go @@ -10,17 +10,17 @@ import ( ) type NetworkRouter struct { - ID string `gorm:"primaryKey"` - NetworkID string `gorm:"index"` - AccountID string `gorm:"index"` + ID string `gorm:"primaryKey"` + NetworkID string `gorm:"index"` + AccountID string `gorm:"index"` // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_network_routers_account_seq_id;not null;default:0"` - Peer string - PeerGroups []string `gorm:"serializer:json"` - Masquerade bool - Metric int - Enabled bool + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + Peer string + PeerGroups []string `gorm:"serializer:json"` + Masquerade bool + Metric int + Enabled bool } func NewNetworkRouter(accountID string, networkID string, peer string, peerGroups []string, masquerade bool, metric int, enabled bool) (*NetworkRouter, error) { diff --git a/management/server/networks/types/network.go b/management/server/networks/types/network.go index 0c6920879c0..ad43d9827b8 100644 --- a/management/server/networks/types/network.go +++ b/management/server/networks/types/network.go @@ -12,7 +12,7 @@ type Network struct { // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_networks_account_seq_id;not null;default:0"` + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` Name string Description string diff --git a/management/server/posture/checks.go b/management/server/posture/checks.go index ec3e14c60c9..b5a84ab5c3d 100644 --- a/management/server/posture/checks.go +++ b/management/server/posture/checks.go @@ -51,7 +51,7 @@ type Checks struct { // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_posture_checks_account_seq_id;not null;default:0"` + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` // Checks is a set of objects that perform the actual checks Checks ChecksDefinition `gorm:"serializer:json"` diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 5da36c1fc35..839b6df2a63 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -3637,11 +3637,11 @@ func (s *SqlStore) withTx(tx *gorm.DB) Store { // AllocateAccountSeqID returns the next per-account integer id for the given // component kind. Must be called inside ExecuteInTransaction so the increment // is serialized with the component insert. -func (s *SqlStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (uint32, error) { +func (s *SqlStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (int32, error) { return allocateAccountSeqID(ctx, s.db, s.storeEngine, accountID, entity) } -func allocateAccountSeqID(_ context.Context, db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity) (uint32, error) { +func allocateAccountSeqID(_ context.Context, db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity) (int32, error) { switch engine { case types.PostgresStoreEngine, types.SqliteStoreEngine: return allocateAccountSeqIDReturning(db, accountID, entity) @@ -3657,7 +3657,7 @@ func allocateAccountSeqID(_ context.Context, db *gorm.DB, engine types.Engine, a // SELECT FOR UPDATE. Two concurrent allocations for the same (account, entity) // produce two distinct ids: one wins the INSERT, the other wins the UPDATE // branch and returns next_id+1. -func allocateAccountSeqIDReturning(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (uint32, error) { +func allocateAccountSeqIDReturning(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (int32, error) { const sqlStr = ` INSERT INTO account_seq_counters (account_id, entity, next_id) VALUES (?, ?, 2) @@ -3665,7 +3665,7 @@ func allocateAccountSeqIDReturning(db *gorm.DB, accountID string, entity types.A SET next_id = account_seq_counters.next_id + 1 RETURNING (next_id - 1) ` - var allocated uint32 + var allocated int32 if err := db.Raw(sqlStr, accountID, string(entity)).Scan(&allocated).Error; err != nil { return 0, fmt.Errorf("upsert account seq counter: %w", err) } @@ -3682,7 +3682,7 @@ func allocateAccountSeqIDReturning(db *gorm.DB, accountID string, entity types.A // no-conflict path also surfaces the new next_id, keeping the read-back uniform. // LAST_INSERT_ID is per-connection; GORM transactions pin a single connection, // so the follow-up SELECT sees the same value. -func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (uint32, error) { +func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (int32, error) { const upsertSQL = ` INSERT INTO account_seq_counters (account_id, entity, next_id) VALUES (?, ?, LAST_INSERT_ID(2)) @@ -3698,7 +3698,7 @@ func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.Accou if newNext == 0 { return 0, fmt.Errorf("LAST_INSERT_ID returned 0; account_seq_counters misconfigured") } - return uint32(newNext - 1), nil + return int32(newNext - 1), nil } // assignAccountSeqIDs allocates a per-account integer id for any component on @@ -3709,8 +3709,8 @@ func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.Accou // per-entity counter is bumped so subsequent AllocateAccountSeqID calls don't // hand out a colliding id. func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account *types.Account) error { - maxByEntity := make(map[types.AccountSeqEntity]uint32, 8) - bump := func(entity types.AccountSeqEntity, seq uint32) { + maxByEntity := make(map[types.AccountSeqEntity]int32, 8) + bump := func(entity types.AccountSeqEntity, seq int32) { if seq > maxByEntity[entity] { maxByEntity[entity] = seq } @@ -3856,7 +3856,7 @@ func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account // AccountSeqIDs (e.g. test bulk-load from sqlite to postgres, or migrations // running before component data lands) so that the next AllocateAccountSeqID // call returns a fresh id beyond what was just written. -func ensureAccountSeqCounter(db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity, target uint32) error { +func ensureAccountSeqCounter(db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity, target int32) error { switch engine { case types.PostgresStoreEngine, types.SqliteStoreEngine: const sqlStr = ` diff --git a/management/server/store/store.go b/management/server/store/store.go index 959ce373d25..5c3ea4d79d0 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -226,7 +226,7 @@ type Store interface { // AllocateAccountSeqID returns the next per-account integer id for the given // component kind. Must run inside a transaction so the increment is serialized // with the component insert. - AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (uint32, error) + AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (int32, error) GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*networkTypes.Network, error) GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networkTypes.Network, error) diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 4dd785273ca..77dab9f6c80 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -134,8 +134,8 @@ func (a *Account) GetPeerNetworkMapComponents( NetworkResources: make([]*resourceTypes.NetworkResource, 0), PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)), RouterPeers: make(map[string]*nbpeer.Peer), - NetworkXIDToSeq: make(map[string]uint32, len(a.Networks)), - PostureCheckXIDToSeq: make(map[string]uint32, len(a.PostureChecks)), + NetworkXIDToSeq: make(map[string]int32, len(a.Networks)), + PostureCheckXIDToSeq: make(map[string]int32, len(a.PostureChecks)), } for _, n := range a.Networks { if n != nil && n.HasSeqID() { diff --git a/route/route.go b/route/route.go index 8a26cc3bbfa..b926dc4c068 100644 --- a/route/route.go +++ b/route/route.go @@ -97,7 +97,7 @@ type Route struct { AccountID string `gorm:"index"` // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_routes_account_seq_id;not null;default:0"` + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` // Network and Domains are mutually exclusive Network netip.Prefix `gorm:"serializer:json"` Domains domain.List `gorm:"serializer:json"` diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 06e4738d8b0..a01acfab14c 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -4850,14 +4850,14 @@ type NetworkMapComponentsFull struct { // entry because capability=3 has never been released — every cap=3 // producer and consumer carries the same regenerated descriptor. Do NOT // reuse this pattern for any further wire change once cap=3 ships. - RoutersMap map[uint32]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + RoutersMap map[int32]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // For each NetworkResource account_seq_id, the indexes into policies[] // that apply to it. // // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - ResourcePoliciesMap map[uint32]*PolicyIndexes `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ResourcePoliciesMap map[int32]*PolicyIds `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Group-id (account_seq_id) → user ids authorized for SSH on members. - GroupIdToUserIds map[uint32]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + GroupIdToUserIds map[int32]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). AllowedUserIds []string `protobuf:"bytes,21,rep,name=allowed_user_ids,json=allowedUserIds,proto3" json:"allowed_user_ids,omitempty"` @@ -4865,7 +4865,7 @@ type NetworkMapComponentsFull struct { // the check. Server-side evaluation result; clients do not re-evaluate. // // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - PostureFailedPeers map[uint32]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + PostureFailedPeers map[int32]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level DNS forwarder port (mirrors the legacy // proto.DNSConfig.ForwarderPort). Computed by the controller from peer // versions; clients fold it into their Calculate() DNS output. @@ -5034,21 +5034,21 @@ func (x *NetworkMapComponentsFull) GetNetworkResources() []*NetworkResourceRaw { return nil } -func (x *NetworkMapComponentsFull) GetRoutersMap() map[uint32]*NetworkRouterList { +func (x *NetworkMapComponentsFull) GetRoutersMap() map[int32]*NetworkRouterList { if x != nil { return x.RoutersMap } return nil } -func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[uint32]*PolicyIndexes { +func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[int32]*PolicyIds { if x != nil { return x.ResourcePoliciesMap } return nil } -func (x *NetworkMapComponentsFull) GetGroupIdToUserIds() map[uint32]*UserIDList { +func (x *NetworkMapComponentsFull) GetGroupIdToUserIds() map[int32]*UserIDList { if x != nil { return x.GroupIdToUserIds } @@ -5062,7 +5062,7 @@ func (x *NetworkMapComponentsFull) GetAllowedUserIds() []string { return nil } -func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[uint32]*PeerIndexSet { +func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[int32]*PeerIndexSet { if x != nil { return x.PostureFailedPeers } @@ -5391,9 +5391,8 @@ type PeerCompact struct { SshPubKey []byte `protobuf:"bytes,4,opt,name=ssh_pub_key,json=sshPubKey,proto3" json:"ssh_pub_key,omitempty"` // DNS label without the account's domain suffix. Full FQDN is // dns_label + "." + NetworkMapComponentsFull.dns_domain. - DnsLabel string `protobuf:"bytes,5,opt,name=dns_label,json=dnsLabel,proto3" json:"dns_label,omitempty"` - // Index into NetworkMapComponentsFull.agent_versions. - AgentVersionIdx uint32 `protobuf:"varint,6,opt,name=agent_version_idx,json=agentVersionIdx,proto3" json:"agent_version_idx,omitempty"` + DnsLabel string `protobuf:"bytes,5,opt,name=dns_label,json=dnsLabel,proto3" json:"dns_label,omitempty"` + AgentVersion string `protobuf:"bytes,6,opt,name=agent_version,json=agentVersion,proto3" json:"agent_version,omitempty"` // True iff the peer was added via SSO login (i.e., types.Peer.UserID is // non-empty). Combined with login_expiration_enabled and // last_login_unix_nano this lets the client reproduce @@ -5416,17 +5415,17 @@ type PeerCompact struct { // HasCapability(PeerCapabilityIPv6Overlay). Used by the local peer's // Calculate() when deciding whether to emit IPv6 firewall rules // (appendIPv6FirewallRule) against this peer's IPv6 address. - SupportsIpv6 bool `protobuf:"varint,12,opt,name=supports_ipv6,json=supportsIpv6,proto3" json:"supports_ipv6,omitempty"` + SupportsIpv6 bool `protobuf:"varint,11,opt,name=supports_ipv6,json=supportsIpv6,proto3" json:"supports_ipv6,omitempty"` // Mirror of types.Peer.SupportsSourcePrefixes() — // HasCapability(PeerCapabilitySourcePrefixes). Determines whether the // local peer's Calculate() emits SourcePrefixes alongside legacy PeerIP // fields in proto.FirewallRule. - SupportsSourcePrefixes bool `protobuf:"varint,13,opt,name=supports_source_prefixes,json=supportsSourcePrefixes,proto3" json:"supports_source_prefixes,omitempty"` + SupportsSourcePrefixes bool `protobuf:"varint,12,opt,name=supports_source_prefixes,json=supportsSourcePrefixes,proto3" json:"supports_source_prefixes,omitempty"` // Mirror of types.Peer.Meta.Flags.ServerSSHAllowed. Read by Calculate() // when expanding TCP port-22 firewall rules — the native SSH companion // (port 22022) is only added when this flag is set and the peer agent // version supports it. - ServerSshAllowed bool `protobuf:"varint,14,opt,name=server_ssh_allowed,json=serverSshAllowed,proto3" json:"server_ssh_allowed,omitempty"` + ServerSshAllowed bool `protobuf:"varint,13,opt,name=server_ssh_allowed,json=serverSshAllowed,proto3" json:"server_ssh_allowed,omitempty"` } func (x *PeerCompact) Reset() { @@ -5496,11 +5495,11 @@ func (x *PeerCompact) GetDnsLabel() string { return "" } -func (x *PeerCompact) GetAgentVersionIdx() uint32 { +func (x *PeerCompact) GetAgentVersion() string { if x != nil { - return x.AgentVersionIdx + return x.AgentVersion } - return 0 + return "" } func (x *PeerCompact) GetAddedWithSsoLogin() bool { @@ -5565,7 +5564,7 @@ type PolicyCompact struct { // Per-account integer id (matches policies.account_seq_id). Used as a // stable reference for ResourcePoliciesMap.indexes and future delta // updates. - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Action RuleAction `protobuf:"varint,2,opt,name=action,proto3,enum=management.RuleAction" json:"action,omitempty"` Protocol RuleProtocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` Bidirectional bool `protobuf:"varint,4,opt,name=bidirectional,proto3" json:"bidirectional,omitempty"` @@ -5574,8 +5573,8 @@ type PolicyCompact struct { // Port ranges (start..end) referenced by the rule. PortRanges []*PortInfo_Range `protobuf:"bytes,6,rep,name=port_ranges,json=portRanges,proto3" json:"port_ranges,omitempty"` // Group ids (account_seq_id) of source / destination groups. - SourceGroupIds []uint32 `protobuf:"varint,7,rep,packed,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` - DestinationGroupIds []uint32 `protobuf:"varint,8,rep,packed,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` + SourceGroupIds []int32 `protobuf:"varint,7,rep,packed,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` + DestinationGroupIds []int32 `protobuf:"varint,8,rep,packed,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's // applicable group ids (account_seq_id) to a list of local-user names — // when a peer in one of those groups is the SSH destination, the named @@ -5584,8 +5583,8 @@ type PolicyCompact struct { // // Both fields are only consumed by Calculate() when the rule's protocol // is NetbirdSSH (or the legacy implicit-SSH heuristic). - AuthorizedGroups map[uint32]*UserNameList `protobuf:"bytes,10,rep,name=authorized_groups,json=authorizedGroups,proto3" json:"authorized_groups,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - AuthorizedUser string `protobuf:"bytes,11,opt,name=authorized_user,json=authorizedUser,proto3" json:"authorized_user,omitempty"` + AuthorizedGroups map[int32]*UserNameList `protobuf:"bytes,9,rep,name=authorized_groups,json=authorizedGroups,proto3" json:"authorized_groups,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + AuthorizedUser string `protobuf:"bytes,10,opt,name=authorized_user,json=authorizedUser,proto3" json:"authorized_user,omitempty"` // Resource-typed rule sources/destinations. When a rule targets a specific // peer (rather than groups), Calculate() reads SourceResource / // DestinationResource — without these the rule's connection resources @@ -5593,13 +5592,13 @@ type PolicyCompact struct { // NetworkMapComponentsFull.peers; type is the raw ResourceType string // ("peer", "host", "subnet", "domain"). Only "peer" is meaningful for // Calculate's resource-typed rule path today. - SourceResource *ResourceCompact `protobuf:"bytes,12,opt,name=source_resource,json=sourceResource,proto3" json:"source_resource,omitempty"` - DestinationResource *ResourceCompact `protobuf:"bytes,13,opt,name=destination_resource,json=destinationResource,proto3" json:"destination_resource,omitempty"` + SourceResource *ResourceCompact `protobuf:"bytes,11,opt,name=source_resource,json=sourceResource,proto3" json:"source_resource,omitempty"` + DestinationResource *ResourceCompact `protobuf:"bytes,12,opt,name=destination_resource,json=destinationResource,proto3" json:"destination_resource,omitempty"` // Posture-check seq ids gating this policy's source peers. Calculate() // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. - SourcePostureCheckSeqIds []uint32 `protobuf:"varint,15,rep,packed,name=source_posture_check_seq_ids,json=sourcePostureCheckSeqIds,proto3" json:"source_posture_check_seq_ids,omitempty"` + SourcePostureCheckSeqIds []int32 `protobuf:"varint,13,rep,packed,name=source_posture_check_seq_ids,json=sourcePostureCheckSeqIds,proto3" json:"source_posture_check_seq_ids,omitempty"` } func (x *PolicyCompact) Reset() { @@ -5634,7 +5633,7 @@ func (*PolicyCompact) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{63} } -func (x *PolicyCompact) GetId() uint32 { +func (x *PolicyCompact) GetId() int32 { if x != nil { return x.Id } @@ -5676,21 +5675,21 @@ func (x *PolicyCompact) GetPortRanges() []*PortInfo_Range { return nil } -func (x *PolicyCompact) GetSourceGroupIds() []uint32 { +func (x *PolicyCompact) GetSourceGroupIds() []int32 { if x != nil { return x.SourceGroupIds } return nil } -func (x *PolicyCompact) GetDestinationGroupIds() []uint32 { +func (x *PolicyCompact) GetDestinationGroupIds() []int32 { if x != nil { return x.DestinationGroupIds } return nil } -func (x *PolicyCompact) GetAuthorizedGroups() map[uint32]*UserNameList { +func (x *PolicyCompact) GetAuthorizedGroups() map[int32]*UserNameList { if x != nil { return x.AuthorizedGroups } @@ -5718,7 +5717,7 @@ func (x *PolicyCompact) GetDestinationResource() *ResourceCompact { return nil } -func (x *PolicyCompact) GetSourcePostureCheckSeqIds() []uint32 { +func (x *PolicyCompact) GetSourcePostureCheckSeqIds() []int32 { if x != nil { return x.SourcePostureCheckSeqIds } @@ -5851,7 +5850,7 @@ type GroupCompact struct { // Per-account integer id (matches groups.account_seq_id). Used by // PolicyCompact.source_group_ids / destination_group_ids. - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // Group name; only sent when non-empty (clients use it for diagnostics). Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Indexes into NetworkMapComponentsFull.peers. @@ -5890,7 +5889,7 @@ func (*GroupCompact) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{66} } -func (x *GroupCompact) GetId() uint32 { +func (x *GroupCompact) GetId() int32 { if x != nil { return x.Id } @@ -5918,7 +5917,7 @@ type DNSSettingsCompact struct { unknownFields protoimpl.UnknownFields // Group ids (account_seq_id) whose DNS management is disabled. - DisabledManagementGroupIds []uint32 `protobuf:"varint,1,rep,packed,name=disabled_management_group_ids,json=disabledManagementGroupIds,proto3" json:"disabled_management_group_ids,omitempty"` + DisabledManagementGroupIds []int32 `protobuf:"varint,1,rep,packed,name=disabled_management_group_ids,json=disabledManagementGroupIds,proto3" json:"disabled_management_group_ids,omitempty"` } func (x *DNSSettingsCompact) Reset() { @@ -5953,7 +5952,7 @@ func (*DNSSettingsCompact) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{67} } -func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []uint32 { +func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []int32 { if x != nil { return x.DisabledManagementGroupIds } @@ -5970,7 +5969,7 @@ type RouteRaw struct { unknownFields protoimpl.UnknownFields // Per-account integer id (matches routes.account_seq_id). - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` NetId string `protobuf:"bytes,2,opt,name=net_id,json=netId,proto3" json:"net_id,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Either network_cidr (e.g. "10.0.0.0/16") or domains is set, not both. @@ -5987,16 +5986,16 @@ type RouteRaw struct { // it to peer.Key only after the route has been admitted to the network // map. Decoders MUST set Route.Peer = peer.ID; the legacy Calculate() // path will substitute the WG key downstream. - PeerIndexSet bool `protobuf:"varint,7,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` - PeerIndex uint32 `protobuf:"varint,8,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` - PeerGroupIds []uint32 `protobuf:"varint,9,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` - NetworkType int32 `protobuf:"varint,10,opt,name=network_type,json=networkType,proto3" json:"network_type,omitempty"` - Masquerade bool `protobuf:"varint,11,opt,name=masquerade,proto3" json:"masquerade,omitempty"` - Metric int32 `protobuf:"varint,12,opt,name=metric,proto3" json:"metric,omitempty"` - Enabled bool `protobuf:"varint,13,opt,name=enabled,proto3" json:"enabled,omitempty"` - GroupIds []uint32 `protobuf:"varint,14,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` - AccessControlGroupIds []uint32 `protobuf:"varint,15,rep,packed,name=access_control_group_ids,json=accessControlGroupIds,proto3" json:"access_control_group_ids,omitempty"` - SkipAutoApply bool `protobuf:"varint,16,opt,name=skip_auto_apply,json=skipAutoApply,proto3" json:"skip_auto_apply,omitempty"` + PeerIndexSet bool `protobuf:"varint,7,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerIndex uint32 `protobuf:"varint,8,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` + PeerGroupIds []int32 `protobuf:"varint,9,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` + NetworkType int32 `protobuf:"varint,10,opt,name=network_type,json=networkType,proto3" json:"network_type,omitempty"` + Masquerade bool `protobuf:"varint,11,opt,name=masquerade,proto3" json:"masquerade,omitempty"` + Metric int32 `protobuf:"varint,12,opt,name=metric,proto3" json:"metric,omitempty"` + Enabled bool `protobuf:"varint,13,opt,name=enabled,proto3" json:"enabled,omitempty"` + GroupIds []int32 `protobuf:"varint,14,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` + AccessControlGroupIds []int32 `protobuf:"varint,15,rep,packed,name=access_control_group_ids,json=accessControlGroupIds,proto3" json:"access_control_group_ids,omitempty"` + SkipAutoApply bool `protobuf:"varint,16,opt,name=skip_auto_apply,json=skipAutoApply,proto3" json:"skip_auto_apply,omitempty"` } func (x *RouteRaw) Reset() { @@ -6031,7 +6030,7 @@ func (*RouteRaw) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{68} } -func (x *RouteRaw) GetId() uint32 { +func (x *RouteRaw) GetId() int32 { if x != nil { return x.Id } @@ -6087,7 +6086,7 @@ func (x *RouteRaw) GetPeerIndex() uint32 { return 0 } -func (x *RouteRaw) GetPeerGroupIds() []uint32 { +func (x *RouteRaw) GetPeerGroupIds() []int32 { if x != nil { return x.PeerGroupIds } @@ -6122,14 +6121,14 @@ func (x *RouteRaw) GetEnabled() bool { return false } -func (x *RouteRaw) GetGroupIds() []uint32 { +func (x *RouteRaw) GetGroupIds() []int32 { if x != nil { return x.GroupIds } return nil } -func (x *RouteRaw) GetAccessControlGroupIds() []uint32 { +func (x *RouteRaw) GetAccessControlGroupIds() []int32 { if x != nil { return x.AccessControlGroupIds } @@ -6151,13 +6150,13 @@ type NameServerGroupRaw struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // nameserver_groups.account_seq_id + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // nameserver_groups.account_seq_id Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Reuses the legacy NameServer wire shape (IP as string). Nameservers []*NameServer `protobuf:"bytes,4,rep,name=nameservers,proto3" json:"nameservers,omitempty"` // Group ids (account_seq_id) the NSG distributes nameservers to. - GroupIds []uint32 `protobuf:"varint,5,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` + GroupIds []int32 `protobuf:"varint,5,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` Primary bool `protobuf:"varint,6,opt,name=primary,proto3" json:"primary,omitempty"` Domains []string `protobuf:"bytes,7,rep,name=domains,proto3" json:"domains,omitempty"` Enabled bool `protobuf:"varint,8,opt,name=enabled,proto3" json:"enabled,omitempty"` @@ -6196,7 +6195,7 @@ func (*NameServerGroupRaw) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{69} } -func (x *NameServerGroupRaw) GetId() uint32 { +func (x *NameServerGroupRaw) GetId() int32 { if x != nil { return x.Id } @@ -6224,7 +6223,7 @@ func (x *NameServerGroupRaw) GetNameservers() []*NameServer { return nil } -func (x *NameServerGroupRaw) GetGroupIds() []uint32 { +func (x *NameServerGroupRaw) GetGroupIds() []int32 { if x != nil { return x.GroupIds } @@ -6271,8 +6270,8 @@ type NetworkResourceRaw struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id - NetworkSeq uint32 `protobuf:"varint,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` // networks.account_seq_id (replaces xid) + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id + NetworkSeq int32 `protobuf:"varint,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` // networks.account_seq_id (replaces xid) Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // Resource type: "host" / "subnet" / "domain". @@ -6315,14 +6314,14 @@ func (*NetworkResourceRaw) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{70} } -func (x *NetworkResourceRaw) GetId() uint32 { +func (x *NetworkResourceRaw) GetId() int32 { if x != nil { return x.Id } return 0 } -func (x *NetworkResourceRaw) GetNetworkSeq() uint32 { +func (x *NetworkResourceRaw) GetNetworkSeq() int32 { if x != nil { return x.NetworkSeq } @@ -6434,13 +6433,13 @@ type NetworkRouterEntry struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_routers.account_seq_id - PeerIndex uint32 `protobuf:"varint,2,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` - PeerIndexSet bool `protobuf:"varint,3,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` - PeerGroupIds []uint32 `protobuf:"varint,4,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` - Masquerade bool `protobuf:"varint,5,opt,name=masquerade,proto3" json:"masquerade,omitempty"` - Metric int32 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` - Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_routers.account_seq_id + PeerIndex uint32 `protobuf:"varint,2,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` + PeerIndexSet bool `protobuf:"varint,3,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerGroupIds []int32 `protobuf:"varint,4,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` + Masquerade bool `protobuf:"varint,5,opt,name=masquerade,proto3" json:"masquerade,omitempty"` + Metric int32 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` + Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` } func (x *NetworkRouterEntry) Reset() { @@ -6475,7 +6474,7 @@ func (*NetworkRouterEntry) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{72} } -func (x *NetworkRouterEntry) GetId() uint32 { +func (x *NetworkRouterEntry) GetId() int32 { if x != nil { return x.Id } @@ -6496,7 +6495,7 @@ func (x *NetworkRouterEntry) GetPeerIndexSet() bool { return false } -func (x *NetworkRouterEntry) GetPeerGroupIds() []uint32 { +func (x *NetworkRouterEntry) GetPeerGroupIds() []int32 { if x != nil { return x.PeerGroupIds } @@ -6524,17 +6523,16 @@ func (x *NetworkRouterEntry) GetEnabled() bool { return false } -// PolicyIndexes is a list of indexes into NetworkMapComponentsFull.policies. -type PolicyIndexes struct { +type PolicyIds struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Indexes []uint32 `protobuf:"varint,1,rep,packed,name=indexes,proto3" json:"indexes,omitempty"` + Ids []int32 `protobuf:"varint,1,rep,packed,name=ids,proto3" json:"ids,omitempty"` } -func (x *PolicyIndexes) Reset() { - *x = PolicyIndexes{} +func (x *PolicyIds) Reset() { + *x = PolicyIds{} if protoimpl.UnsafeEnabled { mi := &file_management_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -6542,13 +6540,13 @@ func (x *PolicyIndexes) Reset() { } } -func (x *PolicyIndexes) String() string { +func (x *PolicyIds) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PolicyIndexes) ProtoMessage() {} +func (*PolicyIds) ProtoMessage() {} -func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { +func (x *PolicyIds) ProtoReflect() protoreflect.Message { mi := &file_management_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -6560,14 +6558,14 @@ func (x *PolicyIndexes) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PolicyIndexes.ProtoReflect.Descriptor instead. -func (*PolicyIndexes) Descriptor() ([]byte, []int) { +// Deprecated: Use PolicyIds.ProtoReflect.Descriptor instead. +func (*PolicyIds) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{73} } -func (x *PolicyIndexes) GetIndexes() []uint32 { +func (x *PolicyIds) GetIds() []int32 { if x != nil { - return x.Indexes + return x.Ids } return nil } @@ -7394,7 +7392,7 @@ var file_management_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, - 0x6c, 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x96, + 0x6c, 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x92, 0x0f, 0x0a, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, @@ -7494,368 +7492,365 @@ var file_management_proto_rawDesc = []byte{ 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x61, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, + 0x1a, 0x5d, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, - 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, - 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x4a, 0x04, 0x08, 0x1a, 0x10, 0x33, 0x22, 0x87, 0x03, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x6f, 0x66, - 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x79, 0x49, 0x64, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x5b, 0x0a, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5f, 0x0a, 0x17, + 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, + 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, + 0x1a, 0x10, 0x33, 0x22, 0x87, 0x03, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x61, 0x74, + 0x63, 0x68, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, - 0x0e, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, - 0x0d, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x29, - 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x14, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, - 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, - 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x10, 0x66, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x52, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x22, 0x94, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, - 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x37, 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, - 0x65, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, - 0x65, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x5f, 0x76, 0x36, - 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, - 0x56, 0x36, 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x22, 0x21, 0x0a, 0x19, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, - 0x01, 0x10, 0x65, 0x22, 0x88, 0x04, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, - 0x70, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x69, 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x78, 0x12, 0x2f, - 0x0a, 0x14, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x73, 0x6f, - 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x64, - 0x64, 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, - 0x38, 0x0a, 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, - 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x73, - 0x68, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x49, 0x70, 0x76, 0x36, - 0x12, 0x38, 0x0a, 0x18, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x16, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x73, - 0x68, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x0b, 0x10, 0x0c, 0x22, 0xa4, - 0x06, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, - 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, - 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, - 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x72, - 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, - 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x5c, 0x0a, - 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x63, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, - 0x55, 0x73, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x14, 0x64, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x1c, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0d, - 0x52, 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x71, 0x49, 0x64, 0x73, 0x1a, 0x5d, 0x0a, 0x15, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, - 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x22, 0x70, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, - 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x24, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x4e, - 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x55, 0x0a, - 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x65, 0x73, 0x22, 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0d, 0x52, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x93, 0x04, - 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, - 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x63, - 0x69, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, - 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, + 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, + 0x65, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, + 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x66, 0x69, 0x72, + 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, + 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x66, 0x69, 0x72, + 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x14, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x66, + 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, + 0x6c, 0x65, 0x52, 0x12, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, + 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x66, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x94, 0x01, + 0x0a, 0x16, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1a, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x70, + 0x65, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x5f, 0x63, + 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x43, 0x69, + 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x5f, 0x76, 0x36, 0x5f, 0x63, 0x69, 0x64, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x56, 0x36, 0x43, 0x69, + 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x64, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x21, 0x0a, 0x19, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x65, 0x22, + 0xfb, 0x03, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, + 0x1c, 0x0a, 0x0a, 0x77, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, + 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, + 0x36, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x23, + 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, + 0x68, 0x5f, 0x73, 0x73, 0x6f, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x61, 0x64, 0x64, 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x53, 0x73, 0x6f, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2f, + 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x6e, 0x69, + 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, + 0x73, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x73, 0x68, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x69, 0x70, 0x76, + 0x36, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x73, 0x49, 0x70, 0x76, 0x36, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x73, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, + 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x12, + 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x53, 0x73, 0x68, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x22, 0x98, 0x06, + 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, + 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x69, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, + 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, + 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, + 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x05, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x5c, 0x0a, 0x11, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, + 0x63, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x55, + 0x73, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x14, 0x64, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x63, 0x74, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x1c, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x53, 0x65, 0x71, 0x49, 0x64, 0x73, 0x1a, 0x5d, 0x0a, 0x15, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x70, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, - 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, - 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, - 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x15, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, - 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, - 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x4a, 0x04, 0x08, - 0x11, 0x10, 0x12, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8d, 0x02, 0x0a, 0x12, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, - 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x53, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x22, 0x4d, 0x0a, 0x11, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, - 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, - 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, - 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x29, - 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, - 0x52, 0x07, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, - 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x73, 0x22, 0x31, 0x0a, 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, + 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x24, 0x0a, 0x0c, 0x55, 0x73, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, + 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x05, 0x52, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, + 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, + 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, + 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x0e, 0x20, 0x03, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, + 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, + 0x05, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, + 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, + 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x87, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x71, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x65, 0x66, 0x69, + 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, + 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, + 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x1d, 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, + 0x64, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x03, 0x69, 0x64, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, 0x0a, + 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, 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, + 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, - 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, + 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, 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, + 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, 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, 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, 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, + 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, 0x42, + 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -7954,7 +7949,7 @@ var file_management_proto_goTypes = []interface{}{ (*NetworkResourceRaw)(nil), // 78: management.NetworkResourceRaw (*NetworkRouterList)(nil), // 79: management.NetworkRouterList (*NetworkRouterEntry)(nil), // 80: management.NetworkRouterEntry - (*PolicyIndexes)(nil), // 81: management.PolicyIndexes + (*PolicyIds)(nil), // 81: management.PolicyIds (*UserIDList)(nil), // 82: management.UserIDList (*PeerIndexSet)(nil), // 83: management.PeerIndexSet nil, // 84: management.SSHAuth.MachineUsersEntry @@ -8071,7 +8066,7 @@ var file_management_proto_depIdxs = []int32{ 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.PolicyIndexes + 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 @@ -8991,7 +8986,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyIndexes); i { + switch v := v.(*PolicyIds); i { case 0: return &v.state case 1: diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index f310eeb6a00..78eafad1256 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -864,16 +864,16 @@ message NetworkMapComponentsFull { // entry because capability=3 has never been released — every cap=3 // producer and consumer carries the same regenerated descriptor. Do NOT // reuse this pattern for any further wire change once cap=3 ships. - map routers_map = 18; + map routers_map = 18; // For each NetworkResource account_seq_id, the indexes into policies[] // that apply to it. // // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - map resource_policies_map = 19; + map resource_policies_map = 19; // Group-id (account_seq_id) → user ids authorized for SSH on members. - map group_id_to_user_ids = 20; + map group_id_to_user_ids = 20; // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). @@ -883,7 +883,7 @@ message NetworkMapComponentsFull { // the check. Server-side evaluation result; clients do not re-evaluate. // // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - map posture_failed_peers = 22; + map posture_failed_peers = 22; // Account-level DNS forwarder port (mirrors the legacy // proto.DNSConfig.ForwarderPort). Computed by the controller from peer @@ -978,8 +978,7 @@ message PeerCompact { // dns_label + "." + NetworkMapComponentsFull.dns_domain. string dns_label = 5; - // Index into NetworkMapComponentsFull.agent_versions. - uint32 agent_version_idx = 6; + string agent_version = 6; // True iff the peer was added via SSO login (i.e., types.Peer.UserID is // non-empty). Combined with login_expiration_enabled and @@ -1003,25 +1002,23 @@ message PeerCompact { // peer when this bit is set, even without an explicit NetbirdSSH rule. bool ssh_enabled = 10; - reserved 11; // was: id (string xid) - // Mirror of types.Peer.SupportsIPv6() — !Meta.Flags.DisableIPv6 && // HasCapability(PeerCapabilityIPv6Overlay). Used by the local peer's // Calculate() when deciding whether to emit IPv6 firewall rules // (appendIPv6FirewallRule) against this peer's IPv6 address. - bool supports_ipv6 = 12; + bool supports_ipv6 = 11; // Mirror of types.Peer.SupportsSourcePrefixes() — // HasCapability(PeerCapabilitySourcePrefixes). Determines whether the // local peer's Calculate() emits SourcePrefixes alongside legacy PeerIP // fields in proto.FirewallRule. - bool supports_source_prefixes = 13; + bool supports_source_prefixes = 12; // Mirror of types.Peer.Meta.Flags.ServerSSHAllowed. Read by Calculate() // when expanding TCP port-22 firewall rules — the native SSH companion // (port 22022) is only added when this flag is set and the peer agent // version supports it. - bool server_ssh_allowed = 14; + bool server_ssh_allowed = 13; } // PolicyCompact is the compact form of a policy rule. Group references use @@ -1033,7 +1030,7 @@ message PolicyCompact { // Per-account integer id (matches policies.account_seq_id). Used as a // stable reference for ResourcePoliciesMap.indexes and future delta // updates. - uint32 id = 1; + int32 id = 1; RuleAction action = 2; RuleProtocol protocol = 3; @@ -1046,10 +1043,8 @@ message PolicyCompact { repeated PortInfo.Range port_ranges = 6; // Group ids (account_seq_id) of source / destination groups. - repeated uint32 source_group_ids = 7; - repeated uint32 destination_group_ids = 8; - - reserved 9; // was: xid (string) + repeated int32 source_group_ids = 7; + repeated int32 destination_group_ids = 8; // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's // applicable group ids (account_seq_id) to a list of local-user names — @@ -1059,8 +1054,8 @@ message PolicyCompact { // // Both fields are only consumed by Calculate() when the rule's protocol // is NetbirdSSH (or the legacy implicit-SSH heuristic). - map authorized_groups = 10; - string authorized_user = 11; + map authorized_groups = 9; + string authorized_user = 10; // Resource-typed rule sources/destinations. When a rule targets a specific // peer (rather than groups), Calculate() reads SourceResource / @@ -1069,16 +1064,14 @@ message PolicyCompact { // NetworkMapComponentsFull.peers; type is the raw ResourceType string // ("peer", "host", "subnet", "domain"). Only "peer" is meaningful for // Calculate's resource-typed rule path today. - ResourceCompact source_resource = 12; - ResourceCompact destination_resource = 13; + ResourceCompact source_resource = 11; + ResourceCompact destination_resource = 12; // Posture-check seq ids gating this policy's source peers. Calculate() // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. - repeated uint32 source_posture_check_seq_ids = 15; - - reserved 14; // was: source_posture_check_ids (repeated string xid) + repeated int32 source_posture_check_seq_ids = 13; } // ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry @@ -1104,7 +1097,7 @@ message UserNameList { message GroupCompact { // Per-account integer id (matches groups.account_seq_id). Used by // PolicyCompact.source_group_ids / destination_group_ids. - uint32 id = 1; + int32 id = 1; // Group name; only sent when non-empty (clients use it for diagnostics). string name = 2; @@ -1116,7 +1109,7 @@ message GroupCompact { // DNSSettingsCompact mirrors types.DNSSettings. message DNSSettingsCompact { // Group ids (account_seq_id) whose DNS management is disabled. - repeated uint32 disabled_management_group_ids = 1; + repeated int32 disabled_management_group_ids = 1; } // RouteRaw mirrors *route.Route (the domain type), trimmed to fields that @@ -1125,7 +1118,7 @@ message DNSSettingsCompact { // NetworkMapComponentsFull.peers. message RouteRaw { // Per-account integer id (matches routes.account_seq_id). - uint32 id = 1; + int32 id = 1; string net_id = 2; string description = 3; @@ -1146,30 +1139,28 @@ message RouteRaw { // path will substitute the WG key downstream. bool peer_index_set = 7; uint32 peer_index = 8; - repeated uint32 peer_group_ids = 9; + repeated int32 peer_group_ids = 9; int32 network_type = 10; bool masquerade = 11; int32 metric = 12; bool enabled = 13; - repeated uint32 group_ids = 14; - repeated uint32 access_control_group_ids = 15; + repeated int32 group_ids = 14; + repeated int32 access_control_group_ids = 15; bool skip_auto_apply = 16; - - reserved 17; // was: xid (string) } // NameServerGroupRaw mirrors *nbdns.NameServerGroup. Distinct from the // legacy NameServerGroup (which is the wire-trimmed shape consumed by // proto.DNSConfig and lacks the Name/Description/Groups/Enabled fields). message NameServerGroupRaw { - uint32 id = 1; // nameserver_groups.account_seq_id + int32 id = 1; // nameserver_groups.account_seq_id string name = 2; string description = 3; // Reuses the legacy NameServer wire shape (IP as string). repeated NameServer nameservers = 4; // Group ids (account_seq_id) the NSG distributes nameservers to. - repeated uint32 group_ids = 5; + repeated int32 group_ids = 5; bool primary = 6; repeated string domains = 7; bool enabled = 8; @@ -1184,8 +1175,8 @@ message NameServerGroupRaw { // carries the same regenerated descriptor. Do NOT reuse this pattern once // cap=3 ships. message NetworkResourceRaw { - uint32 id = 1; // network_resources.account_seq_id - uint32 network_seq = 2; // networks.account_seq_id (replaces xid) + int32 id = 1; // network_resources.account_seq_id + int32 network_seq = 2; // networks.account_seq_id (replaces xid) string name = 3; string description = 4; // Resource type: "host" / "subnet" / "domain". @@ -1194,7 +1185,6 @@ message NetworkResourceRaw { string domain_value = 7; // resource.Domain string prefix_cidr = 8; bool enabled = 9; - reserved 10; // was: xid (string) } // NetworkRouterList carries the routers backing one network. @@ -1206,18 +1196,17 @@ message NetworkRouterList { // NetworkRouterEntry mirrors a single *routerTypes.NetworkRouter; the routing // peer is referenced by index into NetworkMapComponentsFull.peers. message NetworkRouterEntry { - uint32 id = 1; // network_routers.account_seq_id + int32 id = 1; // network_routers.account_seq_id uint32 peer_index = 2; bool peer_index_set = 3; - repeated uint32 peer_group_ids = 4; + repeated int32 peer_group_ids = 4; bool masquerade = 5; int32 metric = 6; bool enabled = 7; } -// PolicyIndexes is a list of indexes into NetworkMapComponentsFull.policies. -message PolicyIndexes { - repeated uint32 indexes = 1; +message PolicyIds { + repeated int32 ids = 1; } // UserIDList is a list of user ids — used as the value type in diff --git a/shared/management/types/group.go b/shared/management/types/group.go index dd1526a743c..8d47b5f037d 100644 --- a/shared/management/types/group.go +++ b/shared/management/types/group.go @@ -21,7 +21,7 @@ type Group struct { // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_groups_account_seq_id;not null;default:0"` + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` // Name visible in the UI Name string diff --git a/shared/management/types/networkmap_components.go b/shared/management/types/networkmap_components.go index a8772d005d0..4d622b05495 100644 --- a/shared/management/types/networkmap_components.go +++ b/shared/management/types/networkmap_components.go @@ -47,12 +47,12 @@ type NetworkMapComponents struct { // account-side component builder; consumed by the envelope encoder to // translate RoutersMap keys and NetworkResource.NetworkID references // to compact uint32 ids. Legacy Calculate() doesn't consult it. - NetworkXIDToSeq map[string]uint32 + NetworkXIDToSeq map[string]int32 // PostureCheckXIDToSeq maps posture.Checks.ID (xid) → AccountSeqID. // Same role as NetworkXIDToSeq, used for PostureFailedPeers keys and // policy SourcePostureChecks references. - PostureCheckXIDToSeq map[string]uint32 + PostureCheckXIDToSeq map[string]int32 } type AccountSettingsInfo struct { diff --git a/shared/management/types/policy.go b/shared/management/types/policy.go index fdda30b6675..425f0ecdd89 100644 --- a/shared/management/types/policy.go +++ b/shared/management/types/policy.go @@ -61,7 +61,7 @@ type Policy struct { // AccountSeqID is a per-account monotonically increasing identifier used as the // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID uint32 `json:"-" gorm:"index:idx_policies_account_seq_id;not null;default:0"` + AccountSeqID int32 `json:"-" gorm:"not null;default:0"` // Name of the Policy Name string From 7cde2c46e64c5ab06d8b47b9e67933df50ac6e56 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Wed, 8 Jul 2026 21:47:54 +0200 Subject: [PATCH 21/42] regenerated sql store mock Signed-off-by: Dmitri Dolguikh --- management/server/store/store_mock.go | 865 ++++++++++++++---- .../server/store/store_mock_agentnetwork.go | 495 ---------- 2 files changed, 675 insertions(+), 685 deletions(-) delete mode 100644 management/server/store/store_mock_agentnetwork.go diff --git a/management/server/store/store_mock.go b/management/server/store/store_mock.go index 5cfc7a2eef9..2416432eecb 100644 --- a/management/server/store/store_mock.go +++ b/management/server/store/store_mock.go @@ -13,18 +13,19 @@ import ( gomock "github.com/golang/mock/gomock" dns "github.com/netbirdio/netbird/dns" + types "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" accesslogs "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/accesslogs" domain "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domain" proxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/proxy" service "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service" zones "github.com/netbirdio/netbird/management/internals/modules/zones" records "github.com/netbirdio/netbird/management/internals/modules/zones/records" - types "github.com/netbirdio/netbird/management/server/networks/resources/types" - types0 "github.com/netbirdio/netbird/management/server/networks/routers/types" - types1 "github.com/netbirdio/netbird/management/server/networks/types" + types0 "github.com/netbirdio/netbird/management/server/networks/resources/types" + types1 "github.com/netbirdio/netbird/management/server/networks/routers/types" + types2 "github.com/netbirdio/netbird/management/server/networks/types" peer "github.com/netbirdio/netbird/management/server/peer" posture "github.com/netbirdio/netbird/management/server/posture" - types2 "github.com/netbirdio/netbird/management/server/types" + types3 "github.com/netbirdio/netbird/management/server/types" route "github.com/netbirdio/netbird/route" crypt "github.com/netbirdio/netbird/util/crypt" ) @@ -124,7 +125,7 @@ func (mr *MockStoreMockRecorder) AddPeerToGroup(ctx, accountID, peerId, groupID } // AddResourceToGroup mocks base method. -func (m *MockStore) AddResourceToGroup(ctx context.Context, accountId, groupID string, resource *types2.Resource) error { +func (m *MockStore) AddResourceToGroup(ctx context.Context, accountId, groupID string, resource *types3.Resource) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddResourceToGroup", ctx, accountId, groupID, resource) ret0, _ := ret[0].(error) @@ -137,6 +138,21 @@ func (mr *MockStoreMockRecorder) AddResourceToGroup(ctx, accountId, groupID, res return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResourceToGroup", reflect.TypeOf((*MockStore)(nil).AddResourceToGroup), ctx, accountId, groupID, resource) } +// AllocateAccountSeqID mocks base method. +func (m *MockStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types3.AccountSeqEntity) (int32, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllocateAccountSeqID", ctx, accountID, entity) + ret0, _ := ret[0].(int32) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AllocateAccountSeqID indicates an expected call of AllocateAccountSeqID. +func (mr *MockStoreMockRecorder) AllocateAccountSeqID(ctx, accountID, entity interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocateAccountSeqID", reflect.TypeOf((*MockStore)(nil).AllocateAccountSeqID), ctx, accountID, entity) +} + // ApproveAccountPeers mocks base method. func (m *MockStore) ApproveAccountPeers(ctx context.Context, accountID string) (int, error) { m.ctrl.T.Helper() @@ -181,7 +197,7 @@ func (mr *MockStoreMockRecorder) Close(ctx interface{}) *gomock.Call { } // CompletePeerJob mocks base method. -func (m *MockStore) CompletePeerJob(ctx context.Context, job *types2.Job) error { +func (m *MockStore) CompletePeerJob(ctx context.Context, job *types3.Job) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompletePeerJob", ctx, job) ret0, _ := ret[0].(error) @@ -253,6 +269,34 @@ func (mr *MockStoreMockRecorder) CreateAccessLog(ctx, log interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccessLog", reflect.TypeOf((*MockStore)(nil).CreateAccessLog), ctx, log) } +// CreateAgentNetworkAccessLog mocks base method. +func (m *MockStore) CreateAgentNetworkAccessLog(ctx context.Context, entry *types.AgentNetworkAccessLog, groups []types.AgentNetworkAccessLogGroup) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateAgentNetworkAccessLog", ctx, entry, groups) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateAgentNetworkAccessLog indicates an expected call of CreateAgentNetworkAccessLog. +func (mr *MockStoreMockRecorder) CreateAgentNetworkAccessLog(ctx, entry, groups interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkAccessLog", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkAccessLog), ctx, entry, groups) +} + +// CreateAgentNetworkUsage mocks base method. +func (m *MockStore) CreateAgentNetworkUsage(ctx context.Context, usage *types.AgentNetworkUsage, groups []types.AgentNetworkUsageGroup) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateAgentNetworkUsage", ctx, usage, groups) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateAgentNetworkUsage indicates an expected call of CreateAgentNetworkUsage. +func (mr *MockStoreMockRecorder) CreateAgentNetworkUsage(ctx, usage, groups interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkUsage", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkUsage), ctx, usage, groups) +} + // CreateCustomDomain mocks base method. func (m *MockStore) CreateCustomDomain(ctx context.Context, accountID, domainName, targetCluster string, validated bool) (*domain.Domain, error) { m.ctrl.T.Helper() @@ -283,7 +327,7 @@ func (mr *MockStoreMockRecorder) CreateDNSRecord(ctx, record interface{}) *gomoc } // CreateGroup mocks base method. -func (m *MockStore) CreateGroup(ctx context.Context, group *types2.Group) error { +func (m *MockStore) CreateGroup(ctx context.Context, group *types3.Group) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateGroup", ctx, group) ret0, _ := ret[0].(error) @@ -297,7 +341,7 @@ func (mr *MockStoreMockRecorder) CreateGroup(ctx, group interface{}) *gomock.Cal } // CreateGroups mocks base method. -func (m *MockStore) CreateGroups(ctx context.Context, accountID string, groups []*types2.Group) error { +func (m *MockStore) CreateGroups(ctx context.Context, accountID string, groups []*types3.Group) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateGroups", ctx, accountID, groups) ret0, _ := ret[0].(error) @@ -311,7 +355,7 @@ func (mr *MockStoreMockRecorder) CreateGroups(ctx, accountID, groups interface{} } // CreateNetworkRouter mocks base method. -func (m *MockStore) CreateNetworkRouter(ctx context.Context, router *types0.NetworkRouter) error { +func (m *MockStore) CreateNetworkRouter(ctx context.Context, router *types1.NetworkRouter) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateNetworkRouter", ctx, router) ret0, _ := ret[0].(error) @@ -325,7 +369,7 @@ func (mr *MockStoreMockRecorder) CreateNetworkRouter(ctx, router interface{}) *g } // CreatePeerJob mocks base method. -func (m *MockStore) CreatePeerJob(ctx context.Context, job *types2.Job) error { +func (m *MockStore) CreatePeerJob(ctx context.Context, job *types3.Job) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreatePeerJob", ctx, job) ret0, _ := ret[0].(error) @@ -339,7 +383,7 @@ func (mr *MockStoreMockRecorder) CreatePeerJob(ctx, job interface{}) *gomock.Cal } // CreatePolicy mocks base method. -func (m *MockStore) CreatePolicy(ctx context.Context, policy *types2.Policy) error { +func (m *MockStore) CreatePolicy(ctx context.Context, policy *types3.Policy) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreatePolicy", ctx, policy) ret0, _ := ret[0].(error) @@ -381,7 +425,7 @@ func (mr *MockStoreMockRecorder) CreateZone(ctx, zone interface{}) *gomock.Call } // DeleteAccount mocks base method. -func (m *MockStore) DeleteAccount(ctx context.Context, account *types2.Account) error { +func (m *MockStore) DeleteAccount(ctx context.Context, account *types3.Account) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteAccount", ctx, account) ret0, _ := ret[0].(error) @@ -408,6 +452,62 @@ func (mr *MockStoreMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accou return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockStore)(nil).DeleteAccountCluster), ctx, clusterAddress, accountID) } +// DeleteAgentNetworkBudgetRule mocks base method. +func (m *MockStore) DeleteAgentNetworkBudgetRule(ctx context.Context, accountID, ruleID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAgentNetworkBudgetRule", ctx, accountID, ruleID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAgentNetworkBudgetRule indicates an expected call of DeleteAgentNetworkBudgetRule. +func (mr *MockStoreMockRecorder) DeleteAgentNetworkBudgetRule(ctx, accountID, ruleID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkBudgetRule", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkBudgetRule), ctx, accountID, ruleID) +} + +// DeleteAgentNetworkGuardrail mocks base method. +func (m *MockStore) DeleteAgentNetworkGuardrail(ctx context.Context, accountID, guardrailID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAgentNetworkGuardrail", ctx, accountID, guardrailID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAgentNetworkGuardrail indicates an expected call of DeleteAgentNetworkGuardrail. +func (mr *MockStoreMockRecorder) DeleteAgentNetworkGuardrail(ctx, accountID, guardrailID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkGuardrail", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkGuardrail), ctx, accountID, guardrailID) +} + +// DeleteAgentNetworkPolicy mocks base method. +func (m *MockStore) DeleteAgentNetworkPolicy(ctx context.Context, accountID, policyID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAgentNetworkPolicy", ctx, accountID, policyID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAgentNetworkPolicy indicates an expected call of DeleteAgentNetworkPolicy. +func (mr *MockStoreMockRecorder) DeleteAgentNetworkPolicy(ctx, accountID, policyID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkPolicy", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkPolicy), ctx, accountID, policyID) +} + +// DeleteAgentNetworkProvider mocks base method. +func (m *MockStore) DeleteAgentNetworkProvider(ctx context.Context, accountID, providerID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAgentNetworkProvider", ctx, accountID, providerID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAgentNetworkProvider indicates an expected call of DeleteAgentNetworkProvider. +func (mr *MockStoreMockRecorder) DeleteAgentNetworkProvider(ctx, accountID, providerID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkProvider", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkProvider), ctx, accountID, providerID) +} + // DeleteCustomDomain mocks base method. func (m *MockStore) DeleteCustomDomain(ctx context.Context, accountID, domainID string) error { m.ctrl.T.Helper() @@ -549,6 +649,21 @@ func (mr *MockStoreMockRecorder) DeleteOldAccessLogs(ctx, olderThan interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldAccessLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldAccessLogs), ctx, olderThan) } +// DeleteOldAgentNetworkAccessLogs mocks base method. +func (m *MockStore) DeleteOldAgentNetworkAccessLogs(ctx context.Context, accountID string, olderThan time.Time) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteOldAgentNetworkAccessLogs", ctx, accountID, olderThan) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteOldAgentNetworkAccessLogs indicates an expected call of DeleteOldAgentNetworkAccessLogs. +func (mr *MockStoreMockRecorder) DeleteOldAgentNetworkAccessLogs(ctx, accountID, olderThan interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldAgentNetworkAccessLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldAgentNetworkAccessLogs), ctx, accountID, olderThan) +} + // DeletePAT mocks base method. func (m *MockStore) DeletePAT(ctx context.Context, userID, patID string) error { m.ctrl.T.Helper() @@ -774,21 +889,6 @@ func (mr *MockStoreMockRecorder) EphemeralServiceExists(ctx, lockStrength, accou return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EphemeralServiceExists", reflect.TypeOf((*MockStore)(nil).EphemeralServiceExists), ctx, lockStrength, accountID, peerID, domain) } -// AllocateAccountSeqID mocks base method. -func (m *MockStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types2.AccountSeqEntity) (uint32, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AllocateAccountSeqID", ctx, accountID, entity) - ret0, _ := ret[0].(uint32) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AllocateAccountSeqID indicates an expected call of AllocateAccountSeqID. -func (mr *MockStoreMockRecorder) AllocateAccountSeqID(ctx, accountID, entity interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocateAccountSeqID", reflect.TypeOf((*MockStore)(nil).AllocateAccountSeqID), ctx, accountID, entity) -} - // ExecuteInTransaction mocks base method. func (m *MockStore) ExecuteInTransaction(ctx context.Context, f func(Store) error) error { m.ctrl.T.Helper() @@ -804,10 +904,10 @@ func (mr *MockStoreMockRecorder) ExecuteInTransaction(ctx, f interface{}) *gomoc } // GetAccount mocks base method. -func (m *MockStore) GetAccount(ctx context.Context, accountID string) (*types2.Account, error) { +func (m *MockStore) GetAccount(ctx context.Context, accountID string) (*types3.Account, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccount", ctx, accountID) - ret0, _ := ret[0].(*types2.Account) + ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -834,11 +934,71 @@ func (mr *MockStoreMockRecorder) GetAccountAccessLogs(ctx, lockStrength, account return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAccessLogs", reflect.TypeOf((*MockStore)(nil).GetAccountAccessLogs), ctx, lockStrength, accountID, filter) } +// GetAccountAgentNetworkBudgetRules mocks base method. +func (m *MockStore) GetAccountAgentNetworkBudgetRules(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types.AccountBudgetRule, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountAgentNetworkBudgetRules", ctx, lockStrength, accountID) + ret0, _ := ret[0].([]*types.AccountBudgetRule) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountAgentNetworkBudgetRules indicates an expected call of GetAccountAgentNetworkBudgetRules. +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkBudgetRules(ctx, lockStrength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkBudgetRules", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkBudgetRules), ctx, lockStrength, accountID) +} + +// GetAccountAgentNetworkGuardrails mocks base method. +func (m *MockStore) GetAccountAgentNetworkGuardrails(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types.Guardrail, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountAgentNetworkGuardrails", ctx, lockStrength, accountID) + ret0, _ := ret[0].([]*types.Guardrail) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountAgentNetworkGuardrails indicates an expected call of GetAccountAgentNetworkGuardrails. +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkGuardrails(ctx, lockStrength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkGuardrails", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkGuardrails), ctx, lockStrength, accountID) +} + +// GetAccountAgentNetworkPolicies mocks base method. +func (m *MockStore) GetAccountAgentNetworkPolicies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types.Policy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountAgentNetworkPolicies", ctx, lockStrength, accountID) + ret0, _ := ret[0].([]*types.Policy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountAgentNetworkPolicies indicates an expected call of GetAccountAgentNetworkPolicies. +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkPolicies(ctx, lockStrength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkPolicies", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkPolicies), ctx, lockStrength, accountID) +} + +// GetAccountAgentNetworkProviders mocks base method. +func (m *MockStore) GetAccountAgentNetworkProviders(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types.Provider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountAgentNetworkProviders", ctx, lockStrength, accountID) + ret0, _ := ret[0].([]*types.Provider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountAgentNetworkProviders indicates an expected call of GetAccountAgentNetworkProviders. +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkProviders(ctx, lockStrength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkProviders", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkProviders), ctx, lockStrength, accountID) +} + // GetAccountByPeerID mocks base method. -func (m *MockStore) GetAccountByPeerID(ctx context.Context, peerID string) (*types2.Account, error) { +func (m *MockStore) GetAccountByPeerID(ctx context.Context, peerID string) (*types3.Account, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountByPeerID", ctx, peerID) - ret0, _ := ret[0].(*types2.Account) + ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -850,10 +1010,10 @@ func (mr *MockStoreMockRecorder) GetAccountByPeerID(ctx, peerID interface{}) *go } // GetAccountByPeerPubKey mocks base method. -func (m *MockStore) GetAccountByPeerPubKey(ctx context.Context, peerKey string) (*types2.Account, error) { +func (m *MockStore) GetAccountByPeerPubKey(ctx context.Context, peerKey string) (*types3.Account, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountByPeerPubKey", ctx, peerKey) - ret0, _ := ret[0].(*types2.Account) + ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -865,10 +1025,10 @@ func (mr *MockStoreMockRecorder) GetAccountByPeerPubKey(ctx, peerKey interface{} } // GetAccountByPrivateDomain mocks base method. -func (m *MockStore) GetAccountByPrivateDomain(ctx context.Context, domain string) (*types2.Account, error) { +func (m *MockStore) GetAccountByPrivateDomain(ctx context.Context, domain string) (*types3.Account, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountByPrivateDomain", ctx, domain) - ret0, _ := ret[0].(*types2.Account) + ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -880,10 +1040,10 @@ func (mr *MockStoreMockRecorder) GetAccountByPrivateDomain(ctx, domain interface } // GetAccountBySetupKey mocks base method. -func (m *MockStore) GetAccountBySetupKey(ctx context.Context, setupKey string) (*types2.Account, error) { +func (m *MockStore) GetAccountBySetupKey(ctx context.Context, setupKey string) (*types3.Account, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountBySetupKey", ctx, setupKey) - ret0, _ := ret[0].(*types2.Account) + ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -895,10 +1055,10 @@ func (mr *MockStoreMockRecorder) GetAccountBySetupKey(ctx, setupKey interface{}) } // GetAccountByUser mocks base method. -func (m *MockStore) GetAccountByUser(ctx context.Context, userID string) (*types2.Account, error) { +func (m *MockStore) GetAccountByUser(ctx context.Context, userID string) (*types3.Account, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountByUser", ctx, userID) - ret0, _ := ret[0].(*types2.Account) + ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -925,10 +1085,10 @@ func (mr *MockStoreMockRecorder) GetAccountCreatedBy(ctx, lockStrength, accountI } // GetAccountDNSSettings mocks base method. -func (m *MockStore) GetAccountDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*types2.DNSSettings, error) { +func (m *MockStore) GetAccountDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*types3.DNSSettings, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountDNSSettings", ctx, lockStrength, accountID) - ret0, _ := ret[0].(*types2.DNSSettings) + ret0, _ := ret[0].(*types3.DNSSettings) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -971,10 +1131,10 @@ func (mr *MockStoreMockRecorder) GetAccountGroupPeers(ctx, lockStrength, account } // GetAccountGroups mocks base method. -func (m *MockStore) GetAccountGroups(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.Group, error) { +func (m *MockStore) GetAccountGroups(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types3.Group, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountGroups", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types2.Group) + ret0, _ := ret[0].([]*types3.Group) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1061,10 +1221,10 @@ func (mr *MockStoreMockRecorder) GetAccountIDByUserID(ctx, lockStrength, userID } // GetAccountMeta mocks base method. -func (m *MockStore) GetAccountMeta(ctx context.Context, lockStrength LockingStrength, accountID string) (*types2.AccountMeta, error) { +func (m *MockStore) GetAccountMeta(ctx context.Context, lockStrength LockingStrength, accountID string) (*types3.AccountMeta, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountMeta", ctx, lockStrength, accountID) - ret0, _ := ret[0].(*types2.AccountMeta) + ret0, _ := ret[0].(*types3.AccountMeta) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1091,10 +1251,10 @@ func (mr *MockStoreMockRecorder) GetAccountNameServerGroups(ctx, lockStrength, a } // GetAccountNetwork mocks base method. -func (m *MockStore) GetAccountNetwork(ctx context.Context, lockStrength LockingStrength, accountId string) (*types2.Network, error) { +func (m *MockStore) GetAccountNetwork(ctx context.Context, lockStrength LockingStrength, accountId string) (*types3.Network, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountNetwork", ctx, lockStrength, accountId) - ret0, _ := ret[0].(*types2.Network) + ret0, _ := ret[0].(*types3.Network) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1106,10 +1266,10 @@ func (mr *MockStoreMockRecorder) GetAccountNetwork(ctx, lockStrength, accountId } // GetAccountNetworks mocks base method. -func (m *MockStore) GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types1.Network, error) { +func (m *MockStore) GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.Network, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountNetworks", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types1.Network) + ret0, _ := ret[0].([]*types2.Network) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1121,10 +1281,10 @@ func (mr *MockStoreMockRecorder) GetAccountNetworks(ctx, lockStrength, accountID } // GetAccountOnboarding mocks base method. -func (m *MockStore) GetAccountOnboarding(ctx context.Context, accountID string) (*types2.AccountOnboarding, error) { +func (m *MockStore) GetAccountOnboarding(ctx context.Context, accountID string) (*types3.AccountOnboarding, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountOnboarding", ctx, accountID) - ret0, _ := ret[0].(*types2.AccountOnboarding) + ret0, _ := ret[0].(*types3.AccountOnboarding) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1136,10 +1296,10 @@ func (mr *MockStoreMockRecorder) GetAccountOnboarding(ctx, accountID interface{} } // GetAccountOwner mocks base method. -func (m *MockStore) GetAccountOwner(ctx context.Context, lockStrength LockingStrength, accountID string) (*types2.User, error) { +func (m *MockStore) GetAccountOwner(ctx context.Context, lockStrength LockingStrength, accountID string) (*types3.User, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountOwner", ctx, lockStrength, accountID) - ret0, _ := ret[0].(*types2.User) + ret0, _ := ret[0].(*types3.User) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1196,10 +1356,10 @@ func (mr *MockStoreMockRecorder) GetAccountPeersWithInactivity(ctx, lockStrength } // GetAccountPolicies mocks base method. -func (m *MockStore) GetAccountPolicies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.Policy, error) { +func (m *MockStore) GetAccountPolicies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types3.Policy, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountPolicies", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types2.Policy) + ret0, _ := ret[0].([]*types3.Policy) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1256,10 +1416,10 @@ func (mr *MockStoreMockRecorder) GetAccountServices(ctx, lockStrength, accountID } // GetAccountSettings mocks base method. -func (m *MockStore) GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*types2.Settings, error) { +func (m *MockStore) GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*types3.Settings, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountSettings", ctx, lockStrength, accountID) - ret0, _ := ret[0].(*types2.Settings) + ret0, _ := ret[0].(*types3.Settings) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1271,10 +1431,10 @@ func (mr *MockStoreMockRecorder) GetAccountSettings(ctx, lockStrength, accountID } // GetAccountSetupKeys mocks base method. -func (m *MockStore) GetAccountSetupKeys(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.SetupKey, error) { +func (m *MockStore) GetAccountSetupKeys(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types3.SetupKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountSetupKeys", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types2.SetupKey) + ret0, _ := ret[0].([]*types3.SetupKey) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1286,10 +1446,10 @@ func (mr *MockStoreMockRecorder) GetAccountSetupKeys(ctx, lockStrength, accountI } // GetAccountUserInvites mocks base method. -func (m *MockStore) GetAccountUserInvites(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.UserInviteRecord, error) { +func (m *MockStore) GetAccountUserInvites(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types3.UserInviteRecord, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountUserInvites", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types2.UserInviteRecord) + ret0, _ := ret[0].([]*types3.UserInviteRecord) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1301,10 +1461,10 @@ func (mr *MockStoreMockRecorder) GetAccountUserInvites(ctx, lockStrength, accoun } // GetAccountUsers mocks base method. -func (m *MockStore) GetAccountUsers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.User, error) { +func (m *MockStore) GetAccountUsers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types3.User, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccountUsers", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types2.User) + ret0, _ := ret[0].([]*types3.User) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1375,11 +1535,193 @@ func (mr *MockStoreMockRecorder) GetActiveProxyClusterAddressesForAccount(ctx, a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveProxyClusterAddressesForAccount", reflect.TypeOf((*MockStore)(nil).GetActiveProxyClusterAddressesForAccount), ctx, accountID) } +// GetAgentNetworkAccessLogSessions mocks base method. +func (m *MockStore) GetAgentNetworkAccessLogSessions(ctx context.Context, lockStrength LockingStrength, accountID string, filter types.AgentNetworkAccessLogFilter) ([]*types.AgentNetworkAccessLogSession, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkAccessLogSessions", ctx, lockStrength, accountID, filter) + ret0, _ := ret[0].([]*types.AgentNetworkAccessLogSession) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetAgentNetworkAccessLogSessions indicates an expected call of GetAgentNetworkAccessLogSessions. +func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogSessions(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkAccessLogSessions", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkAccessLogSessions), ctx, lockStrength, accountID, filter) +} + +// GetAgentNetworkAccessLogs mocks base method. +func (m *MockStore) GetAgentNetworkAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter types.AgentNetworkAccessLogFilter) ([]*types.AgentNetworkAccessLog, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkAccessLogs", ctx, lockStrength, accountID, filter) + ret0, _ := ret[0].([]*types.AgentNetworkAccessLog) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetAgentNetworkAccessLogs indicates an expected call of GetAgentNetworkAccessLogs. +func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogs(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkAccessLogs", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkAccessLogs), ctx, lockStrength, accountID, filter) +} + +// GetAgentNetworkBudgetRuleByID mocks base method. +func (m *MockStore) GetAgentNetworkBudgetRuleByID(ctx context.Context, lockStrength LockingStrength, accountID, ruleID string) (*types.AccountBudgetRule, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkBudgetRuleByID", ctx, lockStrength, accountID, ruleID) + ret0, _ := ret[0].(*types.AccountBudgetRule) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkBudgetRuleByID indicates an expected call of GetAgentNetworkBudgetRuleByID. +func (mr *MockStoreMockRecorder) GetAgentNetworkBudgetRuleByID(ctx, lockStrength, accountID, ruleID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkBudgetRuleByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkBudgetRuleByID), ctx, lockStrength, accountID, ruleID) +} + +// GetAgentNetworkConsumption mocks base method. +func (m *MockStore) GetAgentNetworkConsumption(ctx context.Context, lockStrength LockingStrength, accountID string, kind types.ConsumptionDimension, dimID string, windowSeconds int64, windowStart time.Time) (*types.Consumption, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkConsumption", ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart) + ret0, _ := ret[0].(*types.Consumption) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkConsumption indicates an expected call of GetAgentNetworkConsumption. +func (mr *MockStoreMockRecorder) GetAgentNetworkConsumption(ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkConsumption), ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart) +} + +// GetAgentNetworkConsumptionBatch mocks base method. +func (m *MockStore) GetAgentNetworkConsumptionBatch(ctx context.Context, lockStrength LockingStrength, accountID string, keys []types.ConsumptionKey) (map[types.ConsumptionKey]*types.Consumption, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkConsumptionBatch", ctx, lockStrength, accountID, keys) + ret0, _ := ret[0].(map[types.ConsumptionKey]*types.Consumption) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkConsumptionBatch indicates an expected call of GetAgentNetworkConsumptionBatch. +func (mr *MockStoreMockRecorder) GetAgentNetworkConsumptionBatch(ctx, lockStrength, accountID, keys interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkConsumptionBatch", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkConsumptionBatch), ctx, lockStrength, accountID, keys) +} + +// GetAgentNetworkGuardrailByID mocks base method. +func (m *MockStore) GetAgentNetworkGuardrailByID(ctx context.Context, lockStrength LockingStrength, accountID, guardrailID string) (*types.Guardrail, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkGuardrailByID", ctx, lockStrength, accountID, guardrailID) + ret0, _ := ret[0].(*types.Guardrail) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkGuardrailByID indicates an expected call of GetAgentNetworkGuardrailByID. +func (mr *MockStoreMockRecorder) GetAgentNetworkGuardrailByID(ctx, lockStrength, accountID, guardrailID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkGuardrailByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkGuardrailByID), ctx, lockStrength, accountID, guardrailID) +} + +// GetAgentNetworkMetrics mocks base method. +func (m *MockStore) GetAgentNetworkMetrics(ctx context.Context) (AgentNetworkMetrics, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkMetrics", ctx) + ret0, _ := ret[0].(AgentNetworkMetrics) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkMetrics indicates an expected call of GetAgentNetworkMetrics. +func (mr *MockStoreMockRecorder) GetAgentNetworkMetrics(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkMetrics", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkMetrics), ctx) +} + +// GetAgentNetworkPolicyByID mocks base method. +func (m *MockStore) GetAgentNetworkPolicyByID(ctx context.Context, lockStrength LockingStrength, accountID, policyID string) (*types.Policy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkPolicyByID", ctx, lockStrength, accountID, policyID) + ret0, _ := ret[0].(*types.Policy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkPolicyByID indicates an expected call of GetAgentNetworkPolicyByID. +func (mr *MockStoreMockRecorder) GetAgentNetworkPolicyByID(ctx, lockStrength, accountID, policyID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkPolicyByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkPolicyByID), ctx, lockStrength, accountID, policyID) +} + +// GetAgentNetworkProviderByID mocks base method. +func (m *MockStore) GetAgentNetworkProviderByID(ctx context.Context, lockStrength LockingStrength, accountID, providerID string) (*types.Provider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkProviderByID", ctx, lockStrength, accountID, providerID) + ret0, _ := ret[0].(*types.Provider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkProviderByID indicates an expected call of GetAgentNetworkProviderByID. +func (mr *MockStoreMockRecorder) GetAgentNetworkProviderByID(ctx, lockStrength, accountID, providerID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkProviderByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkProviderByID), ctx, lockStrength, accountID, providerID) +} + +// GetAgentNetworkSettings mocks base method. +func (m *MockStore) GetAgentNetworkSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*types.Settings, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkSettings", ctx, lockStrength, accountID) + ret0, _ := ret[0].(*types.Settings) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkSettings indicates an expected call of GetAgentNetworkSettings. +func (mr *MockStoreMockRecorder) GetAgentNetworkSettings(ctx, lockStrength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettings), ctx, lockStrength, accountID) +} + +// GetAgentNetworkSettingsByCluster mocks base method. +func (m *MockStore) GetAgentNetworkSettingsByCluster(ctx context.Context, lockStrength LockingStrength, cluster string) ([]*types.Settings, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkSettingsByCluster", ctx, lockStrength, cluster) + ret0, _ := ret[0].([]*types.Settings) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkSettingsByCluster indicates an expected call of GetAgentNetworkSettingsByCluster. +func (mr *MockStoreMockRecorder) GetAgentNetworkSettingsByCluster(ctx, lockStrength, cluster interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettingsByCluster", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettingsByCluster), ctx, lockStrength, cluster) +} + +// GetAgentNetworkUsageRows mocks base method. +func (m *MockStore) GetAgentNetworkUsageRows(ctx context.Context, lockStrength LockingStrength, accountID string, filter types.AgentNetworkAccessLogFilter) ([]*types.AgentNetworkUsage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentNetworkUsageRows", ctx, lockStrength, accountID, filter) + ret0, _ := ret[0].([]*types.AgentNetworkUsage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAgentNetworkUsageRows indicates an expected call of GetAgentNetworkUsageRows. +func (mr *MockStoreMockRecorder) GetAgentNetworkUsageRows(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkUsageRows", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkUsageRows), ctx, lockStrength, accountID, filter) +} + // GetAllAccounts mocks base method. -func (m *MockStore) GetAllAccounts(ctx context.Context) []*types2.Account { +func (m *MockStore) GetAllAccounts(ctx context.Context) []*types3.Account { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllAccounts", ctx) - ret0, _ := ret[0].([]*types2.Account) + ret0, _ := ret[0].([]*types3.Account) return ret0 } @@ -1389,6 +1731,36 @@ func (mr *MockStoreMockRecorder) GetAllAccounts(ctx interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAccounts", reflect.TypeOf((*MockStore)(nil).GetAllAccounts), ctx) } +// GetAllAgentNetworkProviders mocks base method. +func (m *MockStore) GetAllAgentNetworkProviders(ctx context.Context, lockStrength LockingStrength) ([]*types.Provider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllAgentNetworkProviders", ctx, lockStrength) + ret0, _ := ret[0].([]*types.Provider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAllAgentNetworkProviders indicates an expected call of GetAllAgentNetworkProviders. +func (mr *MockStoreMockRecorder) GetAllAgentNetworkProviders(ctx, lockStrength interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAgentNetworkProviders", reflect.TypeOf((*MockStore)(nil).GetAllAgentNetworkProviders), ctx, lockStrength) +} + +// GetAllAgentNetworkSettings mocks base method. +func (m *MockStore) GetAllAgentNetworkSettings(ctx context.Context, lockStrength LockingStrength) ([]*types.Settings, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllAgentNetworkSettings", ctx, lockStrength) + ret0, _ := ret[0].([]*types.Settings) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAllAgentNetworkSettings indicates an expected call of GetAllAgentNetworkSettings. +func (mr *MockStoreMockRecorder) GetAllAgentNetworkSettings(ctx, lockStrength interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).GetAllAgentNetworkSettings), ctx, lockStrength) +} + // GetAllEphemeralPeers mocks base method. func (m *MockStore) GetAllEphemeralPeers(ctx context.Context, lockStrength LockingStrength) ([]*peer.Peer, error) { m.ctrl.T.Helper() @@ -1405,10 +1777,10 @@ func (mr *MockStoreMockRecorder) GetAllEphemeralPeers(ctx, lockStrength interfac } // GetAllProxyAccessTokens mocks base method. -func (m *MockStore) GetAllProxyAccessTokens(ctx context.Context, lockStrength LockingStrength) ([]*types2.ProxyAccessToken, error) { +func (m *MockStore) GetAllProxyAccessTokens(ctx context.Context, lockStrength LockingStrength) ([]*types3.ProxyAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllProxyAccessTokens", ctx, lockStrength) - ret0, _ := ret[0].([]*types2.ProxyAccessToken) + ret0, _ := ret[0].([]*types3.ProxyAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1536,6 +1908,21 @@ func (mr *MockStoreMockRecorder) GetDNSRecordByID(ctx, lockStrength, accountID, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDNSRecordByID", reflect.TypeOf((*MockStore)(nil).GetDNSRecordByID), ctx, lockStrength, accountID, zoneID, recordID) } +// GetEmbeddedProxyPeerIDsByCluster mocks base method. +func (m *MockStore) GetEmbeddedProxyPeerIDsByCluster(ctx context.Context, accountID string) (map[string][]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEmbeddedProxyPeerIDsByCluster", ctx, accountID) + ret0, _ := ret[0].(map[string][]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEmbeddedProxyPeerIDsByCluster indicates an expected call of GetEmbeddedProxyPeerIDsByCluster. +func (mr *MockStoreMockRecorder) GetEmbeddedProxyPeerIDsByCluster(ctx, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEmbeddedProxyPeerIDsByCluster", reflect.TypeOf((*MockStore)(nil).GetEmbeddedProxyPeerIDsByCluster), ctx, accountID) +} + // GetExpiredEphemeralServices mocks base method. func (m *MockStore) GetExpiredEphemeralServices(ctx context.Context, ttl time.Duration, limit int) ([]*service.Service, error) { m.ctrl.T.Helper() @@ -1552,10 +1939,10 @@ func (mr *MockStoreMockRecorder) GetExpiredEphemeralServices(ctx, ttl, limit int } // GetGroupByID mocks base method. -func (m *MockStore) GetGroupByID(ctx context.Context, lockStrength LockingStrength, accountID, groupID string) (*types2.Group, error) { +func (m *MockStore) GetGroupByID(ctx context.Context, lockStrength LockingStrength, accountID, groupID string) (*types3.Group, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetGroupByID", ctx, lockStrength, accountID, groupID) - ret0, _ := ret[0].(*types2.Group) + ret0, _ := ret[0].(*types3.Group) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1567,10 +1954,10 @@ func (mr *MockStoreMockRecorder) GetGroupByID(ctx, lockStrength, accountID, grou } // GetGroupByName mocks base method. -func (m *MockStore) GetGroupByName(ctx context.Context, lockStrength LockingStrength, accountID, groupName string) (*types2.Group, error) { +func (m *MockStore) GetGroupByName(ctx context.Context, lockStrength LockingStrength, accountID, groupName string) (*types3.Group, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetGroupByName", ctx, lockStrength, accountID, groupName) - ret0, _ := ret[0].(*types2.Group) + ret0, _ := ret[0].(*types3.Group) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1581,11 +1968,26 @@ func (mr *MockStoreMockRecorder) GetGroupByName(ctx, lockStrength, accountID, gr return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupByName", reflect.TypeOf((*MockStore)(nil).GetGroupByName), ctx, lockStrength, accountID, groupName) } +// GetGroupIDsByPeerIDs mocks base method. +func (m *MockStore) GetGroupIDsByPeerIDs(ctx context.Context, accountID string, peerIDs []string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGroupIDsByPeerIDs", ctx, accountID, peerIDs) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetGroupIDsByPeerIDs indicates an expected call of GetGroupIDsByPeerIDs. +func (mr *MockStoreMockRecorder) GetGroupIDsByPeerIDs(ctx, accountID, peerIDs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupIDsByPeerIDs", reflect.TypeOf((*MockStore)(nil).GetGroupIDsByPeerIDs), ctx, accountID, peerIDs) +} + // GetGroupsByIDs mocks base method. -func (m *MockStore) GetGroupsByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, groupIDs []string) (map[string]*types2.Group, error) { +func (m *MockStore) GetGroupsByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, groupIDs []string) (map[string]*types3.Group, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetGroupsByIDs", ctx, lockStrength, accountID, groupIDs) - ret0, _ := ret[0].(map[string]*types2.Group) + ret0, _ := ret[0].(map[string]*types3.Group) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1626,10 +2028,10 @@ func (mr *MockStoreMockRecorder) GetNameServerGroupByID(ctx, lockStrength, nameS } // GetNetworkByID mocks base method. -func (m *MockStore) GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*types1.Network, error) { +func (m *MockStore) GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*types2.Network, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkByID", ctx, lockStrength, accountID, networkID) - ret0, _ := ret[0].(*types1.Network) + ret0, _ := ret[0].(*types2.Network) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1641,10 +2043,10 @@ func (mr *MockStoreMockRecorder) GetNetworkByID(ctx, lockStrength, accountID, ne } // GetNetworkResourceByID mocks base method. -func (m *MockStore) GetNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*types.NetworkResource, error) { +func (m *MockStore) GetNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*types0.NetworkResource, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkResourceByID", ctx, lockStrength, accountID, resourceID) - ret0, _ := ret[0].(*types.NetworkResource) + ret0, _ := ret[0].(*types0.NetworkResource) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1656,10 +2058,10 @@ func (mr *MockStoreMockRecorder) GetNetworkResourceByID(ctx, lockStrength, accou } // GetNetworkResourceByName mocks base method. -func (m *MockStore) GetNetworkResourceByName(ctx context.Context, lockStrength LockingStrength, accountID, resourceName string) (*types.NetworkResource, error) { +func (m *MockStore) GetNetworkResourceByName(ctx context.Context, lockStrength LockingStrength, accountID, resourceName string) (*types0.NetworkResource, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkResourceByName", ctx, lockStrength, accountID, resourceName) - ret0, _ := ret[0].(*types.NetworkResource) + ret0, _ := ret[0].(*types0.NetworkResource) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1671,10 +2073,10 @@ func (mr *MockStoreMockRecorder) GetNetworkResourceByName(ctx, lockStrength, acc } // GetNetworkResourcesByAccountID mocks base method. -func (m *MockStore) GetNetworkResourcesByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types.NetworkResource, error) { +func (m *MockStore) GetNetworkResourcesByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types0.NetworkResource, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkResourcesByAccountID", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types.NetworkResource) + ret0, _ := ret[0].([]*types0.NetworkResource) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1686,10 +2088,10 @@ func (mr *MockStoreMockRecorder) GetNetworkResourcesByAccountID(ctx, lockStrengt } // GetNetworkResourcesByNetID mocks base method. -func (m *MockStore) GetNetworkResourcesByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*types.NetworkResource, error) { +func (m *MockStore) GetNetworkResourcesByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*types0.NetworkResource, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkResourcesByNetID", ctx, lockStrength, accountID, netID) - ret0, _ := ret[0].([]*types.NetworkResource) + ret0, _ := ret[0].([]*types0.NetworkResource) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1701,10 +2103,10 @@ func (mr *MockStoreMockRecorder) GetNetworkResourcesByNetID(ctx, lockStrength, a } // GetNetworkRouterByID mocks base method. -func (m *MockStore) GetNetworkRouterByID(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) (*types0.NetworkRouter, error) { +func (m *MockStore) GetNetworkRouterByID(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) (*types1.NetworkRouter, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkRouterByID", ctx, lockStrength, accountID, routerID) - ret0, _ := ret[0].(*types0.NetworkRouter) + ret0, _ := ret[0].(*types1.NetworkRouter) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1716,10 +2118,10 @@ func (mr *MockStoreMockRecorder) GetNetworkRouterByID(ctx, lockStrength, account } // GetNetworkRoutersByAccountID mocks base method. -func (m *MockStore) GetNetworkRoutersByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types0.NetworkRouter, error) { +func (m *MockStore) GetNetworkRoutersByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types1.NetworkRouter, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkRoutersByAccountID", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types0.NetworkRouter) + ret0, _ := ret[0].([]*types1.NetworkRouter) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1731,10 +2133,10 @@ func (mr *MockStoreMockRecorder) GetNetworkRoutersByAccountID(ctx, lockStrength, } // GetNetworkRoutersByNetID mocks base method. -func (m *MockStore) GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*types0.NetworkRouter, error) { +func (m *MockStore) GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*types1.NetworkRouter, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNetworkRoutersByNetID", ctx, lockStrength, accountID, netID) - ret0, _ := ret[0].([]*types0.NetworkRouter) + ret0, _ := ret[0].([]*types1.NetworkRouter) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1746,10 +2148,10 @@ func (mr *MockStoreMockRecorder) GetNetworkRoutersByNetID(ctx, lockStrength, acc } // GetPATByHashedToken mocks base method. -func (m *MockStore) GetPATByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken string) (*types2.PersonalAccessToken, error) { +func (m *MockStore) GetPATByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken string) (*types3.PersonalAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPATByHashedToken", ctx, lockStrength, hashedToken) - ret0, _ := ret[0].(*types2.PersonalAccessToken) + ret0, _ := ret[0].(*types3.PersonalAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1761,10 +2163,10 @@ func (mr *MockStoreMockRecorder) GetPATByHashedToken(ctx, lockStrength, hashedTo } // GetPATByID mocks base method. -func (m *MockStore) GetPATByID(ctx context.Context, lockStrength LockingStrength, userID, patID string) (*types2.PersonalAccessToken, error) { +func (m *MockStore) GetPATByID(ctx context.Context, lockStrength LockingStrength, userID, patID string) (*types3.PersonalAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPATByID", ctx, lockStrength, userID, patID) - ret0, _ := ret[0].(*types2.PersonalAccessToken) + ret0, _ := ret[0].(*types3.PersonalAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1836,10 +2238,10 @@ func (mr *MockStoreMockRecorder) GetPeerGroupIDs(ctx, lockStrength, accountId, p } // GetPeerGroups mocks base method. -func (m *MockStore) GetPeerGroups(ctx context.Context, lockStrength LockingStrength, accountId, peerId string) ([]*types2.Group, error) { +func (m *MockStore) GetPeerGroups(ctx context.Context, lockStrength LockingStrength, accountId, peerId string) ([]*types3.Group, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPeerGroups", ctx, lockStrength, accountId, peerId) - ret0, _ := ret[0].([]*types2.Group) + ret0, _ := ret[0].([]*types3.Group) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1865,6 +2267,21 @@ func (mr *MockStoreMockRecorder) GetPeerIDByKey(ctx, lockStrength, key interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerIDByKey", reflect.TypeOf((*MockStore)(nil).GetPeerIDByKey), ctx, lockStrength, key) } +// GetPeerIDsByGroups mocks base method. +func (m *MockStore) GetPeerIDsByGroups(ctx context.Context, accountID string, groupIDs []string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPeerIDsByGroups", ctx, accountID, groupIDs) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPeerIDsByGroups indicates an expected call of GetPeerIDsByGroups. +func (mr *MockStoreMockRecorder) GetPeerIDsByGroups(ctx, accountID, groupIDs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerIDsByGroups", reflect.TypeOf((*MockStore)(nil).GetPeerIDsByGroups), ctx, accountID, groupIDs) +} + // GetPeerIdByLabel mocks base method. func (m *MockStore) GetPeerIdByLabel(ctx context.Context, lockStrength LockingStrength, accountID, hostname string) (string, error) { m.ctrl.T.Helper() @@ -1881,10 +2298,10 @@ func (mr *MockStoreMockRecorder) GetPeerIdByLabel(ctx, lockStrength, accountID, } // GetPeerJobByID mocks base method. -func (m *MockStore) GetPeerJobByID(ctx context.Context, accountID, jobID string) (*types2.Job, error) { +func (m *MockStore) GetPeerJobByID(ctx context.Context, accountID, jobID string) (*types3.Job, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPeerJobByID", ctx, accountID, jobID) - ret0, _ := ret[0].(*types2.Job) + ret0, _ := ret[0].(*types3.Job) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1896,10 +2313,10 @@ func (mr *MockStoreMockRecorder) GetPeerJobByID(ctx, accountID, jobID interface{ } // GetPeerJobs mocks base method. -func (m *MockStore) GetPeerJobs(ctx context.Context, accountID, peerID string) ([]*types2.Job, error) { +func (m *MockStore) GetPeerJobs(ctx context.Context, accountID, peerID string) ([]*types3.Job, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPeerJobs", ctx, accountID, peerID) - ret0, _ := ret[0].([]*types2.Job) + ret0, _ := ret[0].([]*types3.Job) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1940,51 +2357,6 @@ func (mr *MockStoreMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupIDs int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeersByGroupIDs", reflect.TypeOf((*MockStore)(nil).GetPeersByGroupIDs), ctx, accountID, groupIDs) } -// GetPeerIDsByGroups mocks base method. -func (m *MockStore) GetPeerIDsByGroups(ctx context.Context, accountID string, groupIDs []string) ([]string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPeerIDsByGroups", ctx, accountID, groupIDs) - ret0, _ := ret[0].([]string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPeerIDsByGroups indicates an expected call of GetPeerIDsByGroups. -func (mr *MockStoreMockRecorder) GetPeerIDsByGroups(ctx, accountID, groupIDs interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerIDsByGroups", reflect.TypeOf((*MockStore)(nil).GetPeerIDsByGroups), ctx, accountID, groupIDs) -} - -// GetGroupIDsByPeerIDs mocks base method. -func (m *MockStore) GetGroupIDsByPeerIDs(ctx context.Context, accountID string, peerIDs []string) ([]string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetGroupIDsByPeerIDs", ctx, accountID, peerIDs) - ret0, _ := ret[0].([]string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetGroupIDsByPeerIDs indicates an expected call of GetGroupIDsByPeerIDs. -func (mr *MockStoreMockRecorder) GetGroupIDsByPeerIDs(ctx, accountID, peerIDs interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupIDsByPeerIDs", reflect.TypeOf((*MockStore)(nil).GetGroupIDsByPeerIDs), ctx, accountID, peerIDs) -} - -// GetEmbeddedProxyPeerIDsByCluster mocks base method. -func (m *MockStore) GetEmbeddedProxyPeerIDsByCluster(ctx context.Context, accountID string) (map[string][]string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetEmbeddedProxyPeerIDsByCluster", ctx, accountID) - ret0, _ := ret[0].(map[string][]string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetEmbeddedProxyPeerIDsByCluster indicates an expected call of GetEmbeddedProxyPeerIDsByCluster. -func (mr *MockStoreMockRecorder) GetEmbeddedProxyPeerIDsByCluster(ctx, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEmbeddedProxyPeerIDsByCluster", reflect.TypeOf((*MockStore)(nil).GetEmbeddedProxyPeerIDsByCluster), ctx, accountID) -} - // GetPeersByIDs mocks base method. func (m *MockStore) GetPeersByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, peerIDs []string) (map[string]*peer.Peer, error) { m.ctrl.T.Helper() @@ -2001,10 +2373,10 @@ func (mr *MockStoreMockRecorder) GetPeersByIDs(ctx, lockStrength, accountID, pee } // GetPolicyByID mocks base method. -func (m *MockStore) GetPolicyByID(ctx context.Context, lockStrength LockingStrength, accountID, policyID string) (*types2.Policy, error) { +func (m *MockStore) GetPolicyByID(ctx context.Context, lockStrength LockingStrength, accountID, policyID string) (*types3.Policy, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPolicyByID", ctx, lockStrength, accountID, policyID) - ret0, _ := ret[0].(*types2.Policy) + ret0, _ := ret[0].(*types3.Policy) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2016,10 +2388,10 @@ func (mr *MockStoreMockRecorder) GetPolicyByID(ctx, lockStrength, accountID, pol } // GetPolicyRulesByResourceID mocks base method. -func (m *MockStore) GetPolicyRulesByResourceID(ctx context.Context, lockStrength LockingStrength, accountID, peerID string) ([]*types2.PolicyRule, error) { +func (m *MockStore) GetPolicyRulesByResourceID(ctx context.Context, lockStrength LockingStrength, accountID, peerID string) ([]*types3.PolicyRule, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPolicyRulesByResourceID", ctx, lockStrength, accountID, peerID) - ret0, _ := ret[0].([]*types2.PolicyRule) + ret0, _ := ret[0].([]*types3.PolicyRule) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2076,10 +2448,10 @@ func (mr *MockStoreMockRecorder) GetPostureChecksByIDs(ctx, lockStrength, accoun } // GetProxyAccessTokenByHashedToken mocks base method. -func (m *MockStore) GetProxyAccessTokenByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken types2.HashedProxyToken) (*types2.ProxyAccessToken, error) { +func (m *MockStore) GetProxyAccessTokenByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken types3.HashedProxyToken) (*types3.ProxyAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetProxyAccessTokenByHashedToken", ctx, lockStrength, hashedToken) - ret0, _ := ret[0].(*types2.ProxyAccessToken) + ret0, _ := ret[0].(*types3.ProxyAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2091,10 +2463,10 @@ func (mr *MockStoreMockRecorder) GetProxyAccessTokenByHashedToken(ctx, lockStren } // GetProxyAccessTokenByID mocks base method. -func (m *MockStore) GetProxyAccessTokenByID(ctx context.Context, lockStrength LockingStrength, tokenID string) (*types2.ProxyAccessToken, error) { +func (m *MockStore) GetProxyAccessTokenByID(ctx context.Context, lockStrength LockingStrength, tokenID string) (*types3.ProxyAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetProxyAccessTokenByID", ctx, lockStrength, tokenID) - ret0, _ := ret[0].(*types2.ProxyAccessToken) + ret0, _ := ret[0].(*types3.ProxyAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2106,10 +2478,10 @@ func (mr *MockStoreMockRecorder) GetProxyAccessTokenByID(ctx, lockStrength, toke } // GetProxyAccessTokensByAccountID mocks base method. -func (m *MockStore) GetProxyAccessTokensByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types2.ProxyAccessToken, error) { +func (m *MockStore) GetProxyAccessTokensByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types3.ProxyAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetProxyAccessTokensByAccountID", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*types2.ProxyAccessToken) + ret0, _ := ret[0].([]*types3.ProxyAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2166,10 +2538,10 @@ func (mr *MockStoreMockRecorder) GetProxyMetrics(ctx interface{}) *gomock.Call { } // GetResourceGroups mocks base method. -func (m *MockStore) GetResourceGroups(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) ([]*types2.Group, error) { +func (m *MockStore) GetResourceGroups(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) ([]*types3.Group, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetResourceGroups", ctx, lockStrength, accountID, resourceID) - ret0, _ := ret[0].([]*types2.Group) + ret0, _ := ret[0].([]*types3.Group) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2301,10 +2673,10 @@ func (mr *MockStoreMockRecorder) GetServicesByClusterAndPort(ctx, lockStrength, } // GetSetupKeyByID mocks base method. -func (m *MockStore) GetSetupKeyByID(ctx context.Context, lockStrength LockingStrength, accountID, setupKeyID string) (*types2.SetupKey, error) { +func (m *MockStore) GetSetupKeyByID(ctx context.Context, lockStrength LockingStrength, accountID, setupKeyID string) (*types3.SetupKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSetupKeyByID", ctx, lockStrength, accountID, setupKeyID) - ret0, _ := ret[0].(*types2.SetupKey) + ret0, _ := ret[0].(*types3.SetupKey) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2316,10 +2688,10 @@ func (mr *MockStoreMockRecorder) GetSetupKeyByID(ctx, lockStrength, accountID, s } // GetSetupKeyBySecret mocks base method. -func (m *MockStore) GetSetupKeyBySecret(ctx context.Context, lockStrength LockingStrength, key string) (*types2.SetupKey, error) { +func (m *MockStore) GetSetupKeyBySecret(ctx context.Context, lockStrength LockingStrength, key string) (*types3.SetupKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSetupKeyBySecret", ctx, lockStrength, key) - ret0, _ := ret[0].(*types2.SetupKey) + ret0, _ := ret[0].(*types3.SetupKey) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2331,10 +2703,10 @@ func (mr *MockStoreMockRecorder) GetSetupKeyBySecret(ctx, lockStrength, key inte } // GetStoreEngine mocks base method. -func (m *MockStore) GetStoreEngine() types2.Engine { +func (m *MockStore) GetStoreEngine() types3.Engine { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStoreEngine") - ret0, _ := ret[0].(types2.Engine) + ret0, _ := ret[0].(types3.Engine) return ret0 } @@ -2390,10 +2762,10 @@ func (mr *MockStoreMockRecorder) GetTokenIDByHashedToken(ctx, secret interface{} } // GetUserByPATID mocks base method. -func (m *MockStore) GetUserByPATID(ctx context.Context, lockStrength LockingStrength, patID string) (*types2.User, error) { +func (m *MockStore) GetUserByPATID(ctx context.Context, lockStrength LockingStrength, patID string) (*types3.User, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserByPATID", ctx, lockStrength, patID) - ret0, _ := ret[0].(*types2.User) + ret0, _ := ret[0].(*types3.User) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2405,10 +2777,10 @@ func (mr *MockStoreMockRecorder) GetUserByPATID(ctx, lockStrength, patID interfa } // GetUserByUserID mocks base method. -func (m *MockStore) GetUserByUserID(ctx context.Context, lockStrength LockingStrength, userID string) (*types2.User, error) { +func (m *MockStore) GetUserByUserID(ctx context.Context, lockStrength LockingStrength, userID string) (*types3.User, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserByUserID", ctx, lockStrength, userID) - ret0, _ := ret[0].(*types2.User) + ret0, _ := ret[0].(*types3.User) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2435,10 +2807,10 @@ func (mr *MockStoreMockRecorder) GetUserIDByPeerKey(ctx, lockStrength, peerKey i } // GetUserInviteByEmail mocks base method. -func (m *MockStore) GetUserInviteByEmail(ctx context.Context, lockStrength LockingStrength, accountID, email string) (*types2.UserInviteRecord, error) { +func (m *MockStore) GetUserInviteByEmail(ctx context.Context, lockStrength LockingStrength, accountID, email string) (*types3.UserInviteRecord, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserInviteByEmail", ctx, lockStrength, accountID, email) - ret0, _ := ret[0].(*types2.UserInviteRecord) + ret0, _ := ret[0].(*types3.UserInviteRecord) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2450,10 +2822,10 @@ func (mr *MockStoreMockRecorder) GetUserInviteByEmail(ctx, lockStrength, account } // GetUserInviteByHashedToken mocks base method. -func (m *MockStore) GetUserInviteByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken string) (*types2.UserInviteRecord, error) { +func (m *MockStore) GetUserInviteByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken string) (*types3.UserInviteRecord, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserInviteByHashedToken", ctx, lockStrength, hashedToken) - ret0, _ := ret[0].(*types2.UserInviteRecord) + ret0, _ := ret[0].(*types3.UserInviteRecord) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2465,10 +2837,10 @@ func (mr *MockStoreMockRecorder) GetUserInviteByHashedToken(ctx, lockStrength, h } // GetUserInviteByID mocks base method. -func (m *MockStore) GetUserInviteByID(ctx context.Context, lockStrength LockingStrength, accountID, inviteID string) (*types2.UserInviteRecord, error) { +func (m *MockStore) GetUserInviteByID(ctx context.Context, lockStrength LockingStrength, accountID, inviteID string) (*types3.UserInviteRecord, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserInviteByID", ctx, lockStrength, accountID, inviteID) - ret0, _ := ret[0].(*types2.UserInviteRecord) + ret0, _ := ret[0].(*types3.UserInviteRecord) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2480,10 +2852,10 @@ func (mr *MockStoreMockRecorder) GetUserInviteByID(ctx, lockStrength, accountID, } // GetUserPATs mocks base method. -func (m *MockStore) GetUserPATs(ctx context.Context, lockStrength LockingStrength, userID string) ([]*types2.PersonalAccessToken, error) { +func (m *MockStore) GetUserPATs(ctx context.Context, lockStrength LockingStrength, userID string) ([]*types3.PersonalAccessToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserPATs", ctx, lockStrength, userID) - ret0, _ := ret[0].([]*types2.PersonalAccessToken) + ret0, _ := ret[0].([]*types3.PersonalAccessToken) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2569,6 +2941,34 @@ func (mr *MockStoreMockRecorder) GetZoneDNSRecordsByName(ctx, lockStrength, acco return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoneDNSRecordsByName", reflect.TypeOf((*MockStore)(nil).GetZoneDNSRecordsByName), ctx, lockStrength, accountID, zoneID, name) } +// IncrementAgentNetworkConsumption mocks base method. +func (m *MockStore) IncrementAgentNetworkConsumption(ctx context.Context, accountID string, kind types.ConsumptionDimension, dimID string, windowSeconds int64, windowStart time.Time, tokensIn, tokensOut int64, costUSD float64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncrementAgentNetworkConsumption", ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD) + ret0, _ := ret[0].(error) + return ret0 +} + +// IncrementAgentNetworkConsumption indicates an expected call of IncrementAgentNetworkConsumption. +func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumption(ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).IncrementAgentNetworkConsumption), ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD) +} + +// IncrementAgentNetworkConsumptionBatch mocks base method. +func (m *MockStore) IncrementAgentNetworkConsumptionBatch(ctx context.Context, accountID string, keys []types.ConsumptionKey, tokensIn, tokensOut int64, costUSD float64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncrementAgentNetworkConsumptionBatch", ctx, accountID, keys, tokensIn, tokensOut, costUSD) + ret0, _ := ret[0].(error) + return ret0 +} + +// IncrementAgentNetworkConsumptionBatch indicates an expected call of IncrementAgentNetworkConsumptionBatch. +func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumptionBatch(ctx, accountID, keys, tokensIn, tokensOut, costUSD interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementAgentNetworkConsumptionBatch", reflect.TypeOf((*MockStore)(nil).IncrementAgentNetworkConsumptionBatch), ctx, accountID, keys, tokensIn, tokensOut, costUSD) +} + // IncrementNetworkSerial mocks base method. func (m *MockStore) IncrementNetworkSerial(ctx context.Context, accountId string) error { m.ctrl.T.Helper() @@ -2643,6 +3043,21 @@ func (mr *MockStoreMockRecorder) IsProxyAccessTokenValid(ctx, tokenID interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsProxyAccessTokenValid", reflect.TypeOf((*MockStore)(nil).IsProxyAccessTokenValid), ctx, tokenID) } +// ListAgentNetworkConsumption mocks base method. +func (m *MockStore) ListAgentNetworkConsumption(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*types.Consumption, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListAgentNetworkConsumption", ctx, lockStrength, accountID) + ret0, _ := ret[0].([]*types.Consumption) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListAgentNetworkConsumption indicates an expected call of ListAgentNetworkConsumption. +func (mr *MockStoreMockRecorder) ListAgentNetworkConsumption(ctx, lockStrength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).ListAgentNetworkConsumption), ctx, lockStrength, accountID) +} + // ListCustomDomains mocks base method. func (m *MockStore) ListCustomDomains(ctx context.Context, accountID string) ([]*domain.Domain, error) { m.ctrl.T.Helper() @@ -2844,7 +3259,7 @@ func (mr *MockStoreMockRecorder) RevokeProxyAccessToken(ctx, tokenID interface{} } // SaveAccount mocks base method. -func (m *MockStore) SaveAccount(ctx context.Context, account *types2.Account) error { +func (m *MockStore) SaveAccount(ctx context.Context, account *types3.Account) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveAccount", ctx, account) ret0, _ := ret[0].(error) @@ -2858,7 +3273,7 @@ func (mr *MockStoreMockRecorder) SaveAccount(ctx, account interface{}) *gomock.C } // SaveAccountOnboarding mocks base method. -func (m *MockStore) SaveAccountOnboarding(ctx context.Context, onboarding *types2.AccountOnboarding) error { +func (m *MockStore) SaveAccountOnboarding(ctx context.Context, onboarding *types3.AccountOnboarding) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveAccountOnboarding", ctx, onboarding) ret0, _ := ret[0].(error) @@ -2872,7 +3287,7 @@ func (mr *MockStoreMockRecorder) SaveAccountOnboarding(ctx, onboarding interface } // SaveAccountSettings mocks base method. -func (m *MockStore) SaveAccountSettings(ctx context.Context, accountID string, settings *types2.Settings) error { +func (m *MockStore) SaveAccountSettings(ctx context.Context, accountID string, settings *types3.Settings) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveAccountSettings", ctx, accountID, settings) ret0, _ := ret[0].(error) @@ -2885,8 +3300,78 @@ func (mr *MockStoreMockRecorder) SaveAccountSettings(ctx, accountID, settings in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAccountSettings", reflect.TypeOf((*MockStore)(nil).SaveAccountSettings), ctx, accountID, settings) } +// SaveAgentNetworkBudgetRule mocks base method. +func (m *MockStore) SaveAgentNetworkBudgetRule(ctx context.Context, rule *types.AccountBudgetRule) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveAgentNetworkBudgetRule", ctx, rule) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveAgentNetworkBudgetRule indicates an expected call of SaveAgentNetworkBudgetRule. +func (mr *MockStoreMockRecorder) SaveAgentNetworkBudgetRule(ctx, rule interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkBudgetRule", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkBudgetRule), ctx, rule) +} + +// SaveAgentNetworkGuardrail mocks base method. +func (m *MockStore) SaveAgentNetworkGuardrail(ctx context.Context, guardrail *types.Guardrail) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveAgentNetworkGuardrail", ctx, guardrail) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveAgentNetworkGuardrail indicates an expected call of SaveAgentNetworkGuardrail. +func (mr *MockStoreMockRecorder) SaveAgentNetworkGuardrail(ctx, guardrail interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkGuardrail", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkGuardrail), ctx, guardrail) +} + +// SaveAgentNetworkPolicy mocks base method. +func (m *MockStore) SaveAgentNetworkPolicy(ctx context.Context, policy *types.Policy) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveAgentNetworkPolicy", ctx, policy) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveAgentNetworkPolicy indicates an expected call of SaveAgentNetworkPolicy. +func (mr *MockStoreMockRecorder) SaveAgentNetworkPolicy(ctx, policy interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkPolicy", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkPolicy), ctx, policy) +} + +// SaveAgentNetworkProvider mocks base method. +func (m *MockStore) SaveAgentNetworkProvider(ctx context.Context, provider *types.Provider) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveAgentNetworkProvider", ctx, provider) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveAgentNetworkProvider indicates an expected call of SaveAgentNetworkProvider. +func (mr *MockStoreMockRecorder) SaveAgentNetworkProvider(ctx, provider interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkProvider", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkProvider), ctx, provider) +} + +// SaveAgentNetworkSettings mocks base method. +func (m *MockStore) SaveAgentNetworkSettings(ctx context.Context, settings *types.Settings) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveAgentNetworkSettings", ctx, settings) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveAgentNetworkSettings indicates an expected call of SaveAgentNetworkSettings. +func (mr *MockStoreMockRecorder) SaveAgentNetworkSettings(ctx, settings interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkSettings), ctx, settings) +} + // SaveDNSSettings mocks base method. -func (m *MockStore) SaveDNSSettings(ctx context.Context, accountID string, settings *types2.DNSSettings) error { +func (m *MockStore) SaveDNSSettings(ctx context.Context, accountID string, settings *types3.DNSSettings) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveDNSSettings", ctx, accountID, settings) ret0, _ := ret[0].(error) @@ -2928,7 +3413,7 @@ func (mr *MockStoreMockRecorder) SaveNameServerGroup(ctx, nameServerGroup interf } // SaveNetwork mocks base method. -func (m *MockStore) SaveNetwork(ctx context.Context, network *types1.Network) error { +func (m *MockStore) SaveNetwork(ctx context.Context, network *types2.Network) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveNetwork", ctx, network) ret0, _ := ret[0].(error) @@ -2942,7 +3427,7 @@ func (mr *MockStoreMockRecorder) SaveNetwork(ctx, network interface{}) *gomock.C } // SaveNetworkResource mocks base method. -func (m *MockStore) SaveNetworkResource(ctx context.Context, resource *types.NetworkResource) error { +func (m *MockStore) SaveNetworkResource(ctx context.Context, resource *types0.NetworkResource) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveNetworkResource", ctx, resource) ret0, _ := ret[0].(error) @@ -2956,7 +3441,7 @@ func (mr *MockStoreMockRecorder) SaveNetworkResource(ctx, resource interface{}) } // SavePAT mocks base method. -func (m *MockStore) SavePAT(ctx context.Context, pat *types2.PersonalAccessToken) error { +func (m *MockStore) SavePAT(ctx context.Context, pat *types3.PersonalAccessToken) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SavePAT", ctx, pat) ret0, _ := ret[0].(error) @@ -2998,7 +3483,7 @@ func (mr *MockStoreMockRecorder) SavePeerStatus(ctx, accountID, peerID, status i } // SavePolicy mocks base method. -func (m *MockStore) SavePolicy(ctx context.Context, policy *types2.Policy) error { +func (m *MockStore) SavePolicy(ctx context.Context, policy *types3.Policy) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SavePolicy", ctx, policy) ret0, _ := ret[0].(error) @@ -3040,7 +3525,7 @@ func (mr *MockStoreMockRecorder) SaveProxy(ctx, proxy interface{}) *gomock.Call } // SaveProxyAccessToken mocks base method. -func (m *MockStore) SaveProxyAccessToken(ctx context.Context, token *types2.ProxyAccessToken) error { +func (m *MockStore) SaveProxyAccessToken(ctx context.Context, token *types3.ProxyAccessToken) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveProxyAccessToken", ctx, token) ret0, _ := ret[0].(error) @@ -3068,7 +3553,7 @@ func (mr *MockStoreMockRecorder) SaveRoute(ctx, route interface{}) *gomock.Call } // SaveSetupKey mocks base method. -func (m *MockStore) SaveSetupKey(ctx context.Context, setupKey *types2.SetupKey) error { +func (m *MockStore) SaveSetupKey(ctx context.Context, setupKey *types3.SetupKey) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveSetupKey", ctx, setupKey) ret0, _ := ret[0].(error) @@ -3082,7 +3567,7 @@ func (mr *MockStoreMockRecorder) SaveSetupKey(ctx, setupKey interface{}) *gomock } // SaveUser mocks base method. -func (m *MockStore) SaveUser(ctx context.Context, user *types2.User) error { +func (m *MockStore) SaveUser(ctx context.Context, user *types3.User) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveUser", ctx, user) ret0, _ := ret[0].(error) @@ -3096,7 +3581,7 @@ func (mr *MockStoreMockRecorder) SaveUser(ctx, user interface{}) *gomock.Call { } // SaveUserInvite mocks base method. -func (m *MockStore) SaveUserInvite(ctx context.Context, invite *types2.UserInviteRecord) error { +func (m *MockStore) SaveUserInvite(ctx context.Context, invite *types3.UserInviteRecord) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveUserInvite", ctx, invite) ret0, _ := ret[0].(error) @@ -3124,7 +3609,7 @@ func (mr *MockStoreMockRecorder) SaveUserLastLogin(ctx, accountID, userID, lastL } // SaveUsers mocks base method. -func (m *MockStore) SaveUsers(ctx context.Context, users []*types2.User) error { +func (m *MockStore) SaveUsers(ctx context.Context, users []*types3.User) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SaveUsers", ctx, users) ret0, _ := ret[0].(error) @@ -3221,7 +3706,7 @@ func (mr *MockStoreMockRecorder) UpdateDNSRecord(ctx, record interface{}) *gomoc } // UpdateGroup mocks base method. -func (m *MockStore) UpdateGroup(ctx context.Context, group *types2.Group) error { +func (m *MockStore) UpdateGroup(ctx context.Context, group *types3.Group) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateGroup", ctx, group) ret0, _ := ret[0].(error) @@ -3235,7 +3720,7 @@ func (mr *MockStoreMockRecorder) UpdateGroup(ctx, group interface{}) *gomock.Cal } // UpdateGroups mocks base method. -func (m *MockStore) UpdateGroups(ctx context.Context, accountID string, groups []*types2.Group) error { +func (m *MockStore) UpdateGroups(ctx context.Context, accountID string, groups []*types3.Group) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateGroups", ctx, accountID, groups) ret0, _ := ret[0].(error) @@ -3249,7 +3734,7 @@ func (mr *MockStoreMockRecorder) UpdateGroups(ctx, accountID, groups interface{} } // UpdateNetworkRouter mocks base method. -func (m *MockStore) UpdateNetworkRouter(ctx context.Context, router *types0.NetworkRouter) error { +func (m *MockStore) UpdateNetworkRouter(ctx context.Context, router *types1.NetworkRouter) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateNetworkRouter", ctx, router) ret0, _ := ret[0].(error) diff --git a/management/server/store/store_mock_agentnetwork.go b/management/server/store/store_mock_agentnetwork.go deleted file mode 100644 index 18adf20f01d..00000000000 --- a/management/server/store/store_mock_agentnetwork.go +++ /dev/null @@ -1,495 +0,0 @@ -package store - -import ( - context "context" - reflect "reflect" - time "time" - - gomock "github.com/golang/mock/gomock" - - agentNetworkTypes "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" -) - -// GetAllAgentNetworkProviders mocks base method. -func (m *MockStore) GetAllAgentNetworkProviders(ctx context.Context, lockStrength LockingStrength) ([]*agentNetworkTypes.Provider, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAllAgentNetworkProviders", ctx, lockStrength) - ret0, _ := ret[0].([]*agentNetworkTypes.Provider) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAllAgentNetworkProviders indicates an expected call of GetAllAgentNetworkProviders. -func (mr *MockStoreMockRecorder) GetAllAgentNetworkProviders(ctx, lockStrength interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAgentNetworkProviders", reflect.TypeOf((*MockStore)(nil).GetAllAgentNetworkProviders), ctx, lockStrength) -} - -// GetAgentNetworkMetrics mocks base method. -func (m *MockStore) GetAgentNetworkMetrics(ctx context.Context) (AgentNetworkMetrics, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkMetrics", ctx) - ret0, _ := ret[0].(AgentNetworkMetrics) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkMetrics indicates an expected call of GetAgentNetworkMetrics. -func (mr *MockStoreMockRecorder) GetAgentNetworkMetrics(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkMetrics", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkMetrics), ctx) -} - -// GetAccountAgentNetworkProviders mocks base method. -func (m *MockStore) GetAccountAgentNetworkProviders(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*agentNetworkTypes.Provider, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountAgentNetworkProviders", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*agentNetworkTypes.Provider) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAccountAgentNetworkProviders indicates an expected call of GetAccountAgentNetworkProviders. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkProviders(ctx, lockStrength, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkProviders", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkProviders), ctx, lockStrength, accountID) -} - -// GetAgentNetworkProviderByID mocks base method. -func (m *MockStore) GetAgentNetworkProviderByID(ctx context.Context, lockStrength LockingStrength, accountID, providerID string) (*agentNetworkTypes.Provider, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkProviderByID", ctx, lockStrength, accountID, providerID) - ret0, _ := ret[0].(*agentNetworkTypes.Provider) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkProviderByID indicates an expected call of GetAgentNetworkProviderByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkProviderByID(ctx, lockStrength, accountID, providerID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkProviderByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkProviderByID), ctx, lockStrength, accountID, providerID) -} - -// SaveAgentNetworkProvider mocks base method. -func (m *MockStore) SaveAgentNetworkProvider(ctx context.Context, provider *agentNetworkTypes.Provider) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveAgentNetworkProvider", ctx, provider) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveAgentNetworkProvider indicates an expected call of SaveAgentNetworkProvider. -func (mr *MockStoreMockRecorder) SaveAgentNetworkProvider(ctx, provider interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkProvider", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkProvider), ctx, provider) -} - -// DeleteAgentNetworkProvider mocks base method. -func (m *MockStore) DeleteAgentNetworkProvider(ctx context.Context, accountID, providerID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAgentNetworkProvider", ctx, accountID, providerID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteAgentNetworkProvider indicates an expected call of DeleteAgentNetworkProvider. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkProvider(ctx, accountID, providerID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkProvider", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkProvider), ctx, accountID, providerID) -} - -// GetAccountAgentNetworkPolicies mocks base method. -func (m *MockStore) GetAccountAgentNetworkPolicies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*agentNetworkTypes.Policy, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountAgentNetworkPolicies", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*agentNetworkTypes.Policy) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAccountAgentNetworkPolicies indicates an expected call of GetAccountAgentNetworkPolicies. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkPolicies(ctx, lockStrength, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkPolicies", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkPolicies), ctx, lockStrength, accountID) -} - -// GetAgentNetworkPolicyByID mocks base method. -func (m *MockStore) GetAgentNetworkPolicyByID(ctx context.Context, lockStrength LockingStrength, accountID, policyID string) (*agentNetworkTypes.Policy, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkPolicyByID", ctx, lockStrength, accountID, policyID) - ret0, _ := ret[0].(*agentNetworkTypes.Policy) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkPolicyByID indicates an expected call of GetAgentNetworkPolicyByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkPolicyByID(ctx, lockStrength, accountID, policyID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkPolicyByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkPolicyByID), ctx, lockStrength, accountID, policyID) -} - -// SaveAgentNetworkPolicy mocks base method. -func (m *MockStore) SaveAgentNetworkPolicy(ctx context.Context, policy *agentNetworkTypes.Policy) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveAgentNetworkPolicy", ctx, policy) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveAgentNetworkPolicy indicates an expected call of SaveAgentNetworkPolicy. -func (mr *MockStoreMockRecorder) SaveAgentNetworkPolicy(ctx, policy interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkPolicy", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkPolicy), ctx, policy) -} - -// DeleteAgentNetworkPolicy mocks base method. -func (m *MockStore) DeleteAgentNetworkPolicy(ctx context.Context, accountID, policyID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAgentNetworkPolicy", ctx, accountID, policyID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteAgentNetworkPolicy indicates an expected call of DeleteAgentNetworkPolicy. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkPolicy(ctx, accountID, policyID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkPolicy", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkPolicy), ctx, accountID, policyID) -} - -// GetAccountAgentNetworkGuardrails mocks base method. -func (m *MockStore) GetAccountAgentNetworkGuardrails(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*agentNetworkTypes.Guardrail, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountAgentNetworkGuardrails", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*agentNetworkTypes.Guardrail) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAccountAgentNetworkGuardrails indicates an expected call of GetAccountAgentNetworkGuardrails. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkGuardrails(ctx, lockStrength, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkGuardrails", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkGuardrails), ctx, lockStrength, accountID) -} - -// GetAgentNetworkGuardrailByID mocks base method. -func (m *MockStore) GetAgentNetworkGuardrailByID(ctx context.Context, lockStrength LockingStrength, accountID, guardrailID string) (*agentNetworkTypes.Guardrail, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkGuardrailByID", ctx, lockStrength, accountID, guardrailID) - ret0, _ := ret[0].(*agentNetworkTypes.Guardrail) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkGuardrailByID indicates an expected call of GetAgentNetworkGuardrailByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkGuardrailByID(ctx, lockStrength, accountID, guardrailID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkGuardrailByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkGuardrailByID), ctx, lockStrength, accountID, guardrailID) -} - -// SaveAgentNetworkGuardrail mocks base method. -func (m *MockStore) SaveAgentNetworkGuardrail(ctx context.Context, guardrail *agentNetworkTypes.Guardrail) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveAgentNetworkGuardrail", ctx, guardrail) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveAgentNetworkGuardrail indicates an expected call of SaveAgentNetworkGuardrail. -func (mr *MockStoreMockRecorder) SaveAgentNetworkGuardrail(ctx, guardrail interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkGuardrail", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkGuardrail), ctx, guardrail) -} - -// DeleteAgentNetworkGuardrail mocks base method. -func (m *MockStore) DeleteAgentNetworkGuardrail(ctx context.Context, accountID, guardrailID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAgentNetworkGuardrail", ctx, accountID, guardrailID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteAgentNetworkGuardrail indicates an expected call of DeleteAgentNetworkGuardrail. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkGuardrail(ctx, accountID, guardrailID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkGuardrail", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkGuardrail), ctx, accountID, guardrailID) -} - -// GetAgentNetworkSettings mocks base method. -func (m *MockStore) GetAgentNetworkSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*agentNetworkTypes.Settings, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkSettings", ctx, lockStrength, accountID) - ret0, _ := ret[0].(*agentNetworkTypes.Settings) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkSettings indicates an expected call of GetAgentNetworkSettings. -func (mr *MockStoreMockRecorder) GetAgentNetworkSettings(ctx, lockStrength, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettings), ctx, lockStrength, accountID) -} - -// GetAgentNetworkSettingsByCluster mocks base method. -func (m *MockStore) GetAgentNetworkSettingsByCluster(ctx context.Context, lockStrength LockingStrength, cluster string) ([]*agentNetworkTypes.Settings, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkSettingsByCluster", ctx, lockStrength, cluster) - ret0, _ := ret[0].([]*agentNetworkTypes.Settings) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkSettingsByCluster indicates an expected call of GetAgentNetworkSettingsByCluster. -func (mr *MockStoreMockRecorder) GetAgentNetworkSettingsByCluster(ctx, lockStrength, cluster interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettingsByCluster", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettingsByCluster), ctx, lockStrength, cluster) -} - -// SaveAgentNetworkSettings mocks base method. -func (m *MockStore) SaveAgentNetworkSettings(ctx context.Context, settings *agentNetworkTypes.Settings) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveAgentNetworkSettings", ctx, settings) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveAgentNetworkSettings indicates an expected call of SaveAgentNetworkSettings. -func (mr *MockStoreMockRecorder) SaveAgentNetworkSettings(ctx, settings interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkSettings), ctx, settings) -} - -// IncrementAgentNetworkConsumption mocks base method. -func (m *MockStore) IncrementAgentNetworkConsumption(ctx context.Context, accountID string, kind agentNetworkTypes.ConsumptionDimension, dimID string, windowSeconds int64, windowStart time.Time, tokensIn, tokensOut int64, costUSD float64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IncrementAgentNetworkConsumption", ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD) - ret0, _ := ret[0].(error) - return ret0 -} - -// IncrementAgentNetworkConsumption indicates an expected call of IncrementAgentNetworkConsumption. -func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumption(ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).IncrementAgentNetworkConsumption), ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD) -} - -// GetAgentNetworkConsumption mocks base method. -func (m *MockStore) GetAgentNetworkConsumption(ctx context.Context, lockStrength LockingStrength, accountID string, kind agentNetworkTypes.ConsumptionDimension, dimID string, windowSeconds int64, windowStart time.Time) (*agentNetworkTypes.Consumption, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkConsumption", ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart) - ret0, _ := ret[0].(*agentNetworkTypes.Consumption) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkConsumption indicates an expected call of GetAgentNetworkConsumption. -func (mr *MockStoreMockRecorder) GetAgentNetworkConsumption(ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkConsumption), ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart) -} - -// GetAgentNetworkConsumptionBatch mocks base method. -func (m *MockStore) GetAgentNetworkConsumptionBatch(ctx context.Context, lockStrength LockingStrength, accountID string, keys []agentNetworkTypes.ConsumptionKey) (map[agentNetworkTypes.ConsumptionKey]*agentNetworkTypes.Consumption, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkConsumptionBatch", ctx, lockStrength, accountID, keys) - ret0, _ := ret[0].(map[agentNetworkTypes.ConsumptionKey]*agentNetworkTypes.Consumption) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkConsumptionBatch indicates an expected call of GetAgentNetworkConsumptionBatch. -func (mr *MockStoreMockRecorder) GetAgentNetworkConsumptionBatch(ctx, lockStrength, accountID, keys interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkConsumptionBatch", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkConsumptionBatch), ctx, lockStrength, accountID, keys) -} - -// IncrementAgentNetworkConsumptionBatch mocks base method. -func (m *MockStore) IncrementAgentNetworkConsumptionBatch(ctx context.Context, accountID string, keys []agentNetworkTypes.ConsumptionKey, tokensIn, tokensOut int64, costUSD float64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IncrementAgentNetworkConsumptionBatch", ctx, accountID, keys, tokensIn, tokensOut, costUSD) - ret0, _ := ret[0].(error) - return ret0 -} - -// IncrementAgentNetworkConsumptionBatch indicates an expected call of IncrementAgentNetworkConsumptionBatch. -func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumptionBatch(ctx, accountID, keys, tokensIn, tokensOut, costUSD interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementAgentNetworkConsumptionBatch", reflect.TypeOf((*MockStore)(nil).IncrementAgentNetworkConsumptionBatch), ctx, accountID, keys, tokensIn, tokensOut, costUSD) -} - -// ListAgentNetworkConsumption mocks base method. -func (m *MockStore) ListAgentNetworkConsumption(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*agentNetworkTypes.Consumption, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAgentNetworkConsumption", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*agentNetworkTypes.Consumption) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListAgentNetworkConsumption indicates an expected call of ListAgentNetworkConsumption. -func (mr *MockStoreMockRecorder) ListAgentNetworkConsumption(ctx, lockStrength, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).ListAgentNetworkConsumption), ctx, lockStrength, accountID) -} - -// GetAccountAgentNetworkBudgetRules mocks base method. -func (m *MockStore) GetAccountAgentNetworkBudgetRules(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*agentNetworkTypes.AccountBudgetRule, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountAgentNetworkBudgetRules", ctx, lockStrength, accountID) - ret0, _ := ret[0].([]*agentNetworkTypes.AccountBudgetRule) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAccountAgentNetworkBudgetRules indicates an expected call of GetAccountAgentNetworkBudgetRules. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkBudgetRules(ctx, lockStrength, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkBudgetRules", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkBudgetRules), ctx, lockStrength, accountID) -} - -// GetAgentNetworkBudgetRuleByID mocks base method. -func (m *MockStore) GetAgentNetworkBudgetRuleByID(ctx context.Context, lockStrength LockingStrength, accountID, ruleID string) (*agentNetworkTypes.AccountBudgetRule, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkBudgetRuleByID", ctx, lockStrength, accountID, ruleID) - ret0, _ := ret[0].(*agentNetworkTypes.AccountBudgetRule) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkBudgetRuleByID indicates an expected call of GetAgentNetworkBudgetRuleByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkBudgetRuleByID(ctx, lockStrength, accountID, ruleID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkBudgetRuleByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkBudgetRuleByID), ctx, lockStrength, accountID, ruleID) -} - -// SaveAgentNetworkBudgetRule mocks base method. -func (m *MockStore) SaveAgentNetworkBudgetRule(ctx context.Context, rule *agentNetworkTypes.AccountBudgetRule) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveAgentNetworkBudgetRule", ctx, rule) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveAgentNetworkBudgetRule indicates an expected call of SaveAgentNetworkBudgetRule. -func (mr *MockStoreMockRecorder) SaveAgentNetworkBudgetRule(ctx, rule interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkBudgetRule", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkBudgetRule), ctx, rule) -} - -// DeleteAgentNetworkBudgetRule mocks base method. -func (m *MockStore) DeleteAgentNetworkBudgetRule(ctx context.Context, accountID, ruleID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAgentNetworkBudgetRule", ctx, accountID, ruleID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteAgentNetworkBudgetRule indicates an expected call of DeleteAgentNetworkBudgetRule. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkBudgetRule(ctx, accountID, ruleID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkBudgetRule", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkBudgetRule), ctx, accountID, ruleID) -} - -// CreateAgentNetworkAccessLog mocks base method. -func (m *MockStore) CreateAgentNetworkAccessLog(ctx context.Context, entry *agentNetworkTypes.AgentNetworkAccessLog, groups []agentNetworkTypes.AgentNetworkAccessLogGroup) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateAgentNetworkAccessLog", ctx, entry, groups) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateAgentNetworkAccessLog indicates an expected call of CreateAgentNetworkAccessLog. -func (mr *MockStoreMockRecorder) CreateAgentNetworkAccessLog(ctx, entry, groups interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkAccessLog", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkAccessLog), ctx, entry, groups) -} - -// CreateAgentNetworkUsage mocks base method. -func (m *MockStore) CreateAgentNetworkUsage(ctx context.Context, usage *agentNetworkTypes.AgentNetworkUsage, groups []agentNetworkTypes.AgentNetworkUsageGroup) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateAgentNetworkUsage", ctx, usage, groups) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateAgentNetworkUsage indicates an expected call of CreateAgentNetworkUsage. -func (mr *MockStoreMockRecorder) CreateAgentNetworkUsage(ctx, usage, groups interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkUsage", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkUsage), ctx, usage, groups) -} - -// GetAgentNetworkAccessLogs mocks base method. -func (m *MockStore) GetAgentNetworkAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter agentNetworkTypes.AgentNetworkAccessLogFilter) ([]*agentNetworkTypes.AgentNetworkAccessLog, int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkAccessLogs", ctx, lockStrength, accountID, filter) - ret0, _ := ret[0].([]*agentNetworkTypes.AgentNetworkAccessLog) - ret1, _ := ret[1].(int64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetAgentNetworkAccessLogs indicates an expected call of GetAgentNetworkAccessLogs. -func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogs(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkAccessLogs", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkAccessLogs), ctx, lockStrength, accountID, filter) -} - -// GetAgentNetworkAccessLogSessions mocks base method. -func (m *MockStore) GetAgentNetworkAccessLogSessions(ctx context.Context, lockStrength LockingStrength, accountID string, filter agentNetworkTypes.AgentNetworkAccessLogFilter) ([]*agentNetworkTypes.AgentNetworkAccessLogSession, int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkAccessLogSessions", ctx, lockStrength, accountID, filter) - ret0, _ := ret[0].([]*agentNetworkTypes.AgentNetworkAccessLogSession) - ret1, _ := ret[1].(int64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetAgentNetworkAccessLogSessions indicates an expected call of GetAgentNetworkAccessLogSessions. -func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogSessions(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkAccessLogSessions", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkAccessLogSessions), ctx, lockStrength, accountID, filter) -} - -// GetAgentNetworkUsageRows mocks base method. -func (m *MockStore) GetAgentNetworkUsageRows(ctx context.Context, lockStrength LockingStrength, accountID string, filter agentNetworkTypes.AgentNetworkAccessLogFilter) ([]*agentNetworkTypes.AgentNetworkUsage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkUsageRows", ctx, lockStrength, accountID, filter) - ret0, _ := ret[0].([]*agentNetworkTypes.AgentNetworkUsage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAgentNetworkUsageRows indicates an expected call of GetAgentNetworkUsageRows. -func (mr *MockStoreMockRecorder) GetAgentNetworkUsageRows(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkUsageRows", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkUsageRows), ctx, lockStrength, accountID, filter) -} - -// DeleteOldAgentNetworkAccessLogs mocks base method. -func (m *MockStore) DeleteOldAgentNetworkAccessLogs(ctx context.Context, accountID string, olderThan time.Time) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteOldAgentNetworkAccessLogs", ctx, accountID, olderThan) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteOldAgentNetworkAccessLogs indicates an expected call of DeleteOldAgentNetworkAccessLogs. -func (mr *MockStoreMockRecorder) DeleteOldAgentNetworkAccessLogs(ctx, accountID, olderThan interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldAgentNetworkAccessLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldAgentNetworkAccessLogs), ctx, accountID, olderThan) -} - -// GetAllAgentNetworkSettings mocks base method. -func (m *MockStore) GetAllAgentNetworkSettings(ctx context.Context, lockStrength LockingStrength) ([]*agentNetworkTypes.Settings, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAllAgentNetworkSettings", ctx, lockStrength) - ret0, _ := ret[0].([]*agentNetworkTypes.Settings) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAllAgentNetworkSettings indicates an expected call of GetAllAgentNetworkSettings. -func (mr *MockStoreMockRecorder) GetAllAgentNetworkSettings(ctx, lockStrength interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).GetAllAgentNetworkSettings), ctx, lockStrength) -} From 7c83c090be00d56f91a7ed35f5fa6da1372c1c25 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Wed, 8 Jul 2026 22:28:03 +0200 Subject: [PATCH 22/42] fixed encoder tests Signed-off-by: Dmitri Dolguikh --- .../shared/grpc/components_encoder_test.go | 118 +++++------------- shared/management/networkmap/envelope.go | 3 +- 2 files changed, 30 insertions(+), 91 deletions(-) diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go index e42675b0339..6b73c883ba0 100644 --- a/management/internals/shared/grpc/components_encoder_test.go +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -41,48 +41,6 @@ func canonicalize(full *proto.NetworkMapComponentsFull) { return } - // Canonicalize agent_versions first: sort the slice and rewrite each - // peer's AgentVersionIdx accordingly. The empty placeholder stays at - // index 0 by convention. - avRemap := make(map[uint32]uint32, len(full.AgentVersions)) - if len(full.AgentVersions) > 0 { - // Pair version → original index, sort, rebuild. - type avEntry struct { - version string - oldIdx uint32 - } - entries := make([]avEntry, len(full.AgentVersions)) - for i, v := range full.AgentVersions { - entries[i] = avEntry{version: v, oldIdx: uint32(i)} - } - // Empty stays at 0; sort the rest by string. Tiebreaker on oldIdx - // keeps the canonicalize output stable when two entries compare - // equal (the encoder dedups, but defending against future inputs). - slices.SortFunc(entries, func(a, b avEntry) int { - if a.version == "" && b.version != "" { - return -1 - } - if b.version == "" && a.version != "" { - return 1 - } - if c := cmp.Compare(a.version, b.version); c != 0 { - return c - } - return cmp.Compare(a.oldIdx, b.oldIdx) - }) - newVersions := make([]string, len(entries)) - for newIdx, e := range entries { - avRemap[e.oldIdx] = uint32(newIdx) - newVersions[newIdx] = e.version - } - full.AgentVersions = newVersions - } - for _, p := range full.Peers { - if newIdx, ok := avRemap[p.AgentVersionIdx]; ok { - p.AgentVersionIdx = newIdx - } - } - type peerEntry struct { peer *proto.PeerCompact oldIdx uint32 @@ -168,7 +126,7 @@ func canonicalize(full *proto.NetworkMapComponentsFull) { policyRemap[policyOldOrder[p]] = uint32(newIdx) } for _, idxs := range full.ResourcePoliciesMap { - idxs.Indexes = remapAndSort(idxs.Indexes, policyRemap) + slices.Sort(idxs.Ids) } for _, list := range full.GroupIdToUserIds { slices.Sort(list.UserIds) @@ -340,12 +298,12 @@ func TestEncodeNetworkMapEnvelope_GroupsByAccountSeqID(t *testing.T) { require.Len(t, full.Groups, 2) - groupByID := map[uint32]*proto.GroupCompact{} + groupByID := map[int32]*proto.GroupCompact{} for _, g := range full.Groups { groupByID[g.Id] = g } - require.Contains(t, groupByID, uint32(1)) - require.Contains(t, groupByID, uint32(2)) + require.Contains(t, groupByID, int32(1)) + require.Contains(t, groupByID, int32(2)) assert.Equal(t, "Src", groupByID[1].Name) assert.Equal(t, "Dst", groupByID[2].Name) assert.Len(t, groupByID[1].PeerIndexes, 1) @@ -367,8 +325,8 @@ func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) { require.Len(t, pc.PortRanges, 1) assert.EqualValues(t, 8000, pc.PortRanges[0].Start) assert.EqualValues(t, 8100, pc.PortRanges[0].End) - assert.Equal(t, []uint32{1}, pc.SourceGroupIds) - assert.Equal(t, []uint32{2}, pc.DestinationGroupIds) + assert.Equal(t, []int32{1}, pc.SourceGroupIds) + assert.Equal(t, []int32{2}, pc.DestinationGroupIds) } func TestEncodeNetworkMapEnvelope_RouterIndexes(t *testing.T) { @@ -382,24 +340,6 @@ func TestEncodeNetworkMapEnvelope_RouterIndexes(t *testing.T) { assert.Equal(t, "peerc", full.Peers[idx].DnsLabel) } -func TestEncodeNetworkMapEnvelope_AgentVersionDedup(t *testing.T) { - c := newTestComponents() - - full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - - require.Len(t, full.AgentVersions, 3, "empty placeholder + 2 distinct versions") - assert.Equal(t, "", full.AgentVersions[0], "index 0 reserved for empty version") - assert.ElementsMatch(t, []string{"0.40.0", "0.25.0"}, full.AgentVersions[1:], - "two distinct versions, order depends on map iteration") - - idxByLabel := map[string]uint32{} - for _, p := range full.Peers { - idxByLabel[p.DnsLabel] = p.AgentVersionIdx - } - assert.Equal(t, idxByLabel["peera"], idxByLabel["peerc"], "peers with the same agent version share an index") - assert.NotEqual(t, idxByLabel["peera"], idxByLabel["peerb"]) -} - func TestEncodeNetworkMapEnvelope_DisabledPolicySkipped(t *testing.T) { c := newTestComponents() c.Policies[0].Enabled = false @@ -421,7 +361,7 @@ func TestEncodeNetworkMapEnvelope_GroupZeroSeqIDSkipped(t *testing.T) { require.Len(t, full.Policies, 1) pc := full.Policies[0] assert.Empty(t, pc.SourceGroupIds, "rule references a group that was filtered out → no group id on wire") - assert.Equal(t, []uint32{2}, pc.DestinationGroupIds) + assert.Equal(t, []int32{2}, pc.DestinationGroupIds) } func TestEncodeNetworkMapEnvelope_TwoPeersSameMalformedKey(t *testing.T) { @@ -594,14 +534,14 @@ func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) { assert.True(t, r1.PeerIndexSet, "route with peer must set peer_index_set") require.Less(t, int(r1.PeerIndex), len(full.Peers)) assert.Equal(t, "peerc", full.Peers[r1.PeerIndex].DnsLabel) - assert.Equal(t, []uint32{1}, r1.GroupIds, "group-src has AccountSeqID 1") - assert.Equal(t, []uint32{2}, r1.AccessControlGroupIds, "group-dst has AccountSeqID 2") + assert.Equal(t, []int32{1}, r1.GroupIds, "group-src has AccountSeqID 1") + assert.Equal(t, []int32{2}, r1.AccessControlGroupIds, "group-dst has AccountSeqID 2") assert.Empty(t, r1.PeerGroupIds) r2 := byNetID["net-B"] require.NotNil(t, r2) assert.False(t, r2.PeerIndexSet, "route with peer_groups must NOT set peer_index_set") - assert.ElementsMatch(t, []uint32{1, 2}, r2.PeerGroupIds) + assert.ElementsMatch(t, []int32{1, 2}, r2.PeerGroupIds) } func TestEncodeNetworkMapEnvelope_RouteWithMissingPeerLeavesIndexUnset(t *testing.T) { @@ -648,19 +588,19 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing require.Len(t, full.Policies, 2, "encoded policies must include both peer-traffic and resource-only") - policyByID := map[uint32]*proto.PolicyCompact{} - policyIdxByID := map[uint32]uint32{} - for i, p := range full.Policies { + policyByID := map[int32]*proto.PolicyCompact{} + policyIds := make([]int32, 0) + for _, p := range full.Policies { policyByID[p.Id] = p - policyIdxByID[p.Id] = uint32(i) + policyIds = append(policyIds, p.Id) } - require.Contains(t, policyByID, uint32(10), "original peer-traffic policy id 10") - require.Contains(t, policyByID, uint32(99), "resource-only policy id 99") + require.Contains(t, policyByID, int32(10), "original peer-traffic policy id 10") + require.Contains(t, policyByID, int32(99), "resource-only policy id 99") - require.Contains(t, full.ResourcePoliciesMap, uint32(77)) - idxs := full.ResourcePoliciesMap[77].Indexes - require.Len(t, idxs, 2) - assert.ElementsMatch(t, []uint32{policyIdxByID[10], policyIdxByID[99]}, idxs, + require.Contains(t, full.ResourcePoliciesMap, int32(77)) + ids := full.ResourcePoliciesMap[77].Ids + require.Len(t, ids, 2) + assert.ElementsMatch(t, policyIds, ids, "resource policies map must reference both wire policy indexes") } @@ -686,12 +626,12 @@ func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) { assert.True(t, nsg.Primary) require.Len(t, nsg.Nameservers, 1) assert.Equal(t, "8.8.8.8", nsg.Nameservers[0].IP) - assert.Equal(t, []uint32{1}, nsg.GroupIds, "group-not-persisted is filtered out (AccountSeqID=0)") + assert.Equal(t, []int32{1}, nsg.GroupIds, "group-not-persisted is filtered out (AccountSeqID=0)") } func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { c := newTestComponents() - c.PostureCheckXIDToSeq = map[string]uint32{"check-1": 33} + c.PostureCheckXIDToSeq = map[string]int32{"check-1": 33} c.PostureFailedPeers = map[string]map[string]struct{}{ "check-1": { "peer-a": {}, @@ -702,14 +642,14 @@ func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.PostureFailedPeers, uint32(33)) + require.Contains(t, full.PostureFailedPeers, int32(33)) idxs := full.PostureFailedPeers[33].PeerIndexes assert.Len(t, idxs, 2, "missing peer is silently dropped (filterPostureFailedPeers guarantees presence in real data)") } func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { c := newTestComponents() - c.NetworkXIDToSeq = map[string]uint32{"net-1": 5} + c.NetworkXIDToSeq = map[string]int32{"net-1": 5} c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ "net-1": { "peer-c": { @@ -721,7 +661,7 @@ func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.RoutersMap, uint32(5)) + require.Contains(t, full.RoutersMap, int32(5)) entries := full.RoutersMap[5].Entries require.Len(t, entries, 1) e := entries[0] @@ -745,14 +685,14 @@ func TestEncodeNetworkMapEnvelope_RouterPeerNotInComponentsPeers(t *testing.T) { DNSLabel: "peerc", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, } c.RouterPeers = map[string]*nbpeer.Peer{"peer-c": routerPeer} - c.NetworkXIDToSeq = map[string]uint32{"net-1": 5} + c.NetworkXIDToSeq = map[string]int32{"net-1": 5} c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ "net-1": {"peer-c": {ID: "r-1", AccountSeqID: 1, Peer: "peer-c", Enabled: true}}, } full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.RoutersMap, uint32(5)) + require.Contains(t, full.RoutersMap, int32(5)) require.Len(t, full.RoutersMap[5].Entries, 1) e := full.RoutersMap[5].Entries[0] assert.True(t, e.PeerIndexSet, "router peer must be indexed even when not in c.Peers") @@ -768,7 +708,7 @@ func TestEncodeNetworkMapEnvelope_DNSSettingsFiltersUnpersistedGroups(t *testing full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() require.NotNil(t, full.DnsSettings) - assert.Equal(t, []uint32{1}, full.DnsSettings.DisabledManagementGroupIds, + assert.Equal(t, []int32{1}, full.DnsSettings.DisabledManagementGroupIds, "only group-src (AccountSeqID=1) survives — missing and unpersisted are dropped") } @@ -784,7 +724,7 @@ func TestEncodeNetworkMapEnvelope_GroupIDToUserIDs(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() require.Len(t, full.GroupIdToUserIds, 1, "only persisted+present groups survive") - require.Contains(t, full.GroupIdToUserIds, uint32(1)) + require.Contains(t, full.GroupIdToUserIds, int32(1)) assert.ElementsMatch(t, []string{"user-1", "user-2"}, full.GroupIdToUserIds[1].UserIds) } diff --git a/shared/management/networkmap/envelope.go b/shared/management/networkmap/envelope.go index 642391e7bf5..3f045a9ebfe 100644 --- a/shared/management/networkmap/envelope.go +++ b/shared/management/networkmap/envelope.go @@ -5,8 +5,8 @@ import ( "encoding/base64" "fmt" - "github.com/netbirdio/netbird/shared/management/types" "github.com/netbirdio/netbird/shared/management/proto" + "github.com/netbirdio/netbird/shared/management/types" ) // EnvelopeResult is what the client engine consumes after receiving a @@ -187,4 +187,3 @@ func canonicalizeWgKey(s string) string { } return base64.StdEncoding.EncodeToString(raw) } - From 9b9f396391cc993fbe5b903516d73cffa2a2ceb9 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Thu, 9 Jul 2026 12:00:17 +0200 Subject: [PATCH 23/42] removed unused func Signed-off-by: Dmitri Dolguikh --- .../shared/grpc/components_encoder.go | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index 9fb85729ca1..68b5612c771 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -174,31 +174,6 @@ func (e *componentEncoder) appendPeer(p *nbpeer.Peer) uint32 { return idx } -func (e *componentEncoder) agentVersionIndex(v string) uint32 { - if idx, ok := e.agentVersionOrder[v]; ok { - return idx - } - // Lazy-initialise the table with "" at index 0 so the empty string - // stays interchangeable with proto3's default uint32=0 — peers without - // a WtVersion don't force the table to materialise. - if v == "" { - idx := uint32(len(e.agentVersions)) - if idx == 0 { - e.agentVersions = append(e.agentVersions, "") - } - e.agentVersionOrder[""] = idx - return idx - } - if len(e.agentVersions) == 0 { - e.agentVersions = append(e.agentVersions, "") - e.agentVersionOrder[""] = 0 - } - idx := uint32(len(e.agentVersions)) - e.agentVersionOrder[v] = idx - e.agentVersions = append(e.agentVersions, v) - return idx -} - // indexRouterPeers ensures every router peer is in the peer dedup table // (c.RouterPeers may contain peers not in c.Peers when validation rules drop // them) and returns their wire indexes for the RouterPeerIndexes field. Must From 5edc168ff1412158fa70b14c8aa2aecab7bc32a8 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Thu, 9 Jul 2026 12:22:29 +0200 Subject: [PATCH 24/42] use policy ids deduping policies during union Signed-off-by: Dmitri Dolguikh --- management/internals/shared/grpc/components_encoder.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index 68b5612c771..f823e79ed52 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -288,16 +288,16 @@ func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*type if len(resourcePolicies) == 0 { return policies } - seen := make(map[*types.Policy]struct{}, len(policies)) + seen := make(map[string]struct{}, len(policies)) out := make([]*types.Policy, 0, len(policies)) for _, p := range policies { if p == nil { continue } - if _, ok := seen[p]; ok { + if _, ok := seen[p.ID]; ok { continue } - seen[p] = struct{}{} + seen[p.ID] = struct{}{} out = append(out, p) } for _, list := range resourcePolicies { @@ -305,10 +305,10 @@ func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*type if p == nil { continue } - if _, ok := seen[p]; ok { + if _, ok := seen[p.ID]; ok { continue } - seen[p] = struct{}{} + seen[p.ID] = struct{}{} out = append(out, p) } } From 7cae1b9dd198bdcd0c5d831e736a3230cc7c6f4c Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Thu, 9 Jul 2026 16:15:58 +0200 Subject: [PATCH 25/42] replaced sequential IDs with xids (random, uuid-like) Signed-off-by: Dmitri Dolguikh --- dns/nameserver.go | 4 +- .../shared/grpc/components_encoder.go | 155 +++--- .../shared/grpc/components_encoder_test.go | 156 ++---- management/server/account.go | 6 +- management/server/account_test.go | 2 +- management/server/group.go | 17 +- management/server/migration/account_seq.go | 156 ------ management/server/nameserver.go | 8 +- management/server/networks/manager.go | 9 +- management/server/networks/manager_test.go | 14 +- .../server/networks/resources/manager.go | 9 +- .../networks/resources/types/resource.go | 50 +- management/server/networks/routers/manager.go | 11 +- .../server/networks/routers/types/router.go | 38 +- management/server/networks/types/network.go | 21 +- management/server/policy.go | 9 +- management/server/posture/checks.go | 23 +- management/server/posture_checks.go | 9 +- management/server/posture_checks_test.go | 10 +- management/server/route.go | 8 +- management/server/store/account_seq_test.go | 506 ------------------ management/server/store/sql_store.go | 275 +--------- management/server/store/store.go | 29 - management/server/store/store_mock.go | 15 - management/server/types/account_components.go | 30 +- .../server/types/account_seq_counter.go | 29 - .../networkmap_components_correctness_test.go | 22 +- .../types/networkmap_wire_benchmark_test.go | 21 +- .../types/networkmap_wire_breakdown_test.go | 1 - route/route.go | 6 +- shared/management/networkmap/envelope_test.go | 6 +- shared/management/proto/management.pb.go | 184 +++---- shared/management/proto/management.proto | 56 +- shared/management/types/group.go | 14 +- .../management/types/networkmap_components.go | 8 +- shared/management/types/policy.go | 15 +- 36 files changed, 378 insertions(+), 1554 deletions(-) delete mode 100644 management/server/migration/account_seq.go delete mode 100644 management/server/store/account_seq_test.go delete mode 100644 management/server/types/account_seq_counter.go diff --git a/dns/nameserver.go b/dns/nameserver.go index 24faf9be105..84e83e2b479 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -53,9 +53,7 @@ type NameServerGroup struct { ID string `gorm:"primaryKey"` // AccountID is a reference to Account that this object belongs AccountID string `gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + PublicID string `json:"-"` // Name group name Name string // Description group description diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index f823e79ed52..a594502bf92 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -199,9 +199,6 @@ func (e *componentEncoder) encodeGroups() []*proto.GroupCompact { out := make([]*proto.GroupCompact, 0, len(e.components.Groups)) for _, g := range e.components.Groups { - if !g.HasSeqID() { - continue - } peerIdxs := make([]uint32, 0, len(g.Peers)) for _, peerID := range g.Peers { if idx, ok := e.peerOrder[peerID]; ok { @@ -209,7 +206,7 @@ func (e *componentEncoder) encodeGroups() []*proto.GroupCompact { } } out = append(out, &proto.GroupCompact{ - Id: g.AccountSeqID, + Id: g.PublicID, Name: g.Name, PeerIndexes: peerIdxs, }) @@ -229,7 +226,7 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) []*proto.Pol out := make([]*proto.PolicyCompact, 0, len(policies)) for _, pol := range policies { - if !pol.HasSeqID() || !pol.Enabled { + if !pol.Enabled { continue } for _, r := range pol.Rules { @@ -245,14 +242,14 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) []*proto.Pol // encodePolicyRule maps a single PolicyRule under pol to a PolicyCompact entry. func (e *componentEncoder) encodePolicyRule(pol *types.Policy, r *types.PolicyRule) *proto.PolicyCompact { return &proto.PolicyCompact{ - Id: pol.AccountSeqID, + Id: pol.PublicID, Action: networkmap.GetProtoAction(string(r.Action)), Protocol: networkmap.GetProtoProtocol(string(r.Protocol)), Bidirectional: r.Bidirectional, Ports: portsToUint32(r.Ports), PortRanges: portRangesToProto(r.PortRanges), - SourceGroupIds: e.groupSeqIDs(r.Sources), - DestinationGroupIds: e.groupSeqIDs(r.Destinations), + SourceGroupIds: e.groupPublicXids(r.Sources), + DestinationGroupIds: e.groupPublicXids(r.Destinations), AuthorizedUser: r.AuthorizedUser, AuthorizedGroups: e.encodeAuthorizedGroups(r.AuthorizedGroups), SourceResource: e.resourceToProto(r.SourceResource), @@ -261,16 +258,16 @@ func (e *componentEncoder) encodePolicyRule(pol *types.Policy, r *types.PolicyRu } } -// groupSeqIDs maps the xid group IDs in src to their per-account seq ids, -// dropping any group that has no seq id assigned. -func (e *componentEncoder) groupSeqIDs(src []string) []int32 { +// groupPublicXids maps the xid group IDs in src to their public xids, +// dropping any group with invalid public xid. +func (e *componentEncoder) groupPublicXids(src []string) []string { if len(src) == 0 { return nil } - out := make([]int32, 0, len(src)) + out := make([]string, 0, len(src)) for _, gid := range src { - if seq, ok := e.groupSeq(gid); ok { - out = append(out, seq) + if id, ok := e.groupPublicXid(gid); ok { + out = append(out, id) } } return out @@ -319,27 +316,27 @@ func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*type // group xid → local-user names) to the wire form (map keyed by group // account_seq_id → UserNameList). Groups without a seq id are dropped — // matches how source/destination group references handle the same case. -func (e *componentEncoder) encodeAuthorizedGroups(m map[string][]string) map[int32]*proto.UserNameList { +func (e *componentEncoder) encodeAuthorizedGroups(m map[string][]string) map[string]*proto.UserNameList { if len(m) == 0 { return nil } - out := make(map[int32]*proto.UserNameList, len(m)) + out := make(map[string]*proto.UserNameList, len(m)) for groupID, names := range m { - seq, ok := e.groupSeq(groupID) + id, ok := e.groupPublicXid(groupID) if !ok { continue } - out[seq] = &proto.UserNameList{Names: append([]string(nil), names...)} + out[id] = &proto.UserNameList{Names: names} } return out } -func (e *componentEncoder) groupSeq(groupID string) (int32, bool) { +func (e *componentEncoder) groupPublicXid(groupID string) (string, bool) { g, ok := e.components.Groups[groupID] - if !ok || !g.HasSeqID() { - return 0, false + if !ok { + return "", false } - return g.AccountSeqID, true + return g.PublicID, true } // resourceToProto translates types.Resource for the wire. For peer-typed @@ -362,34 +359,33 @@ func (e *componentEncoder) resourceToProto(r types.Resource) *proto.ResourceComp } // postureCheckSeqs translates a slice of posture-check xids to their -// per-account integer ids using the NetworkMapComponents.PostureCheckXIDToSeq -// lookup. Unresolvable xids are silently dropped — matches how group/peer +// public xids. Unresolvable xids are silently dropped — matches how group/peer // references handle the same case. -func (e *componentEncoder) postureCheckSeqs(xids []string) []int32 { - if len(xids) == 0 || len(e.components.PostureCheckXIDToSeq) == 0 { +func (e *componentEncoder) postureCheckSeqs(xids []string) []string { + if len(xids) == 0 || len(e.components.PostureCheckXIDToPublicID) == 0 { return nil } - out := make([]int32, 0, len(xids)) + out := make([]string, 0, len(xids)) for _, xid := range xids { - if seq, ok := e.components.PostureCheckXIDToSeq[xid]; ok { + if seq, ok := e.components.PostureCheckXIDToPublicID[xid]; ok { out = append(out, seq) } } return out } -// networkSeq translates a Network xid to its per-account integer id using -// the NetworkMapComponents.NetworkXIDToSeq lookup. Returns (0,false) when +// networkSeq translates a Network xid to its public id using +// the NetworkMapComponents.NetworkXIDToPublicID lookup. Returns (0,false) when // the xid isn't known — callers decide whether to skip the parent record. -func (e *componentEncoder) networkSeq(xid string) (int32, bool) { +func (e *componentEncoder) networkPublicId(xid string) (string, bool) { if xid == "" { - return 0, false + return "", false } - seq, ok := e.components.NetworkXIDToSeq[xid] - if !ok || seq == 0 { - return 0, false + id, ok := e.components.NetworkXIDToPublicID[xid] + if !ok { + return "", false } - return seq, true + return id, true } func (e *componentEncoder) encodeDNSSettings(s *types.DNSSettings) *proto.DNSSettingsCompact { @@ -397,11 +393,11 @@ func (e *componentEncoder) encodeDNSSettings(s *types.DNSSettings) *proto.DNSSet return nil } out := &proto.DNSSettingsCompact{ - DisabledManagementGroupIds: make([]int32, 0, len(s.DisabledManagementGroups)), + DisabledManagementGroupIds: make([]string, 0, len(s.DisabledManagementGroups)), } for _, gid := range s.DisabledManagementGroups { - if seq, ok := e.groupSeq(gid); ok { - out.DisabledManagementGroupIds = append(out.DisabledManagementGroupIds, seq) + if id, ok := e.groupPublicXid(gid); ok { + out.DisabledManagementGroupIds = append(out.DisabledManagementGroupIds, id) } } return out @@ -417,7 +413,7 @@ func (e *componentEncoder) encodeRoutes(routes []*nbroute.Route) []*proto.RouteR continue } rr := &proto.RouteRaw{ - Id: r.AccountSeqID, + Id: r.PublicID, NetId: string(r.NetID), Description: r.Description, KeepRoute: r.KeepRoute, @@ -427,9 +423,9 @@ func (e *componentEncoder) encodeRoutes(routes []*nbroute.Route) []*proto.RouteR Enabled: r.Enabled, SkipAutoApply: r.SkipAutoApply, Domains: r.Domains.ToPunycodeList(), - GroupIds: e.groupIDsToSeq(r.Groups), - AccessControlGroupIds: e.groupIDsToSeq(r.AccessControlGroups), - PeerGroupIds: e.groupIDsToSeq(r.PeerGroups), + GroupIds: e.groupPublicXids(r.Groups), + AccessControlGroupIds: e.groupPublicXids(r.AccessControlGroups), + PeerGroupIds: e.groupPublicXids(r.PeerGroups), } if r.Network.IsValid() { rr.NetworkCidr = r.Network.String() @@ -445,19 +441,6 @@ func (e *componentEncoder) encodeRoutes(routes []*nbroute.Route) []*proto.RouteR return out } -func (e *componentEncoder) groupIDsToSeq(groupIDs []string) []int32 { - if len(groupIDs) == 0 { - return nil - } - out := make([]int32, 0, len(groupIDs)) - for _, gid := range groupIDs { - if seq, ok := e.groupSeq(gid); ok { - out = append(out, seq) - } - } - return out -} - func (e *componentEncoder) encodeNameServerGroups(nsgs []*nbdns.NameServerGroup) []*proto.NameServerGroupRaw { if len(nsgs) == 0 { return nil @@ -468,11 +451,11 @@ func (e *componentEncoder) encodeNameServerGroups(nsgs []*nbdns.NameServerGroup) continue } entry := &proto.NameServerGroupRaw{ - Id: nsg.AccountSeqID, + Id: nsg.PublicID, Name: nsg.Name, Description: nsg.Description, Nameservers: encodeNameServers(nsg.NameServers), - GroupIds: e.groupIDsToSeq(nsg.Groups), + GroupIds: e.groupPublicXids(nsg.Groups), Primary: nsg.Primary, Domains: nsg.Domains, Enabled: nsg.Enabled, @@ -541,7 +524,7 @@ func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.Net continue } entry := &proto.NetworkResourceRaw{ - Id: r.AccountSeqID, + Id: r.PublicID, Name: r.Name, Description: r.Description, Type: string(r.Type), @@ -549,8 +532,8 @@ func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.Net DomainValue: r.Domain, Enabled: r.Enabled, } - if seq, ok := e.networkSeq(r.NetworkID); ok { - entry.NetworkSeq = seq + if id, ok := e.networkPublicId(r.NetworkID); ok { + entry.NetworkSeq = id } if r.Prefix.IsValid() { entry.PrefixCidr = r.Prefix.String() @@ -560,16 +543,16 @@ func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.Net return out } -func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[int32]*proto.NetworkRouterList { +func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[string]*proto.NetworkRouterList { if len(routersMap) == 0 { return nil } - out := make(map[int32]*proto.NetworkRouterList, len(routersMap)) + out := make(map[string]*proto.NetworkRouterList, len(routersMap)) for networkXID, routers := range routersMap { if len(routers) == 0 { continue } - netSeq, ok := e.networkSeq(networkXID) + id, ok := e.networkPublicId(networkXID) if !ok { continue } @@ -579,8 +562,8 @@ func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*ro continue } entry := &proto.NetworkRouterEntry{ - Id: r.AccountSeqID, - PeerGroupIds: e.groupIDsToSeq(r.PeerGroups), + Id: r.PublicID, + PeerGroupIds: e.groupPublicXids(r.PeerGroups), Masquerade: r.Masquerade, Metric: int32(r.Metric), Enabled: r.Enabled, @@ -591,53 +574,53 @@ func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*ro } entries = append(entries, entry) } - out[netSeq] = &proto.NetworkRouterList{Entries: entries} + out[id] = &proto.NetworkRouterList{Entries: entries} } return out } -func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*types.Policy) map[int32]*proto.PolicyIds { +func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*types.Policy) map[string]*proto.PolicyIds { if len(rpm) == 0 { return nil } - // resourceXIDToSeq is local to one encode — built from components.NetworkResources + // resourceXIDToPublicID is local to one encode — built from components.NetworkResources // (small slice). Network resources without seq id are dropped, matching how // other components-without-seq are silently filtered. - resourceXIDToSeq := make(map[string]int32, len(e.components.NetworkResources)) + resourceXIDToPublicID := make(map[string]string, len(e.components.NetworkResources)) for _, r := range e.components.NetworkResources { - if r != nil && r.AccountSeqID != 0 { - resourceXIDToSeq[r.ID] = r.AccountSeqID + if r != nil { + resourceXIDToPublicID[r.ID] = r.PublicID } } - out := make(map[int32]*proto.PolicyIds, len(rpm)) + out := make(map[string]*proto.PolicyIds, len(rpm)) for resourceXID, policies := range rpm { - seq, ok := resourceXIDToSeq[resourceXID] + resId, ok := resourceXIDToPublicID[resourceXID] if !ok { continue } - ids := make([]int32, 0, len(policies)) + ids := make([]string, 0, len(policies)) for _, pol := range policies { - ids = append(ids, pol.AccountSeqID) + ids = append(ids, pol.PublicID) } if len(ids) == 0 { continue } - out[seq] = &proto.PolicyIds{Ids: ids} + out[resId] = &proto.PolicyIds{Ids: ids} } return out } -func (e *componentEncoder) encodeGroupIDToUserIDs(m map[string][]string) map[int32]*proto.UserIDList { +func (e *componentEncoder) encodeGroupIDToUserIDs(m map[string][]string) map[string]*proto.UserIDList { if len(m) == 0 { return nil } - out := make(map[int32]*proto.UserIDList, len(m)) + out := make(map[string]*proto.UserIDList, len(m)) for groupID, userIDs := range m { - seq, ok := e.groupSeq(groupID) + id, ok := e.groupPublicXid(groupID) if !ok || len(userIDs) == 0 { continue } - out[seq] = &proto.UserIDList{UserIds: userIDs} + out[id] = &proto.UserIDList{UserIds: userIDs} } return out } @@ -653,14 +636,14 @@ func stringSetToSlice(s map[string]struct{}) []string { return out } -func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[int32]*proto.PeerIndexSet { +func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]struct{}) map[string]*proto.PeerIndexSet { if len(m) == 0 { return nil } - out := make(map[int32]*proto.PeerIndexSet, len(m)) + out := make(map[string]*proto.PeerIndexSet, len(m)) for checkXID, failedPeerIDs := range m { - seq, ok := e.components.PostureCheckXIDToSeq[checkXID] - if !ok || seq == 0 { + id, ok := e.components.PostureCheckXIDToPublicID[checkXID] + if !ok { continue } idxs := make([]uint32, 0, len(failedPeerIDs)) @@ -672,7 +655,7 @@ func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]stru if len(idxs) == 0 { continue } - out[seq] = &proto.PeerIndexSet{PeerIndexes: idxs} + out[id] = &proto.PeerIndexSet{PeerIndexes: idxs} } return out } diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go index 6b73c883ba0..4fca37cbdaa 100644 --- a/management/internals/shared/grpc/components_encoder_test.go +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -197,14 +197,14 @@ func newTestComponents() *types.NetworkMapComponents { "peer-c": peerC, }, Groups: map[string]*types.Group{ - "group-src": {ID: "group-src", AccountSeqID: 1, Name: "Src", Peers: []string{"peer-a"}}, - "group-dst": {ID: "group-dst", AccountSeqID: 2, Name: "Dst", Peers: []string{"peer-b", "peer-c"}}, + "group-src": {ID: "group-src", PublicID: "1", Name: "Src", Peers: []string{"peer-a"}}, + "group-dst": {ID: "group-dst", PublicID: "2", Name: "Dst", Peers: []string{"peer-b", "peer-c"}}, }, Policies: []*types.Policy{ { - ID: "pol-1", - AccountSeqID: 10, - Enabled: true, + ID: "pol-1", + PublicID: "10", + Enabled: true, Rules: []*types.PolicyRule{{ ID: "rule-1", Enabled: true, Action: types.PolicyTrafficActionAccept, Protocol: types.PolicyRuleProtocolTCP, Bidirectional: true, @@ -291,23 +291,23 @@ func TestEncodeNetworkMapEnvelope_ConcurrentEncodesEquivalent(t *testing.T) { } } -func TestEncodeNetworkMapEnvelope_GroupsByAccountSeqID(t *testing.T) { +func TestEncodeNetworkMapEnvelope_GroupsByAccountPublicId(t *testing.T) { c := newTestComponents() full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() require.Len(t, full.Groups, 2) - groupByID := map[int32]*proto.GroupCompact{} + groupByID := map[string]*proto.GroupCompact{} for _, g := range full.Groups { groupByID[g.Id] = g } - require.Contains(t, groupByID, int32(1)) - require.Contains(t, groupByID, int32(2)) - assert.Equal(t, "Src", groupByID[1].Name) - assert.Equal(t, "Dst", groupByID[2].Name) - assert.Len(t, groupByID[1].PeerIndexes, 1) - assert.Len(t, groupByID[2].PeerIndexes, 2) + require.Contains(t, groupByID, "1") + require.Contains(t, groupByID, "2") + assert.Equal(t, "Src", groupByID["1"].Name) + assert.Equal(t, "Dst", groupByID["2"].Name) + assert.Len(t, groupByID["1"].PeerIndexes, 1) + assert.Len(t, groupByID["2"].PeerIndexes, 2) } func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) { @@ -317,7 +317,7 @@ func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) { require.Len(t, full.Policies, 1) pc := full.Policies[0] - assert.EqualValues(t, 10, pc.Id) + assert.EqualValues(t, "10", pc.Id) assert.Equal(t, proto.RuleAction_ACCEPT, pc.Action) assert.Equal(t, proto.RuleProtocol_TCP, pc.Protocol) assert.True(t, pc.Bidirectional) @@ -325,8 +325,8 @@ func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) { require.Len(t, pc.PortRanges, 1) assert.EqualValues(t, 8000, pc.PortRanges[0].Start) assert.EqualValues(t, 8100, pc.PortRanges[0].End) - assert.Equal(t, []int32{1}, pc.SourceGroupIds) - assert.Equal(t, []int32{2}, pc.DestinationGroupIds) + assert.Equal(t, []string{"1"}, pc.SourceGroupIds) + assert.Equal(t, []string{"2"}, pc.DestinationGroupIds) } func TestEncodeNetworkMapEnvelope_RouterIndexes(t *testing.T) { @@ -349,21 +349,6 @@ func TestEncodeNetworkMapEnvelope_DisabledPolicySkipped(t *testing.T) { assert.Empty(t, full.Policies) } -func TestEncodeNetworkMapEnvelope_GroupZeroSeqIDSkipped(t *testing.T) { - c := newTestComponents() - c.Groups["group-src"].AccountSeqID = 0 - - full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - - require.Len(t, full.Groups, 1, "groups with AccountSeqID=0 are not yet persisted and must be skipped") - assert.EqualValues(t, 2, full.Groups[0].Id) - - require.Len(t, full.Policies, 1) - pc := full.Policies[0] - assert.Empty(t, pc.SourceGroupIds, "rule references a group that was filtered out → no group id on wire") - assert.Equal(t, []int32{2}, pc.DestinationGroupIds) -} - func TestEncodeNetworkMapEnvelope_TwoPeersSameMalformedKey(t *testing.T) { // Both peers have nil WgPubKey after decode; canonicalize must still // produce a stable order using DnsLabel as a tiebreaker, so 100 encodes @@ -496,7 +481,7 @@ func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) { c.Routes = []*nbroute.Route{ { ID: "route-peer", - AccountSeqID: 100, + PublicID: "100", NetID: "net-A", Description: "via peer-c", Network: netip.MustParsePrefix("10.0.0.0/16"), @@ -506,24 +491,18 @@ func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) { Enabled: true, }, { - ID: "route-peergroup", - AccountSeqID: 101, - NetID: "net-B", - Network: netip.MustParsePrefix("10.1.0.0/16"), - PeerGroups: []string{"group-src", "group-dst"}, - Enabled: true, - }, - { - ID: "route-no-seq", - AccountSeqID: 0, // unset — should still ship (no group seq filter on routes) - Network: netip.MustParsePrefix("10.2.0.0/16"), - Enabled: true, + ID: "route-peergroup", + PublicID: "101", + NetID: "net-B", + Network: netip.MustParsePrefix("10.1.0.0/16"), + PeerGroups: []string{"group-src", "group-dst"}, + Enabled: true, }, } full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Len(t, full.Routes, 3) + require.Len(t, full.Routes, 2) byNetID := map[string]*proto.RouteRaw{} for _, r := range full.Routes { byNetID[r.NetId] = r @@ -534,24 +513,24 @@ func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) { assert.True(t, r1.PeerIndexSet, "route with peer must set peer_index_set") require.Less(t, int(r1.PeerIndex), len(full.Peers)) assert.Equal(t, "peerc", full.Peers[r1.PeerIndex].DnsLabel) - assert.Equal(t, []int32{1}, r1.GroupIds, "group-src has AccountSeqID 1") - assert.Equal(t, []int32{2}, r1.AccessControlGroupIds, "group-dst has AccountSeqID 2") + assert.Equal(t, []string{"1"}, r1.GroupIds, "group-src has AccountSeqID 1") + assert.Equal(t, []string{"2"}, r1.AccessControlGroupIds, "group-dst has AccountSeqID 2") assert.Empty(t, r1.PeerGroupIds) r2 := byNetID["net-B"] require.NotNil(t, r2) assert.False(t, r2.PeerIndexSet, "route with peer_groups must NOT set peer_index_set") - assert.ElementsMatch(t, []int32{1, 2}, r2.PeerGroupIds) + assert.ElementsMatch(t, []string{"1", "2"}, r2.PeerGroupIds) } func TestEncodeNetworkMapEnvelope_RouteWithMissingPeerLeavesIndexUnset(t *testing.T) { c := newTestComponents() c.Routes = []*nbroute.Route{{ - ID: "route-x", - AccountSeqID: 100, - Peer: "peer-not-in-components", - Network: netip.MustParsePrefix("10.0.0.0/16"), - Enabled: true, + ID: "route-x", + PublicID: "100", + Peer: "peer-not-in-components", + Network: netip.MustParsePrefix("10.0.0.0/16"), + Enabled: true, }} full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() @@ -567,7 +546,7 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing // is the I1 case — without unionPolicies the encoder would silently // drop it from the wire. resourceOnlyPolicy := &types.Policy{ - ID: "pol-resource", AccountSeqID: 99, Enabled: true, + ID: "pol-resource", PublicID: "99", Enabled: true, Rules: []*types.PolicyRule{{ ID: "rule-r", Enabled: true, Action: types.PolicyTrafficActionAccept, Protocol: types.PolicyRuleProtocolTCP, @@ -581,24 +560,24 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing // Resource must appear in components.NetworkResources with a seq id — // encoder uses that to translate the xid map key to uint32. c.NetworkResources = []*resourceTypes.NetworkResource{ - {ID: "resource-x", AccountSeqID: 77, Name: "res-x", Enabled: true}, + {ID: "resource-x", PublicID: "77", Name: "res-x", Enabled: true}, } full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() require.Len(t, full.Policies, 2, "encoded policies must include both peer-traffic and resource-only") - policyByID := map[int32]*proto.PolicyCompact{} - policyIds := make([]int32, 0) + policyByID := map[string]*proto.PolicyCompact{} + policyIds := make([]string, 0) for _, p := range full.Policies { policyByID[p.Id] = p policyIds = append(policyIds, p.Id) } - require.Contains(t, policyByID, int32(10), "original peer-traffic policy id 10") - require.Contains(t, policyByID, int32(99), "resource-only policy id 99") + require.Contains(t, policyByID, "10", "original peer-traffic policy id 10") + require.Contains(t, policyByID, "99", "resource-only policy id 99") - require.Contains(t, full.ResourcePoliciesMap, int32(77)) - ids := full.ResourcePoliciesMap[77].Ids + require.Contains(t, full.ResourcePoliciesMap, "77") + ids := full.ResourcePoliciesMap["77"].Ids require.Len(t, ids, 2) assert.ElementsMatch(t, policyIds, ids, "resource policies map must reference both wire policy indexes") @@ -607,7 +586,7 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) { c := newTestComponents() c.NameServerGroups = []*nbdns.NameServerGroup{{ - ID: "nsg-1", AccountSeqID: 50, Name: "Main", Description: "primary", + ID: "nsg-1", PublicID: "50", Name: "Main", Description: "primary", NameServers: []nbdns.NameServer{{ IP: netip.MustParseAddr("8.8.8.8"), NSType: nbdns.UDPNameServerType, Port: 53, }}, @@ -615,23 +594,22 @@ func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) { Primary: true, Enabled: true, Domains: []string{"corp.example"}, }} - c.Groups["group-not-persisted"] = &types.Group{ID: "group-not-persisted", AccountSeqID: 0, Peers: []string{}} full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() require.Len(t, full.NameserverGroups, 1) nsg := full.NameserverGroups[0] - assert.EqualValues(t, 50, nsg.Id) + assert.EqualValues(t, "50", nsg.Id) assert.Equal(t, "Main", nsg.Name) assert.True(t, nsg.Primary) require.Len(t, nsg.Nameservers, 1) assert.Equal(t, "8.8.8.8", nsg.Nameservers[0].IP) - assert.Equal(t, []int32{1}, nsg.GroupIds, "group-not-persisted is filtered out (AccountSeqID=0)") + assert.Equal(t, []string{"1"}, nsg.GroupIds) } func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { c := newTestComponents() - c.PostureCheckXIDToSeq = map[string]int32{"check-1": 33} + c.PostureCheckXIDToPublicID = map[string]string{"check-1": "33"} c.PostureFailedPeers = map[string]map[string]struct{}{ "check-1": { "peer-a": {}, @@ -642,18 +620,18 @@ func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.PostureFailedPeers, int32(33)) - idxs := full.PostureFailedPeers[33].PeerIndexes + require.Contains(t, full.PostureFailedPeers, "33") + idxs := full.PostureFailedPeers["33"].PeerIndexes assert.Len(t, idxs, 2, "missing peer is silently dropped (filterPostureFailedPeers guarantees presence in real data)") } func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { c := newTestComponents() - c.NetworkXIDToSeq = map[string]int32{"net-1": 5} + c.NetworkXIDToPublicID = map[string]string{"net-1": "5"} c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ "net-1": { "peer-c": { - ID: "router-1", AccountSeqID: 200, + ID: "router-1", PublicID: "200", Peer: "peer-c", Masquerade: true, Metric: 10, Enabled: true, }, }, @@ -661,11 +639,11 @@ func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.RoutersMap, int32(5)) - entries := full.RoutersMap[5].Entries + require.Contains(t, full.RoutersMap, "5") + entries := full.RoutersMap["5"].Entries require.Len(t, entries, 1) e := entries[0] - assert.EqualValues(t, 200, e.Id) + assert.EqualValues(t, "200", e.Id) assert.True(t, e.PeerIndexSet) require.Less(t, int(e.PeerIndex), len(full.Peers)) assert.Equal(t, "peerc", full.Peers[e.PeerIndex].DnsLabel) @@ -685,47 +663,31 @@ func TestEncodeNetworkMapEnvelope_RouterPeerNotInComponentsPeers(t *testing.T) { DNSLabel: "peerc", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, } c.RouterPeers = map[string]*nbpeer.Peer{"peer-c": routerPeer} - c.NetworkXIDToSeq = map[string]int32{"net-1": 5} + c.NetworkXIDToPublicID = map[string]string{"net-1": "5"} c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ - "net-1": {"peer-c": {ID: "r-1", AccountSeqID: 1, Peer: "peer-c", Enabled: true}}, + "net-1": {"peer-c": {ID: "r-1", PublicID: "1", Peer: "peer-c", Enabled: true}}, } full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Contains(t, full.RoutersMap, int32(5)) - require.Len(t, full.RoutersMap[5].Entries, 1) - e := full.RoutersMap[5].Entries[0] + require.Contains(t, full.RoutersMap, "5") + require.Len(t, full.RoutersMap["5"].Entries, 1) + e := full.RoutersMap["5"].Entries[0] assert.True(t, e.PeerIndexSet, "router peer must be indexed even when not in c.Peers") } -func TestEncodeNetworkMapEnvelope_DNSSettingsFiltersUnpersistedGroups(t *testing.T) { - c := newTestComponents() - c.DNSSettings = &types.DNSSettings{ - DisabledManagementGroups: []string{"group-src", "group-missing", "group-no-seq"}, - } - c.Groups["group-no-seq"] = &types.Group{ID: "group-no-seq", AccountSeqID: 0} - - full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - - require.NotNil(t, full.DnsSettings) - assert.Equal(t, []int32{1}, full.DnsSettings.DisabledManagementGroupIds, - "only group-src (AccountSeqID=1) survives — missing and unpersisted are dropped") -} - func TestEncodeNetworkMapEnvelope_GroupIDToUserIDs(t *testing.T) { c := newTestComponents() c.GroupIDToUserIDs = map[string][]string{ "group-src": {"user-1", "user-2"}, - "group-no-seq": {"user-3"}, // group not persisted → drop "group-missing": {"user-4"}, // group not in components → drop } - c.Groups["group-no-seq"] = &types.Group{ID: "group-no-seq", AccountSeqID: 0} full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() - require.Len(t, full.GroupIdToUserIds, 1, "only persisted+present groups survive") - require.Contains(t, full.GroupIdToUserIds, int32(1)) - assert.ElementsMatch(t, []string{"user-1", "user-2"}, full.GroupIdToUserIds[1].UserIds) + require.Len(t, full.GroupIdToUserIds, 1, "only present groups survive") + require.Contains(t, full.GroupIdToUserIds, "1") + assert.ElementsMatch(t, []string{"user-1", "user-2"}, full.GroupIdToUserIds["1"].UserIds) } func TestToProxyPatch_EmptyInputReturnsNil(t *testing.T) { diff --git a/management/server/account.go b/management/server/account.go index 7c62e706c55..619036d0c88 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1649,11 +1649,7 @@ func (am *DefaultAccountManager) SyncUserJWTGroups(ctx context.Context, userAuth } for _, g := range newGroupsToCreate { - seq, err := transaction.AllocateAccountSeqID(ctx, userAuth.AccountId, types.AccountSeqEntityGroup) - if err != nil { - return fmt.Errorf("error allocating group seq id: %w", err) - } - g.AccountSeqID = seq + g.PublicID = xid.New().String() } if err = transaction.CreateGroups(ctx, userAuth.AccountId, newGroupsToCreate); err != nil { diff --git a/management/server/account_test.go b/management/server/account_test.go index e51afada86f..f8211fc1abf 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -3179,7 +3179,7 @@ func TestAccount_SetJWTGroups(t *testing.T) { } } require.NotNil(t, newJWTGroup, "JIT-created JWT group not found") - assert.NotZero(t, newJWTGroup.AccountSeqID, "JIT-created JWT group must have a non-zero AccountSeqID") + assert.NotEqual(t, "", newJWTGroup.PublicID, "JIT-created JWT group must have a non-empty PublicID") }) t.Run("remove all JWT groups when list is empty", func(t *testing.T) { diff --git a/management/server/group.go b/management/server/group.go index 1cbf88315dc..dab891f2abd 100644 --- a/management/server/group.go +++ b/management/server/group.go @@ -93,11 +93,7 @@ func (am *DefaultAccountManager) CreateGroup(ctx context.Context, accountID, use events := am.prepareGroupEvents(ctx, transaction, accountID, userID, newGroup) eventsToStore = append(eventsToStore, events...) - seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityGroup) - if err != nil { - return status.Errorf(status.Internal, "failed to allocate group seq id: %v", err) - } - newGroup.AccountSeqID = seq + newGroup.PublicID = xid.New().String() if err := transaction.CreateGroup(ctx, newGroup); err != nil { return status.Errorf(status.Internal, "failed to create group: %v", err) @@ -164,7 +160,7 @@ func (am *DefaultAccountManager) UpdateGroup(ctx context.Context, accountID, use return err } - newGroup.AccountSeqID = oldGroup.AccountSeqID + newGroup.PublicID = oldGroup.PublicID if err = transaction.UpdateGroup(ctx, newGroup); err != nil { return err @@ -243,12 +239,7 @@ func (am *DefaultAccountManager) CreateGroups(ctx context.Context, accountID, us } newGroup.AccountID = accountID - - seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityGroup) - if err != nil { - return err - } - newGroup.AccountSeqID = seq + newGroup.PublicID = xid.New().String() if err = transaction.CreateGroup(ctx, newGroup); err != nil { return err @@ -345,7 +336,7 @@ func (am *DefaultAccountManager) updateSingleGroup(ctx context.Context, accountI if err != nil { return err } - newGroup.AccountSeqID = oldGroup.AccountSeqID + newGroup.PublicID = oldGroup.PublicID if err := transaction.UpdateGroup(ctx, newGroup); err != nil { return err diff --git a/management/server/migration/account_seq.go b/management/server/migration/account_seq.go deleted file mode 100644 index ce8095d258a..00000000000 --- a/management/server/migration/account_seq.go +++ /dev/null @@ -1,156 +0,0 @@ -package migration - -import ( - "context" - "fmt" - - log "github.com/sirupsen/logrus" - "gorm.io/gorm" - - "github.com/netbirdio/netbird/management/server/types" -) - -// BackfillAccountSeqIDs assigns a deterministic per-account sequential id to all -// rows of `model` whose account_seq_id is zero, then seeds account_seq_counters -// with the next free id per account. Idempotent: safe to re-run; both steps -// no-op once everything is consistent. -// -// Implemented as two table-wide SQL statements with window functions, one -// transaction. Backfilling 246k rows across 154k accounts on Postgres takes -// well under a second instead of the per-account-loop ~2 minutes. -// -// orderColumn is the column to use when assigning the deterministic ordering -// (typically the primary-key string id). -func BackfillAccountSeqIDs[T any]( - ctx context.Context, - db *gorm.DB, - entity types.AccountSeqEntity, - orderColumn string, -) error { - var model T - if !db.Migrator().HasTable(&model) { - log.WithContext(ctx).Debugf("backfill seq id: table for %T missing, skip", model) - return nil - } - - stmt := &gorm.Statement{DB: db} - if err := stmt.Parse(&model); err != nil { - return fmt.Errorf("parse model: %w", err) - } - table := quoteIdent(db, stmt.Schema.Table) - orderCol := quoteIdent(db, orderColumn) - - return db.Transaction(func(tx *gorm.DB) error { - var pending int64 - if err := tx.Raw( - fmt.Sprintf("SELECT count(*) FROM %s WHERE account_seq_id IS NULL OR account_seq_id = 0", table), - ).Scan(&pending).Error; err != nil { - return fmt.Errorf("count pending on %s: %w", table, err) - } - - if pending > 0 { - log.WithContext(ctx).Infof("backfill seq id: %s — %d rows pending", table, pending) - if err := backfillRankSQL(tx, table, orderCol); err != nil { - return fmt.Errorf("rank %s: %w", table, err) - } - } - - if err := seedCountersSQL(tx, table, entity); err != nil { - return fmt.Errorf("seed counters for %s: %w", entity, err) - } - return nil - }) -} - -func quoteIdent(db *gorm.DB, name string) string { - switch db.Dialector.Name() { - case "mysql": - return "`" + name + "`" - case "postgres": - return `"` + name + `"` - default: - return name - } -} - -func backfillRankSQL(db *gorm.DB, table, orderCol string) error { - dialect := db.Dialector.Name() - var sql string - switch dialect { - case "postgres", "sqlite": - sql = fmt.Sprintf(` -WITH max_seq AS ( - SELECT account_id, COALESCE(MAX(account_seq_id), 0) AS max_seq - FROM %s - GROUP BY account_id -), -ranked AS ( - SELECT p.id, - m.max_seq + ROW_NUMBER() OVER (PARTITION BY p.account_id ORDER BY p.%s) AS new_seq - FROM %s p - JOIN max_seq m ON p.account_id = m.account_id - WHERE p.account_seq_id IS NULL OR p.account_seq_id = 0 -) -UPDATE %s SET account_seq_id = ranked.new_seq -FROM ranked -WHERE %s.id = ranked.id -`, table, orderCol, table, table, table) - case "mysql": - sql = fmt.Sprintf(` -UPDATE %s p -JOIN ( - SELECT account_id, COALESCE(MAX(account_seq_id), 0) AS max_seq - FROM %s - GROUP BY account_id -) m ON p.account_id = m.account_id -JOIN ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY account_id ORDER BY %s) AS rn - FROM %s - WHERE account_seq_id IS NULL OR account_seq_id = 0 -) r ON p.id = r.id -SET p.account_seq_id = m.max_seq + r.rn -`, table, table, orderCol, table) - default: - return fmt.Errorf("unsupported dialect: %s", dialect) - } - return db.Exec(sql).Error -} - -func seedCountersSQL(db *gorm.DB, table string, entity types.AccountSeqEntity) error { - dialect := db.Dialector.Name() - var sql string - switch dialect { - case "postgres": - sql = fmt.Sprintf(` -INSERT INTO account_seq_counters (account_id, entity, next_id) -SELECT account_id, ?, MAX(account_seq_id) + 1 -FROM %s -WHERE account_seq_id IS NOT NULL AND account_seq_id > 0 -GROUP BY account_id -ON CONFLICT (account_id, entity) DO UPDATE - SET next_id = GREATEST(account_seq_counters.next_id, EXCLUDED.next_id) -`, table) - case "sqlite": - sql = fmt.Sprintf(` -INSERT INTO account_seq_counters (account_id, entity, next_id) -SELECT account_id, ?, MAX(account_seq_id) + 1 -FROM %s -WHERE account_seq_id IS NOT NULL AND account_seq_id > 0 -GROUP BY account_id -ON CONFLICT (account_id, entity) DO UPDATE - SET next_id = max(account_seq_counters.next_id, excluded.next_id) -`, table) - case "mysql": - sql = fmt.Sprintf(` -INSERT INTO account_seq_counters (account_id, entity, next_id) -SELECT account_id, ?, MAX(account_seq_id) + 1 -FROM %s -WHERE account_seq_id IS NOT NULL AND account_seq_id > 0 -GROUP BY account_id -ON DUPLICATE KEY UPDATE next_id = GREATEST(next_id, VALUES(next_id)) -`, table) - default: - return fmt.Errorf("unsupported dialect: %s", dialect) - } - return db.Exec(sql, string(entity)).Error -} diff --git a/management/server/nameserver.go b/management/server/nameserver.go index 8f12facee56..0a4c2291e89 100644 --- a/management/server/nameserver.go +++ b/management/server/nameserver.go @@ -67,11 +67,7 @@ func (am *DefaultAccountManager) CreateNameServerGroup(ctx context.Context, acco return err } - seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityNameserverGroup) - if err != nil { - return err - } - newNSGroup.AccountSeqID = seq + newNSGroup.PublicID = xid.New().String() if err = transaction.SaveNameServerGroup(ctx, newNSGroup); err != nil { return err @@ -122,7 +118,7 @@ func (am *DefaultAccountManager) SaveNameServerGroup(ctx context.Context, accoun return err } - nsGroupToSave.AccountSeqID = oldNSGroup.AccountSeqID + nsGroupToSave.PublicID = oldNSGroup.PublicID if err = transaction.SaveNameServerGroup(ctx, nsGroupToSave); err != nil { return err diff --git a/management/server/networks/manager.go b/management/server/networks/manager.go index a94170f6e54..fc03fff9f3d 100644 --- a/management/server/networks/manager.go +++ b/management/server/networks/manager.go @@ -16,7 +16,6 @@ import ( "github.com/netbirdio/netbird/management/server/permissions/modules" "github.com/netbirdio/netbird/management/server/permissions/operations" "github.com/netbirdio/netbird/management/server/store" - serverTypes "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/shared/management/status" ) @@ -73,11 +72,7 @@ func (m *managerImpl) CreateNetwork(ctx context.Context, userID string, network network.ID = xid.New().String() err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error { - seq, err := transaction.AllocateAccountSeqID(ctx, network.AccountID, serverTypes.AccountSeqEntityNetwork) - if err != nil { - return fmt.Errorf("failed to allocate network seq id: %w", err) - } - network.AccountSeqID = seq + network.PublicID = xid.New().String() if err := transaction.SaveNetwork(ctx, network); err != nil { return fmt.Errorf("failed to save network: %w", err) @@ -119,7 +114,7 @@ func (m *managerImpl) UpdateNetwork(ctx context.Context, userID string, network if err != nil { return fmt.Errorf("failed to get network: %w", err) } - network.AccountSeqID = existing.AccountSeqID + network.PublicID = existing.PublicID if err := transaction.SaveNetwork(ctx, network); err != nil { return fmt.Errorf("failed to save network: %w", err) diff --git a/management/server/networks/manager_test.go b/management/server/networks/manager_test.go index 50629e6f5b2..b5fb1c72d0b 100644 --- a/management/server/networks/manager_test.go +++ b/management/server/networks/manager_test.go @@ -259,7 +259,7 @@ func Test_UpdateNetworkFailsWithPermissionDenied(t *testing.T) { // Test_CreateNetworkAllocatesSeqID verifies that CreateNetwork sets a // non-zero AccountSeqID on the persisted network (allocated through the // account_seq_counters table). -func Test_CreateNetworkAllocatesSeqID(t *testing.T) { +func Test_CreateNetworkSetsPublicId(t *testing.T) { ctx := context.Background() const accountID = "testAccountId" const userID = "testAdminId" @@ -280,13 +280,13 @@ func Test_CreateNetworkAllocatesSeqID(t *testing.T) { Name: "seq-allocation-test", }) require.NoError(t, err) - require.NotZero(t, created.AccountSeqID, "CreateNetwork must allocate a non-zero AccountSeqID") + require.NotEqual(t, "", created.PublicID, "CreateNetwork must allocate a non-zero AccountSeqID") } // Test_UpdateNetworkPreservesSeqID verifies UpdateNetwork does not reset // AccountSeqID even when the caller passes a zero value (the shape REST // handlers produce because the field is `json:"-"`). -func Test_UpdateNetworkPreservesSeqID(t *testing.T) { +func Test_UpdateNetworkPreservesPublicId(t *testing.T) { ctx := context.Background() const accountID = "testAccountId" const userID = "testAdminId" @@ -307,21 +307,21 @@ func Test_UpdateNetworkPreservesSeqID(t *testing.T) { Name: "seq-preserve-original", }) require.NoError(t, err) - originalSeq := created.AccountSeqID - require.NotZero(t, originalSeq) + originalPublicId := created.PublicID + require.NotZero(t, originalPublicId) update := &types.Network{ AccountID: accountID, ID: created.ID, Name: "seq-preserve-renamed", } - require.Zero(t, update.AccountSeqID, "incoming struct must mirror an HTTP handler shape") + require.Equal(t, "", update.PublicID, "incoming struct must mirror an HTTP handler shape") _, err = manager.UpdateNetwork(ctx, userID, update) require.NoError(t, err) got, err := manager.GetNetwork(ctx, accountID, userID, created.ID) require.NoError(t, err) - require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must survive UpdateNetwork") + require.Equal(t, originalPublicId, got.PublicID, "PublicID must survive UpdateNetwork") require.Equal(t, "seq-preserve-renamed", got.Name) } diff --git a/management/server/networks/resources/manager.go b/management/server/networks/resources/manager.go index 470bab10c9b..001af4d8338 100644 --- a/management/server/networks/resources/manager.go +++ b/management/server/networks/resources/manager.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/rs/xid" log "github.com/sirupsen/logrus" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service" @@ -146,11 +147,7 @@ func (m *managerImpl) createResourceInTransaction(ctx context.Context, transacti return nil, nil, fmt.Errorf("failed to get network: %w", err) } - seq, err := transaction.AllocateAccountSeqID(ctx, resource.AccountID, nbtypes.AccountSeqEntityNetworkResource) - if err != nil { - return nil, nil, fmt.Errorf("failed to allocate network resource seq id: %w", err) - } - resource.AccountSeqID = seq + resource.PublicID = xid.New().String() if err = transaction.SaveNetworkResource(ctx, resource); err != nil { return nil, nil, fmt.Errorf("failed to save network resource: %w", err) @@ -251,7 +248,7 @@ func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resourc if err != nil { return fmt.Errorf("failed to get network resource: %w", err) } - resource.AccountSeqID = oldResource.AccountSeqID + resource.PublicID = oldResource.PublicID oldGroups, err := m.groupsManager.GetResourceGroupsInTransaction(ctx, transaction, store.LockingStrengthNone, resource.AccountID, resource.ID) if err != nil { diff --git a/management/server/networks/resources/types/resource.go b/management/server/networks/resources/types/resource.go index 6db991e8f93..4cf7f7ea36a 100644 --- a/management/server/networks/resources/types/resource.go +++ b/management/server/networks/resources/types/resource.go @@ -29,20 +29,18 @@ func (p NetworkResourceType) String() string { } type NetworkResource struct { - ID string `gorm:"primaryKey"` - NetworkID string `gorm:"index"` - AccountID string `gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` - Name string - Description string - Type NetworkResourceType - Address string `gorm:"-"` - GroupIDs []string `gorm:"-"` - Domain string - Prefix netip.Prefix `gorm:"serializer:json"` - Enabled bool + ID string `gorm:"primaryKey"` + NetworkID string `gorm:"index"` + AccountID string `gorm:"index"` + PublicID string `json:"-"` + Name string + Description string + Type NetworkResourceType + Address string `gorm:"-"` + GroupIDs []string `gorm:"-"` + Domain string + Prefix netip.Prefix `gorm:"serializer:json"` + Enabled bool } func NewNetworkResource(accountID, networkID, name, description, address string, groupIDs []string, enabled bool) (*NetworkResource, error) { @@ -96,18 +94,18 @@ func (n *NetworkResource) FromAPIRequest(req *api.NetworkResourceRequest) { func (n *NetworkResource) Copy() *NetworkResource { return &NetworkResource{ - ID: n.ID, - AccountID: n.AccountID, - NetworkID: n.NetworkID, - AccountSeqID: n.AccountSeqID, - Name: n.Name, - Description: n.Description, - Type: n.Type, - Address: n.Address, - Domain: n.Domain, - Prefix: n.Prefix, - GroupIDs: n.GroupIDs, - Enabled: n.Enabled, + ID: n.ID, + AccountID: n.AccountID, + NetworkID: n.NetworkID, + PublicID: n.PublicID, + Name: n.Name, + Description: n.Description, + Type: n.Type, + Address: n.Address, + Domain: n.Domain, + Prefix: n.Prefix, + GroupIDs: n.GroupIDs, + Enabled: n.Enabled, } } diff --git a/management/server/networks/routers/manager.go b/management/server/networks/routers/manager.go index 24f989d5bc6..f727165798d 100644 --- a/management/server/networks/routers/manager.go +++ b/management/server/networks/routers/manager.go @@ -16,7 +16,6 @@ import ( "github.com/netbirdio/netbird/management/server/permissions/modules" "github.com/netbirdio/netbird/management/server/permissions/operations" "github.com/netbirdio/netbird/management/server/store" - serverTypes "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/shared/management/status" ) @@ -105,11 +104,7 @@ func (m *managerImpl) CreateRouter(ctx context.Context, userID string, router *t router.ID = xid.New().String() - seq, err := transaction.AllocateAccountSeqID(ctx, router.AccountID, serverTypes.AccountSeqEntityNetworkRouter) - if err != nil { - return fmt.Errorf("failed to allocate network router seq id: %w", err) - } - router.AccountSeqID = seq + router.PublicID = xid.New().String() err = transaction.CreateNetworkRouter(ctx, router) if err != nil { @@ -206,10 +201,10 @@ func (m *managerImpl) updateRouterInTransaction(ctx context.Context, transaction return nil, nil, affectedpeers.Change{}, status.NewRouterNotPartOfNetworkError(router.ID, router.NetworkID) } - // Preserve AccountSeqID from the existing router so the upstream + // Preserve PublicID from the existing router so the upstream // UpdateNetworkRouter (which does Updates(router) with Select("*")) // doesn't clobber it with the request's zero value. - router.AccountSeqID = existing.AccountSeqID + router.PublicID = existing.PublicID if err = transaction.UpdateNetworkRouter(ctx, router); err != nil { return nil, nil, affectedpeers.Change{}, fmt.Errorf("failed to update network router: %w", err) diff --git a/management/server/networks/routers/types/router.go b/management/server/networks/routers/types/router.go index 48dfff3aab4..189d7f792d1 100644 --- a/management/server/networks/routers/types/router.go +++ b/management/server/networks/routers/types/router.go @@ -10,17 +10,15 @@ import ( ) type NetworkRouter struct { - ID string `gorm:"primaryKey"` - NetworkID string `gorm:"index"` - AccountID string `gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` - Peer string - PeerGroups []string `gorm:"serializer:json"` - Masquerade bool - Metric int - Enabled bool + ID string `gorm:"primaryKey"` + NetworkID string `gorm:"index"` + AccountID string `gorm:"index"` + PublicID string `json:"-"` + Peer string + PeerGroups []string `gorm:"serializer:json"` + Masquerade bool + Metric int + Enabled bool } func NewNetworkRouter(accountID string, networkID string, peer string, peerGroups []string, masquerade bool, metric int, enabled bool) (*NetworkRouter, error) { @@ -81,15 +79,15 @@ func (n *NetworkRouter) FromAPIRequest(req *api.NetworkRouterRequest) { func (n *NetworkRouter) Copy() *NetworkRouter { return &NetworkRouter{ - ID: n.ID, - NetworkID: n.NetworkID, - AccountID: n.AccountID, - AccountSeqID: n.AccountSeqID, - Peer: n.Peer, - PeerGroups: n.PeerGroups, - Masquerade: n.Masquerade, - Metric: n.Metric, - Enabled: n.Enabled, + ID: n.ID, + NetworkID: n.NetworkID, + AccountID: n.AccountID, + PublicID: n.PublicID, + Peer: n.Peer, + PeerGroups: n.PeerGroups, + Masquerade: n.Masquerade, + Metric: n.Metric, + Enabled: n.Enabled, } } diff --git a/management/server/networks/types/network.go b/management/server/networks/types/network.go index ad43d9827b8..6f7381bffce 100644 --- a/management/server/networks/types/network.go +++ b/management/server/networks/types/network.go @@ -10,21 +10,12 @@ type Network struct { ID string `gorm:"primaryKey"` AccountID string `gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + PublicID string `json:"-"` Name string Description string } -// HasSeqID reports whether the network has been persisted long enough to have -// a per-account sequence id allocated. Wire encoders that key off AccountSeqID -// must skip networks that return false here. -func (n *Network) HasSeqID() bool { - return n != nil && n.AccountSeqID != 0 -} - func NewNetwork(accountId, name, description string) *Network { return &Network{ ID: xid.New().String(), @@ -56,11 +47,11 @@ func (n *Network) FromAPIRequest(req *api.NetworkRequest) { // Copy returns a copy of a network. func (n *Network) Copy() *Network { return &Network{ - ID: n.ID, - AccountID: n.AccountID, - AccountSeqID: n.AccountSeqID, - Name: n.Name, - Description: n.Description, + ID: n.ID, + AccountID: n.AccountID, + PublicID: n.PublicID, + Name: n.Name, + Description: n.Description, } } diff --git a/management/server/policy.go b/management/server/policy.go index 2516fbfa6ef..30b101aae19 100644 --- a/management/server/policy.go +++ b/management/server/policy.go @@ -67,18 +67,13 @@ func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, user action = activity.PolicyUpdated - policy.AccountSeqID = existingPolicy.AccountSeqID + policy.PublicID = existingPolicy.PublicID if err = transaction.SavePolicy(ctx, policy); err != nil { return err } } else { - seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPolicy) - if err != nil { - return err - } - policy.AccountSeqID = seq - + policy.PublicID = xid.New().String() if err = transaction.CreatePolicy(ctx, policy); err != nil { return err } diff --git a/management/server/posture/checks.go b/management/server/posture/checks.go index b5a84ab5c3d..72b71925254 100644 --- a/management/server/posture/checks.go +++ b/management/server/posture/checks.go @@ -49,9 +49,7 @@ type Checks struct { // AccountID is a reference to the Account that this object belongs AccountID string `json:"-" gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + PublicID string `json:"-"` // Checks is a set of objects that perform the actual checks Checks ChecksDefinition `gorm:"serializer:json"` @@ -97,13 +95,6 @@ func verdictChanged(ctx context.Context, check Check, oldPeer, newPeer nbpeer.Pe return changed } -// HasSeqID reports whether the posture check has been persisted long enough -// to have a per-account sequence id allocated. Wire encoders that key off -// AccountSeqID must skip checks that return false here. -func (pc *Checks) HasSeqID() bool { - return pc != nil && pc.AccountSeqID != 0 -} - // ChecksDefinition contains definition of actual check type ChecksDefinition struct { NBVersionCheck *NBVersionCheck `json:",omitempty"` @@ -174,12 +165,12 @@ func (*Checks) TableName() string { // Copy returns a copy of a posture checks. func (pc *Checks) Copy() *Checks { checks := &Checks{ - ID: pc.ID, - Name: pc.Name, - Description: pc.Description, - AccountID: pc.AccountID, - AccountSeqID: pc.AccountSeqID, - Checks: pc.Checks.Copy(), + ID: pc.ID, + Name: pc.Name, + Description: pc.Description, + AccountID: pc.AccountID, + PublicID: pc.PublicID, + Checks: pc.Checks.Copy(), } return checks } diff --git a/management/server/posture_checks.go b/management/server/posture_checks.go index c851390695e..08122686617 100644 --- a/management/server/posture_checks.go +++ b/management/server/posture_checks.go @@ -12,7 +12,6 @@ import ( "github.com/netbirdio/netbird/management/server/permissions/operations" "github.com/netbirdio/netbird/management/server/posture" "github.com/netbirdio/netbird/management/server/store" - "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/shared/management/status" ) @@ -57,15 +56,11 @@ func (am *DefaultAccountManager) SavePostureChecks(ctx context.Context, accountI if err != nil { return err } - postureChecks.AccountSeqID = existing.AccountSeqID + postureChecks.PublicID = existing.PublicID action = activity.PostureCheckUpdated } else { - seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPostureCheck) - if err != nil { - return err - } - postureChecks.AccountSeqID = seq + postureChecks.PublicID = xid.New().String() } postureChecks.AccountID = accountID diff --git a/management/server/posture_checks_test.go b/management/server/posture_checks_test.go index e6704b81335..74738e72d95 100644 --- a/management/server/posture_checks_test.go +++ b/management/server/posture_checks_test.go @@ -581,7 +581,7 @@ func TestSavePostureChecks_AllocatesSeqIDOnCreate(t *testing.T) { }, }, true) require.NoError(t, err) - require.NotZero(t, created.AccountSeqID, "SavePostureChecks on create must allocate a non-zero AccountSeqID") + require.NotEqual(t, "", created.PublicID, "SavePostureChecks on create must create PublicID") } // TestSavePostureChecks_PreservesSeqIDOnUpdate verifies the update path does @@ -601,8 +601,8 @@ func TestSavePostureChecks_PreservesSeqIDOnUpdate(t *testing.T) { }, }, true) require.NoError(t, err) - originalSeq := created.AccountSeqID - require.NotZero(t, originalSeq) + originalPublicID := created.PublicID + require.NotEqual(t, "", originalPublicID) update := &posture.Checks{ ID: created.ID, @@ -611,13 +611,13 @@ func TestSavePostureChecks_PreservesSeqIDOnUpdate(t *testing.T) { NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.27.0"}, }, } - require.Zero(t, update.AccountSeqID, "incoming struct must mirror an HTTP handler shape") + require.Equal(t, "", update.PublicID, "incoming struct must mirror an HTTP handler shape") _, err = am.SavePostureChecks(context.Background(), account.Id, adminUserID, update, false) require.NoError(t, err) got, err := am.GetPostureChecks(context.Background(), account.Id, created.ID, adminUserID) require.NoError(t, err) - require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must survive SavePostureChecks update") + require.Equal(t, originalPublicID, got.PublicID, "PublicID must survive SavePostureChecks update") require.Equal(t, "seq-preserve-renamed", got.Name) } diff --git a/management/server/route.go b/management/server/route.go index 9141dffe811..5a55bf2b398 100644 --- a/management/server/route.go +++ b/management/server/route.go @@ -175,11 +175,7 @@ func (am *DefaultAccountManager) CreateRoute(ctx context.Context, accountID stri return err } - seq, err := transaction.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityRoute) - if err != nil { - return err - } - newRoute.AccountSeqID = seq + newRoute.PublicID = xid.New().String() if err = transaction.SaveRoute(ctx, newRoute); err != nil { return err @@ -228,7 +224,7 @@ func (am *DefaultAccountManager) SaveRoute(ctx context.Context, accountID, userI } routeToSave.AccountID = accountID - routeToSave.AccountSeqID = oldRoute.AccountSeqID + routeToSave.PublicID = oldRoute.PublicID if err = transaction.SaveRoute(ctx, routeToSave); err != nil { return err diff --git a/management/server/store/account_seq_test.go b/management/server/store/account_seq_test.go deleted file mode 100644 index 7ee52e4ccec..00000000000 --- a/management/server/store/account_seq_test.go +++ /dev/null @@ -1,506 +0,0 @@ -package store - -import ( - "context" - "errors" - "net/netip" - "testing" - - "github.com/stretchr/testify/require" - - nbdns "github.com/netbirdio/netbird/dns" - resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" - routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" - networkTypes "github.com/netbirdio/netbird/management/server/networks/types" - "github.com/netbirdio/netbird/management/server/posture" - "github.com/netbirdio/netbird/management/server/types" - "github.com/netbirdio/netbird/route" -) - -var errRollback = errors.New("intentional rollback") - -func TestAllocateAccountSeqID_SequentialPerAccount(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accA = "acc-a" - const accB = "acc-b" - - require.NoError(t, store.ExecuteInTransaction(ctx, func(tx Store) error { - got, err := tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityPolicy) - require.NoError(t, err) - require.Equal(t, uint32(1), got) - - got, err = tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityPolicy) - require.NoError(t, err) - require.Equal(t, uint32(2), got) - - got, err = tx.AllocateAccountSeqID(ctx, accB, types.AccountSeqEntityPolicy) - require.NoError(t, err) - require.Equal(t, uint32(1), got, "different account starts from 1") - - got, err = tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityGroup) - require.NoError(t, err) - require.Equal(t, uint32(1), got, "different entity starts from 1") - - return nil - })) - - require.NoError(t, store.ExecuteInTransaction(ctx, func(tx Store) error { - got, err := tx.AllocateAccountSeqID(ctx, accA, types.AccountSeqEntityPolicy) - require.NoError(t, err) - require.Equal(t, uint32(3), got, "counter persists across transactions") - return nil - })) -} - -func TestPolicyBackfill_AssignsSeqIDsToExistingPolicies(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" - - policies, err := store.GetAccountPolicies(ctx, LockingStrengthNone, accountID) - require.NoError(t, err) - require.NotEmpty(t, policies, "test fixture must have policies") - - seen := make(map[uint32]bool) - for _, p := range policies { - require.NotZero(t, p.AccountSeqID, "policy %s must have a non-zero AccountSeqID after migration", p.ID) - require.False(t, seen[p.AccountSeqID], "duplicate AccountSeqID %d in account %s", p.AccountSeqID, accountID) - seen[p.AccountSeqID] = true - } -} - -func TestPolicyUpdate_PreservesSeqID(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" - const policyID = "cs1tnh0hhcjnqoiuebf0" - - original, err := store.GetPolicyByID(ctx, LockingStrengthNone, accountID, policyID) - require.NoError(t, err) - originalSeq := original.AccountSeqID - require.NotZero(t, originalSeq, "fixture must have non-zero AccountSeqID after backfill") - - updated := &types.Policy{ - ID: policyID, - AccountID: accountID, - Name: "renamed", - Enabled: false, - Rules: original.Rules, - } - require.Zero(t, updated.AccountSeqID, "incoming struct should have zero AccountSeqID like an HTTP handler would") - - require.NoError(t, store.SavePolicy(ctx, updated)) - - got, err := store.GetPolicyByID(ctx, LockingStrengthNone, accountID, policyID) - require.NoError(t, err) - require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must not be reset by update path") - require.Equal(t, "renamed", got.Name) -} - -func TestGroupUpdate_PreservesSeqID(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" - - groups, err := store.GetAccountGroups(ctx, LockingStrengthNone, accountID) - require.NoError(t, err) - require.NotEmpty(t, groups) - - original := groups[0] - originalSeq := original.AccountSeqID - require.NotZero(t, originalSeq) - - updated := &types.Group{ - ID: original.ID, - AccountID: accountID, - Name: "renamed", - Issued: original.Issued, - } - require.Zero(t, updated.AccountSeqID) - - require.NoError(t, store.UpdateGroup(ctx, updated)) - - got, err := store.GetGroupByID(ctx, LockingStrengthNone, accountID, original.ID) - require.NoError(t, err) - require.Equal(t, originalSeq, got.AccountSeqID, "AccountSeqID must not be reset by UpdateGroup") - require.Equal(t, "renamed", got.Name) -} - -func TestSaveAccount_AllocatesSeqIDsForDefaultGroupAndPolicy(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "save-account-seqid-test" - - account := &types.Account{ - Id: accountID, - CreatedBy: "user1", - Domain: "example.test", - DNSSettings: types.DNSSettings{}, - Settings: &types.Settings{}, - Network: &types.Network{ - Identifier: "net-test", - }, - Users: map[string]*types.User{ - "user1": {Id: "user1", AccountID: accountID, Role: types.UserRoleOwner}, - }, - } - require.NoError(t, account.AddAllGroup(false), "AddAllGroup should populate default Group + Policy") - require.Len(t, account.Groups, 1, "default 'All' group must be present") - require.Len(t, account.Policies, 1, "default policy must be present") - - for _, g := range account.Groups { - require.Zero(t, g.AccountSeqID, "default group must start with seq=0") - } - require.Zero(t, account.Policies[0].AccountSeqID, "default policy must start with seq=0") - - require.NoError(t, store.SaveAccount(ctx, account)) - - groups, err := store.GetAccountGroups(ctx, LockingStrengthNone, accountID) - require.NoError(t, err) - require.Len(t, groups, 1) - require.NotZerof(t, groups[0].AccountSeqID, "default group must have seq>0 after SaveAccount") - - policies, err := store.GetAccountPolicies(ctx, LockingStrengthNone, accountID) - require.NoError(t, err) - require.Len(t, policies, 1) - require.NotZerof(t, policies[0].AccountSeqID, "default policy must have seq>0 after SaveAccount") - - require.ErrorIs(t, store.ExecuteInTransaction(ctx, func(tx Store) error { - next, err := tx.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityGroup) - require.NoError(t, err) - require.Equal(t, groups[0].AccountSeqID+1, next, "next group seq must be max+1") - - next, err = tx.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPolicy) - require.NoError(t, err) - require.Equal(t, policies[0].AccountSeqID+1, next, "next policy seq must be max+1") - return errRollback - }), errRollback) -} - -func TestSaveAccount_PreservesExistingSeqIDs(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" - - account, err := store.GetAccount(ctx, accountID) - require.NoError(t, err) - - groupSeqs := make(map[string]uint32) - policySeqs := make(map[string]uint32) - routeSeqs := make(map[route.ID]uint32) - nsgSeqs := make(map[string]uint32) - resourceSeqs := make(map[string]uint32) - routerSeqs := make(map[string]uint32) - networkSeqs := make(map[string]uint32) - - for _, g := range account.Groups { - require.NotZero(t, g.AccountSeqID, "fixture group must have seq>0 after backfill") - groupSeqs[g.ID] = g.AccountSeqID - } - for _, p := range account.Policies { - require.NotZero(t, p.AccountSeqID, "fixture policy must have seq>0") - policySeqs[p.ID] = p.AccountSeqID - } - for _, r := range account.Routes { - require.NotZero(t, r.AccountSeqID, "fixture route must have seq>0") - routeSeqs[r.ID] = r.AccountSeqID - } - for _, n := range account.NameServerGroups { - require.NotZero(t, n.AccountSeqID, "fixture name_server_group must have seq>0") - nsgSeqs[n.ID] = n.AccountSeqID - } - for _, nr := range account.NetworkResources { - require.NotZero(t, nr.AccountSeqID, "fixture network_resource must have seq>0") - resourceSeqs[nr.ID] = nr.AccountSeqID - } - for _, nr := range account.NetworkRouters { - require.NotZero(t, nr.AccountSeqID, "fixture network_router must have seq>0") - routerSeqs[nr.ID] = nr.AccountSeqID - } - for _, n := range account.Networks { - require.NotZero(t, n.AccountSeqID, "fixture network must have seq>0 after backfill") - networkSeqs[n.ID] = n.AccountSeqID - } - - require.NoError(t, store.SaveAccount(ctx, account)) - - after, err := store.GetAccount(ctx, accountID) - require.NoError(t, err) - for _, g := range after.Groups { - require.Equal(t, groupSeqs[g.ID], g.AccountSeqID, "group %s seq must be preserved on re-save", g.ID) - } - for _, p := range after.Policies { - require.Equal(t, policySeqs[p.ID], p.AccountSeqID, "policy %s seq must be preserved", p.ID) - } - for _, r := range after.Routes { - require.Equal(t, routeSeqs[r.ID], r.AccountSeqID, "route %s seq must be preserved (slice-of-value addressability)", r.ID) - } - for _, n := range after.NameServerGroups { - require.Equal(t, nsgSeqs[n.ID], n.AccountSeqID, "name_server_group %s seq must be preserved (slice-of-value addressability)", n.ID) - } - for _, nr := range after.NetworkResources { - require.Equal(t, resourceSeqs[nr.ID], nr.AccountSeqID, "network_resource %s seq must be preserved", nr.ID) - } - for _, nr := range after.NetworkRouters { - require.Equal(t, routerSeqs[nr.ID], nr.AccountSeqID, "network_router %s seq must be preserved", nr.ID) - } - for _, n := range after.Networks { - require.Equal(t, networkSeqs[n.ID], n.AccountSeqID, "network %s seq must be preserved", n.ID) - } -} - -func TestSaveAccount_AllocatesSeqIDsForAllEntityTypes(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "save-account-all-entities" - - addr, err := netip.ParseAddr("8.8.8.8") - require.NoError(t, err) - - account := &types.Account{ - Id: accountID, - CreatedBy: "user1", - Domain: "example.test", - Settings: &types.Settings{}, - Network: &types.Network{Identifier: "net-test"}, - Users: map[string]*types.User{ - "user1": {Id: "user1", AccountID: accountID, Role: types.UserRoleOwner}, - }, - Groups: map[string]*types.Group{ - "g1": {ID: "g1", AccountID: accountID, Name: "g1", Issued: types.GroupIssuedAPI}, - }, - Policies: []*types.Policy{ - {ID: "p1", AccountID: accountID, Name: "p1", Enabled: true, - Rules: []*types.PolicyRule{{ID: "r1", PolicyID: "p1", Enabled: true}}}, - }, - Routes: map[route.ID]*route.Route{ - "rt1": {ID: "rt1", AccountID: accountID, NetID: "net1", Peer: "peer1"}, - }, - NameServerGroups: map[string]*nbdns.NameServerGroup{ - "nsg1": {ID: "nsg1", AccountID: accountID, Name: "nsg1", Enabled: true, - NameServers: []nbdns.NameServer{{IP: addr, NSType: nbdns.UDPNameServerType, Port: 53}}}, - }, - NetworkResources: []*resourceTypes.NetworkResource{ - {ID: "nr1", AccountID: accountID, NetworkID: "net1", Name: "res1", Enabled: true}, - }, - NetworkRouters: []*routerTypes.NetworkRouter{ - {ID: "nrt1", AccountID: accountID, NetworkID: "net1", Peer: "peer1", Enabled: true}, - }, - Networks: []*networkTypes.Network{ - {ID: "n1", AccountID: accountID, Name: "n1"}, - }, - PostureChecks: []*posture.Checks{ - {ID: "pc1", AccountID: accountID, Name: "pc1", - Checks: posture.ChecksDefinition{ - NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.26.0"}, - }}, - }, - } - - require.NoError(t, store.SaveAccount(ctx, account)) - - after, err := store.GetAccount(ctx, accountID) - require.NoError(t, err) - - require.Len(t, after.Groups, 1) - require.Len(t, after.Policies, 1) - require.Len(t, after.Routes, 1) - require.Len(t, after.NameServerGroups, 1) - require.Len(t, after.NetworkResources, 1) - require.Len(t, after.NetworkRouters, 1) - require.Len(t, after.Networks, 1) - require.Len(t, after.PostureChecks, 1) - - for _, g := range after.Groups { - require.NotZero(t, g.AccountSeqID, "group seq must be allocated") - } - for _, p := range after.Policies { - require.NotZero(t, p.AccountSeqID, "policy seq must be allocated") - } - for _, r := range after.Routes { - require.NotZero(t, r.AccountSeqID, "route seq must be allocated (slice-of-value addressability)") - } - for _, n := range after.NameServerGroups { - require.NotZero(t, n.AccountSeqID, "name_server_group seq must be allocated (slice-of-value addressability)") - } - for _, nr := range after.NetworkResources { - require.NotZero(t, nr.AccountSeqID, "network_resource seq must be allocated") - } - for _, nr := range after.NetworkRouters { - require.NotZero(t, nr.AccountSeqID, "network_router seq must be allocated") - } - for _, n := range after.Networks { - require.NotZero(t, n.AccountSeqID, "network seq must be allocated") - } - for _, pc := range after.PostureChecks { - require.NotZero(t, pc.AccountSeqID, "posture_check seq must be allocated") - } - - require.NoError(t, store.SaveAccount(ctx, after)) - final, err := store.GetAccount(ctx, accountID) - require.NoError(t, err) - for _, r := range final.Routes { - require.Equal(t, after.Routes[r.ID].AccountSeqID, r.AccountSeqID, "route seq preserved on re-save") - } - for _, n := range final.NameServerGroups { - require.Equal(t, after.NameServerGroups[n.ID].AccountSeqID, n.AccountSeqID, "name_server_group seq preserved on re-save") - } - afterByID := map[string]uint32{} - for _, n := range after.Networks { - afterByID[n.ID] = n.AccountSeqID - } - for _, n := range final.Networks { - require.Equal(t, afterByID[n.ID], n.AccountSeqID, "network seq preserved on re-save") - } - afterPCByID := map[string]uint32{} - for _, pc := range after.PostureChecks { - afterPCByID[pc.ID] = pc.AccountSeqID - } - for _, pc := range final.PostureChecks { - require.Equal(t, afterPCByID[pc.ID], pc.AccountSeqID, "posture_check seq preserved on re-save") - } -} - -func TestAllocateAccountSeqID_ConcurrentSameAccountEntity(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "concurrent-test" - const entity = types.AccountSeqEntityPolicy - const goroutines = 32 - - type result struct { - seq uint32 - err error - } - results := make(chan result, goroutines) - start := make(chan struct{}) - - for i := 0; i < goroutines; i++ { - go func() { - <-start - var allocated uint32 - err := store.ExecuteInTransaction(ctx, func(tx Store) error { - seq, err := tx.AllocateAccountSeqID(ctx, accountID, entity) - allocated = seq - return err - }) - results <- result{seq: allocated, err: err} - }() - } - close(start) - - seen := make(map[uint32]int, goroutines) - for i := 0; i < goroutines; i++ { - r := <-results - require.NoError(t, r.err, "concurrent allocate must not fail") - require.NotZero(t, r.seq, "allocated seq must be non-zero") - seen[r.seq]++ - } - - require.Lenf(t, seen, goroutines, "every concurrent allocation must yield a unique id; got duplicates in %v", seen) - for i := uint32(1); i <= goroutines; i++ { - require.Equalf(t, 1, seen[i], "id %d must appear exactly once across concurrent allocations", i) - } -} - -func TestStoreCreateGroups_AllocatedSeqIDIsNotClobbered(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" - - groups := []*types.Group{ - {ID: "seq-test-g1", AccountID: accountID, Name: "g1", Issued: "jwt", AccountSeqID: 7777}, - {ID: "seq-test-g2", AccountID: accountID, Name: "g2", Issued: "jwt", AccountSeqID: 7778}, - } - require.NoError(t, store.CreateGroups(ctx, accountID, groups)) - - for _, want := range groups { - got, err := store.GetGroupByID(ctx, LockingStrengthNone, accountID, want.ID) - require.NoError(t, err) - require.Equal(t, want.AccountSeqID, got.AccountSeqID, "seq id from caller must be persisted on insert") - } - - groups[0].Name = "g1-renamed" - groups[0].AccountSeqID = 0 - require.NoError(t, store.CreateGroups(ctx, accountID, groups[:1])) - - got, err := store.GetGroupByID(ctx, LockingStrengthNone, accountID, "seq-test-g1") - require.NoError(t, err) - require.Equal(t, "g1-renamed", got.Name, "upsert path still updates other columns") - require.Equal(t, uint32(7777), got.AccountSeqID, "upsert path must NOT overwrite account_seq_id") -} - -func TestPolicyCreate_AllocatesSeqID(t *testing.T) { - store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir()) - t.Cleanup(cleanup) - require.NoError(t, err) - - ctx := context.Background() - const accountID = "bf1c8084-ba50-4ce7-9439-34653001fc3b" - - existing, err := store.GetAccountPolicies(ctx, LockingStrengthNone, accountID) - require.NoError(t, err) - maxSeq := uint32(0) - for _, p := range existing { - if p.AccountSeqID > maxSeq { - maxSeq = p.AccountSeqID - } - } - - require.NoError(t, store.ExecuteInTransaction(ctx, func(tx Store) error { - seq, err := tx.AllocateAccountSeqID(ctx, accountID, types.AccountSeqEntityPolicy) - if err != nil { - return err - } - require.Equal(t, maxSeq+1, seq, "next id should be max+1 after backfill") - - newPolicy := &types.Policy{ - ID: "bench-new-policy", - AccountID: accountID, - AccountSeqID: seq, - Enabled: true, - Rules: []*types.PolicyRule{{ - ID: "bench-new-policy-rule", - PolicyID: "bench-new-policy", - Enabled: true, - Action: types.PolicyTrafficActionAccept, - Sources: []string{"groupA"}, - Destinations: []string{"groupC"}, - Bidirectional: true, - }}, - } - return tx.CreatePolicy(ctx, newPolicy) - })) - - created, err := store.GetPolicyByID(ctx, LockingStrengthNone, accountID, "bench-new-policy") - require.NoError(t, err) - require.Equal(t, maxSeq+1, created.AccountSeqID) -} diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 839b6df2a63..4806563ef5e 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -142,7 +142,6 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine types.Engine, met &agentNetworkTypes.Consumption{}, &agentNetworkTypes.AccountBudgetRule{}, &agentNetworkTypes.AgentNetworkAccessLog{}, &agentNetworkTypes.AgentNetworkAccessLogGroup{}, &agentNetworkTypes.AgentNetworkUsage{}, &agentNetworkTypes.AgentNetworkUsageGroup{}, - &types.AccountSeqCounter{}, ) if err != nil { return nil, fmt.Errorf("auto migratePreAuto: %w", err) @@ -314,10 +313,6 @@ func (s *SqlStore) SaveAccount(ctx context.Context, account *types.Account) erro return result.Error } - if err := s.assignAccountSeqIDs(ctx, tx, account); err != nil { - return fmt.Errorf("assign seq ids: %w", err) - } - result = tx. Session(&gorm.Session{FullSaveAssociations: true}). Clauses(clause.OnConflict{UpdateAll: true}). @@ -2060,7 +2055,7 @@ func (s *SqlStore) getGroups(ctx context.Context, accountID string) ([]*types.Gr var resources []byte var refID sql.NullInt64 var refType sql.NullString - err := row.Scan(&g.ID, &g.AccountID, &g.AccountSeqID, &g.Name, &g.Issued, &resources, &refID, &refType) + err := row.Scan(&g.ID, &g.AccountID, &g.PublicID, &g.Name, &g.Issued, &resources, &refID, &refType) if err == nil { if refID.Valid { g.IntegrationReference.ID = int(refID.Int64) @@ -2094,7 +2089,7 @@ func (s *SqlStore) getPolicies(ctx context.Context, accountID string) ([]*types. var p types.Policy var checks []byte var enabled sql.NullBool - err := row.Scan(&p.ID, &p.AccountID, &p.AccountSeqID, &p.Name, &p.Description, &enabled, &checks) + err := row.Scan(&p.ID, &p.AccountID, &p.PublicID, &p.Name, &p.Description, &enabled, &checks) if err == nil { if enabled.Valid { p.Enabled = enabled.Bool @@ -2122,7 +2117,7 @@ func (s *SqlStore) getRoutes(ctx context.Context, accountID string) ([]route.Rou var network, domains, peerGroups, groups, accessGroups []byte var keepRoute, masquerade, enabled, skipAutoApply sql.NullBool var metric sql.NullInt64 - err := row.Scan(&r.ID, &r.AccountID, &r.AccountSeqID, &network, &domains, &keepRoute, &r.NetID, &r.Description, &r.Peer, &peerGroups, &r.NetworkType, &masquerade, &metric, &enabled, &groups, &accessGroups, &skipAutoApply) + err := row.Scan(&r.ID, &r.AccountID, &r.PublicID, &network, &domains, &keepRoute, &r.NetID, &r.Description, &r.Peer, &peerGroups, &r.NetworkType, &masquerade, &metric, &enabled, &groups, &accessGroups, &skipAutoApply) if err == nil { if keepRoute.Valid { r.KeepRoute = keepRoute.Bool @@ -2173,7 +2168,7 @@ func (s *SqlStore) getNameServerGroups(ctx context.Context, accountID string) ([ var n nbdns.NameServerGroup var ns, groups, domains []byte var primary, enabled, searchDomainsEnabled sql.NullBool - err := row.Scan(&n.ID, &n.AccountID, &n.AccountSeqID, &n.Name, &n.Description, &ns, &groups, &primary, &domains, &enabled, &searchDomainsEnabled) + err := row.Scan(&n.ID, &n.AccountID, &n.PublicID, &n.Name, &n.Description, &ns, &groups, &primary, &domains, &enabled, &searchDomainsEnabled) if err == nil { if primary.Valid { n.Primary = primary.Bool @@ -2217,7 +2212,7 @@ func (s *SqlStore) getPostureChecks(ctx context.Context, accountID string) ([]*p checks, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (*posture.Checks, error) { var c posture.Checks var checksDef []byte - err := row.Scan(&c.ID, &c.AccountID, &c.AccountSeqID, &c.Name, &c.Description, &checksDef) + err := row.Scan(&c.ID, &c.AccountID, &c.PublicID, &c.Name, &c.Description, &checksDef) if err == nil && checksDef != nil { _ = json.Unmarshal(checksDef, &c.Checks) } @@ -2424,7 +2419,7 @@ func (s *SqlStore) getNetworkRouters(ctx context.Context, accountID string) ([]* var peerGroups []byte var masquerade, enabled sql.NullBool var metric sql.NullInt64 - err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.AccountSeqID, &r.Peer, &peerGroups, &masquerade, &metric, &enabled) + err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.PublicID, &r.Peer, &peerGroups, &masquerade, &metric, &enabled) if err == nil { if masquerade.Valid { r.Masquerade = masquerade.Bool @@ -2461,7 +2456,7 @@ func (s *SqlStore) getNetworkResources(ctx context.Context, accountID string) ([ var r resourceTypes.NetworkResource var prefix []byte var enabled sql.NullBool - err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.AccountSeqID, &r.Name, &r.Description, &r.Type, &r.Domain, &prefix, &enabled) + err := row.Scan(&r.ID, &r.NetworkID, &r.AccountID, &r.PublicID, &r.Name, &r.Description, &r.Type, &r.Domain, &prefix, &enabled) if err == nil { if enabled.Valid { r.Enabled = enabled.Bool @@ -3634,262 +3629,6 @@ func (s *SqlStore) withTx(tx *gorm.DB) Store { } } -// AllocateAccountSeqID returns the next per-account integer id for the given -// component kind. Must be called inside ExecuteInTransaction so the increment -// is serialized with the component insert. -func (s *SqlStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (int32, error) { - return allocateAccountSeqID(ctx, s.db, s.storeEngine, accountID, entity) -} - -func allocateAccountSeqID(_ context.Context, db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity) (int32, error) { - switch engine { - case types.PostgresStoreEngine, types.SqliteStoreEngine: - return allocateAccountSeqIDReturning(db, accountID, entity) - case types.MysqlStoreEngine: - return allocateAccountSeqIDMysql(db, accountID, entity) - default: - return 0, fmt.Errorf("unsupported store engine for account_seq allocator: %v", engine) - } -} - -// allocateAccountSeqIDReturning runs a single atomic INSERT ... ON CONFLICT -// DO UPDATE ... RETURNING that gives us the allocated id without a separate -// SELECT FOR UPDATE. Two concurrent allocations for the same (account, entity) -// produce two distinct ids: one wins the INSERT, the other wins the UPDATE -// branch and returns next_id+1. -func allocateAccountSeqIDReturning(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (int32, error) { - const sqlStr = ` - INSERT INTO account_seq_counters (account_id, entity, next_id) - VALUES (?, ?, 2) - ON CONFLICT (account_id, entity) DO UPDATE - SET next_id = account_seq_counters.next_id + 1 - RETURNING (next_id - 1) - ` - var allocated int32 - if err := db.Raw(sqlStr, accountID, string(entity)).Scan(&allocated).Error; err != nil { - return 0, fmt.Errorf("upsert account seq counter: %w", err) - } - if allocated == 0 { - return 0, fmt.Errorf("upsert account seq counter returned 0") - } - return allocated, nil -} - -// allocateAccountSeqIDMysql is the MySQL equivalent of allocateAccountSeqIDReturning. -// MySQL has no RETURNING on ON DUPLICATE KEY UPDATE, so we use the LAST_INSERT_ID -// trick: passing an expression to LAST_INSERT_ID(expr) both sets the session value -// and returns it from the INSERT. The INSERT's value uses LAST_INSERT_ID(2) so the -// no-conflict path also surfaces the new next_id, keeping the read-back uniform. -// LAST_INSERT_ID is per-connection; GORM transactions pin a single connection, -// so the follow-up SELECT sees the same value. -func allocateAccountSeqIDMysql(db *gorm.DB, accountID string, entity types.AccountSeqEntity) (int32, error) { - const upsertSQL = ` - INSERT INTO account_seq_counters (account_id, entity, next_id) - VALUES (?, ?, LAST_INSERT_ID(2)) - ON DUPLICATE KEY UPDATE next_id = LAST_INSERT_ID(next_id + 1) - ` - if err := db.Exec(upsertSQL, accountID, string(entity)).Error; err != nil { - return 0, fmt.Errorf("upsert account seq counter: %w", err) - } - var newNext uint64 - if err := db.Raw("SELECT LAST_INSERT_ID()").Scan(&newNext).Error; err != nil { - return 0, fmt.Errorf("get last insert id: %w", err) - } - if newNext == 0 { - return 0, fmt.Errorf("LAST_INSERT_ID returned 0; account_seq_counters misconfigured") - } - return int32(newNext - 1), nil -} - -// assignAccountSeqIDs allocates a per-account integer id for any component on -// the in-memory account whose AccountSeqID is zero. Called from SaveAccount so -// the canonical "save the whole account" path produces the same persisted seq -// ids that the manager-level Create paths produce. Update flows that go -// through SaveAccount preserve existing non-zero values; for those, the -// per-entity counter is bumped so subsequent AllocateAccountSeqID calls don't -// hand out a colliding id. -func (s *SqlStore) assignAccountSeqIDs(ctx context.Context, tx *gorm.DB, account *types.Account) error { - maxByEntity := make(map[types.AccountSeqEntity]int32, 8) - bump := func(entity types.AccountSeqEntity, seq int32) { - if seq > maxByEntity[entity] { - maxByEntity[entity] = seq - } - } - - for i := range account.GroupsG { - g := account.GroupsG[i] - if g == nil { - continue - } - if g.AccountSeqID != 0 { - bump(types.AccountSeqEntityGroup, g.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityGroup) - if err != nil { - return err - } - g.AccountSeqID = seq - // Defensive: generateAccountSQLTypes currently aliases the same - // *Group pointer into GroupsG and Groups[id] (so this is a no-op - // today), but mirror the seq anyway so any future divergence in - // how the two collections are populated doesn't silently leave - // the canonical map view stale. - if original, ok := account.Groups[g.ID]; ok && original != nil && original != g { - original.AccountSeqID = seq - } - } - for _, p := range account.Policies { - if p == nil { - continue - } - if p.AccountSeqID != 0 { - bump(types.AccountSeqEntityPolicy, p.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityPolicy) - if err != nil { - return err - } - p.AccountSeqID = seq - } - for i := range account.RoutesG { - r := &account.RoutesG[i] - if r.AccountSeqID != 0 { - bump(types.AccountSeqEntityRoute, r.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityRoute) - if err != nil { - return err - } - r.AccountSeqID = seq - // Mirror the new seq onto the canonical map view so callers that - // hold the same in-memory account post-Save read a consistent - // AccountSeqID — without this, components/encoder code would see - // 0 for routes saved this transaction until the account is reloaded. - if original, ok := account.Routes[r.ID]; ok && original != nil { - original.AccountSeqID = seq - } - } - for i := range account.NameServerGroupsG { - ng := &account.NameServerGroupsG[i] - if ng.AccountSeqID != 0 { - bump(types.AccountSeqEntityNameserverGroup, ng.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNameserverGroup) - if err != nil { - return err - } - ng.AccountSeqID = seq - if original, ok := account.NameServerGroups[ng.ID]; ok && original != nil { - original.AccountSeqID = seq - } - } - for _, nr := range account.NetworkResources { - if nr == nil { - continue - } - if nr.AccountSeqID != 0 { - bump(types.AccountSeqEntityNetworkResource, nr.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetworkResource) - if err != nil { - return err - } - nr.AccountSeqID = seq - } - for _, nr := range account.NetworkRouters { - if nr == nil { - continue - } - if nr.AccountSeqID != 0 { - bump(types.AccountSeqEntityNetworkRouter, nr.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetworkRouter) - if err != nil { - return err - } - nr.AccountSeqID = seq - } - for _, n := range account.Networks { - if n == nil { - continue - } - if n.AccountSeqID != 0 { - bump(types.AccountSeqEntityNetwork, n.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityNetwork) - if err != nil { - return err - } - n.AccountSeqID = seq - } - for _, pc := range account.PostureChecks { - if pc == nil { - continue - } - if pc.AccountSeqID != 0 { - bump(types.AccountSeqEntityPostureCheck, pc.AccountSeqID) - continue - } - seq, err := allocateAccountSeqID(ctx, tx, s.storeEngine, account.Id, types.AccountSeqEntityPostureCheck) - if err != nil { - return err - } - pc.AccountSeqID = seq - } - for entity, maxSeq := range maxByEntity { - if err := ensureAccountSeqCounter(tx, s.storeEngine, account.Id, entity, maxSeq+1); err != nil { - return fmt.Errorf("seed counter for %s: %w", entity, err) - } - } - return nil -} - -// ensureAccountSeqCounter raises the per-account counter for entity to at -// least target. Used when SaveAccount persists components that already carry -// AccountSeqIDs (e.g. test bulk-load from sqlite to postgres, or migrations -// running before component data lands) so that the next AllocateAccountSeqID -// call returns a fresh id beyond what was just written. -func ensureAccountSeqCounter(db *gorm.DB, engine types.Engine, accountID string, entity types.AccountSeqEntity, target int32) error { - switch engine { - case types.PostgresStoreEngine, types.SqliteStoreEngine: - const sqlStr = ` - INSERT INTO account_seq_counters (account_id, entity, next_id) - VALUES (?, ?, ?) - ON CONFLICT (account_id, entity) DO UPDATE - SET next_id = GREATEST(account_seq_counters.next_id, EXCLUDED.next_id) - ` - // sqlite's UPSERT understands max() but the migration uses GREATEST - // for postgres and max() for sqlite. We collapse to dialect-specific - // statements only when needed. - if engine == types.SqliteStoreEngine { - const sqliteSQL = ` - INSERT INTO account_seq_counters (account_id, entity, next_id) - VALUES (?, ?, ?) - ON CONFLICT (account_id, entity) DO UPDATE - SET next_id = max(account_seq_counters.next_id, excluded.next_id) - ` - return db.Exec(sqliteSQL, accountID, string(entity), target).Error - } - return db.Exec(sqlStr, accountID, string(entity), target).Error - case types.MysqlStoreEngine: - const sqlStr = ` - INSERT INTO account_seq_counters (account_id, entity, next_id) - VALUES (?, ?, ?) - ON DUPLICATE KEY UPDATE next_id = GREATEST(next_id, VALUES(next_id)) - ` - return db.Exec(sqlStr, accountID, string(entity), target).Error - default: - return fmt.Errorf("unsupported store engine for account_seq counter: %v", engine) - } -} - // transaction wraps a GORM transaction with MySQL-specific FK checks handling // Use this instead of db.Transaction() directly to avoid deadlocks on MySQL/Aurora func (s *SqlStore) transaction(fn func(*gorm.DB) error) error { diff --git a/management/server/store/store.go b/management/server/store/store.go index 5c3ea4d79d0..908c199f568 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -223,11 +223,6 @@ type Store interface { GetStoreEngine() types.Engine ExecuteInTransaction(ctx context.Context, f func(store Store) error) error - // AllocateAccountSeqID returns the next per-account integer id for the given - // component kind. Must run inside a transaction so the increment is serialized - // with the component insert. - AllocateAccountSeqID(ctx context.Context, accountID string, entity types.AccountSeqEntity) (int32, error) - GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*networkTypes.Network, error) GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networkTypes.Network, error) SaveNetwork(ctx context.Context, network *networkTypes.Network) error @@ -629,30 +624,6 @@ func getMigrationsPostAuto(ctx context.Context) []migrationFunc { func(db *gorm.DB) error { return migration.DropIndex[proxy.Proxy](ctx, db, "idx_proxy_account_id_unique") }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[types.Policy](ctx, db, types.AccountSeqEntityPolicy, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[types.Group](ctx, db, types.AccountSeqEntityGroup, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[route.Route](ctx, db, types.AccountSeqEntityRoute, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[resourceTypes.NetworkResource](ctx, db, types.AccountSeqEntityNetworkResource, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[routerTypes.NetworkRouter](ctx, db, types.AccountSeqEntityNetworkRouter, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[dns.NameServerGroup](ctx, db, types.AccountSeqEntityNameserverGroup, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[networkTypes.Network](ctx, db, types.AccountSeqEntityNetwork, "id") - }, - func(db *gorm.DB) error { - return migration.BackfillAccountSeqIDs[posture.Checks](ctx, db, types.AccountSeqEntityPostureCheck, "id") - }, } } diff --git a/management/server/store/store_mock.go b/management/server/store/store_mock.go index 2416432eecb..2da9881ded4 100644 --- a/management/server/store/store_mock.go +++ b/management/server/store/store_mock.go @@ -138,21 +138,6 @@ func (mr *MockStoreMockRecorder) AddResourceToGroup(ctx, accountId, groupID, res return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResourceToGroup", reflect.TypeOf((*MockStore)(nil).AddResourceToGroup), ctx, accountId, groupID, resource) } -// AllocateAccountSeqID mocks base method. -func (m *MockStore) AllocateAccountSeqID(ctx context.Context, accountID string, entity types3.AccountSeqEntity) (int32, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AllocateAccountSeqID", ctx, accountID, entity) - ret0, _ := ret[0].(int32) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AllocateAccountSeqID indicates an expected call of AllocateAccountSeqID. -func (mr *MockStoreMockRecorder) AllocateAccountSeqID(ctx, accountID, entity interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocateAccountSeqID", reflect.TypeOf((*MockStore)(nil).AllocateAccountSeqID), ctx, accountID, entity) -} - // ApproveAccountPeers mocks base method. func (m *MockStore) ApproveAccountPeers(ctx context.Context, accountID string) (int, error) { m.ctrl.T.Helper() diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 77dab9f6c80..556f3a3a58a 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -125,26 +125,26 @@ func (a *Account) GetPeerNetworkMapComponents( } components := &NetworkMapComponents{ - PeerID: peerID, - Network: a.Network.Copy(), - NameServerGroups: make([]*nbdns.NameServerGroup, 0), - CustomZoneDomain: peersCustomZone.Domain, - ResourcePoliciesMap: make(map[string][]*Policy), - RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), - NetworkResources: make([]*resourceTypes.NetworkResource, 0), - PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)), - RouterPeers: make(map[string]*nbpeer.Peer), - NetworkXIDToSeq: make(map[string]int32, len(a.Networks)), - PostureCheckXIDToSeq: make(map[string]int32, len(a.PostureChecks)), + PeerID: peerID, + Network: a.Network.Copy(), + NameServerGroups: make([]*nbdns.NameServerGroup, 0), + CustomZoneDomain: peersCustomZone.Domain, + ResourcePoliciesMap: make(map[string][]*Policy), + RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), + NetworkResources: make([]*resourceTypes.NetworkResource, 0), + PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)), + RouterPeers: make(map[string]*nbpeer.Peer), + NetworkXIDToPublicID: make(map[string]string, len(a.Networks)), + PostureCheckXIDToPublicID: make(map[string]string, len(a.PostureChecks)), } for _, n := range a.Networks { - if n != nil && n.HasSeqID() { - components.NetworkXIDToSeq[n.ID] = n.AccountSeqID + if n != nil { + components.NetworkXIDToPublicID[n.ID] = n.PublicID } } for _, pc := range a.PostureChecks { - if pc != nil && pc.HasSeqID() { - components.PostureCheckXIDToSeq[pc.ID] = pc.AccountSeqID + if pc != nil { + components.PostureCheckXIDToPublicID[pc.ID] = pc.PublicID } } diff --git a/management/server/types/account_seq_counter.go b/management/server/types/account_seq_counter.go deleted file mode 100644 index 80ce74099db..00000000000 --- a/management/server/types/account_seq_counter.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -// AccountSeqEntity identifies the kind of component that uses a per-account sequence. -type AccountSeqEntity string - -const ( - AccountSeqEntityPolicy AccountSeqEntity = "policy" - AccountSeqEntityGroup AccountSeqEntity = "group" - AccountSeqEntityRoute AccountSeqEntity = "route" - AccountSeqEntityNetworkResource AccountSeqEntity = "network_resource" - AccountSeqEntityNetworkRouter AccountSeqEntity = "network_router" - AccountSeqEntityNameserverGroup AccountSeqEntity = "nameserver_group" - AccountSeqEntityNetwork AccountSeqEntity = "network" - AccountSeqEntityPostureCheck AccountSeqEntity = "posture_check" -) - -// AccountSeqCounter tracks the next per-account integer id for a given component -// kind. Reads/writes go through the store inside the same transaction as the -// component insert so two concurrent inserts cannot collide on the same id. -type AccountSeqCounter struct { - AccountID string `gorm:"primaryKey;size:255"` - Entity string `gorm:"primaryKey;size:32"` - NextID uint32 `gorm:"not null;default:1"` -} - -// TableName overrides the GORM-derived table name. -func (AccountSeqCounter) TableName() string { - return "account_seq_counters" -} diff --git a/management/server/types/networkmap_components_correctness_test.go b/management/server/types/networkmap_components_correctness_test.go index 1e303530043..825d51d4edf 100644 --- a/management/server/types/networkmap_components_correctness_test.go +++ b/management/server/types/networkmap_components_correctness_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/rs/xid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -88,13 +89,13 @@ func buildScalableTestAccount(numPeers, numGroups int, withDefaultPolicy bool) ( for i := start; i < end; i++ { groupPeers = append(groupPeers, fmt.Sprintf("peer-%d", i)) } - groups[groupID] = &types.Group{ID: groupID, Name: fmt.Sprintf("Group %d", g), Peers: groupPeers} + groups[groupID] = &types.Group{ID: groupID, PublicID: xid.New().String(), Name: fmt.Sprintf("Group %d", g), Peers: groupPeers} } policies := make([]*types.Policy, 0, numGroups+2) if withDefaultPolicy { policies = append(policies, &types.Policy{ - ID: "policy-all", Name: "Default-Allow", Enabled: true, + ID: "policy-all", PublicID: xid.New().String(), Name: "Default-Allow", Enabled: true, Rules: []*types.PolicyRule{{ ID: "rule-all", Name: "Allow All", Enabled: true, Action: types.PolicyTrafficActionAccept, Protocol: types.PolicyRuleProtocolALL, Bidirectional: true, @@ -107,7 +108,7 @@ func buildScalableTestAccount(numPeers, numGroups int, withDefaultPolicy bool) ( groupID := fmt.Sprintf("group-%d", g) dstGroup := fmt.Sprintf("group-%d", (g+1)%numGroups) policies = append(policies, &types.Policy{ - ID: fmt.Sprintf("policy-%d", g), Name: fmt.Sprintf("Policy %d", g), Enabled: true, + ID: fmt.Sprintf("policy-%d", g), PublicID: xid.New().String(), Name: fmt.Sprintf("Policy %d", g), Enabled: true, Rules: []*types.PolicyRule{{ ID: fmt.Sprintf("rule-%d", g), Name: fmt.Sprintf("Rule %d", g), Enabled: true, Action: types.PolicyTrafficActionAccept, Protocol: types.PolicyRuleProtocolTCP, @@ -120,7 +121,7 @@ func buildScalableTestAccount(numPeers, numGroups int, withDefaultPolicy bool) ( if numGroups >= 2 { policies = append(policies, &types.Policy{ - ID: "policy-drop", Name: "Drop DB traffic", Enabled: true, + ID: "policy-drop", PublicID: xid.New().String(), Name: "Drop DB traffic", Enabled: true, Rules: []*types.PolicyRule{{ ID: "rule-drop", Name: "Drop DB", Enabled: true, Action: types.PolicyTrafficActionDrop, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"5432"}, Bidirectional: true, @@ -144,6 +145,7 @@ func buildScalableTestAccount(numPeers, numGroups int, withDefaultPolicy bool) ( groupID := fmt.Sprintf("group-%d", r%numGroups) routes[routeID] = &route.Route{ ID: routeID, + PublicID: xid.New().String(), Network: netip.MustParsePrefix(fmt.Sprintf("10.%d.0.0/16", r)), Peer: peers[routePeerID].Key, PeerID: routePeerID, @@ -178,18 +180,18 @@ func buildScalableTestAccount(numPeers, numGroups int, withDefaultPolicy bool) ( } routerPeerID := fmt.Sprintf("peer-%d", routerPeerIdx) - networksList = append(networksList, &networkTypes.Network{ID: netID, Name: fmt.Sprintf("Network %d", nr), AccountID: "test-account"}) + networksList = append(networksList, &networkTypes.Network{ID: netID, PublicID: xid.New().String(), Name: fmt.Sprintf("Network %d", nr), AccountID: "test-account"}) networkResources = append(networkResources, &resourceTypes.NetworkResource{ - ID: resID, NetworkID: netID, AccountID: "test-account", Enabled: true, + ID: resID, PublicID: xid.New().String(), NetworkID: netID, AccountID: "test-account", Enabled: true, Address: fmt.Sprintf("svc-%d.netbird.cloud", nr), }) networkRouters = append(networkRouters, &routerTypes.NetworkRouter{ - ID: fmt.Sprintf("router-%d", nr), NetworkID: netID, Peer: routerPeerID, + ID: fmt.Sprintf("router-%d", nr), PublicID: xid.New().String(), NetworkID: netID, Peer: routerPeerID, Enabled: true, AccountID: "test-account", }) policies = append(policies, &types.Policy{ - ID: fmt.Sprintf("policy-res-%d", nr), Name: fmt.Sprintf("Resource Policy %d", nr), Enabled: true, + ID: fmt.Sprintf("policy-res-%d", nr), PublicID: xid.New().String(), Name: fmt.Sprintf("Resource Policy %d", nr), Enabled: true, SourcePostureChecks: []string{"posture-check-ver"}, Rules: []*types.PolicyRule{{ ID: fmt.Sprintf("rule-res-%d", nr), Name: fmt.Sprintf("Allow Resource %d", nr), Enabled: true, @@ -215,12 +217,12 @@ func buildScalableTestAccount(numPeers, numGroups int, withDefaultPolicy bool) ( DNSSettings: types.DNSSettings{DisabledManagementGroups: []string{}}, NameServerGroups: map[string]*nbdns.NameServerGroup{ "ns-group-main": { - ID: "ns-group-main", Name: "Main NS", Enabled: true, Groups: []string{"group-all"}, + ID: "ns-group-main", PublicID: xid.New().String(), Name: "Main NS", Enabled: true, Groups: []string{"group-all"}, NameServers: []nbdns.NameServer{{IP: netip.MustParseAddr("8.8.8.8"), NSType: nbdns.UDPNameServerType, Port: 53}}, }, }, PostureChecks: []*posture.Checks{ - {ID: "posture-check-ver", Name: "Check version", Checks: posture.ChecksDefinition{ + {ID: "posture-check-ver", PublicID: xid.New().String(), Name: "Check version", Checks: posture.ChecksDefinition{ NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.26.0"}, }}, }, diff --git a/management/server/types/networkmap_wire_benchmark_test.go b/management/server/types/networkmap_wire_benchmark_test.go index 087b1cbfa4b..ee9839a3ff0 100644 --- a/management/server/types/networkmap_wire_benchmark_test.go +++ b/management/server/types/networkmap_wire_benchmark_test.go @@ -24,23 +24,6 @@ var wireBenchScales = []benchmarkScale{ {"5000peers_100groups", 5000, 100}, } -// populateAccountSeqIDs assigns deterministic AccountSeqIDs to every group and -// policy in the account so that the component encoder can reference them. The -// scalableTestAccount fixture builds entities by struct literal and skips this -// step, but production paths populate the IDs via the store layer. -func populateAccountSeqIDs(account *types.Account) { - var nextGroupSeq uint32 = 1 - for _, g := range account.Groups { - g.AccountSeqID = nextGroupSeq - nextGroupSeq++ - } - var nextPolicySeq uint32 = 1 - for _, p := range account.Policies { - p.AccountSeqID = nextPolicySeq - nextPolicySeq++ - } -} - // assignValidWgKeys overwrites every peer's Key with a valid base64-encoded // 32-byte string. The default scalableTestAccount uses unparsable strings // like "key-peer-0", which makes the components encoder emit a nil WgPubKey @@ -64,7 +47,7 @@ func BenchmarkNetworkMapWireEncode(b *testing.B) { for _, scale := range wireBenchScales { account, validatedPeers := scalableTestAccount(scale.peers, scale.groups) - populateAccountSeqIDs(account) + // populateAccountSeqIDs(account) assignValidWgKeys(account) ctx := context.Background() @@ -135,7 +118,7 @@ func BenchmarkNetworkMapWireSize(b *testing.B) { for _, scale := range wireBenchScales { account, validatedPeers := scalableTestAccount(scale.peers, scale.groups) - populateAccountSeqIDs(account) + // populateAccountSeqIDs(account) assignValidWgKeys(account) ctx := context.Background() diff --git a/management/server/types/networkmap_wire_breakdown_test.go b/management/server/types/networkmap_wire_breakdown_test.go index 39b0b81c4b7..ac2855fa3ca 100644 --- a/management/server/types/networkmap_wire_breakdown_test.go +++ b/management/server/types/networkmap_wire_breakdown_test.go @@ -30,7 +30,6 @@ func TestNetworkMapWireBreakdown(t *testing.T) { const peerCount, groupCount = 5000, 100 account, validatedPeers := scalableTestAccount(peerCount, groupCount) - populateAccountSeqIDs(account) assignValidWgKeys(account) ctx := context.Background() diff --git a/route/route.go b/route/route.go index b926dc4c068..3bdb0a3a196 100644 --- a/route/route.go +++ b/route/route.go @@ -95,9 +95,7 @@ type Route struct { ID ID `gorm:"primaryKey"` // AccountID is a reference to Account that this object belongs AccountID string `gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + PublicID string `json:"-"` // Network and Domains are mutually exclusive Network netip.Prefix `gorm:"serializer:json"` Domains domain.List `gorm:"serializer:json"` @@ -131,7 +129,7 @@ func (r *Route) Copy() *Route { route := &Route{ ID: r.ID, AccountID: r.AccountID, - AccountSeqID: r.AccountSeqID, + PublicID: r.PublicID, Description: r.Description, NetID: r.NetID, Network: r.Network, diff --git a/shared/management/networkmap/envelope_test.go b/shared/management/networkmap/envelope_test.go index 666e86cee59..7ff20441869 100644 --- a/shared/management/networkmap/envelope_test.go +++ b/shared/management/networkmap/envelope_test.go @@ -56,7 +56,7 @@ func TestCalculate_FirewallRuleProtocol_NeverNetbirdSSH(t *testing.T) { c, localPeerKey := buildSmokeComponents(t) // Replace the smoke policy with a NetbirdSSH-protocol allow. c.Policies = []*types.Policy{{ - ID: "pol-ssh", AccountSeqID: 2, Enabled: true, + ID: "pol-ssh", PublicID: "2", Enabled: true, Rules: []*types.PolicyRule{{ ID: "rule-ssh", Enabled: true, @@ -154,12 +154,12 @@ func buildSmokeComponents(t *testing.T) (*types.NetworkMapComponents, string) { } group := &types.Group{ - ID: "group-all", AccountSeqID: 1, Name: "All", + ID: "group-all", PublicID: "1", Name: "All", Peers: []string{"peer-A", "peer-B"}, } policy := &types.Policy{ - ID: "pol-allow", AccountSeqID: 1, Enabled: true, + ID: "pol-allow", PublicID: "1", Enabled: true, Rules: []*types.PolicyRule{{ ID: "rule-allow", Enabled: true, diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index a01acfab14c..13855b01242 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -4844,28 +4844,18 @@ type NetworkMapComponentsFull struct { NetworkResources []*NetworkResourceRaw `protobuf:"bytes,17,rep,name=network_resources,json=networkResources,proto3" json:"network_resources,omitempty"` // Routers per network. Outer key: network account_seq_id. Each entry is // the set of routers backing that network for this peer's view. - // - // INCOMPATIBLE WIRE CHANGE: the map key changed from string (network xid) - // to uint32 (account_seq_id). Field 18 was reused without a `reserved` - // entry because capability=3 has never been released — every cap=3 - // producer and consumer carries the same regenerated descriptor. Do NOT - // reuse this pattern for any further wire change once cap=3 ships. - RoutersMap map[int32]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + RoutersMap map[string]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // For each NetworkResource account_seq_id, the indexes into policies[] // that apply to it. - // - // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - ResourcePoliciesMap map[int32]*PolicyIds `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ResourcePoliciesMap map[string]*PolicyIds `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Group-id (account_seq_id) → user ids authorized for SSH on members. - GroupIdToUserIds map[int32]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + GroupIdToUserIds map[string]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). AllowedUserIds []string `protobuf:"bytes,21,rep,name=allowed_user_ids,json=allowedUserIds,proto3" json:"allowed_user_ids,omitempty"` // Per posture-check account_seq_id, the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. - // - // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - PostureFailedPeers map[int32]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + PostureFailedPeers map[string]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level DNS forwarder port (mirrors the legacy // proto.DNSConfig.ForwarderPort). Computed by the controller from peer // versions; clients fold it into their Calculate() DNS output. @@ -5034,21 +5024,21 @@ func (x *NetworkMapComponentsFull) GetNetworkResources() []*NetworkResourceRaw { return nil } -func (x *NetworkMapComponentsFull) GetRoutersMap() map[int32]*NetworkRouterList { +func (x *NetworkMapComponentsFull) GetRoutersMap() map[string]*NetworkRouterList { if x != nil { return x.RoutersMap } return nil } -func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[int32]*PolicyIds { +func (x *NetworkMapComponentsFull) GetResourcePoliciesMap() map[string]*PolicyIds { if x != nil { return x.ResourcePoliciesMap } return nil } -func (x *NetworkMapComponentsFull) GetGroupIdToUserIds() map[int32]*UserIDList { +func (x *NetworkMapComponentsFull) GetGroupIdToUserIds() map[string]*UserIDList { if x != nil { return x.GroupIdToUserIds } @@ -5062,7 +5052,7 @@ func (x *NetworkMapComponentsFull) GetAllowedUserIds() []string { return nil } -func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[int32]*PeerIndexSet { +func (x *NetworkMapComponentsFull) GetPostureFailedPeers() map[string]*PeerIndexSet { if x != nil { return x.PostureFailedPeers } @@ -5564,7 +5554,7 @@ type PolicyCompact struct { // Per-account integer id (matches policies.account_seq_id). Used as a // stable reference for ResourcePoliciesMap.indexes and future delta // updates. - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Action RuleAction `protobuf:"varint,2,opt,name=action,proto3,enum=management.RuleAction" json:"action,omitempty"` Protocol RuleProtocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` Bidirectional bool `protobuf:"varint,4,opt,name=bidirectional,proto3" json:"bidirectional,omitempty"` @@ -5573,8 +5563,8 @@ type PolicyCompact struct { // Port ranges (start..end) referenced by the rule. PortRanges []*PortInfo_Range `protobuf:"bytes,6,rep,name=port_ranges,json=portRanges,proto3" json:"port_ranges,omitempty"` // Group ids (account_seq_id) of source / destination groups. - SourceGroupIds []int32 `protobuf:"varint,7,rep,packed,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` - DestinationGroupIds []int32 `protobuf:"varint,8,rep,packed,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` + SourceGroupIds []string `protobuf:"bytes,7,rep,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` + DestinationGroupIds []string `protobuf:"bytes,8,rep,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's // applicable group ids (account_seq_id) to a list of local-user names — // when a peer in one of those groups is the SSH destination, the named @@ -5583,8 +5573,8 @@ type PolicyCompact struct { // // Both fields are only consumed by Calculate() when the rule's protocol // is NetbirdSSH (or the legacy implicit-SSH heuristic). - AuthorizedGroups map[int32]*UserNameList `protobuf:"bytes,9,rep,name=authorized_groups,json=authorizedGroups,proto3" json:"authorized_groups,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - AuthorizedUser string `protobuf:"bytes,10,opt,name=authorized_user,json=authorizedUser,proto3" json:"authorized_user,omitempty"` + AuthorizedGroups map[string]*UserNameList `protobuf:"bytes,9,rep,name=authorized_groups,json=authorizedGroups,proto3" json:"authorized_groups,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + AuthorizedUser string `protobuf:"bytes,10,opt,name=authorized_user,json=authorizedUser,proto3" json:"authorized_user,omitempty"` // Resource-typed rule sources/destinations. When a rule targets a specific // peer (rather than groups), Calculate() reads SourceResource / // DestinationResource — without these the rule's connection resources @@ -5598,7 +5588,7 @@ type PolicyCompact struct { // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. - SourcePostureCheckSeqIds []int32 `protobuf:"varint,13,rep,packed,name=source_posture_check_seq_ids,json=sourcePostureCheckSeqIds,proto3" json:"source_posture_check_seq_ids,omitempty"` + SourcePostureCheckSeqIds []string `protobuf:"bytes,13,rep,name=source_posture_check_seq_ids,json=sourcePostureCheckSeqIds,proto3" json:"source_posture_check_seq_ids,omitempty"` } func (x *PolicyCompact) Reset() { @@ -5633,11 +5623,11 @@ func (*PolicyCompact) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{63} } -func (x *PolicyCompact) GetId() int32 { +func (x *PolicyCompact) GetId() string { if x != nil { return x.Id } - return 0 + return "" } func (x *PolicyCompact) GetAction() RuleAction { @@ -5675,21 +5665,21 @@ func (x *PolicyCompact) GetPortRanges() []*PortInfo_Range { return nil } -func (x *PolicyCompact) GetSourceGroupIds() []int32 { +func (x *PolicyCompact) GetSourceGroupIds() []string { if x != nil { return x.SourceGroupIds } return nil } -func (x *PolicyCompact) GetDestinationGroupIds() []int32 { +func (x *PolicyCompact) GetDestinationGroupIds() []string { if x != nil { return x.DestinationGroupIds } return nil } -func (x *PolicyCompact) GetAuthorizedGroups() map[int32]*UserNameList { +func (x *PolicyCompact) GetAuthorizedGroups() map[string]*UserNameList { if x != nil { return x.AuthorizedGroups } @@ -5717,7 +5707,7 @@ func (x *PolicyCompact) GetDestinationResource() *ResourceCompact { return nil } -func (x *PolicyCompact) GetSourcePostureCheckSeqIds() []int32 { +func (x *PolicyCompact) GetSourcePostureCheckSeqIds() []string { if x != nil { return x.SourcePostureCheckSeqIds } @@ -5850,7 +5840,7 @@ type GroupCompact struct { // Per-account integer id (matches groups.account_seq_id). Used by // PolicyCompact.source_group_ids / destination_group_ids. - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Group name; only sent when non-empty (clients use it for diagnostics). Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Indexes into NetworkMapComponentsFull.peers. @@ -5889,11 +5879,11 @@ func (*GroupCompact) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{66} } -func (x *GroupCompact) GetId() int32 { +func (x *GroupCompact) GetId() string { if x != nil { return x.Id } - return 0 + return "" } func (x *GroupCompact) GetName() string { @@ -5917,7 +5907,7 @@ type DNSSettingsCompact struct { unknownFields protoimpl.UnknownFields // Group ids (account_seq_id) whose DNS management is disabled. - DisabledManagementGroupIds []int32 `protobuf:"varint,1,rep,packed,name=disabled_management_group_ids,json=disabledManagementGroupIds,proto3" json:"disabled_management_group_ids,omitempty"` + DisabledManagementGroupIds []string `protobuf:"bytes,1,rep,name=disabled_management_group_ids,json=disabledManagementGroupIds,proto3" json:"disabled_management_group_ids,omitempty"` } func (x *DNSSettingsCompact) Reset() { @@ -5952,7 +5942,7 @@ func (*DNSSettingsCompact) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{67} } -func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []int32 { +func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []string { if x != nil { return x.DisabledManagementGroupIds } @@ -5969,7 +5959,7 @@ type RouteRaw struct { unknownFields protoimpl.UnknownFields // Per-account integer id (matches routes.account_seq_id). - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` NetId string `protobuf:"bytes,2,opt,name=net_id,json=netId,proto3" json:"net_id,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Either network_cidr (e.g. "10.0.0.0/16") or domains is set, not both. @@ -5986,16 +5976,16 @@ type RouteRaw struct { // it to peer.Key only after the route has been admitted to the network // map. Decoders MUST set Route.Peer = peer.ID; the legacy Calculate() // path will substitute the WG key downstream. - PeerIndexSet bool `protobuf:"varint,7,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` - PeerIndex uint32 `protobuf:"varint,8,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` - PeerGroupIds []int32 `protobuf:"varint,9,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` - NetworkType int32 `protobuf:"varint,10,opt,name=network_type,json=networkType,proto3" json:"network_type,omitempty"` - Masquerade bool `protobuf:"varint,11,opt,name=masquerade,proto3" json:"masquerade,omitempty"` - Metric int32 `protobuf:"varint,12,opt,name=metric,proto3" json:"metric,omitempty"` - Enabled bool `protobuf:"varint,13,opt,name=enabled,proto3" json:"enabled,omitempty"` - GroupIds []int32 `protobuf:"varint,14,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` - AccessControlGroupIds []int32 `protobuf:"varint,15,rep,packed,name=access_control_group_ids,json=accessControlGroupIds,proto3" json:"access_control_group_ids,omitempty"` - SkipAutoApply bool `protobuf:"varint,16,opt,name=skip_auto_apply,json=skipAutoApply,proto3" json:"skip_auto_apply,omitempty"` + PeerIndexSet bool `protobuf:"varint,7,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerIndex uint32 `protobuf:"varint,8,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` + PeerGroupIds []string `protobuf:"bytes,9,rep,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` + NetworkType int32 `protobuf:"varint,10,opt,name=network_type,json=networkType,proto3" json:"network_type,omitempty"` + Masquerade bool `protobuf:"varint,11,opt,name=masquerade,proto3" json:"masquerade,omitempty"` + Metric int32 `protobuf:"varint,12,opt,name=metric,proto3" json:"metric,omitempty"` + Enabled bool `protobuf:"varint,13,opt,name=enabled,proto3" json:"enabled,omitempty"` + GroupIds []string `protobuf:"bytes,14,rep,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` + AccessControlGroupIds []string `protobuf:"bytes,15,rep,name=access_control_group_ids,json=accessControlGroupIds,proto3" json:"access_control_group_ids,omitempty"` + SkipAutoApply bool `protobuf:"varint,16,opt,name=skip_auto_apply,json=skipAutoApply,proto3" json:"skip_auto_apply,omitempty"` } func (x *RouteRaw) Reset() { @@ -6030,11 +6020,11 @@ func (*RouteRaw) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{68} } -func (x *RouteRaw) GetId() int32 { +func (x *RouteRaw) GetId() string { if x != nil { return x.Id } - return 0 + return "" } func (x *RouteRaw) GetNetId() string { @@ -6086,7 +6076,7 @@ func (x *RouteRaw) GetPeerIndex() uint32 { return 0 } -func (x *RouteRaw) GetPeerGroupIds() []int32 { +func (x *RouteRaw) GetPeerGroupIds() []string { if x != nil { return x.PeerGroupIds } @@ -6121,14 +6111,14 @@ func (x *RouteRaw) GetEnabled() bool { return false } -func (x *RouteRaw) GetGroupIds() []int32 { +func (x *RouteRaw) GetGroupIds() []string { if x != nil { return x.GroupIds } return nil } -func (x *RouteRaw) GetAccessControlGroupIds() []int32 { +func (x *RouteRaw) GetAccessControlGroupIds() []string { if x != nil { return x.AccessControlGroupIds } @@ -6150,13 +6140,13 @@ type NameServerGroupRaw struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // nameserver_groups.account_seq_id + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Reuses the legacy NameServer wire shape (IP as string). Nameservers []*NameServer `protobuf:"bytes,4,rep,name=nameservers,proto3" json:"nameservers,omitempty"` - // Group ids (account_seq_id) the NSG distributes nameservers to. - GroupIds []int32 `protobuf:"varint,5,rep,packed,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` + // Group ids the NSG distributes nameservers to. + GroupIds []string `protobuf:"bytes,5,rep,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` Primary bool `protobuf:"varint,6,opt,name=primary,proto3" json:"primary,omitempty"` Domains []string `protobuf:"bytes,7,rep,name=domains,proto3" json:"domains,omitempty"` Enabled bool `protobuf:"varint,8,opt,name=enabled,proto3" json:"enabled,omitempty"` @@ -6195,11 +6185,11 @@ func (*NameServerGroupRaw) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{69} } -func (x *NameServerGroupRaw) GetId() int32 { +func (x *NameServerGroupRaw) GetId() string { if x != nil { return x.Id } - return 0 + return "" } func (x *NameServerGroupRaw) GetName() string { @@ -6223,7 +6213,7 @@ func (x *NameServerGroupRaw) GetNameservers() []*NameServer { return nil } -func (x *NameServerGroupRaw) GetGroupIds() []int32 { +func (x *NameServerGroupRaw) GetGroupIds() []string { if x != nil { return x.GroupIds } @@ -6270,8 +6260,8 @@ type NetworkResourceRaw struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id - NetworkSeq int32 `protobuf:"varint,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` // networks.account_seq_id (replaces xid) + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id + NetworkSeq string `protobuf:"bytes,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` // networks.account_seq_id (replaces xid) Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // Resource type: "host" / "subnet" / "domain". @@ -6314,18 +6304,18 @@ func (*NetworkResourceRaw) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{70} } -func (x *NetworkResourceRaw) GetId() int32 { +func (x *NetworkResourceRaw) GetId() string { if x != nil { return x.Id } - return 0 + return "" } -func (x *NetworkResourceRaw) GetNetworkSeq() int32 { +func (x *NetworkResourceRaw) GetNetworkSeq() string { if x != nil { return x.NetworkSeq } - return 0 + return "" } func (x *NetworkResourceRaw) GetName() string { @@ -6433,13 +6423,13 @@ type NetworkRouterEntry struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // network_routers.account_seq_id - PeerIndex uint32 `protobuf:"varint,2,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` - PeerIndexSet bool `protobuf:"varint,3,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` - PeerGroupIds []int32 `protobuf:"varint,4,rep,packed,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` - Masquerade bool `protobuf:"varint,5,opt,name=masquerade,proto3" json:"masquerade,omitempty"` - Metric int32 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` - Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + PeerIndex uint32 `protobuf:"varint,2,opt,name=peer_index,json=peerIndex,proto3" json:"peer_index,omitempty"` + PeerIndexSet bool `protobuf:"varint,3,opt,name=peer_index_set,json=peerIndexSet,proto3" json:"peer_index_set,omitempty"` + PeerGroupIds []string `protobuf:"bytes,4,rep,name=peer_group_ids,json=peerGroupIds,proto3" json:"peer_group_ids,omitempty"` + Masquerade bool `protobuf:"varint,5,opt,name=masquerade,proto3" json:"masquerade,omitempty"` + Metric int32 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` + Enabled bool `protobuf:"varint,7,opt,name=enabled,proto3" json:"enabled,omitempty"` } func (x *NetworkRouterEntry) Reset() { @@ -6474,11 +6464,11 @@ func (*NetworkRouterEntry) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{72} } -func (x *NetworkRouterEntry) GetId() int32 { +func (x *NetworkRouterEntry) GetId() string { if x != nil { return x.Id } - return 0 + return "" } func (x *NetworkRouterEntry) GetPeerIndex() uint32 { @@ -6495,7 +6485,7 @@ func (x *NetworkRouterEntry) GetPeerIndexSet() bool { return false } -func (x *NetworkRouterEntry) GetPeerGroupIds() []int32 { +func (x *NetworkRouterEntry) GetPeerGroupIds() []string { if x != nil { return x.PeerGroupIds } @@ -6528,7 +6518,7 @@ type PolicyIds struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Ids []int32 `protobuf:"varint,1,rep,packed,name=ids,proto3" json:"ids,omitempty"` + Ids []string `protobuf:"bytes,1,rep,name=ids,proto3" json:"ids,omitempty"` } func (x *PolicyIds) Reset() { @@ -6563,7 +6553,7 @@ func (*PolicyIds) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{73} } -func (x *PolicyIds) GetIds() []int32 { +func (x *PolicyIds) GetIds() []string { if x != nil { return x.Ids } @@ -7492,25 +7482,25 @@ var file_management_proto_rawDesc = []byte{ 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x1a, 0x5c, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5f, 0x0a, 0x17, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, @@ -7593,7 +7583,7 @@ var file_management_proto_rawDesc = []byte{ 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x73, 0x68, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x22, 0x98, 0x06, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, @@ -7609,10 +7599,10 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x05, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x5c, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, @@ -7633,11 +7623,11 @@ var file_management_proto_rawDesc = []byte{ 0x70, 0x61, 0x63, 0x74, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x1c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x71, 0x49, 0x64, 0x73, 0x1a, 0x5d, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, @@ -7652,7 +7642,7 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, @@ -7660,10 +7650,10 @@ var file_management_proto_rawDesc = []byte{ 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x05, 0x52, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, @@ -7678,7 +7668,7 @@ var file_management_proto_rawDesc = []byte{ 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, @@ -7688,16 +7678,16 @@ var file_management_proto_rawDesc = []byte{ 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x0e, 0x20, 0x03, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, + 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, - 0x05, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x09, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, @@ -7705,7 +7695,7 @@ var file_management_proto_rawDesc = []byte{ 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, @@ -7716,9 +7706,9 @@ var file_management_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x87, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x71, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, @@ -7738,20 +7728,20 @@ var file_management_proto_rawDesc = []byte{ 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x1d, 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, - 0x64, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x52, + 0x64, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, 0x0a, diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 78eafad1256..07b5646ed01 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -858,22 +858,14 @@ message NetworkMapComponentsFull { // Routers per network. Outer key: network account_seq_id. Each entry is // the set of routers backing that network for this peer's view. - // - // INCOMPATIBLE WIRE CHANGE: the map key changed from string (network xid) - // to uint32 (account_seq_id). Field 18 was reused without a `reserved` - // entry because capability=3 has never been released — every cap=3 - // producer and consumer carries the same regenerated descriptor. Do NOT - // reuse this pattern for any further wire change once cap=3 ships. - map routers_map = 18; + map routers_map = 18; // For each NetworkResource account_seq_id, the indexes into policies[] // that apply to it. - // - // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - map resource_policies_map = 19; + map resource_policies_map = 19; // Group-id (account_seq_id) → user ids authorized for SSH on members. - map group_id_to_user_ids = 20; + map group_id_to_user_ids = 20; // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). @@ -881,9 +873,7 @@ message NetworkMapComponentsFull { // Per posture-check account_seq_id, the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. - // - // INCOMPATIBLE WIRE CHANGE: see routers_map note above. - map posture_failed_peers = 22; + map posture_failed_peers = 22; // Account-level DNS forwarder port (mirrors the legacy // proto.DNSConfig.ForwarderPort). Computed by the controller from peer @@ -1030,7 +1020,7 @@ message PolicyCompact { // Per-account integer id (matches policies.account_seq_id). Used as a // stable reference for ResourcePoliciesMap.indexes and future delta // updates. - int32 id = 1; + string id = 1; RuleAction action = 2; RuleProtocol protocol = 3; @@ -1043,8 +1033,8 @@ message PolicyCompact { repeated PortInfo.Range port_ranges = 6; // Group ids (account_seq_id) of source / destination groups. - repeated int32 source_group_ids = 7; - repeated int32 destination_group_ids = 8; + repeated string source_group_ids = 7; + repeated string destination_group_ids = 8; // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's // applicable group ids (account_seq_id) to a list of local-user names — @@ -1054,7 +1044,7 @@ message PolicyCompact { // // Both fields are only consumed by Calculate() when the rule's protocol // is NetbirdSSH (or the legacy implicit-SSH heuristic). - map authorized_groups = 9; + map authorized_groups = 9; string authorized_user = 10; // Resource-typed rule sources/destinations. When a rule targets a specific @@ -1071,7 +1061,7 @@ message PolicyCompact { // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. - repeated int32 source_posture_check_seq_ids = 13; + repeated string source_posture_check_seq_ids = 13; } // ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry @@ -1097,7 +1087,7 @@ message UserNameList { message GroupCompact { // Per-account integer id (matches groups.account_seq_id). Used by // PolicyCompact.source_group_ids / destination_group_ids. - int32 id = 1; + string id = 1; // Group name; only sent when non-empty (clients use it for diagnostics). string name = 2; @@ -1109,7 +1099,7 @@ message GroupCompact { // DNSSettingsCompact mirrors types.DNSSettings. message DNSSettingsCompact { // Group ids (account_seq_id) whose DNS management is disabled. - repeated int32 disabled_management_group_ids = 1; + repeated string disabled_management_group_ids = 1; } // RouteRaw mirrors *route.Route (the domain type), trimmed to fields that @@ -1118,7 +1108,7 @@ message DNSSettingsCompact { // NetworkMapComponentsFull.peers. message RouteRaw { // Per-account integer id (matches routes.account_seq_id). - int32 id = 1; + string id = 1; string net_id = 2; string description = 3; @@ -1139,14 +1129,14 @@ message RouteRaw { // path will substitute the WG key downstream. bool peer_index_set = 7; uint32 peer_index = 8; - repeated int32 peer_group_ids = 9; + repeated string peer_group_ids = 9; int32 network_type = 10; bool masquerade = 11; int32 metric = 12; bool enabled = 13; - repeated int32 group_ids = 14; - repeated int32 access_control_group_ids = 15; + repeated string group_ids = 14; + repeated string access_control_group_ids = 15; bool skip_auto_apply = 16; } @@ -1154,13 +1144,13 @@ message RouteRaw { // legacy NameServerGroup (which is the wire-trimmed shape consumed by // proto.DNSConfig and lacks the Name/Description/Groups/Enabled fields). message NameServerGroupRaw { - int32 id = 1; // nameserver_groups.account_seq_id + string id = 1; string name = 2; string description = 3; // Reuses the legacy NameServer wire shape (IP as string). repeated NameServer nameservers = 4; - // Group ids (account_seq_id) the NSG distributes nameservers to. - repeated int32 group_ids = 5; + // Group ids the NSG distributes nameservers to. + repeated string group_ids = 5; bool primary = 6; repeated string domains = 7; bool enabled = 8; @@ -1175,8 +1165,8 @@ message NameServerGroupRaw { // carries the same regenerated descriptor. Do NOT reuse this pattern once // cap=3 ships. message NetworkResourceRaw { - int32 id = 1; // network_resources.account_seq_id - int32 network_seq = 2; // networks.account_seq_id (replaces xid) + string id = 1; // network_resources.account_seq_id + string network_seq = 2; // networks.account_seq_id (replaces xid) string name = 3; string description = 4; // Resource type: "host" / "subnet" / "domain". @@ -1196,17 +1186,17 @@ message NetworkRouterList { // NetworkRouterEntry mirrors a single *routerTypes.NetworkRouter; the routing // peer is referenced by index into NetworkMapComponentsFull.peers. message NetworkRouterEntry { - int32 id = 1; // network_routers.account_seq_id + string id = 1; uint32 peer_index = 2; bool peer_index_set = 3; - repeated int32 peer_group_ids = 4; + repeated string peer_group_ids = 4; bool masquerade = 5; int32 metric = 6; bool enabled = 7; } message PolicyIds { - repeated int32 ids = 1; + repeated string ids = 1; } // UserIDList is a list of user ids — used as the value type in diff --git a/shared/management/types/group.go b/shared/management/types/group.go index 8d47b5f037d..e6e285e62d1 100644 --- a/shared/management/types/group.go +++ b/shared/management/types/group.go @@ -19,9 +19,7 @@ type Group struct { // AccountID is a reference to Account that this object belongs AccountID string `json:"-" gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` + PublicID string `json:"-"` // Name visible in the UI Name string @@ -45,14 +43,6 @@ type GroupPeer struct { PeerID string `gorm:"primaryKey"` } -// HasSeqID reports whether the group has been persisted long enough to have a -// per-account sequence id allocated. Wire encoders that key off AccountSeqID -// must skip groups that return false here — otherwise multiple unpersisted -// groups would collide on id 0. -func (g *Group) HasSeqID() bool { - return g != nil && g.AccountSeqID != 0 -} - func (g *Group) LoadGroupPeers() { g.Peers = make([]string, len(g.GroupPeers)) for i, peer := range g.GroupPeers { @@ -86,7 +76,7 @@ func (g *Group) Copy() *Group { group := &Group{ ID: g.ID, AccountID: g.AccountID, - AccountSeqID: g.AccountSeqID, + PublicID: g.PublicID, Name: g.Name, Issued: g.Issued, Peers: make([]string, len(g.Peers)), diff --git a/shared/management/types/networkmap_components.go b/shared/management/types/networkmap_components.go index 4d622b05495..97ca01816c0 100644 --- a/shared/management/types/networkmap_components.go +++ b/shared/management/types/networkmap_components.go @@ -43,16 +43,16 @@ type NetworkMapComponents struct { RouterPeers map[string]*nbpeer.Peer - // NetworkXIDToSeq maps Network.ID (xid) → AccountSeqID. Populated by the + // NetworkXIDToPublicID maps Network.ID (xid) → AccountSeqID. Populated by the // account-side component builder; consumed by the envelope encoder to // translate RoutersMap keys and NetworkResource.NetworkID references // to compact uint32 ids. Legacy Calculate() doesn't consult it. - NetworkXIDToSeq map[string]int32 + NetworkXIDToPublicID map[string]string - // PostureCheckXIDToSeq maps posture.Checks.ID (xid) → AccountSeqID. + // PostureCheckXIDToPublicID maps posture.Checks.ID (xid) → AccountSeqID. // Same role as NetworkXIDToSeq, used for PostureFailedPeers keys and // policy SourcePostureChecks references. - PostureCheckXIDToSeq map[string]int32 + PostureCheckXIDToPublicID map[string]string } type AccountSettingsInfo struct { diff --git a/shared/management/types/policy.go b/shared/management/types/policy.go index 425f0ecdd89..ca63fa5e295 100644 --- a/shared/management/types/policy.go +++ b/shared/management/types/policy.go @@ -56,13 +56,11 @@ type Policy struct { // ID of the policy' ID string `gorm:"primaryKey"` + PublicID string + // AccountID is a reference to Account that this object belongs AccountID string `json:"-" gorm:"index"` - // AccountSeqID is a per-account monotonically increasing identifier used as the - // compact wire id when sending NetworkMap components to capable peers. - AccountSeqID int32 `json:"-" gorm:"not null;default:0"` - // Name of the Policy Name string @@ -79,19 +77,12 @@ type Policy struct { SourcePostureChecks []string `gorm:"serializer:json"` } -// HasSeqID reports whether the policy has been persisted long enough to have -// a per-account sequence id allocated. Wire encoders that key off -// AccountSeqID must skip policies that return false here. -func (p *Policy) HasSeqID() bool { - return p != nil && p.AccountSeqID != 0 -} - // Copy returns a copy of the policy. func (p *Policy) Copy() *Policy { c := &Policy{ ID: p.ID, AccountID: p.AccountID, - AccountSeqID: p.AccountSeqID, + PublicID: p.PublicID, Name: p.Name, Description: p.Description, Enabled: p.Enabled, From db231fd6cacd1d220314163400e7e5795fb5ee79 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Thu, 9 Jul 2026 17:18:57 +0200 Subject: [PATCH 26/42] updated decoder Signed-off-by: Dmitri Dolguikh --- .../shared/grpc/components_encoder.go | 26 +- shared/management/networkmap/decode.go | 153 +++---- shared/management/proto/management.pb.go | 417 +++++++++--------- shared/management/proto/management.proto | 2 +- 4 files changed, 284 insertions(+), 314 deletions(-) diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index a594502bf92..4dcd69fe4df 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -242,19 +242,19 @@ func (e *componentEncoder) encodePolicies(policies []*types.Policy) []*proto.Pol // encodePolicyRule maps a single PolicyRule under pol to a PolicyCompact entry. func (e *componentEncoder) encodePolicyRule(pol *types.Policy, r *types.PolicyRule) *proto.PolicyCompact { return &proto.PolicyCompact{ - Id: pol.PublicID, - Action: networkmap.GetProtoAction(string(r.Action)), - Protocol: networkmap.GetProtoProtocol(string(r.Protocol)), - Bidirectional: r.Bidirectional, - Ports: portsToUint32(r.Ports), - PortRanges: portRangesToProto(r.PortRanges), - SourceGroupIds: e.groupPublicXids(r.Sources), - DestinationGroupIds: e.groupPublicXids(r.Destinations), - AuthorizedUser: r.AuthorizedUser, - AuthorizedGroups: e.encodeAuthorizedGroups(r.AuthorizedGroups), - SourceResource: e.resourceToProto(r.SourceResource), - DestinationResource: e.resourceToProto(r.DestinationResource), - SourcePostureCheckSeqIds: e.postureCheckSeqs(pol.SourcePostureChecks), + Id: pol.PublicID, + Action: networkmap.GetProtoAction(string(r.Action)), + Protocol: networkmap.GetProtoProtocol(string(r.Protocol)), + Bidirectional: r.Bidirectional, + Ports: portsToUint32(r.Ports), + PortRanges: portRangesToProto(r.PortRanges), + SourceGroupIds: e.groupPublicXids(r.Sources), + DestinationGroupIds: e.groupPublicXids(r.Destinations), + AuthorizedUser: r.AuthorizedUser, + AuthorizedGroups: e.encodeAuthorizedGroups(r.AuthorizedGroups), + SourceResource: e.resourceToProto(r.SourceResource), + DestinationResource: e.resourceToProto(r.DestinationResource), + SourcePostureCheckIds: e.postureCheckSeqs(pol.SourcePostureChecks), } } diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go index 73ff308099e..4ba447a7c1b 100644 --- a/shared/management/networkmap/decode.go +++ b/shared/management/networkmap/decode.go @@ -69,7 +69,7 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, if full.DnsSettings != nil { c.DNSSettings = &types.DNSSettings{ - DisabledManagementGroups: groupIDsFromSeqs(full.DnsSettings.DisabledManagementGroupIds), + DisabledManagementGroups: full.DnsSettings.DisabledManagementGroupIds, } } else { c.DNSSettings = &types.DNSSettings{} @@ -100,37 +100,38 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, peerIDByIndex[idx] = peerID } - // Phase 2: groups. AccountSeqID becomes both the synthesized string ID - // and the GroupCompact.id wire value. + // Phase 2: groups. for i, gc := range full.Groups { if gc == nil { return nil, fmt.Errorf("invalid envelope: groups[%d] is nil", i) } - groupID := synthGroupID(gc.Id) + groupID := gc.Id peerIDs := make([]string, 0, len(gc.PeerIndexes)) for _, idx := range gc.PeerIndexes { if int(idx) < len(peerIDByIndex) { peerIDs = append(peerIDs, peerIDByIndex[idx]) + } else { + log.WithField("peer idx", idx).Error("unrecognized peer idx during decoding") } } c.Groups[groupID] = &types.Group{ - ID: groupID, - AccountSeqID: gc.Id, - Name: gc.Name, - Peers: peerIDs, + ID: groupID, + PublicID: gc.Id, + Name: gc.Name, + Peers: peerIDs, } } // Phase 3: policies (PolicyCompact = one rule per entry; current data - // model is 1 rule per policy). Policy.ID is synthesized from the - // per-account seq id; proto.FirewallRule.PolicyID downstream carries - // the same synth string (no xid on the wire). + // model is 1 rule per policy). + policyByID := make(map[string]*types.Policy, len(full.Policies)) for i, pc := range full.Policies { if pc == nil { return nil, fmt.Errorf("invalid envelope: policies[%d] is nil", i) } - policyID := synthPolicyID(pc.Id) - c.Policies = append(c.Policies, decodePolicyCompact(pc, policyID, peerIDByIndex)) + policy := decodePolicyCompact(pc, pc.Id, peerIDByIndex) + c.Policies = append(c.Policies, policy) + policyByID[pc.Id] = policy } // Phase 4: routes. @@ -159,26 +160,26 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, // Phase 7: routers_map (outer key = network seq id, inner key = peer-id // reconstructed from peer_index). Synthesized network id is "net_". - for networkSeq, list := range full.RoutersMap { - networkID := synthNetworkID(networkSeq) + for networkID, list := range full.RoutersMap { inner := make(map[string]*routerTypes.NetworkRouter, len(list.Entries)) for _, entry := range list.Entries { if !entry.PeerIndexSet { continue } if int(entry.PeerIndex) >= len(peerIDByIndex) { + log.WithField("peer idx", entry.PeerIndex).Error("unrecognized peer id when decoding router map") continue } peerID := peerIDByIndex[entry.PeerIndex] inner[peerID] = &routerTypes.NetworkRouter{ - ID: "", - NetworkID: networkID, - AccountSeqID: entry.Id, - Peer: peerID, - PeerGroups: groupIDsFromSeqs(entry.PeerGroupIds), - Masquerade: entry.Masquerade, - Metric: int(entry.Metric), - Enabled: entry.Enabled, + ID: "", + NetworkID: networkID, + PublicID: entry.Id, + Peer: peerID, + PeerGroups: entry.PeerGroupIds, + Masquerade: entry.Masquerade, + Metric: int(entry.Metric), + Enabled: entry.Enabled, } } if len(inner) > 0 { @@ -189,15 +190,16 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, // Phase 8: resource_policies_map (resource seq id → list of *types.Policy // pointers from the decoded policies slice). Resource ID is synthesized // the same way as in decodeNetworkResource. - for resourceSeq, idxs := range full.ResourcePoliciesMap { - if len(idxs.Indexes) == 0 { + for resourceID, ids := range full.ResourcePoliciesMap { + if len(ids.Ids) == 0 { continue } - resourceID := synthNetworkResourceID(resourceSeq) - policies := make([]*types.Policy, 0, len(idxs.Indexes)) - for _, i := range idxs.Indexes { - if int(i) < len(c.Policies) { - policies = append(policies, c.Policies[i]) + policies := make([]*types.Policy, 0, len(ids.Ids)) + for _, id := range ids.Ids { + if p, ok := policyByID[id]; ok { + policies = append(policies, p) + } else { + log.WithField("policy id", id).Error("unrecognized policy when decoding resource policies") } } if len(policies) > 0 { @@ -206,19 +208,20 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, } // Phase 9: group_id_to_user_ids — wire keys are seq ids, synth to strings. - for groupSeq, list := range full.GroupIdToUserIds { - c.GroupIDToUserIDs[synthGroupID(groupSeq)] = append([]string(nil), list.UserIds...) + for groupId, list := range full.GroupIdToUserIds { + c.GroupIDToUserIDs[groupId] = append([]string(nil), list.UserIds...) } // Phase 10: posture_failed_peers — wire keys are posture-check seq ids, // values are peer indexes that need to be turned into peer ids. PolicyRule // SourcePostureChecks (also synth ids) reference the same key space. - for checkSeq, set := range full.PostureFailedPeers { - checkID := synthPostureCheckID(checkSeq) + for checkID, set := range full.PostureFailedPeers { failed := make(map[string]struct{}, len(set.PeerIndexes)) for _, idx := range set.PeerIndexes { if int(idx) < len(peerIDByIndex) { failed[peerIDByIndex[idx]] = struct{}{} + } else { + log.WithField("peer idx", idx).Error("unrecognized peer when decoding posture failed peers") } } if len(failed) > 0 { @@ -287,7 +290,7 @@ func decodePeerCompact(pc *proto.PeerCompact, peerID string, agentVersions []str DNSLabel: pc.DnsLabel, LoginExpirationEnabled: pc.LoginExpirationEnabled, Meta: nbpeer.PeerSystemMeta{ - WtVersion: lookupAgentVersion(agentVersions, pc.AgentVersionIdx), + WtVersion: pc.AgentVersion, Capabilities: caps, Flags: nbpeer.Flags{ ServerSSHAllowed: pc.ServerSshAllowed, @@ -332,8 +335,8 @@ func decodePolicyCompact(pc *proto.PolicyCompact, policyID string, peerIDByIndex Bidirectional: pc.Bidirectional, Ports: uint32SliceToStrings(pc.Ports), PortRanges: portRangesFromProto(pc.PortRanges), - Sources: groupIDsFromSeqs(pc.SourceGroupIds), - Destinations: groupIDsFromSeqs(pc.DestinationGroupIds), + Sources: pc.SourceGroupIds, + Destinations: pc.DestinationGroupIds, AuthorizedUser: pc.AuthorizedUser, AuthorizedGroups: authorizedGroupsFromProto(pc.AuthorizedGroups), SourceResource: resourceFromProto(pc.SourceResource, peerIDByIndex), @@ -341,10 +344,10 @@ func decodePolicyCompact(pc *proto.PolicyCompact, policyID string, peerIDByIndex } return &types.Policy{ ID: policyID, - AccountSeqID: pc.Id, + PublicID: pc.Id, Enabled: true, Rules: []*types.PolicyRule{rule}, - SourcePostureChecks: postureCheckIDsFromSeqs(pc.SourcePostureCheckSeqIds), + SourcePostureChecks: pc.SourcePostureCheckIds, } } @@ -362,41 +365,28 @@ func resourceFromProto(r *proto.ResourceCompact, peerIDByIndex []string) types.R return out } -// postureCheckIDsFromSeqs synths posture-check ids from per-account seq ids. -// Mirrors groupIDsFromSeqs. -func postureCheckIDsFromSeqs(seqs []uint32) []string { - if len(seqs) == 0 { - return nil - } - out := make([]string, len(seqs)) - for i, s := range seqs { - out[i] = synthPostureCheckID(s) - } - return out -} - // authorizedGroupsFromProto inverts encodeAuthorizedGroups: the wire form // keys by group account_seq_id, the typed PolicyRule field keys by group // xid string. We rebuild using the same synthetic scheme the rest of the // decoder uses ("g"). -func authorizedGroupsFromProto(m map[uint32]*proto.UserNameList) map[string][]string { +func authorizedGroupsFromProto(m map[string]*proto.UserNameList) map[string][]string { if len(m) == 0 { return nil } out := make(map[string][]string, len(m)) - for seq, list := range m { + for id, list := range m { if list == nil { continue } - out[synthGroupID(seq)] = append([]string(nil), list.Names...) + out[id] = append([]string(nil), list.Names...) } return out } func decodeRouteRaw(rr *proto.RouteRaw, peerIDByIndex []string) *nbroute.Route { r := &nbroute.Route{ - ID: nbroute.ID(synthRouteID(rr.Id)), - AccountSeqID: rr.Id, + ID: nbroute.ID(rr.Id), + PublicID: rr.Id, NetID: nbroute.NetID(rr.NetId), Description: rr.Description, Domains: domainsFromPunycode(rr.Domains), @@ -405,9 +395,9 @@ func decodeRouteRaw(rr *proto.RouteRaw, peerIDByIndex []string) *nbroute.Route { Masquerade: rr.Masquerade, Metric: int(rr.Metric), Enabled: rr.Enabled, - Groups: groupIDsFromSeqs(rr.GroupIds), - AccessControlGroups: groupIDsFromSeqs(rr.AccessControlGroupIds), - PeerGroups: groupIDsFromSeqs(rr.PeerGroupIds), + Groups: rr.GroupIds, + AccessControlGroups: rr.AccessControlGroupIds, + PeerGroups: rr.PeerGroupIds, SkipAutoApply: rr.SkipAutoApply, } if rr.NetworkCidr != "" { @@ -423,11 +413,11 @@ func decodeRouteRaw(rr *proto.RouteRaw, peerIDByIndex []string) *nbroute.Route { func decodeNameServerGroupRaw(nsg *proto.NameServerGroupRaw) *nbdns.NameServerGroup { out := &nbdns.NameServerGroup{ - ID: synthNameServerGroupID(nsg.Id), - AccountSeqID: nsg.Id, + ID: nsg.Id, + PublicID: nsg.Id, Name: nsg.Name, Description: nsg.Description, - Groups: groupIDsFromSeqs(nsg.GroupIds), + Groups: nsg.GroupIds, Primary: nsg.Primary, Domains: nsg.Domains, Enabled: nsg.Enabled, @@ -448,15 +438,15 @@ func decodeNameServerGroupRaw(nsg *proto.NameServerGroupRaw) *nbdns.NameServerGr func decodeNetworkResource(nr *proto.NetworkResourceRaw) *resourceTypes.NetworkResource { out := &resourceTypes.NetworkResource{ - ID: synthNetworkResourceID(nr.Id), - AccountSeqID: nr.Id, - NetworkID: synthNetworkID(nr.NetworkSeq), - Name: nr.Name, - Description: nr.Description, - Type: resourceTypes.NetworkResourceType(nr.Type), - Address: nr.Address, - Domain: nr.DomainValue, - Enabled: nr.Enabled, + ID: nr.Id, + PublicID: nr.Id, + NetworkID: nr.NetworkSeq, + Name: nr.Name, + Description: nr.Description, + Type: resourceTypes.NetworkResourceType(nr.Type), + Address: nr.Address, + Domain: nr.DomainValue, + Enabled: nr.Enabled, } if nr.PrefixCidr != "" { if p, err := netip.ParsePrefix(nr.PrefixCidr); err == nil { @@ -505,25 +495,6 @@ func synthID(prefix string, n uint32) string { return string(buf) } -func synthGroupID(seq uint32) string { return synthID("g_", seq) } -func synthPolicyID(seq uint32) string { return synthID("pol_", seq) } -func synthRouteID(seq uint32) string { return synthID("r_", seq) } -func synthNetworkResourceID(seq uint32) string { return synthID("nres_", seq) } -func synthPostureCheckID(seq uint32) string { return synthID("pc_", seq) } -func synthNetworkID(seq uint32) string { return synthID("net_", seq) } -func synthNameServerGroupID(seq uint32) string { return synthID("nsg_", seq) } - -func groupIDsFromSeqs(seqs []uint32) []string { - if len(seqs) == 0 { - return nil - } - out := make([]string, len(seqs)) - for i, s := range seqs { - out[i] = synthGroupID(s) - } - return out -} - func uint32SliceToStrings(ports []uint32) []string { if len(ports) == 0 { return nil diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 13855b01242..749c46f4f0b 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -5588,7 +5588,7 @@ type PolicyCompact struct { // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. - SourcePostureCheckSeqIds []string `protobuf:"bytes,13,rep,name=source_posture_check_seq_ids,json=sourcePostureCheckSeqIds,proto3" json:"source_posture_check_seq_ids,omitempty"` + SourcePostureCheckIds []string `protobuf:"bytes,13,rep,name=source_posture_check_ids,json=sourcePostureCheckIds,proto3" json:"source_posture_check_ids,omitempty"` } func (x *PolicyCompact) Reset() { @@ -5707,9 +5707,9 @@ func (x *PolicyCompact) GetDestinationResource() *ResourceCompact { return nil } -func (x *PolicyCompact) GetSourcePostureCheckSeqIds() []string { +func (x *PolicyCompact) GetSourcePostureCheckIds() []string { if x != nil { - return x.SourcePostureCheckSeqIds + return x.SourcePostureCheckIds } return nil } @@ -7581,7 +7581,7 @@ var file_management_proto_rawDesc = []byte{ 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x53, 0x73, 0x68, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x22, 0x98, 0x06, + 0x76, 0x65, 0x72, 0x53, 0x73, 0x68, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x22, 0x91, 0x06, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, @@ -7621,226 +7621,225 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x1c, 0x73, 0x6f, 0x75, + 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x53, 0x65, 0x71, 0x49, 0x64, 0x73, 0x1a, 0x5d, 0x0a, 0x15, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x70, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x24, 0x0a, 0x0c, 0x55, 0x73, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, + 0x64, 0x73, 0x1a, 0x5d, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x70, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4a, 0x04, 0x08, + 0x04, 0x10, 0x05, 0x22, 0x24, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, + 0x22, 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, + 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, + 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, + 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, + 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, + 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, + 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x22, 0x57, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x41, 0x0a, - 0x1d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, - 0x22, 0x8d, 0x04, 0x0a, 0x08, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, - 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, - 0x65, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x5f, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, - 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, - 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, - 0x37, 0x0a, 0x18, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, - 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x79, - 0x22, 0xb5, 0x02, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, - 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, - 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x87, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x71, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x65, 0x66, 0x69, - 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, - 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, - 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, - 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x1d, 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, - 0x64, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x03, 0x69, 0x64, 0x73, 0x22, 0x27, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, 0x0a, - 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, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x22, 0x87, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x43, 0x69, 0x64, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x11, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x12, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x73, + 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, + 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, + 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x6d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x1d, + 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x69, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x27, 0x0a, + 0x0a, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x31, 0x0a, 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, 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, + 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, 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, + 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, 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, + 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, 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, + 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, 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, + 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, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 07b5646ed01..2f84a1e4960 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -1061,7 +1061,7 @@ message PolicyCompact { // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. - repeated string source_posture_check_seq_ids = 13; + repeated string source_posture_check_ids = 13; } // ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry From 395bdf82b6c293d69ff4bec9485c4e15bfe6b524 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Thu, 9 Jul 2026 18:28:02 +0200 Subject: [PATCH 27/42] removed unused code Signed-off-by: Dmitri Dolguikh --- shared/management/networkmap/decode.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go index 4ba447a7c1b..b37ce7bb529 100644 --- a/shared/management/networkmap/decode.go +++ b/shared/management/networkmap/decode.go @@ -483,18 +483,6 @@ func decodeCustomZones(zones []*proto.CustomZone) []nbdns.CustomZone { return out } -// Synthetic ID generators — deterministic given the same wire input. -// Underscore-separated ("p_", "pol_", ...) so they're visually -// distinct in operator logs. fmt.Sprintf would dominate the decode hot path -// on large accounts (a 10k-peer envelope produces ~50k synth calls); the -// strconv.AppendUint builder keeps it allocation-light. -func synthID(prefix string, n uint32) string { - buf := make([]byte, 0, len(prefix)+10) - buf = append(buf, prefix...) - buf = strconv.AppendUint(buf, uint64(n), 10) - return string(buf) -} - func uint32SliceToStrings(ports []uint32) []string { if len(ports) == 0 { return nil @@ -547,13 +535,6 @@ func protocolFromProto(p proto.RuleProtocol) types.PolicyRuleProtocolType { } } -func lookupAgentVersion(table []string, idx uint32) string { - if int(idx) < len(table) { - return table[idx] - } - return "" -} - func stringSliceToSet(s []string) map[string]struct{} { if len(s) == 0 { return nil From 5941b267d2af5d3a543142f8060ea278a6b5ba65 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Thu, 9 Jul 2026 18:40:33 +0200 Subject: [PATCH 28/42] responding to coderabbit comments Signed-off-by: Dmitri Dolguikh --- management/server/store/sql_store.go | 24 ++++++++++++------------ shared/management/networkmap/decode.go | 12 +----------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 4806563ef5e..1d4afc1b165 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -643,9 +643,9 @@ func (s *SqlStore) SaveUser(ctx context.Context, user *types.User) error { // CreateGroups creates the given list of groups to the database. // groupUpsertColumns is the explicit allowlist of columns that get updated when -// CreateGroups / UpdateGroups hit a PK conflict. account_seq_id is intentionally +// CreateGroups / UpdateGroups hit a PK conflict. public_id is intentionally // omitted so a caller passing an entity with the zero value (e.g. an HTTP -// handler-built struct) cannot reset the persisted seq id during an upsert. +// handler-built struct) cannot reset the persisted public_id during an upsert. // Keep this in sync with the Group schema in management/server/types/group.go. func groupUpsertColumns() clause.Set { return clause.AssignmentColumns([]string{ @@ -2045,7 +2045,7 @@ func (s *SqlStore) getUsers(ctx context.Context, accountID string) ([]types.User } func (s *SqlStore) getGroups(ctx context.Context, accountID string) ([]*types.Group, error) { - const query = `SELECT id, account_id, account_seq_id, name, issued, resources, integration_ref_id, integration_ref_integration_type FROM groups WHERE account_id = $1` + const query = `SELECT id, account_id, public_id, name, issued, resources, integration_ref_id, integration_ref_integration_type FROM groups WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2080,7 +2080,7 @@ func (s *SqlStore) getGroups(ctx context.Context, accountID string) ([]*types.Gr } func (s *SqlStore) getPolicies(ctx context.Context, accountID string) ([]*types.Policy, error) { - const query = `SELECT id, account_id, account_seq_id, name, description, enabled, source_posture_checks FROM policies WHERE account_id = $1` + const query = `SELECT id, account_id, public_id, name, description, enabled, source_posture_checks FROM policies WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2107,7 +2107,7 @@ func (s *SqlStore) getPolicies(ctx context.Context, accountID string) ([]*types. } func (s *SqlStore) getRoutes(ctx context.Context, accountID string) ([]route.Route, error) { - const query = `SELECT id, account_id, account_seq_id, network, domains, keep_route, net_id, description, peer, peer_groups, network_type, masquerade, metric, enabled, groups, access_control_groups, skip_auto_apply FROM routes WHERE account_id = $1` + const query = `SELECT id, account_id, public_id, network, domains, keep_route, net_id, description, peer, peer_groups, network_type, masquerade, metric, enabled, groups, access_control_groups, skip_auto_apply FROM routes WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2159,7 +2159,7 @@ func (s *SqlStore) getRoutes(ctx context.Context, accountID string) ([]route.Rou } func (s *SqlStore) getNameServerGroups(ctx context.Context, accountID string) ([]nbdns.NameServerGroup, error) { - const query = `SELECT id, account_id, account_seq_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled FROM name_server_groups WHERE account_id = $1` + const query = `SELECT id, account_id, public_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled FROM name_server_groups WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2204,7 +2204,7 @@ func (s *SqlStore) getNameServerGroups(ctx context.Context, accountID string) ([ } func (s *SqlStore) getPostureChecks(ctx context.Context, accountID string) ([]*posture.Checks, error) { - const query = `SELECT id, account_id, account_seq_id, name, description, checks FROM posture_checks WHERE account_id = $1` + const query = `SELECT id, account_id, public_id, name, description, checks FROM posture_checks WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2392,7 +2392,7 @@ func (s *SqlStore) getServices(ctx context.Context, accountID string) ([]*rpserv } func (s *SqlStore) getNetworks(ctx context.Context, accountID string) ([]*networkTypes.Network, error) { - const query = `SELECT id, account_id, account_seq_id, name, description FROM networks WHERE account_id = $1` + const query = `SELECT id, account_id, public_id, name, description FROM networks WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2409,7 +2409,7 @@ func (s *SqlStore) getNetworks(ctx context.Context, accountID string) ([]*networ } func (s *SqlStore) getNetworkRouters(ctx context.Context, accountID string) ([]*routerTypes.NetworkRouter, error) { - const query = `SELECT id, network_id, account_id, account_seq_id, peer, peer_groups, masquerade, metric, enabled FROM network_routers WHERE account_id = $1` + const query = `SELECT id, network_id, account_id, public_id, peer, peer_groups, masquerade, metric, enabled FROM network_routers WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -2447,7 +2447,7 @@ func (s *SqlStore) getNetworkRouters(ctx context.Context, accountID string) ([]* } func (s *SqlStore) getNetworkResources(ctx context.Context, accountID string) ([]*resourceTypes.NetworkResource, error) { - const query = `SELECT id, network_id, account_id, account_seq_id, name, description, type, domain, prefix, enabled FROM network_resources WHERE account_id = $1` + const query = `SELECT id, network_id, account_id, public_id, name, description, type, domain, prefix, enabled FROM network_resources WHERE account_id = $1` rows, err := s.pool.Query(ctx, query, accountID) if err != nil { return nil, err @@ -3818,7 +3818,7 @@ func (s *SqlStore) UpdateGroup(ctx context.Context, group *types.Group) error { return status.Errorf(status.InvalidArgument, "group is nil") } - if err := s.db.Omit(clause.Associations, "account_seq_id").Save(group).Error; err != nil { + if err := s.db.Omit(clause.Associations, "public_id").Save(group).Error; err != nil { log.WithContext(ctx).Errorf("failed to save group to store: %v", err) return status.Errorf(status.Internal, "failed to save group to store") } @@ -3906,7 +3906,7 @@ func (s *SqlStore) CreatePolicy(ctx context.Context, policy *types.Policy) error // SavePolicy saves a policy to the database. func (s *SqlStore) SavePolicy(ctx context.Context, policy *types.Policy) error { - result := s.db.Session(&gorm.Session{FullSaveAssociations: true}).Omit("account_seq_id").Save(policy) + result := s.db.Session(&gorm.Session{FullSaveAssociations: true}).Omit("public_id").Save(policy) if err := result.Error; err != nil { log.WithContext(ctx).Errorf("failed to save policy to the store: %s", err) return status.Errorf(status.Internal, "failed to save policy to store") diff --git a/shared/management/networkmap/decode.go b/shared/management/networkmap/decode.go index b37ce7bb529..0a96ad80506 100644 --- a/shared/management/networkmap/decode.go +++ b/shared/management/networkmap/decode.go @@ -22,21 +22,11 @@ import ( // DecodeEnvelope converts a NetworkMapEnvelope into a NetworkMapComponents // the client can run Calculate() over. Every ID-reference on the wire is a -// uint32 (peer index or account_seq_id) — no xid strings travel. The decoder -// synthesises consistent string IDs from the uint32s so the reconstructed -// components struct round-trips through Calculate exactly the way the -// server-side typed components would. +// xid from corresponding public_id field. // // ID scheme on the client side: // // Peers base64(wg_pub_key) // stable across snapshots -// Groups "g_" -// Policies "pol_" // 1 rule per policy -// Routes "r_" -// Network resources "nres_" -// Posture checks "pc_" -// Networks "net_" -// Nameserver groups "nsg_" func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents, error) { if env == nil { return nil, fmt.Errorf("nil envelope") From fc40eb923173b278679596fe7691ea800db5ffb2 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 10:37:52 +0200 Subject: [PATCH 29/42] fixed a flaky test Signed-off-by: Dmitri Dolguikh --- shared/management/networkmap/envelope_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shared/management/networkmap/envelope_test.go b/shared/management/networkmap/envelope_test.go index 7ff20441869..21b8d0bf79a 100644 --- a/shared/management/networkmap/envelope_test.go +++ b/shared/management/networkmap/envelope_test.go @@ -114,7 +114,11 @@ func TestDecodeEnvelope_MalformedWgKeyPeerSkipped(t *testing.T) { require.Len(t, full.Peers, 2, "smoke fixture should have two peers") // Truncate the second peer's wg_pub_key so it fails the length gate. - full.Peers[1].WgPubKey = full.Peers[1].WgPubKey[:31] + for _, p := range full.Peers { + if base64.StdEncoding.EncodeToString(p.WgPubKey) != localPeerKey { + p.WgPubKey = p.WgPubKey[:31] + } + } wire, err := goproto.Marshal(envelope) require.NoError(t, err, "marshal envelope") From 35e339b6a97fe6e7f5e8cc56daf373d582d52b61 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 10:45:54 +0200 Subject: [PATCH 30/42] set session expiry in ToComponentSyncResponse(), same as ToSyncResponse() path Signed-off-by: Dmitri Dolguikh --- .../shared/grpc/components_envelope_response.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index b48b9b3dc4c..7880b51357b 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -72,6 +72,16 @@ func ToComponentSyncResponse( nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings, settings) resp.NetbirdConfig = integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings) + // settings == nil → field stays nil → "no info in this snapshot", client + // preserves the deadline it already had. settings non-nil → emit either a + // valid deadline or the explicit-zero "disabled" sentinel via + // encodeSessionExpiresAt. + if settings != nil { + resp.SessionExpiresAt = encodeSessionExpiresAt( + peer.SessionExpiresAt(settings.PeerLoginExpirationEnabled, settings.PeerLoginExpiration), + ) + } + return resp } From 384b057f076c3a9c6ab34c6bc75d99d8e1a7c86e Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 10:59:30 +0200 Subject: [PATCH 31/42] use return from GetValidatedPeerWithComponents when computing ComponentSyncResponse to prevent config drift Signed-off-by: Dmitri Dolguikh --- management/internals/shared/grpc/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index 88a70b90aba..dfefada48dd 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -1032,12 +1032,12 @@ func (s *Server) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer // stops doing duplicate work. Deferred until the client-side // decoder lands and there's a real deployment of capability=3 peers // worth optimizing for. - _, components, proxyPatch, _, _, err := s.networkMapController.GetValidatedPeerWithComponents(ctx, false, peer.AccountID, peer) + freshPeer, components, proxyPatch, freshPostureChecks, freshDnsFwdPort, err := s.networkMapController.GetValidatedPeerWithComponents(ctx, false, peer.AccountID, peer) if err != nil { log.WithContext(ctx).Errorf("failed to build components for peer %s on initial sync: %v", peer.ID, err) return status.Errorf(codes.Internal, "failed to build initial sync envelope") } - plainResp = ToComponentSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, components, proxyPatch, dnsName, postureChecks, settings, settings.Extra, peerGroups, dnsFwdPort) + plainResp = ToComponentSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, freshPeer, turnToken, relayToken, components, proxyPatch, dnsName, freshPostureChecks, settings, settings.Extra, peerGroups, freshDnsFwdPort) } else { plainResp = ToSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, networkMap, dnsName, postureChecks, nil, settings, settings.Extra, peerGroups, dnsFwdPort) } From 42c0cb5398e900fa7e9160f461554ec612fa9f60 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 12:58:49 +0200 Subject: [PATCH 32/42] sort lookup inconsistencies when processing routes when computing components Signed-off-by: Dmitri Dolguikh --- management/server/types/account_components.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 556f3a3a58a..9dc082d8784 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -327,7 +327,7 @@ func (a *Account) getPeersGroupsPoliciesRoutes( if r == nil { continue } - relevant := r.Peer == peerID + relevant := r.PeerID == peerID if !relevant { for _, groupID := range r.PeerGroups { if _, ok := peerGroupSet[groupID]; ok { @@ -375,9 +375,9 @@ func (a *Account) getPeersGroupsPoliciesRoutes( // this loop, and the legacy invariant that only validated peers // reach a client's view). if r.Peer != "" { - if _, ok := validatedPeersMap[r.Peer]; ok { - if p := a.GetPeer(r.Peer); p != nil { - relevantPeerIDs[r.Peer] = p + if _, ok := validatedPeersMap[r.PeerID]; ok { + if p := a.GetPeer(r.PeerID); p != nil { + relevantPeerIDs[r.PeerID] = p } } } From 8803a58f12662c8949d520a665db9b1e1cf06117 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 15:46:33 +0200 Subject: [PATCH 33/42] inject agent network services when injecting proxy policies Signed-off-by: Dmitri Dolguikh --- .../internals/controllers/network_map/controller/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 3d92af66d23..833af320ecb 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -567,7 +567,7 @@ func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequi return nil, nil, nil, nil, 0, err } - account.InjectProxyPolicies(ctx) + c.injectAllProxyPolicies(ctx, account) approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra) if err != nil { From 4aa486fde80eb5fcb61faa41d1aa1b743800da26 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 17:12:51 +0200 Subject: [PATCH 34/42] handle nil components consistently Signed-off-by: Dmitri Dolguikh --- .../network_map/controller/controller.go | 1 - .../grpc/components_envelope_response.go | 14 ++-------- management/server/types/account_components.go | 28 +++++++++---------- management/server/types/aliases.go | 3 ++ .../management/types/networkmap_components.go | 11 ++++++++ 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 833af320ecb..d25165e0c89 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -601,7 +601,6 @@ func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequi routers := account.GetResourceRoutersMap() groupIDToUserIDs := account.GetActiveGroupUsers() components := account.GetPeerNetworkMapComponents(ctx, peer.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, groupIDToUserIDs) - dnsFwdPort := computeForwarderPort(maps.Values(account.Peers), network_map.DnsForwarderPortMinVersion) return peer, components, proxyNetworkMaps[peer.ID], postureChecks, dnsFwdPort, nil diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index 7880b51357b..95fb87271c2 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -25,6 +25,8 @@ import ( // computed by the client from the envelope's GroupIDToUserIDs / AllowedUserIDs // inside Calculate(), so the SshConfig.SshEnabled bit may flip true on the // client even though the server-side PeerConfig reports false. +// +// components parameter is expected to be !nil func ToComponentSyncResponse( ctx context.Context, config *nbconfig.Config, @@ -42,9 +44,8 @@ func ToComponentSyncResponse( peerGroups []string, dnsFwdPort int64, ) *proto.SyncResponse { - network := networkOrZero(components) enableSSH := computeSSHEnabledForPeer(components, peer) - peerConfig := toPeerConfig(peer, network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH) + peerConfig := toPeerConfig(peer, components.Network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH) includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid() useSourcePrefixes := peer.SupportsSourcePrefixes() @@ -85,15 +86,6 @@ func ToComponentSyncResponse( return resp } -// networkOrZero returns components.Network or a zero Network — toPeerConfig -// dereferences network.Net which would panic on nil. -func networkOrZero(c *types.NetworkMapComponents) *types.Network { - if c == nil || c.Network == nil { - return &types.Network{} - } - return c.Network -} - // toProxyPatch converts a proxy-injected *types.NetworkMap into the wire // patch the components envelope ships alongside. Returns nil when there are // no fragments to merge — proto3 omits a nil message field, so the receiver diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index 9dc082d8784..e2fc9b94c81 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -40,16 +40,6 @@ func (a *Account) GetPeerNetworkMapResult( components := a.GetPeerNetworkMapComponents( ctx, peerID, peersCustomZone, accountZones, validatedPeersMap, resourcePolicies, routers, groupIDToUserIDs, ) - // Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents - // returns &NetworkMap{Network: a.Network.Copy()} when components is - // nil. Match that floor so the receiving client always sees the - // account Network identifier, not a fully-empty envelope. - if components == nil { - components = &NetworkMapComponents{ - PeerID: peerID, - Network: a.Network.Copy(), - } - } return PeerNetworkMapResult{Components: components} } return PeerNetworkMapResult{ @@ -83,8 +73,8 @@ func (a *Account) GetPeerNetworkMapFromComponents( groupIDToUserIDs, ) - if components == nil { - return &NetworkMap{Network: a.Network.Copy()} + if components.IsEmpty() { + return &NetworkMap{Network: components.Network} } nm := CalculateNetworkMapFromComponents(ctx, components) @@ -117,11 +107,21 @@ func (a *Account) GetPeerNetworkMapComponents( peer := a.Peers[peerID] if peer == nil { - return nil + // Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents + // returns &NetworkMap{Network: a.Network.Copy()} when components is + // nil. Match that floor so the receiving client always sees the + // account Network identifier, not a fully-empty envelope. + return EmptyNetworkMapComponents(&NetworkMapComponents{ + PeerID: peerID, + Network: a.Network.Copy(), + }) } if _, ok := validatedPeersMap[peerID]; !ok { - return nil + return EmptyNetworkMapComponents(&NetworkMapComponents{ + PeerID: peerID, + Network: a.Network.Copy(), + }) } components := &NetworkMapComponents{ diff --git a/management/server/types/aliases.go b/management/server/types/aliases.go index 949bd743263..f5837a343cb 100644 --- a/management/server/types/aliases.go +++ b/management/server/types/aliases.go @@ -41,6 +41,9 @@ type ResourceType = sharedtypes.ResourceType type RouteFirewallRule = sharedtypes.RouteFirewallRule type NetworkMapComponents = sharedtypes.NetworkMapComponents + +var EmptyNetworkMapComponents = sharedtypes.EmptyNetworkMapComponents + type AccountSettingsInfo = sharedtypes.AccountSettingsInfo type GroupCompact = sharedtypes.GroupCompact diff --git a/shared/management/types/networkmap_components.go b/shared/management/types/networkmap_components.go index 97ca01816c0..c080ff055da 100644 --- a/shared/management/types/networkmap_components.go +++ b/shared/management/types/networkmap_components.go @@ -53,6 +53,8 @@ type NetworkMapComponents struct { // Same role as NetworkXIDToSeq, used for PostureFailedPeers keys and // policy SourcePostureChecks references. PostureCheckXIDToPublicID map[string]string + // true when returning an empty-like map (returned instead of nil) + empty bool } type AccountSettingsInfo struct { @@ -62,6 +64,11 @@ type AccountSettingsInfo struct { PeerInactivityExpiration time.Duration } +func EmptyNetworkMapComponents(nm *NetworkMapComponents) *NetworkMapComponents { + nm.empty = true + return nm +} + func (c *NetworkMapComponents) GetPeerInfo(peerID string) *nbpeer.Peer { return c.Peers[peerID] } @@ -180,6 +187,10 @@ func (c *NetworkMapComponents) Calculate(ctx context.Context) *NetworkMap { } } +func (c *NetworkMapComponents) IsEmpty() bool { + return c.empty +} + func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) ([]*nbpeer.Peer, []*FirewallRule, map[string]map[string]struct{}, bool) { targetPeer := c.GetPeerInfo(targetPeerID) if targetPeer == nil { From aff723e3bb5e93229ca679926c7494840d69977e Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 19:23:14 +0200 Subject: [PATCH 35/42] add migrations for public_id column Signed-off-by: Dmitri Dolguikh --- dns/nameserver.go | 2 +- management/server/migration/migration.go | 48 +++++++++++++++++++++++ management/server/store/sql_store_test.go | 1 + management/server/store/store.go | 24 ++++++++++++ shared/management/types/policy.go | 2 +- 5 files changed, 75 insertions(+), 2 deletions(-) diff --git a/dns/nameserver.go b/dns/nameserver.go index 84e83e2b479..06c0c47db19 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -53,7 +53,7 @@ type NameServerGroup struct { ID string `gorm:"primaryKey"` // AccountID is a reference to Account that this object belongs AccountID string `gorm:"index"` - PublicID string `json:"-"` + PublicID string `json:"-""` // Name group name Name string // Description group description diff --git a/management/server/migration/migration.go b/management/server/migration/migration.go index 7a51cc20032..ae26a254ebe 100644 --- a/management/server/migration/migration.go +++ b/management/server/migration/migration.go @@ -13,6 +13,7 @@ import ( "strings" "unicode/utf8" + "github.com/rs/xid" log "github.com/sirupsen/logrus" "gorm.io/gorm" "gorm.io/gorm/clause" @@ -635,3 +636,50 @@ func RemoveDuplicatePeerKeys(ctx context.Context, db *gorm.DB) error { return nil } + +func BackfillPublicIDs[T any](ctx context.Context, db *gorm.DB) error { + var model T + + if !db.Migrator().HasTable(&model) { + log.WithContext(ctx).Debugf("Table for %T does not exist, no backfill needed", model) + return nil + } + + stmt := &gorm.Statement{DB: db} + err := stmt.Parse(&model) + if err != nil { + return fmt.Errorf("parse model: %w", err) + } + tableName := stmt.Schema.Table + + if err := db.Transaction(func(tx *gorm.DB) error { + if !tx.Migrator().HasColumn(&model, "public_id") { + log.WithContext(ctx).Infof("Column public_id does not exist in table %s, adding it", tableName) + if err := tx.Migrator().AddColumn(&model, "public_id"); err != nil { + return fmt.Errorf("add column public_id: %w", err) + } + } + + var rows []map[string]any + if err := tx.Table(tableName).Select("id", "public_id").Where("public_id IS NULL").Or("public_id = ''").Find(&rows).Error; err != nil { + return fmt.Errorf("failed to find rows with empty public_id: %w", err) + } + + if len(rows) == 0 { + log.WithContext(ctx).Infof("No rows with empty public_id found in table %s, no migration needed", tableName) + return nil + } + + for _, row := range rows { + if err := tx.Table(tableName).Where("id = ?", row["id"]).Update("public_id", xid.New().String()).Error; err != nil { + return fmt.Errorf("failed to update row with id %v: %w", row["id"], err) + } + } + return nil + }); err != nil { + return err + } + + log.WithContext(ctx).Infof("Backfill of empty public_id in table %s completed", tableName) + return nil +} diff --git a/management/server/store/sql_store_test.go b/management/server/store/sql_store_test.go index 92784af836f..12e7167922b 100644 --- a/management/server/store/sql_store_test.go +++ b/management/server/store/sql_store_test.go @@ -45,6 +45,7 @@ func runTestForAllEngines(t *testing.T, testDataFile string, f func(t *testing.T } t.Setenv("NETBIRD_STORE_ENGINE", string(engine)) store, cleanUp, err := NewTestStoreFromSQL(context.Background(), testDataFile, t.TempDir()) + assert.NoError(t, err, "engine: ", string(engine)) t.Cleanup(cleanUp) assert.NoError(t, err) t.Run(string(engine), func(t *testing.T) { diff --git a/management/server/store/store.go b/management/server/store/store.go index 908c199f568..0bc385d831e 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -582,6 +582,30 @@ func getMigrationsPreAuto(ctx context.Context) []migrationFunc { func(db *gorm.DB) error { return migration.CleanupOrphanedResources[domain.Domain, types.Account](ctx, db, "account_id") }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[types.Policy](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[types.Group](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[route.Route](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[resourceTypes.NetworkResource](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[routerTypes.NetworkRouter](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[dns.NameServerGroup](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[networkTypes.Network](ctx, db) + }, + func(db *gorm.DB) error { + return migration.BackfillPublicIDs[posture.Checks](ctx, db) + }, } } diff --git a/shared/management/types/policy.go b/shared/management/types/policy.go index ca63fa5e295..b8f605b94d9 100644 --- a/shared/management/types/policy.go +++ b/shared/management/types/policy.go @@ -56,7 +56,7 @@ type Policy struct { // ID of the policy' ID string `gorm:"primaryKey"` - PublicID string + PublicID string `json:"-"` // AccountID is a reference to Account that this object belongs AccountID string `json:"-" gorm:"index"` From 9383b6b0f5271fdb9dbeadbc659d72df8c46f5d1 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 20:12:48 +0200 Subject: [PATCH 36/42] fix field tag Signed-off-by: Dmitri Dolguikh --- dns/nameserver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/nameserver.go b/dns/nameserver.go index 06c0c47db19..84e83e2b479 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -53,7 +53,7 @@ type NameServerGroup struct { ID string `gorm:"primaryKey"` // AccountID is a reference to Account that this object belongs AccountID string `gorm:"index"` - PublicID string `json:"-""` + PublicID string `json:"-"` // Name group name Name string // Description group description From ff33942c5211f2f2ce7233efd31d8c85d179629d Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 20:17:16 +0200 Subject: [PATCH 37/42] do not instantiate cache to benchmark ToProtocolDNSConfig() without cache Signed-off-by: Dmitri Dolguikh --- management/internals/shared/grpc/conversion_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/management/internals/shared/grpc/conversion_test.go b/management/internals/shared/grpc/conversion_test.go index 208358c4d33..402b4fd0779 100644 --- a/management/internals/shared/grpc/conversion_test.go +++ b/management/internals/shared/grpc/conversion_test.go @@ -110,8 +110,7 @@ func BenchmarkToProtocolDNSConfig(b *testing.B) { b.Run(fmt.Sprintf("WithoutCache-Size%d", size), func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - cache := &cache.DNSConfigCache{} - networkmap.ToProtocolDNSConfig(testData, cache, int64(network_map.DnsForwarderPort)) + networkmap.ToProtocolDNSConfig(testData, nil, int64(network_map.DnsForwarderPort)) } }) } From 6830777d65404e579b9e834413f498e3aa507887 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 20:26:54 +0200 Subject: [PATCH 38/42] updated comments in management.proto Signed-off-by: Dmitri Dolguikh --- shared/management/proto/management.pb.go | 45 ++++++++++-------------- shared/management/proto/management.proto | 44 ++++++++++------------- 2 files changed, 36 insertions(+), 53 deletions(-) diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 749c46f4f0b..2f1b6f7efcf 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -4828,7 +4828,7 @@ type NetworkMapComponentsFull struct { RouterPeerIndexes []uint32 `protobuf:"varint,10,rep,packed,name=router_peer_indexes,json=routerPeerIndexes,proto3" json:"router_peer_indexes,omitempty"` // Policies that affect the receiving peer. Policies []*PolicyCompact `protobuf:"bytes,11,rep,name=policies,proto3" json:"policies,omitempty"` - // Groups in unspecified order — clients key off id (account_seq_id). + // Groups in unspecified order — clients key off id (public_id). Groups []*GroupCompact `protobuf:"bytes,12,rep,name=groups,proto3" json:"groups,omitempty"` // Routes relevant to this peer, raw shape (mirrors []*route.Route). Routes []*RouteRaw `protobuf:"bytes,13,rep,name=routes,proto3" json:"routes,omitempty"` @@ -4842,18 +4842,18 @@ type NetworkMapComponentsFull struct { AccountZones []*CustomZone `protobuf:"bytes,16,rep,name=account_zones,json=accountZones,proto3" json:"account_zones,omitempty"` // Network resources (mirrors []*resourceTypes.NetworkResource). NetworkResources []*NetworkResourceRaw `protobuf:"bytes,17,rep,name=network_resources,json=networkResources,proto3" json:"network_resources,omitempty"` - // Routers per network. Outer key: network account_seq_id. Each entry is + // Routers per network. Outer key: network public_id. Each entry is // the set of routers backing that network for this peer's view. RoutersMap map[string]*NetworkRouterList `protobuf:"bytes,18,rep,name=routers_map,json=routersMap,proto3" json:"routers_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // For each NetworkResource account_seq_id, the indexes into policies[] + // For each NetworkResource public_id, the indexes into policies[] // that apply to it. ResourcePoliciesMap map[string]*PolicyIds `protobuf:"bytes,19,rep,name=resource_policies_map,json=resourcePoliciesMap,proto3" json:"resource_policies_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // Group-id (account_seq_id) → user ids authorized for SSH on members. + // Group-id (public_id) → user ids authorized for SSH on members. GroupIdToUserIds map[string]*UserIDList `protobuf:"bytes,20,rep,name=group_id_to_user_ids,json=groupIdToUserIds,proto3" json:"group_id_to_user_ids,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). AllowedUserIds []string `protobuf:"bytes,21,rep,name=allowed_user_ids,json=allowedUserIds,proto3" json:"allowed_user_ids,omitempty"` - // Per posture-check account_seq_id, the set of peer indexes that failed + // Per posture-check public_id, the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. PostureFailedPeers map[string]*PeerIndexSet `protobuf:"bytes,22,rep,name=posture_failed_peers,json=postureFailedPeers,proto3" json:"posture_failed_peers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Account-level DNS forwarder port (mirrors the legacy @@ -5542,7 +5542,7 @@ func (x *PeerCompact) GetServerSshAllowed() bool { } // PolicyCompact is the compact form of a policy rule. Group references use -// the per-account integer ids from account_seq_counters; the client resolves +// the public_ids; the client resolves // them against NetworkMapComponentsFull.groups. Direction is derived per-peer // on the client (ingress when the peer is in destination_group_ids, egress // when in source_group_ids; both when bidirectional). @@ -5551,9 +5551,8 @@ type PolicyCompact struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Per-account integer id (matches policies.account_seq_id). Used as a - // stable reference for ResourcePoliciesMap.indexes and future delta - // updates. + // public_id. Used as a stable reference for + // ResourcePoliciesMap.indexes and future delta updates. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Action RuleAction `protobuf:"varint,2,opt,name=action,proto3,enum=management.RuleAction" json:"action,omitempty"` Protocol RuleProtocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` @@ -5562,11 +5561,11 @@ type PolicyCompact struct { Ports []uint32 `protobuf:"varint,5,rep,packed,name=ports,proto3" json:"ports,omitempty"` // Port ranges (start..end) referenced by the rule. PortRanges []*PortInfo_Range `protobuf:"bytes,6,rep,name=port_ranges,json=portRanges,proto3" json:"port_ranges,omitempty"` - // Group ids (account_seq_id) of source / destination groups. + // Group ids (public_ids) of source / destination groups. SourceGroupIds []string `protobuf:"bytes,7,rep,name=source_group_ids,json=sourceGroupIds,proto3" json:"source_group_ids,omitempty"` DestinationGroupIds []string `protobuf:"bytes,8,rep,name=destination_group_ids,json=destinationGroupIds,proto3" json:"destination_group_ids,omitempty"` // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's - // applicable group ids (account_seq_id) to a list of local-user names — + // applicable group ids (public_ids) to a list of local-user names — // when a peer in one of those groups is the SSH destination, the named // local users gain access. AuthorizedUser is the single-user form // (legacy: rule scopes SSH to one specific user id). @@ -5584,7 +5583,7 @@ type PolicyCompact struct { // Calculate's resource-typed rule path today. SourceResource *ResourceCompact `protobuf:"bytes,11,opt,name=source_resource,json=sourceResource,proto3" json:"source_resource,omitempty"` DestinationResource *ResourceCompact `protobuf:"bytes,12,opt,name=destination_resource,json=destinationResource,proto3" json:"destination_resource,omitempty"` - // Posture-check seq ids gating this policy's source peers. Calculate() + // Posture-check ids gating this policy's source peers. Calculate() // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. @@ -5831,15 +5830,14 @@ func (x *UserNameList) GetNames() []string { return nil } -// GroupCompact is the wire-shape of a group: per-account integer id, optional +// GroupCompact is the wire-shape of a group: public id, optional // name, and indexes into NetworkMapComponentsFull.peers identifying members. type GroupCompact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Per-account integer id (matches groups.account_seq_id). Used by - // PolicyCompact.source_group_ids / destination_group_ids. + // id comes from PublicID. Used by PolicyCompact.source_group_ids / destination_group_ids. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Group name; only sent when non-empty (clients use it for diagnostics). Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` @@ -5906,7 +5904,7 @@ type DNSSettingsCompact struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Group ids (account_seq_id) whose DNS management is disabled. + // Group ids (public_id) whose DNS management is disabled. DisabledManagementGroupIds []string `protobuf:"bytes,1,rep,name=disabled_management_group_ids,json=disabledManagementGroupIds,proto3" json:"disabled_management_group_ids,omitempty"` } @@ -5951,15 +5949,14 @@ func (x *DNSSettingsCompact) GetDisabledManagementGroupIds() []string { // RouteRaw mirrors *route.Route (the domain type), trimmed to fields that // types.NetworkMapComponents.Calculate() reads. Group references are -// account_seq_ids; the routing peer (when set) is referenced by index into +// public_ids; the routing peer (when set) is referenced by index into // NetworkMapComponentsFull.peers. type RouteRaw struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Per-account integer id (matches routes.account_seq_id). - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // public_id NetId string `protobuf:"bytes,2,opt,name=net_id,json=netId,proto3" json:"net_id,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Either network_cidr (e.g. "10.0.0.0/16") or domains is set, not both. @@ -6249,19 +6246,13 @@ func (x *NameServerGroupRaw) GetSearchDomainsEnabled() bool { } // NetworkResourceRaw mirrors *resourceTypes.NetworkResource. -// -// INCOMPATIBLE WIRE CHANGE: field 2 changed from `string network_id` (xid) -// to `uint32 network_seq` without a `reserved` entry. Safe only because -// capability=3 has never been released — every cap=3 producer and consumer -// carries the same regenerated descriptor. Do NOT reuse this pattern once -// cap=3 ships. type NetworkResourceRaw struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // network_resources.account_seq_id - NetworkSeq string `protobuf:"bytes,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` // networks.account_seq_id (replaces xid) + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + NetworkSeq string `protobuf:"bytes,2,opt,name=network_seq,json=networkSeq,proto3" json:"network_seq,omitempty"` Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // Resource type: "host" / "subnet" / "domain". diff --git a/shared/management/proto/management.proto b/shared/management/proto/management.proto index 2f84a1e4960..f89bdffb98c 100644 --- a/shared/management/proto/management.proto +++ b/shared/management/proto/management.proto @@ -836,7 +836,7 @@ message NetworkMapComponentsFull { // Policies that affect the receiving peer. repeated PolicyCompact policies = 11; - // Groups in unspecified order — clients key off id (account_seq_id). + // Groups in unspecified order — clients key off id (public_id). repeated GroupCompact groups = 12; // Routes relevant to this peer, raw shape (mirrors []*route.Route). @@ -856,22 +856,22 @@ message NetworkMapComponentsFull { // Network resources (mirrors []*resourceTypes.NetworkResource). repeated NetworkResourceRaw network_resources = 17; - // Routers per network. Outer key: network account_seq_id. Each entry is + // Routers per network. Outer key: network public_id. Each entry is // the set of routers backing that network for this peer's view. map routers_map = 18; - // For each NetworkResource account_seq_id, the indexes into policies[] + // For each NetworkResource public_id, the indexes into policies[] // that apply to it. map resource_policies_map = 19; - // Group-id (account_seq_id) → user ids authorized for SSH on members. + // Group-id (public_id) → user ids authorized for SSH on members. map group_id_to_user_ids = 20; // Account-level allowed user ids (used by Calculate() when assembling SSH // authorized users for the receiving peer). repeated string allowed_user_ids = 21; - // Per posture-check account_seq_id, the set of peer indexes that failed + // Per posture-check public_id, the set of peer indexes that failed // the check. Server-side evaluation result; clients do not re-evaluate. map posture_failed_peers = 22; @@ -1012,14 +1012,13 @@ message PeerCompact { } // PolicyCompact is the compact form of a policy rule. Group references use -// the per-account integer ids from account_seq_counters; the client resolves +// the public_ids; the client resolves // them against NetworkMapComponentsFull.groups. Direction is derived per-peer // on the client (ingress when the peer is in destination_group_ids, egress // when in source_group_ids; both when bidirectional). message PolicyCompact { - // Per-account integer id (matches policies.account_seq_id). Used as a - // stable reference for ResourcePoliciesMap.indexes and future delta - // updates. + // public_id. Used as a stable reference for + // ResourcePoliciesMap.indexes and future delta updates. string id = 1; RuleAction action = 2; @@ -1032,12 +1031,12 @@ message PolicyCompact { // Port ranges (start..end) referenced by the rule. repeated PortInfo.Range port_ranges = 6; - // Group ids (account_seq_id) of source / destination groups. + // Group ids (public_ids) of source / destination groups. repeated string source_group_ids = 7; repeated string destination_group_ids = 8; // SSH authorization fields. PolicyRule.AuthorizedGroups maps the rule's - // applicable group ids (account_seq_id) to a list of local-user names — + // applicable group ids (public_ids) to a list of local-user names — // when a peer in one of those groups is the SSH destination, the named // local users gain access. AuthorizedUser is the single-user form // (legacy: rule scopes SSH to one specific user id). @@ -1057,7 +1056,7 @@ message PolicyCompact { ResourceCompact source_resource = 11; ResourceCompact destination_resource = 12; - // Posture-check seq ids gating this policy's source peers. Calculate() + // Posture-check ids gating this policy's source peers. Calculate() // reads them when filtering rule peers (peers that fail any listed check // are dropped from sourcePeers). Match keys in // NetworkMapComponentsFull.posture_failed_peers. @@ -1082,11 +1081,10 @@ message UserNameList { repeated string names = 1; } -// GroupCompact is the wire-shape of a group: per-account integer id, optional +// GroupCompact is the wire-shape of a group: public id, optional // name, and indexes into NetworkMapComponentsFull.peers identifying members. message GroupCompact { - // Per-account integer id (matches groups.account_seq_id). Used by - // PolicyCompact.source_group_ids / destination_group_ids. + // id comes from PublicID. Used by PolicyCompact.source_group_ids / destination_group_ids. string id = 1; // Group name; only sent when non-empty (clients use it for diagnostics). @@ -1098,17 +1096,16 @@ message GroupCompact { // DNSSettingsCompact mirrors types.DNSSettings. message DNSSettingsCompact { - // Group ids (account_seq_id) whose DNS management is disabled. + // Group ids (public_id) whose DNS management is disabled. repeated string disabled_management_group_ids = 1; } // RouteRaw mirrors *route.Route (the domain type), trimmed to fields that // types.NetworkMapComponents.Calculate() reads. Group references are -// account_seq_ids; the routing peer (when set) is referenced by index into +// public_ids; the routing peer (when set) is referenced by index into // NetworkMapComponentsFull.peers. message RouteRaw { - // Per-account integer id (matches routes.account_seq_id). - string id = 1; + string id = 1; // public_id string net_id = 2; string description = 3; @@ -1159,14 +1156,9 @@ message NameServerGroupRaw { // NetworkResourceRaw mirrors *resourceTypes.NetworkResource. // -// INCOMPATIBLE WIRE CHANGE: field 2 changed from `string network_id` (xid) -// to `uint32 network_seq` without a `reserved` entry. Safe only because -// capability=3 has never been released — every cap=3 producer and consumer -// carries the same regenerated descriptor. Do NOT reuse this pattern once -// cap=3 ships. message NetworkResourceRaw { - string id = 1; // network_resources.account_seq_id - string network_seq = 2; // networks.account_seq_id (replaces xid) + string id = 1; + string network_seq = 2; string name = 3; string description = 4; // Resource type: "host" / "subnet" / "domain". From 315ac71e64c27953780cd297ee24b61800f16cae Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 20:32:39 +0200 Subject: [PATCH 39/42] use version helper Signed-off-by: Dmitri Dolguikh --- shared/management/types/firewall_helpers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/management/types/firewall_helpers.go b/shared/management/types/firewall_helpers.go index df2bf14ea50..dd174abe4fa 100644 --- a/shared/management/types/firewall_helpers.go +++ b/shared/management/types/firewall_helpers.go @@ -2,10 +2,10 @@ package types import ( "strconv" - "strings" - "github.com/netbirdio/netbird/management/server/posture" nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/posture" + "github.com/netbirdio/netbird/version" ) const ( @@ -111,7 +111,7 @@ func shouldCheckRulesForNativeSSH(supportsNative bool, rule *PolicyRule, peer *n } func peerSupportedFirewallFeatures(peerVer string) supportedFeatures { - if strings.Contains(peerVer, "dev") { + if version.IsDevelopmentVersion(peerVer) { return supportedFeatures{true, true} } From 6ea97a8d6b5a747ff8b02f4125397b7fe0337ecd Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 10 Jul 2026 21:21:55 +0200 Subject: [PATCH 40/42] support ComponentSyncResponse in affected peers sync path Signed-off-by: Dmitri Dolguikh --- .../network_map/controller/controller.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index d25165e0c89..f83138d6492 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -381,18 +381,26 @@ func (c *Controller) sendUpdateForAffectedPeers(ctx context.Context, accountID s c.metrics.CountCalcPostureChecksDuration(time.Since(start)) start = time.Now() - remotePeerNetworkMap := account.GetPeerNetworkMapFromComponents(ctx, p.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) + result := account.GetPeerNetworkMapResult(ctx, p.ID, c.componentsDisabled, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start)) - proxyNetworkMap, ok := proxyNetworkMaps[p.ID] - if ok { - remotePeerNetworkMap.Merge(proxyNetworkMap) + proxyNetworkMap := proxyNetworkMaps[p.ID] + if result.NetworkMap != nil && proxyNetworkMap != nil { + result.NetworkMap.Merge(proxyNetworkMap) } peerGroups := account.GetPeerGroups(p.ID) start = time.Now() - update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) + var update *proto.SyncResponse + if result.IsComponents() { + // proxyNetworkMap rides the envelope as a ProxyPatch sidecar; + // the client merges it into Calculate()'s output the same + // way the legacy server did via NetworkMap.Merge. + update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, result.Components, proxyNetworkMap, dnsDomain, postureChecks, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) + } else { + update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, result.NetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) + } c.metrics.CountToSyncResponseDuration(time.Since(start)) c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{ From 0cb27ac4ad3212c08d646b74ba0f1e618e9dfea1 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Sat, 11 Jul 2026 00:03:12 +0200 Subject: [PATCH 41/42] include the target peer in the minimal NetworkMapEnvelope's Peers field Signed-off-by: Dmitri Dolguikh --- .../internals/shared/grpc/components_encoder.go | 8 +++++--- .../shared/grpc/components_envelope_response.go | 8 ++++++-- management/server/types/account_components.go | 17 ++++++++++++----- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index 4dcd69fe4df..3587b5e1652 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -57,14 +57,16 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel // Network populated). The receiver gets an envelope it can decode // without crashing; AccountSettings stays non-nil so client-side // dereferences are safe. - if c == nil { + if c == nil || c.IsEmpty() { // Match legacy missing-peer minimum: a NetworkMap with only Network // populated. The receiver gets enough to bootstrap (Network - // identifier, dns_domain, account_settings) and nothing else. + // identifier, dns_domain, account_settings) and the peer itself. return &proto.NetworkMapEnvelope{ Payload: &proto.NetworkMapEnvelope_Full{ Full: &proto.NetworkMapComponentsFull{ - PeerConfig: in.PeerConfig, + PeerConfig: in.PeerConfig, + // components.Peers always contains the target peer + Peers: []*proto.PeerCompact{toPeerCompact(c.Peers[c.PeerID])}, DnsDomain: in.DNSDomain, DnsForwarderPort: in.DNSForwarderPort, UserIdClaim: in.UserIDClaim, diff --git a/management/internals/shared/grpc/components_envelope_response.go b/management/internals/shared/grpc/components_envelope_response.go index 95fb87271c2..89f957eaf9a 100644 --- a/management/internals/shared/grpc/components_envelope_response.go +++ b/management/internals/shared/grpc/components_envelope_response.go @@ -25,8 +25,6 @@ import ( // computed by the client from the envelope's GroupIDToUserIDs / AllowedUserIDs // inside Calculate(), so the SshConfig.SshEnabled bit may flip true on the // client even though the server-side PeerConfig reports false. -// -// components parameter is expected to be !nil func ToComponentSyncResponse( ctx context.Context, config *nbconfig.Config, @@ -44,6 +42,12 @@ func ToComponentSyncResponse( peerGroups []string, dnsFwdPort int64, ) *proto.SyncResponse { + // + // 'component' parameter is expected to never be nil + // 'peer' parameter is expected to never be nil + // + // TODO (dmitri) consider using invariants? + // enableSSH := computeSSHEnabledForPeer(components, peer) peerConfig := toPeerConfig(peer, components.Network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH) diff --git a/management/server/types/account_components.go b/management/server/types/account_components.go index e2fc9b94c81..fa7f93b010b 100644 --- a/management/server/types/account_components.go +++ b/management/server/types/account_components.go @@ -104,23 +104,29 @@ func (a *Account) GetPeerNetworkMapComponents( routers map[string]map[string]*routerTypes.NetworkRouter, groupIDToUserIDs map[string][]string, ) *NetworkMapComponents { - peer := a.Peers[peerID] + // this can never happen, things are very wrong if it did + // TODO (dmitri) maybe consider using invariants? if peer == nil { - // Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents - // returns &NetworkMap{Network: a.Network.Copy()} when components is - // nil. Match that floor so the receiving client always sees the - // account Network identifier, not a fully-empty envelope. + log.WithField("peer id", peerID).Error("NetworkMapComponents are computed for a peer missing from the account") return EmptyNetworkMapComponents(&NetworkMapComponents{ PeerID: peerID, Network: a.Network.Copy(), + // must include the target peer as it's required on the client + Peers: map[string]*nbpeer.Peer{peerID: peer}, }) } if _, ok := validatedPeersMap[peerID]; !ok { + // Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents + // returns &NetworkMap{Network: a.Network.Copy()} when components is + // nil. Match that floor so the receiving client always sees the + // account Network identifier, not a fully-empty envelope. return EmptyNetworkMapComponents(&NetworkMapComponents{ PeerID: peerID, Network: a.Network.Copy(), + // must include the target peer as it's required on the client + Peers: map[string]*nbpeer.Peer{peerID: peer}, }) } @@ -157,6 +163,7 @@ func (a *Account) GetPeerNetworkMapComponents( components.DNSSettings = &a.DNSSettings + // relevantPeers always contains the target peer (peerID) relevantPeers, relevantGroups, relevantPolicies, relevantRoutes, sshReqs := a.getPeersGroupsPoliciesRoutes(ctx, peerID, peer.SSHEnabled, validatedPeersMap, &components.PostureFailedPeers) if len(sshReqs.neededGroupIDs) > 0 { From d5178416af32040d6a88f461a1fd12efb0e1e11d Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Sat, 11 Jul 2026 00:31:33 +0200 Subject: [PATCH 42/42] fix tests around encoding of minimal NetworkMapComponents Signed-off-by: Dmitri Dolguikh --- .../internals/shared/grpc/components_encoder.go | 2 +- .../shared/grpc/components_encoder_test.go | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/management/internals/shared/grpc/components_encoder.go b/management/internals/shared/grpc/components_encoder.go index 3587b5e1652..9c27cdcf6c1 100644 --- a/management/internals/shared/grpc/components_encoder.go +++ b/management/internals/shared/grpc/components_encoder.go @@ -57,7 +57,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel // Network populated). The receiver gets an envelope it can decode // without crashing; AccountSettings stays non-nil so client-side // dereferences are safe. - if c == nil || c.IsEmpty() { + if c.IsEmpty() { // Match legacy missing-peer minimum: a NetworkMap with only Network // populated. The receiver gets enough to bootstrap (Network // identifier, dns_domain, account_settings) and the peer itself. diff --git a/management/internals/shared/grpc/components_encoder_test.go b/management/internals/shared/grpc/components_encoder_test.go index 4fca37cbdaa..60935fe9dbb 100644 --- a/management/internals/shared/grpc/components_encoder_test.go +++ b/management/internals/shared/grpc/components_encoder_test.go @@ -739,9 +739,9 @@ func TestEncodeNetworkMapEnvelope_ProxyPatchPropagated(t *testing.T) { assert.Len(t, full.ProxyPatch.ForwardingRules, 1) }) - t.Run("nil_components_graceful_degrade", func(t *testing.T) { + t.Run("empty_components_graceful_degrade", func(t *testing.T) { full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ - Components: nil, + Components: emptyNetworkMapComponents(), ProxyPatch: patch, }).GetFull() @@ -754,7 +754,7 @@ func TestEncodeNetworkMapEnvelope_NilComponentsGracefulDegrade(t *testing.T) { // nil Components → minimal envelope, no crash. Matches the legacy // behaviour for missing/unvalidated peers. env := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{ - Components: nil, + Components: emptyNetworkMapComponents(), DNSDomain: "netbird.cloud", }) @@ -763,7 +763,7 @@ func TestEncodeNetworkMapEnvelope_NilComponentsGracefulDegrade(t *testing.T) { require.NotNil(t, full) require.NotNil(t, full.AccountSettings, "AccountSettings must always be non-nil") assert.Equal(t, "netbird.cloud", full.DnsDomain) - assert.Empty(t, full.Peers) + assert.Len(t, full.Peers, 1) assert.Empty(t, full.Policies) } @@ -779,3 +779,10 @@ func TestEncodeNetworkMapEnvelope_AccountSettingsAlwaysEmitted(t *testing.T) { assert.False(t, full.AccountSettings.PeerLoginExpirationEnabled) assert.Zero(t, full.AccountSettings.PeerLoginExpirationNs) } + +func emptyNetworkMapComponents() *types.NetworkMapComponents { + return types.EmptyNetworkMapComponents( + &types.NetworkMapComponents{ + PeerID: "peer-id", Peers: map[string]*nbpeer.Peer{"peer-id": {}}}, + ) +}