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 AppendAuditEvents(vaultRoot string, events []AuditEvent) error
- func AppendUsageEvents(vaultRoot string, events []UsageEvent) error
- func ContextWithIdentity(ctx context.Context, email string) context.Context
- func NormalizeEmail(email string) string
- func ResetActorCache()
- func SetAuditProfileTag(name string)
- func SetIdentityOverride(email string)
- type Actor
- type ActorUsageCount
- type AssetUsageCount
- type AuditEvent
- type AuditFilter
- type Bot
- type BotApiKey
- type BotSkill
- 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" EventAssetRecovered = "asset.recovered" EventAssetUpdated = "asset.updated" EventAssetRemoved = "asset.removed" EventAssetRenamed = "asset.renamed" EventInstallSet = "install.set" EventInstallCleared = "install.cleared" EventInstallRemoved = "install.removed" )
Audit event names. Most mirror the set skills.new emits so dashboards and downstream consumers can treat the two streams interchangeably; local-only vault maintenance events are explicit where they do not have a server equivalent.
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. Stamps the active profile (when set via SetAuditProfileTag) so consumers can correlate cross-profile activity.
func AppendAuditEvents ¶ added in v1.3.5
func AppendAuditEvents(vaultRoot string, events []AuditEvent) error
AppendAuditEvents appends a batch of events to the monthly audit files. Events without timestamps share a single timestamp so related audit rows stay together in the log.
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 ContextWithIdentity ¶ added in v1.2.0
ContextWithIdentity returns a derived context that carries the given email as the per-call identity override. CurrentGitActor consults this in preference to the process-global override. An empty email is recorded as an *explicit* opt-out (skip both overrides, resolve via git config) rather than as a no-op — see identityContextValue.
func NormalizeEmail ¶
NormalizeEmail lowercases and trims an email for comparison.
func ResetActorCache ¶
func ResetActorCache()
ResetActorCache clears the actor cache. Exposed for tests.
func SetAuditProfileTag ¶ added in v1.2.0
func SetAuditProfileTag(name string)
SetAuditProfileTag records the profile name to stamp onto subsequent AppendAuditEvent calls. Empty disables tagging.
func SetIdentityOverride ¶ added in v1.2.0
func SetIdentityOverride(email string)
SetIdentityOverride sets the process-wide email override consulted by CurrentGitActor. Pass empty to clear. Trims whitespace.
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 prefers a per-call identity carried on ctx (see ContextWithIdentity) so concurrent multi-profile work can scope identity per goroutine; falling back to the process-global override (SetIdentityOverride), then to `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 v0.16.2
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"`
// Profile records which sx profile produced the event. Optional —
// older sx versions did not populate it. Useful for cross-vault
// correlation when a user runs multiple profiles against the same
// audit stream.
Profile string `json:"profile,omitempty"`
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 v0.16.2
type Bot struct {
Name string
Slug string
Description string
Teams []string
// InstalledSkills is the bot's resolved skill set (direct + team
// + org-wide installs), deduplicated and sorted. IsDirectInstall marks
// skills attached directly to this bot so callers can edit only those
// attachments without maintaining a second parallel skill list.
InstalledSkills []BotSkill
}
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 v0.16.2
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"`
MemberCount int `toml:"-"` // total members (may exceed len(Members) when paginated)
}
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.
func SummarizeUsageEvents ¶ added in v1.4.0
func SummarizeUsageEvents(events []UsageEvent) *UsageSummary
SummarizeUsageEvents computes per-asset and per-actor rollups over an in-memory event slice. Shared by the file-backed SummarizeUsage and by server-backed vaults that fetch raw events over the wire, so both produce identical UsageSummary shapes.