mgmt

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 28, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package mgmt contains shared data types and serializers for sx management features: teams (interface types only — on-disk storage lives in the manifest package), identity resolution, usage events, and audit events.

Index

Constants

View Source
const (
	EventTeamCreated       = "team.created"
	EventTeamUpdated       = "team.updated"
	EventTeamDeleted       = "team.deleted"
	EventTeamMemberAdded   = "team.member_added"
	EventTeamMemberRemoved = "team.member_removed"
	EventTeamAdminSet      = "team.admin_set"
	EventTeamAdminUnset    = "team.admin_unset"
	EventTeamRepoAdded     = "team.repo_added"
	EventTeamRepoRemoved   = "team.repo_removed"
	EventBotCreated        = "bot.created"
	EventBotUpdated        = "bot.updated"
	EventBotDeleted        = "bot.deleted"
	EventBotTeamAdded      = "bot.team_added"
	EventBotTeamRemoved    = "bot.team_removed"
	// Bot API key lifecycle events live on the Sleuth server's audit
	// stream, not the local .sx/audit JSONL log: file-based vaults
	// reject bot key operations entirely (BotApiKeyManager is
	// Sleuth-only), and Sleuth's audit is captured server-side. No
	// local constants are defined for those events to avoid implying
	// audit coverage that the local log doesn't have. See docs/bots.md.
	EventAssetCreated   = "asset.created"
	EventAssetUpdated   = "asset.updated"
	EventAssetRemoved   = "asset.removed"
	EventAssetRenamed   = "asset.renamed"
	EventInstallSet     = "install.set"
	EventInstallCleared = "install.cleared"
)

Audit event names. These mirror the set skills.new emits so dashboards and downstream consumers can treat the two streams interchangeably.

View Source
const (
	TargetTypeTeam         = "team"
	TargetTypeBot          = "bot"
	TargetTypeAsset        = "asset"
	TargetTypeInstallation = "installation"
)

Audit target type constants.

View Source
const AuditDirName = ".sx/audit"

AuditDirName is the directory under the vault root that holds monthly audit JSONL files.

View Source
const SXBotEnv = "SX_BOT"

SXBotEnv is the environment variable that, when set, makes CurrentGitActor return a bot actor instead of the git-email actor. File-based vaults treat the named bot as identity-only — anyone with vault read access can claim any bot — and Sleuth vaults expect the caller to also be authenticated via a bot API key.

View Source
const SXBotKeyEnv = "SX_BOT_KEY" //nolint:gosec // env var name, not a credential

SXBotKeyEnv is the environment variable that holds the raw bot API key for Sleuth vaults. When set, the Sleuth vault constructor uses it as the bearer token, overriding the user OAuth token saved by `sx cloud connect`. File-based vaults ignore this — bots are identity-only there.

View Source
const UsageDirName = ".sx/usage"

UsageDirName is the directory under the vault root that holds monthly usage JSONL files.

Variables

View Source
var ErrBotExists = errors.New("bot already exists")

ErrBotExists is returned when attempting to create a bot that already exists in the vault.

View Source
var ErrBotNotFound = errors.New("bot not found")

ErrBotNotFound is returned when a bot lookup fails.

View Source
var ErrEmptyBotName = errors.New("bot name cannot be empty")

ErrEmptyBotName is returned when a bot name is blank or whitespace-only. Names are the primary key of bots in the manifest, so an empty one would collide with any other empty-named bot and hide from lookups.

View Source
var ErrEmptyTeamName = errors.New("team name cannot be empty")

ErrEmptyTeamName is returned when a team name is blank or whitespace- only. Names are the primary key of teams in the manifest so an empty one would collide with any other empty-named team and hide from lookups.

View Source
var ErrIdentityNotSet = errors.New("identity not set: run 'git config --global user.email \"you@example.com\"'")

ErrIdentityNotSet is returned when no git email or fallback identity can be determined.

View Source
var ErrLastAdmin = errors.New("team would be left without an admin")

ErrLastAdmin is returned when a mutation would leave a team with zero admins, which would render the team permanently unmanageable.

View Source
var ErrTeamExists = errors.New("team already exists")

ErrTeamExists is returned when attempting to create a team that already exists.

View Source
var ErrTeamNotFound = errors.New("team not found")

ErrTeamNotFound is returned when a team lookup fails.

Functions

func AppendAuditEvent

func AppendAuditEvent(vaultRoot string, event AuditEvent) error

AppendAuditEvent appends an event to the monthly audit file for the event's timestamp. The parent directory is created if needed.

func AppendUsageEvents

func AppendUsageEvents(vaultRoot string, events []UsageEvent) error

AppendUsageEvents appends a batch of usage events to the monthly files for their timestamps. Events with a zero timestamp are stamped with the current UTC time. Events are grouped by month and written with one O_APPEND handle per month to minimize syscalls.

func NormalizeEmail

func NormalizeEmail(email string) string

NormalizeEmail lowercases and trims an email for comparison.

func ResetActorCache

func ResetActorCache()

ResetActorCache clears the actor cache. Exposed for tests.

Types

type Actor

type Actor struct {
	Email string
	Name  string

	// Synthetic is true when Email was derived from $USER@host instead of
	// a real git config value. Synthetic actors cannot pass mgmt
	// mutations because their identity can be spoofed by flipping $USER;
	// see RequireRealIdentity.
	Synthetic bool

	// Bot is non-empty when the caller is acting as a bot (typically via
	// SX_BOT=<name>). Email is then "bot:<name>" so audit attribution
	// stays unique and never collides with a human email. See IsBot.
	Bot string
}

Actor is a resolved caller identity used for audit, usage, and install targeting.

func CurrentGitActor

func CurrentGitActor(ctx context.Context, repoPath string) (Actor, error)

CurrentGitActor resolves the caller's identity. SX_BOT short-circuits the resolution: if set, the actor is a bot identity, with Email "bot:<name>" so audit log entries are attributed unambiguously and never collide with a human email. Otherwise resolution proceeds via `git config user.email` (scoped to the given repoPath if non-empty, falling back to global git config), with a $USER@host fallback for unconfigured workstations. Returns ErrIdentityNotSet only when every source fails.

func (Actor) IsBot added in v1.0.0

func (a Actor) IsBot() bool

IsBot returns true when the actor is a bot identity (e.g. resolved from SX_BOT). Used by resolution code to switch to the bot scope rule instead of the human one.

func (Actor) RequireRealIdentity

func (a Actor) RequireRealIdentity() error

RequireRealIdentity returns ErrIdentityNotSet if the actor is synthetic. Call this at the top of any mgmt mutation helper that writes to shared vault state (teams, installations, scopes) — a synthetic identity is fine for reads but cannot be trusted as the authoritative actor behind a persisted change. Bot actors are rejected: bot identities are read-only by design (they fetch installed assets but never mutate vault state).

func (Actor) String

func (a Actor) String() string

String returns "name <email>" if both are set, just the email otherwise.

type ActorUsageCount

type ActorUsageCount struct {
	Actor     string
	TotalUses int
}

ActorUsageCount is the per-actor rollup in a UsageSummary.

type AssetUsageCount

type AssetUsageCount struct {
	AssetName    string
	AssetType    string
	TotalUses    int
	UniqueActors int
	LastUsed     time.Time
}

AssetUsageCount is the per-asset rollup in a UsageSummary.

type AuditEvent

type AuditEvent struct {
	Timestamp  time.Time      `json:"ts"`
	Actor      string         `json:"actor"`
	Event      string         `json:"event"`
	TargetType string         `json:"target_type"`
	Target     string         `json:"target"`
	Data       map[string]any `json:"data,omitempty"`
}

AuditEvent is a single row in .sx/audit/YYYY-MM.jsonl.

func QueryAuditEvents

func QueryAuditEvents(vaultRoot string, filter AuditFilter) ([]AuditEvent, error)

QueryAuditEvents reads all monthly files under .sx/audit and returns the events that match the filter, sorted newest first. A non-existent audit directory returns an empty slice, not an error.

type AuditFilter

type AuditFilter struct {
	Actor       string
	EventPrefix string
	Target      string
	Since       time.Time
	Until       time.Time
	Limit       int
}

AuditFilter narrows an audit query. Zero values mean "don't filter on that field". EventPrefix matches if the event string starts with the prefix (e.g. "team." matches every team event).

type Bot added in v1.0.0

type Bot struct {
	Name        string
	Description string
	Teams       []string
}

Bot is a non-human service identity that consumes assets. Bots gain repository context by being members of one or more teams; assets can also be installed directly to a bot via the InstallKindBot scope.

File-based vaults (path/git) treat bots as identity-only — the trust boundary is "vault read access ⇒ asset access", so anyone with access to the vault can claim any bot identity by setting SX_BOT=<name>. Sleuth vaults issue real OAuth API keys via createBotApiKey; that capability is exposed only on the BotApiKeyManager interface.

func (*Bot) IsOnTeam added in v1.0.0

func (b *Bot) IsOnTeam(name string) bool

IsOnTeam returns true if the bot is a member of the named team.

type BotApiKey added in v1.0.0

type BotApiKey struct {
	ID          string
	Label       string
	MaskedToken string
	CreatedAt   time.Time
}

BotApiKey is metadata about a bot API key, returned by Sleuth vaults. The raw token is only available at creation time. File-based vaults don't issue keys, so callers should expect ErrNotImplemented from the BotApiKeyManager methods on those.

type Team

type Team struct {
	Name         string   `toml:"name"`
	Description  string   `toml:"description,omitempty"`
	Members      []string `toml:"members,omitempty"`
	Admins       []string `toml:"admins,omitempty"`
	Repositories []string `toml:"repositories,omitempty"`
}

Team is a named grouping of members, admins, and repositories. It is the unit of targeted installation for git and path vaults and the shape returned by the Vault interface's team-management methods.

func (*Team) IsAdmin

func (t *Team) IsAdmin(email string) bool

IsAdmin returns true if the given email is in the team's admin list. Comparison is case-insensitive.

func (*Team) IsMember

func (t *Team) IsMember(email string) bool

IsMember returns true if the given email is in the team's member list. Comparison is case-insensitive.

type UsageEvent

type UsageEvent struct {
	Timestamp    time.Time `json:"ts"`
	Actor        string    `json:"actor"`
	AssetName    string    `json:"asset_name"`
	AssetVersion string    `json:"asset_version"`
	AssetType    string    `json:"asset_type"`
}

UsageEvent is a single row in .sx/usage/YYYY-MM.jsonl. Timestamp and actor are normalized to UTC and lowercase respectively at append time.

func ReadUsageEvents

func ReadUsageEvents(vaultRoot string, filter UsageFilter) ([]UsageEvent, error)

ReadUsageEvents reads every event from the vault's usage directory, filtered by the given filter. Returned events are not sorted.

type UsageFilter

type UsageFilter struct {
	AssetName string
	AssetType string
	Actor     string
	Since     time.Time
	Until     time.Time
}

UsageFilter narrows a usage query.

type UsageSummary

type UsageSummary struct {
	TotalEvents int
	PerAsset    []AssetUsageCount
	PerActor    []ActorUsageCount
}

UsageSummary is the aggregated result of a usage query.

func SummarizeUsage

func SummarizeUsage(vaultRoot string, filter UsageFilter) (*UsageSummary, error)

SummarizeUsage computes per-asset and per-actor rollups over the matching events. The slices are returned sorted by TotalUses descending.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL