Documentation
¶
Overview ¶
Package catgroup is the named category-group engine: bundles of URL categories (e.g. "AI", "Marketing", "Messaging") under a single name (e.g. "Prod Allowed") that policy rules reference instead of individual categories, enabling Zero Trust postures like: Auth Users → Prod Allowed → Allow, Deny Any Any. Extracted from package main's categorygroup.go per a recorded ADR-0002-style design (post-program extraction).
Performance: each group maintains a pre-computed catSet (map[string]bool) for O(1) membership checks on the proxy hot path. The set is rebuilt on every mutation (admin edit, UT1 sync, cluster sync) — never during request evaluation. All category names are normalized to lowercase.
Concurrency: RWMutex protects the store. Read path (GetByName) takes RLock; write path (ReplaceAll) builds the new map outside the lock, then swaps the pointer under a brief Lock.
package main keeps: the `globalCategoryGroups` singleton, the API handlers, cluster sync, rollback, and — deliberately — the HOST-level match (`categoryGroupMatchesHost`): resolving a host to its category is the two-tier catStore+communityDB fusion that lives in main (same verdict as the urlcat extraction), so this engine exposes the pure MatchesCategory instead.
Index ¶
- type Group
- type Store
- func (s *Store) Add(name string, categories []string) (*Group, error)
- func (s *Store) ContainsCategory(catName string) (groupName string, found bool)
- func (s *Store) Delete(name string) error
- func (s *Store) DeleteByID(id string) (string, error)
- func (s *Store) GetByID(id string) *Group
- func (s *Store) GetByName(name string) *Group
- func (s *Store) List() []Group
- func (s *Store) Load(path string) error
- func (s *Store) MatchesCategory(groupName, category string) bool
- func (s *Store) MatchesCategoryByID(id, category string) (matched, resolved bool)
- func (s *Store) Names() []string
- func (s *Store) Path() string
- func (s *Store) Rename(id, newName string) (oldName string, err error)
- func (s *Store) ReplaceAll(groups []Group)
- func (s *Store) Save()
- func (s *Store) SetPathForTest(path string)
- func (s *Store) Update(name string, categories []string) error
- func (s *Store) UpdateByID(id string, categories []string) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Group ¶
type Group struct {
ID string `json:"id"`
Name string `json:"name"`
Categories []string `json:"categories"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
// contains filtered or unexported fields
}
Group is a named bundle of URL category names.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store manages persistent category groups with O(1) lookups.
func (*Store) ContainsCategory ¶
ContainsCategory returns true if any group references the given category name. Used for referential integrity when deleting a base category.
func (*Store) DeleteByID ¶ added in v1.0.86
DeleteByID removes the group with the given stable ID. Returns the removed group's name (for audit/reference bookkeeping) or an error if not found.
func (*Store) GetByID ¶ added in v1.0.86
GetByID returns a copy of the group with the given stable ID, or nil. ID addressing is rename-safe (the name key is mutable; the ID is not).
func (*Store) GetByName ¶
GetByName returns a group by name (case-insensitive). O(1). Returns nil if not found. The returned pointer is safe to read concurrently — catSet is immutable between mutations.
func (*Store) MatchesCategory ¶
MatchesCategory reports whether the named group contains the given (already-resolved) category. This is the engine half of the hot-path group match: package main's categoryGroupMatchesHost resolves host → category through its two-tier fusion, then calls this O(1) check. Unknown group = no match (fail-closed); empty category never matches.
func (*Store) MatchesCategoryByID ¶ added in v1.0.96
MatchesCategoryByID is MatchesCategory keyed by the group's stable ID (references-by-id S2 hot-path match). resolved=true iff a group with that id EXISTS; matched is whether that group contains the (already-resolved) category. The ID is AUTHORITATIVE: a resolved group returns its own membership result so the caller does NOT fall back to a possibly-stale denormalized name (which could match a DIFFERENT group). No-copy fast path (one RLock + a catSet probe); O(groups), a small admin set. Empty id / not found ⇒ resolved=false so the caller may fall back to the name.
func (*Store) Rename ¶ added in v1.0.96
Rename changes the display name of the group with the given stable ID, re-keying the name map and the order slice (references-by-id S2: rules link by ID, so the rename is safe — the caller cascades the denormalized name onto referencing rules). Validates the new name is non-empty and not already taken by a DIFFERENT group. Returns the OLD name (for audit + the caller's cascade). A case-only change updates the display name in place without a collision check.
func (*Store) ReplaceAll ¶
ReplaceAll atomically replaces all groups (used by cluster config sync). Builds catSets outside the lock for zero contention.
func (*Store) Save ¶
func (s *Store) Save()
Save persists the current groups to disk (atomic write).
func (*Store) SetPathForTest ¶
SetPathForTest points persistence at path without loading.
func (*Store) Update ¶
Update replaces the categories in an existing group. Returns error if not found.
func (*Store) UpdateByID ¶ added in v1.0.86
UpdateByID replaces the categories of the group with the given stable ID. Rename-safe counterpart to Update — the edit lands on the intended group even if its name changed. Returns error if no group carries the id.