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
- Variables
- func AppendAuditEvent(vaultRoot string, event AuditEvent) error
- func AppendUsageEvents(vaultRoot string, events []UsageEvent) error
- func NormalizeEmail(email string) string
- func ResetActorCache()
- type Actor
- type ActorUsageCount
- type AssetUsageCount
- type AuditEvent
- type AuditFilter
- type Bot
- type BotApiKey
- type Team
- type UsageEvent
- type UsageFilter
- type UsageSummary
Constants ¶
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.
const ( TargetTypeTeam = "team" TargetTypeBot = "bot" TargetTypeAsset = "asset" TargetTypeInstallation = "installation" )
Audit target type constants.
const AuditDirName = ".sx/audit"
AuditDirName is the directory under the vault root that holds monthly audit JSONL files.
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.
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.
const UsageDirName = ".sx/usage"
UsageDirName is the directory under the vault root that holds monthly usage JSONL files.
Variables ¶
var ErrBotExists = errors.New("bot already exists")
ErrBotExists is returned when attempting to create a bot that already exists in the vault.
var ErrBotNotFound = errors.New("bot not found")
ErrBotNotFound is returned when a bot lookup fails.
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.
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.
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.
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.
var ErrTeamExists = errors.New("team already exists")
ErrTeamExists is returned when attempting to create a team that already exists.
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 ¶
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 ¶
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
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 ¶
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).
type ActorUsageCount ¶
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
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.
type BotApiKey ¶ added in v1.0.0
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.
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.