Documentation
¶
Overview ¶
Package database — query tracer.
queryTracer implements the pgx v5 QueryTracer interface and records two metrics for every query the driver executes:
- llmsafespaces_db_query_duration_seconds{operation}
- llmsafespaces_db_errors_total{operation,error_type}
It is attached to both the *sql.DB pool (via stdlib.OpenDB + pgx.ConnConfig.Tracer) and the *pgxpool.Pool used by the secrets store (via pgxpool.Config.ConnConfig.Tracer). Every SQL statement issued by the API binary therefore flows through one tracer and one metric API, which is the long-term-correct alternative to wrapping every call site or registering a database/sql wrapper driver.
Index ¶
- Variables
- func NewQueryTracer() pgx.QueryTracer
- type ImageFactoryStore
- type OrgStore
- type PendingOrgCleanup
- type PgEmailTokenStore
- type PgOrgStore
- func (s *PgOrgStore) AcceptInvitationTx(ctx context.Context, invID, userID string, role types.OrgRole) (*types.OrgMember, bool, error)
- func (s *PgOrgStore) AddOrgMember(ctx context.Context, orgID, userID string, role types.OrgRole) error
- func (s *PgOrgStore) ClearWorkspaceAgentRole(ctx context.Context, workspaceID, userID string) error
- func (s *PgOrgStore) CountInvitationsLastHour(ctx context.Context, orgID string) (int, error)
- func (s *PgOrgStore) CountOrgAdmins(ctx context.Context, orgID string) (int, error)
- func (s *PgOrgStore) CountSSOConfigs(ctx context.Context) (int, error)
- func (s *PgOrgStore) CreateAgentRole(ctx context.Context, role *types.AgentRole, configJSON []byte) (*types.AgentRole, error)
- func (s *PgOrgStore) CreateInvitation(ctx context.Context, inv *types.OrgInvitation) error
- func (s *PgOrgStore) CreateOrgWithAdmin(ctx context.Context, org *types.Organization, adminUserID string) (*types.Organization, error)
- func (s *PgOrgStore) DeclineInvitation(ctx context.Context, invID string) error
- func (s *PgOrgStore) DeleteAgentRole(ctx context.Context, roleID string) error
- func (s *PgOrgStore) DeleteInvitation(ctx context.Context, invID string) error
- func (s *PgOrgStore) DeleteOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey) error
- func (s *PgOrgStore) DeleteSSOConfig(ctx context.Context, orgID string) error
- func (s *PgOrgStore) DeleteStripeEvent(ctx context.Context, eventID string) error
- func (s *PgOrgStore) DeleteWorkspacePrompt(ctx context.Context, workspaceID string) error
- func (s *PgOrgStore) DemoteOrgAdminIfNotLast(ctx context.Context, orgID, targetUserID string) (bool, error)
- func (s *PgOrgStore) FindSSOConfigByDomain(ctx context.Context, domain string) (*types.OrgSSOConfig, error)
- func (s *PgOrgStore) GetAgentRole(ctx context.Context, roleID string) (*types.AgentRole, error)
- func (s *PgOrgStore) GetInvitationByID(ctx context.Context, invID string) (*types.OrgInvitation, error)
- func (s *PgOrgStore) GetInvitationByTokenHash(ctx context.Context, tokenHash string) (*types.OrgInvitation, error)
- func (s *PgOrgStore) GetOrg(ctx context.Context, orgID string) (*types.Organization, error)
- func (s *PgOrgStore) GetOrgBySlug(ctx context.Context, slug string) (*types.Organization, error)
- func (s *PgOrgStore) GetOrgIDByStripeCustomer(ctx context.Context, stripeCustomerID string) (string, error)
- func (s *PgOrgStore) GetOrgMember(ctx context.Context, orgID, userID string) (*types.OrgMember, error)
- func (s *PgOrgStore) GetOrgPolicies(ctx context.Context, orgID string) ([]*types.OrgPolicy, error)
- func (s *PgOrgStore) GetPlatformSetting(ctx context.Context, key types.PlatformSettingKey) (*types.PlatformSetting, error)
- func (s *PgOrgStore) GetRoleDependents(ctx context.Context, roleID string) ([]*types.AgentRole, error)
- func (s *PgOrgStore) GetSSOConfig(ctx context.Context, orgID string) (*types.OrgSSOConfig, error)
- func (s *PgOrgStore) GetStripeCustomerID(ctx context.Context, orgID string) (string, error)
- func (s *PgOrgStore) GetUserEmail(ctx context.Context, userID string) (string, error)
- func (s *PgOrgStore) GetUserIDByEmail(ctx context.Context, email string) (string, error)
- func (s *PgOrgStore) GetUserOrgID(ctx context.Context, userID string) (string, error)
- func (s *PgOrgStore) GetUserPlan(ctx context.Context, userID string) (string, error)
- func (s *PgOrgStore) GetWorkspaceAgentRole(ctx context.Context, workspaceID string) (*types.AgentRole, error)
- func (s *PgOrgStore) GetWorkspaceOrgID(ctx context.Context, workspaceID string) (string, error)
- func (s *PgOrgStore) GetWorkspacePrompt(ctx context.Context, workspaceID string) (*types.WorkspacePrompt, error)
- func (s *PgOrgStore) HardDeleteOrg(ctx context.Context, orgID string) error
- func (s *PgOrgStore) HasRoleWorkspaceUsage(ctx context.Context, roleID string) (bool, error)
- func (s *PgOrgStore) IsOrgAdmin(ctx context.Context, orgID, userID string) (bool, error)
- func (s *PgOrgStore) IsOrgMember(ctx context.Context, orgID, userID string) (bool, error)
- func (s *PgOrgStore) ListAgentRoles(ctx context.Context, scope string, orgID string) ([]*types.AgentRole, error)
- func (s *PgOrgStore) ListAllAudit(ctx context.Context, filters types.AuditFilters) ([]*types.AuditEntry, *types.PaginationMetadata, error)
- func (s *PgOrgStore) ListAllOrgs(ctx context.Context, limit, offset int, statusFilter *string) ([]types.OrgSummary, *types.PaginationMetadata, error)
- func (s *PgOrgStore) ListOrgAudit(ctx context.Context, orgID string, limit, offset int) ([]*types.AuditEntry, *types.PaginationMetadata, error)
- func (s *PgOrgStore) ListOrgMembers(ctx context.Context, orgID string) ([]*types.OrgMember, error)
- func (s *PgOrgStore) ListOrgWorkspaces(ctx context.Context, orgID string, limit, offset int) ([]*types.WorkspaceMetadata, *types.PaginationMetadata, error)
- func (s *PgOrgStore) ListOrgsForUser(ctx context.Context, userID string) ([]*types.OrgResponse, error)
- func (s *PgOrgStore) ListPendingInvitations(ctx context.Context, orgID string) ([]*types.OrgInvitation, error)
- func (s *PgOrgStore) ListPendingOrgsOlderThan(ctx context.Context, maxAge time.Duration) ([]PendingOrgCleanup, error)
- func (s *PgOrgStore) ListSSODomains(ctx context.Context) ([]types.SSODomain, error)
- func (s *PgOrgStore) LogAuditEvent(ctx context.Context, domain, actorID, action, targetID string, orgID *string, ...) error
- func (s *PgOrgStore) LogOrgEvent(ctx context.Context, orgID, actorID, action, targetID string, ...) error
- func (s *PgOrgStore) MarkUserEmailVerified(ctx context.Context, userID string) error
- func (s *PgOrgStore) OrgsWhereUserIsLastActiveAdmin(ctx context.Context, userID string) ([]types.LastAdminOrg, error)
- func (s *PgOrgStore) RecordStripeEvent(ctx context.Context, eventID, eventType string) (bool, error)
- func (s *PgOrgStore) RemoveOrgAdminIfNotLast(ctx context.Context, orgID, targetUserID string) (bool, error)
- func (s *PgOrgStore) RemoveOrgMember(ctx context.Context, orgID, userID string) error
- func (s *PgOrgStore) RotateVerificationToken(ctx context.Context, orgID string) (string, error)
- func (s *PgOrgStore) SetBillingAccountSubscription(ctx context.Context, ownerID, ownerType, provider, subscriptionID string) error
- func (s *PgOrgStore) SetDomainVerified(ctx context.Context, orgID, domain string) (bool, error)
- func (s *PgOrgStore) SetOrgDefaultRole(ctx context.Context, orgID, roleID string) error
- func (s *PgOrgStore) SetOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey, ...) error
- func (s *PgOrgStore) SetPlatformSetting(ctx context.Context, key types.PlatformSettingKey, value json.RawMessage, ...) error
- func (s *PgOrgStore) SetWorkspaceAgentRole(ctx context.Context, workspaceID, roleID, userID string) error
- func (s *PgOrgStore) SetWorkspacePrompt(ctx context.Context, workspaceID string, prompt string, updatedBy string) error
- func (s *PgOrgStore) SoftDeleteOrg(ctx context.Context, orgID string) error
- func (s *PgOrgStore) SuspendUserGuardedByLastAdmin(ctx context.Context, userID string, force bool) (*types.LastAdminOrg, error)
- func (s *PgOrgStore) UpdateAgentRole(ctx context.Context, roleID string, role *types.AgentRole, configJSON []byte) (*types.AgentRole, error)
- func (s *PgOrgStore) UpdateOrg(ctx context.Context, orgID string, req types.UpdateOrgRequest) (*types.Organization, error)
- func (s *PgOrgStore) UpdateOrgMemberRole(ctx context.Context, orgID, userID string, role types.OrgRole) error
- func (s *PgOrgStore) UpdateOrgStatus(ctx context.Context, orgID string, status *types.OrgStatus, ...) error
- func (s *PgOrgStore) UpsertSSOConfig(ctx context.Context, config *types.OrgSSOConfig) error
- type Service
- func (s *Service) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
- func (s *Service) CheckPermission(ctx context.Context, userID, resourceType, resourceID, action string) (bool, error)
- func (s *Service) CheckResourceOwnership(ctx context.Context, userID, resourceType, resourceID string) (bool, error)
- func (s *Service) CountActiveWorkspacesByUserAndOrg(ctx context.Context, userID, orgID string) (int, error)
- func (s *Service) CountUsers(ctx context.Context) (int, error)
- func (s *Service) CountWorkspacesByUserAndOrg(ctx context.Context, userID, orgID string) (int, error)
- func (s *Service) CreateAPIKey(ctx context.Context, apiKey *types.APIKey) error
- func (s *Service) CreateBuild(ctx context.Context, b *imagefactory.Build) error
- func (s *Service) CreateConfig(ctx context.Context, c *imagefactory.Config) error
- func (s *Service) CreateConfigAndBuild(ctx context.Context, c *imagefactory.Config, b *imagefactory.Build) error
- func (s *Service) CreateUser(ctx context.Context, user *types.User) error
- func (s *Service) CreateWorkspace(ctx context.Context, workspace *types.WorkspaceMetadata) error
- func (s *Service) DeleteAPIKey(ctx context.Context, userID, keyID string) error
- func (s *Service) DeleteBase(ctx context.Context, name, version string) error
- func (s *Service) DeleteConfig(ctx context.Context, id string) error
- func (s *Service) DeleteKnownFailure(ctx context.Context, selectionHash, baseName string) error
- func (s *Service) DeleteSessionIndex(ctx context.Context, workspaceID string) error
- func (s *Service) DeleteSessionTree(ctx context.Context, workspaceID, sessionID string) error
- func (s *Service) DeleteUser(ctx context.Context, userID string) error
- func (s *Service) DeleteWorkspace(ctx context.Context, workspaceID string) error
- func (s *Service) GetAPIKey(ctx context.Context, userID, keyID string) (*types.APIKey, error)
- func (s *Service) GetAPIKeyRecordByHash(ctx context.Context, keyHash string) (*types.APIKey, error)
- func (s *Service) GetAllInstanceSettings(ctx context.Context) (map[string]json.RawMessage, error)
- func (s *Service) GetAllUserSettings(ctx context.Context, userID string) (map[string]json.RawMessage, error)
- func (s *Service) GetBase(ctx context.Context, name, version string) (imagefactory.Base, error)
- func (s *Service) GetBuild(ctx context.Context, id string) (imagefactory.Build, error)
- func (s *Service) GetBuildByGHRunID(ctx context.Context, ghRunID int64) (imagefactory.Build, error)
- func (s *Service) GetConfig(ctx context.Context, id string) (imagefactory.Config, error)
- func (s *Service) GetConfigByHash(ctx context.Context, hash string, scope imagefactory.ConfigScope, ...) (imagefactory.Config, error)
- func (s *Service) GetDefaultModel(ctx context.Context, workspaceID string) (string, error)
- func (s *Service) GetExtension(ctx context.Context, id string) (imagefactory.Extension, error)
- func (s *Service) GetInFlightOrSuccessfulBuild(ctx context.Context, hash, baseVersion string) (*imagefactory.Build, error)
- func (s *Service) GetKnownFailure(ctx context.Context, selectionHash, baseName string) (imagefactory.KnownFailure, error)
- func (s *Service) GetLastCredentialChangedAt(ctx context.Context, workspaceID string) (time.Time, error)
- func (s *Service) GetLaunchableConfigByHash(ctx context.Context, hash string, scope imagefactory.ConfigScope, ...) (imagefactory.Config, string, error)
- func (s *Service) GetPlatformConfig(ctx context.Context) (imagefactory.PlatformConfig, error)
- func (s *Service) GetUser(ctx context.Context, userID string) (*types.User, error)
- func (s *Service) GetUserByAPIKey(ctx context.Context, apiKey string) (*types.User, error)
- func (s *Service) GetUserByEmail(ctx context.Context, email string) (*types.User, error)
- func (s *Service) GetWorkspace(ctx context.Context, workspaceID string) (*types.WorkspaceMetadata, error)
- func (s *Service) InsertInstanceSettingIfMissing(ctx context.Context, key string, value json.RawMessage) (bool, error)
- func (s *Service) ListAPIKeys(ctx context.Context, userID string) ([]*types.APIKey, error)
- func (s *Service) ListAPIKeysWithDecrypt(ctx context.Context, userID string) ([]*types.APIKey, error)
- func (s *Service) ListAllUsers(ctx context.Context, limit, offset int, statusFilter *string) ([]types.UserListEntry, *types.PaginationMetadata, error)
- func (s *Service) ListAllWorkspaceOwners(ctx context.Context) (map[string]string, error)
- func (s *Service) ListAllWorkspacesForBilling(ctx context.Context) ([]WorkspaceBillingRecord, error)
- func (s *Service) ListBases(ctx context.Context) ([]imagefactory.Base, error)
- func (s *Service) ListConfigs(ctx context.Context, scope imagefactory.ConfigScope, ownerID, orgID *string) ([]imagefactory.Config, error)
- func (s *Service) ListExtensions(ctx context.Context, includeRetired bool) ([]imagefactory.Extension, error)
- func (s *Service) ListKnownFailures(ctx context.Context) ([]imagefactory.KnownFailure, error)
- func (s *Service) ListPendingReloadWorkspaces(ctx context.Context, userID string) ([]*types.WorkspaceMetadata, error)
- func (s *Service) ListRejectedConfigsForFailure(ctx context.Context, selectionHash, baseName string) ([]imagefactory.Config, error)
- func (s *Service) ListSessionIndex(ctx context.Context, workspaceID string) ([]types.SessionListItem, error)
- func (s *Service) ListVisibleConfigs(ctx context.Context, ownerID, orgID *string) ([]imagefactory.Config, error)
- func (s *Service) ListWorkspaces(ctx context.Context, userID string, limit, offset int) ([]*types.WorkspaceMetadata, *types.PaginationMetadata, error)
- func (s *Service) MarkAgentReloaded(ctx context.Context, tx *sql.Tx, workspaceID string, priorChangedAt time.Time) (time.Time, error)
- func (s *Service) MarkBuildFailed(ctx context.Context, id, failureReason, explanation string) error
- func (s *Service) MarkBuildSucceeded(ctx context.Context, id, imageRef, digest string) error
- func (s *Service) MarkCredentialChanged(ctx context.Context, workspaceID string) error
- func (s *Service) MarkWorkspaceDeleted(ctx context.Context, workspaceID string)
- func (s *Service) Ping(ctx context.Context) error
- func (s *Service) PublishExtension(ctx context.Context, e imagefactory.Extension) error
- func (s *Service) PurgeUserSecrets(ctx context.Context, userID string) error
- func (s *Service) RecordKnownFailure(ctx context.Context, kf imagefactory.KnownFailure) error
- func (s *Service) RenameConfig(ctx context.Context, id, newName string) error
- func (s *Service) RetireExtension(ctx context.Context, id string) error
- func (s *Service) SetConfigStatus(ctx context.Context, id string, status imagefactory.ConfigStatus) error
- func (s *Service) SetExtensionReviewRequested(ctx context.Context, id string, v bool) error
- func (s *Service) SetInstanceSetting(ctx context.Context, key string, value json.RawMessage) error
- func (s *Service) SetKnownFailureRetriable(ctx context.Context, selectionHash, baseName string, retriable bool) error
- func (s *Service) SetPlatformConfig(ctx context.Context, pc imagefactory.PlatformConfig) error
- func (s *Service) SetUserSetting(ctx context.Context, userID, key string, value json.RawMessage) error
- func (s *Service) SetUserStatus(ctx context.Context, userID string, status types.UserStatus) error
- func (s *Service) Start() error
- func (s *Service) Stop() error
- func (s *Service) SyncWorkspaceVersionInfo(ctx context.Context, workspaceID, imageTag, agentVersion string)
- func (s *Service) TransitionBuildFailed(ctx context.Context, buildID, configID string, kf imagefactory.KnownFailure) error
- func (s *Service) TransitionBuildSucceeded(ctx context.Context, buildID, configID, imageRef, digest string) error
- func (s *Service) UpdateAPIKeyDEK(ctx context.Context, keyID string, wrappedDEK, kekSalt []byte, synced bool) error
- func (s *Service) UpdateSessionLastSeen(ctx context.Context, workspaceID, sessionID string) error
- func (s *Service) UpdateUser(ctx context.Context, userID string, updates types.UserUpdates) error
- func (s *Service) UpdateWorkspace(ctx context.Context, workspaceID string, updates types.WorkspaceUpdates) error
- func (s *Service) UpsertBase(ctx context.Context, b imagefactory.Base) error
- func (s *Service) UpsertSessionContextUsed(ctx context.Context, workspaceID, sessionID string, contextUsed int64) error
- func (s *Service) UpsertSessionMessage(ctx context.Context, workspaceID, sessionID string, at time.Time) error
- func (s *Service) UpsertSessionParent(ctx context.Context, workspaceID, sessionID, parentID string) error
- func (s *Service) UpsertSessionTitle(ctx context.Context, workspaceID, sessionID, title string) error
- type WorkspaceBillingRecord
Constants ¶
This section is empty.
Variables ¶
var ErrConflict = errors.New("conflict")
ErrConflict is returned for unique-constraint violations (e.g. rename collides with an existing name in the same scope).
var ErrNotFound = errors.New("not found")
ErrNotFound is the store's not-found sentinel, returned by GetX methods when no row matches. Callers use errors.Is(err, database.ErrNotFound). Exported so handler-layer code can distinguish 404 from 500 without fragile string matching.
var ErrTokenAlreadyConsumed = fmt.Errorf("token already consumed")
ErrTokenAlreadyConsumed is returned when ConsumeEmailToken affects 0 rows (the token was consumed by a concurrent request between Get and Consume — TOCTOU race). The handler maps this to 410 Gone.
Functions ¶
func NewQueryTracer ¶
func NewQueryTracer() pgx.QueryTracer
NewQueryTracer returns the QueryTracer used by the API's *sql.DB pool. Exported so callers (e.g. the secrets pgxpool initialized in internal/app) can attach the same tracer to their own pool, ensuring every query — regardless of which pool issues it — flows through one metrics path. The returned value implements pgx.QueryTracer.
Types ¶
type ImageFactoryStore ¶ added in v0.7.1
type ImageFactoryStore interface {
// ── Platform config (singleton row) ───────────────────────────────
GetPlatformConfig(ctx context.Context) (imagefactory.PlatformConfig, error)
SetPlatformConfig(ctx context.Context, pc imagefactory.PlatformConfig) error
// ── Bases ─────────────────────────────────────────────────────────
ListBases(ctx context.Context) ([]imagefactory.Base, error)
GetBase(ctx context.Context, name, version string) (imagefactory.Base, error)
UpsertBase(ctx context.Context, b imagefactory.Base) error
DeleteBase(ctx context.Context, name, version string) error
// ── Extensions ────────────────────────────────────────────────────
ListExtensions(ctx context.Context, includeRetired bool) ([]imagefactory.Extension, error)
GetExtension(ctx context.Context, id string) (imagefactory.Extension, error)
PublishExtension(ctx context.Context, e imagefactory.Extension) error
RetireExtension(ctx context.Context, id string) error
SetExtensionReviewRequested(ctx context.Context, id string, v bool) error
// ── Known failures ───────────────────────────────────────────────
ListKnownFailures(ctx context.Context) ([]imagefactory.KnownFailure, error)
GetKnownFailure(ctx context.Context, selectionHash, baseName string) (imagefactory.KnownFailure, error)
RecordKnownFailure(ctx context.Context, kf imagefactory.KnownFailure) error
SetKnownFailureRetriable(ctx context.Context, selectionHash, baseName string, retriable bool) error
DeleteKnownFailure(ctx context.Context, selectionHash, baseName string) error
ListRejectedConfigsForFailure(ctx context.Context, selectionHash, baseName string) ([]imagefactory.Config, error)
// ── Configs ──────────────────────────────────────────────────────
CreateConfig(ctx context.Context, c *imagefactory.Config) error
CreateConfigAndBuild(ctx context.Context, c *imagefactory.Config, b *imagefactory.Build) error
GetConfig(ctx context.Context, id string) (imagefactory.Config, error)
GetConfigByHash(ctx context.Context, hash string, scope imagefactory.ConfigScope, ownerID, orgID *string) (imagefactory.Config, error)
ListConfigs(ctx context.Context, scope imagefactory.ConfigScope, ownerID, orgID *string) ([]imagefactory.Config, error)
ListVisibleConfigs(ctx context.Context, ownerID, orgID *string) ([]imagefactory.Config, error)
SetConfigStatus(ctx context.Context, id string, status imagefactory.ConfigStatus) error
DeleteConfig(ctx context.Context, id string) error
RenameConfig(ctx context.Context, id, newName string) error
// GetLaunchableConfigByHash returns a Ready config matching the hash
// and scope/owner filter, together with the image_ref of its
// successful build. Used by the workspace launch path to resolve a
// user-selected config hash to a concrete, pre-built image. Returns
// ErrNotFound if the config doesn't exist, isn't Ready, or has no
// successful build (the normal "not launchable yet" case).
GetLaunchableConfigByHash(ctx context.Context, hash string, scope imagefactory.ConfigScope, ownerID, orgID *string) (imagefactory.Config, string, error)
// ── Builds ───────────────────────────────────────────────────────
GetBuild(ctx context.Context, id string) (imagefactory.Build, error)
GetInFlightOrSuccessfulBuild(ctx context.Context, hash, baseVersion string) (*imagefactory.Build, error)
GetBuildByGHRunID(ctx context.Context, ghRunID int64) (imagefactory.Build, error)
CreateBuild(ctx context.Context, b *imagefactory.Build) error
MarkBuildSucceeded(ctx context.Context, id, imageRef, digest string) error
MarkBuildFailed(ctx context.Context, id, failureReason, explanation string) error
TransitionBuildSucceeded(ctx context.Context, buildID, configID, imageRef, digest string) error
TransitionBuildFailed(ctx context.Context, buildID, configID string, kf imagefactory.KnownFailure) error
}
ImageFactoryStore is the data-access interface for the image factory (design/0046, design/0047). Methods are grouped: catalog (read), catalog admin (write), known failures, configs, builds.
All methods take ctx and return domain types from api/internal/imagefactory. Handlers depend on this interface (via the *database.Service concrete type) and tests inject fakes.
type OrgStore ¶
type OrgStore interface {
CreateOrgWithAdmin(ctx context.Context, org *types.Organization, adminUserID string) (*types.Organization, error)
GetOrg(ctx context.Context, orgID string) (*types.Organization, error)
GetOrgBySlug(ctx context.Context, slug string) (*types.Organization, error)
ListOrgsForUser(ctx context.Context, userID string) ([]*types.OrgResponse, error)
UpdateOrg(ctx context.Context, orgID string, req types.UpdateOrgRequest) (*types.Organization, error)
SoftDeleteOrg(ctx context.Context, orgID string) error
AddOrgMember(ctx context.Context, orgID, userID string, role types.OrgRole) error
GetOrgMember(ctx context.Context, orgID, userID string) (*types.OrgMember, error)
ListOrgMembers(ctx context.Context, orgID string) ([]*types.OrgMember, error)
// CountOrgAdmins returns the number of admin members in an active (non-
// deleted) org. Used by the SSO login flow to avoid demoting the last admin
// on an IdP-driven role change (org orphaning prevention, cf. D19).
CountOrgAdmins(ctx context.Context, orgID string) (int, error)
UpdateOrgMemberRole(ctx context.Context, orgID, userID string, role types.OrgRole) error
RemoveOrgMember(ctx context.Context, orgID, userID string) error
RemoveOrgAdminIfNotLast(ctx context.Context, orgID, targetUserID string) (bool, error)
DemoteOrgAdminIfNotLast(ctx context.Context, orgID, targetUserID string) (bool, error)
IsOrgMember(ctx context.Context, orgID, userID string) (bool, error)
IsOrgAdmin(ctx context.Context, orgID, userID string) (bool, error)
ListOrgWorkspaces(ctx context.Context, orgID string, limit, offset int) ([]*types.WorkspaceMetadata, *types.PaginationMetadata, error)
// GetUserIDByEmail resolves an owner email to a user ID for admin-driven org
// creation (design 0031 D1). Returns ("", nil) when no user matches. This is
// a single targeted lookup, never a search/list endpoint, to prevent account
// enumeration. Users are hard-deleted (no deleted_at column), so no soft-delete
// filter is needed.
GetUserIDByEmail(ctx context.Context, email string) (string, error)
// GetUserEmail resolves a user ID to their email (inverse of GetUserIDByEmail).
// Used by invitation acceptance to verify email binding.
GetUserEmail(ctx context.Context, userID string) (string, error)
// MarkUserEmailVerified sets users.email_verified=true for the given user,
// bypassing the email-verification token flow. Used by the org-admin
// "Verify" action when an admin has confirmed the member's identity
// out-of-band. Idempotent.
MarkUserEmailVerified(ctx context.Context, userID string) error
// GetUserOrgID returns the user's single org ID (or "" if not in any org).
// With single-org enforcement (D8), a user belongs to at most one org. Used
// by invitation acceptance (S3 cross-org check) and workspace auto-attribution
// (D4). Returns ("", nil) on no membership. S7 in 0034.
GetUserOrgID(ctx context.Context, userID string) (string, error)
// US-43.1: Stripe lifecycle. UpdateOrgStatus sets the operational status
// (active/suspended) and/or subscription_status and/or plan_id. A nil/empty
// argument leaves the column unchanged.
UpdateOrgStatus(ctx context.Context, orgID string, status *types.OrgStatus, subStatus *types.OrgSubscriptionStatus, planID *types.OrgPlan) error
// GetOrgIDByStripeCustomer resolves a Stripe customer ID to the owning org's
// ID via billing_accounts. Returns ("", nil) when no row matches.
GetOrgIDByStripeCustomer(ctx context.Context, stripeCustomerID string) (string, error)
// GetStripeCustomerID resolves an org to its Stripe customer id via
// billing_accounts. Returns ("", nil) when no billing account exists.
GetStripeCustomerID(ctx context.Context, orgID string) (string, error)
// --- US-43.2: Invitations ---
CreateInvitation(ctx context.Context, inv *types.OrgInvitation) error
ListPendingInvitations(ctx context.Context, orgID string) ([]*types.OrgInvitation, error)
GetInvitationByTokenHash(ctx context.Context, tokenHash string) (*types.OrgInvitation, error)
GetInvitationByID(ctx context.Context, invID string) (*types.OrgInvitation, error)
// AcceptInvitation performs the accept flow atomically under FOR UPDATE:
// locks the invitation row, re-checks it is still pending, inserts the
// membership, and marks the invitation accepted. Returns
// (membership, alreadyAccepted, error).
AcceptInvitationTx(ctx context.Context, invID, userID string, role types.OrgRole) (*types.OrgMember, bool, error)
DeclineInvitation(ctx context.Context, invID string) error
DeleteInvitation(ctx context.Context, invID string) error
CountInvitationsLastHour(ctx context.Context, orgID string) (int, error)
// --- US-43.7: Policies ---
GetOrgPolicies(ctx context.Context, orgID string) ([]*types.OrgPolicy, error)
SetOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey, value json.RawMessage, updatedBy string) error
DeleteOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey) error
// --- US-43.13: Org-scoped audit log ---
LogOrgEvent(ctx context.Context, orgID, actorID, action, targetID string, metadata map[string]any) error
// LogAuditEvent is the general audit writer (US-43.19). domain must be one
// of audit_log_domain_chk's allowed values; orgID is nil for platform-level
// (non-org-scoped) events.
LogAuditEvent(ctx context.Context, domain, actorID, action, targetID string, orgID *string, metadata map[string]any) error
ListOrgAudit(ctx context.Context, orgID string, limit, offset int) ([]*types.AuditEntry, *types.PaginationMetadata, error)
// US-43.20: cross-org audit. ListAllAudit returns audit_log rows across all
// orgs, narrowed by the supplied filters (nil pointers ⇒ no filter). Limit
// defaults to 100 and is clamped to [1, 500]; Offset defaults to 0.
ListAllAudit(ctx context.Context, filters types.AuditFilters) ([]*types.AuditEntry, *types.PaginationMetadata, error)
// US-43.19: last-admin deadlock prevention. Returns orgs where the given
// user is the sole active admin — suspending them would orphan the org.
OrgsWhereUserIsLastActiveAdmin(ctx context.Context, userID string) ([]types.LastAdminOrg, error)
// US-43.18: platform-admin dashboard. ListAllOrgs returns every
// non-deleted org with aggregated member + workspace counts, optionally
// narrowed by status. statusFilter is applied only when non-nil/non-empty.
// limit is clamped to [1, adminListMaxLimit]; offset defaults to 0.
ListAllOrgs(ctx context.Context, limit, offset int, statusFilter *string) ([]types.OrgSummary, *types.PaginationMetadata, error)
// --- US-43.10: OIDC SSO configuration ---
// GetSSOConfig returns the org's SSO config or (nil, nil) when none exists.
GetSSOConfig(ctx context.Context, orgID string) (*types.OrgSSOConfig, error)
// UpsertSSOConfig inserts or replaces the org's SSO config. ClientSecret is
// the already-encrypted blob (server KEK, D17-S4). VerifiedDomains is the
// caller-computed subset of ClaimedDomains that remain verified after this
// update (the service layer intersects existing verified with new claimed).
// VerificationToken is generated on INSERT if empty; ON CONFLICT preserves
// the existing token (rotation is via RotateVerificationToken).
UpsertSSOConfig(ctx context.Context, config *types.OrgSSOConfig) error
// DeleteSSOConfig removes the org's SSO config.
DeleteSSOConfig(ctx context.Context, orgID string) error
// FindSSOConfigByDomain resolves a claimed email domain (without leading
// "@") to the owning org's SSO config. Returns (nil, nil) when no org has
// claimed the domain.
FindSSOConfigByDomain(ctx context.Context, domain string) (*types.OrgSSOConfig, error)
// ListSSODomains returns every DNS-verified domain across all orgs, for
// the login-page discovery endpoint. Unverified claimed domains are NOT
// returned — they cannot auto-route until the org admin completes DNS
// verification (D17 Q-S2).
ListSSODomains(ctx context.Context) ([]types.SSODomain, error)
// CountSSOConfigs returns the number of orgs with an SSO config. Used by
// GET /auth/config to set the OIDCEnabled feature flag.
CountSSOConfigs(ctx context.Context) (int, error)
// SetDomainVerified atomically appends a domain to verified_domains. The
// domain MUST already be in claimed_domains (enforced by the WHERE clause);
// a domain not in claimed_domains is silently not added. Idempotent: adding
// an already-verified domain is a no-op. Returns (true, nil) if the domain
// was newly verified, (false, nil) if it was already verified or not claimed.
SetDomainVerified(ctx context.Context, orgID, domain string) (bool, error)
// RotateVerificationToken replaces the org's DNS verification token with a
// fresh random value and returns it. Used both for initial token creation
// (when verification_token is NULL) and for rotation. Old tokens stop
// matching after rotation — admins must update their DNS TXT record.
RotateVerificationToken(ctx context.Context, orgID string) (string, error)
}
OrgStore is the data-access interface for organizations and their memberships.
type PendingOrgCleanup ¶
type PendingOrgCleanup struct {
OrgID string
Slug string
CreatedAt time.Time
StripeCustomerID string
}
PendingOrgCleanup describes a pending_activation org eligible for the cleanup cron. StripeCustomerID lets the cron verify checkout state with Stripe before deleting.
type PgEmailTokenStore ¶
type PgEmailTokenStore struct {
// contains filtered or unexported fields
}
PgEmailTokenStore implements the email-token CRUD against PostgreSQL.
func NewPgEmailTokenStore ¶
func NewPgEmailTokenStore(db *sql.DB) *PgEmailTokenStore
func (*PgEmailTokenStore) ConsumeEmailToken ¶
func (s *PgEmailTokenStore) ConsumeEmailToken(ctx context.Context, id string) error
func (*PgEmailTokenStore) CreateEmailToken ¶
func (s *PgEmailTokenStore) CreateEmailToken(ctx context.Context, t *types.EmailToken) error
func (*PgEmailTokenStore) GetEmailTokenByHash ¶
func (s *PgEmailTokenStore) GetEmailTokenByHash(ctx context.Context, hash string) (*types.EmailToken, error)
type PgOrgStore ¶
type PgOrgStore struct {
// contains filtered or unexported fields
}
PgOrgStore implements OrgStore using database/sql.
func NewPgOrgStore ¶
func NewPgOrgStore(db *sql.DB) *PgOrgStore
NewPgOrgStore creates a new PgOrgStore.
func (*PgOrgStore) AcceptInvitationTx ¶
func (*PgOrgStore) AddOrgMember ¶
func (s *PgOrgStore) AddOrgMember(ctx context.Context, orgID, userID string, role types.OrgRole) error
AddOrgMember inserts an org_memberships row AND migrates the new member's personal (NULL-org_id, non-deleted) workspaces to the org — mirroring CreateOrgWithAdmin (M1/D4) and AcceptInvitationTx (D4). Both operations run in a single transaction so the membership and the workspace migration commit atomically.
Callers (audited 2026-06-25):
- admin "Add member" UI → POST /orgs/:id/members → orgs.go handler
- SSO JIT provisioning during first login (sso.go)
Pre-fix: this function only INSERTed the membership row. The new member's existing workspaces stayed with org_id IS NULL forever, silently skipping org-credential auto-binding via BindCredentialToAllOrgWorkspaces (which filters w.org_id = $orgID). That produced the same orphan state migration 000044 was created to backfill. See the worklog `add-org-member-migrates-workspaces` for the discovery trail.
func (*PgOrgStore) ClearWorkspaceAgentRole ¶
func (s *PgOrgStore) ClearWorkspaceAgentRole(ctx context.Context, workspaceID, userID string) error
ClearWorkspaceAgentRole removes the role assignment from a workspace (sets agent_role_id = NULL). Used by the "use platform default" action.
func (*PgOrgStore) CountInvitationsLastHour ¶
func (*PgOrgStore) CountOrgAdmins ¶
func (*PgOrgStore) CountSSOConfigs ¶
func (s *PgOrgStore) CountSSOConfigs(ctx context.Context) (int, error)
func (*PgOrgStore) CreateAgentRole ¶
func (s *PgOrgStore) CreateAgentRole(ctx context.Context, role *types.AgentRole, configJSON []byte) (*types.AgentRole, error)
CreateAgentRole inserts a new agent role.
func (*PgOrgStore) CreateInvitation ¶
func (s *PgOrgStore) CreateInvitation(ctx context.Context, inv *types.OrgInvitation) error
func (*PgOrgStore) CreateOrgWithAdmin ¶
func (s *PgOrgStore) CreateOrgWithAdmin(ctx context.Context, org *types.Organization, adminUserID string) (*types.Organization, error)
func (*PgOrgStore) DeclineInvitation ¶
func (s *PgOrgStore) DeclineInvitation(ctx context.Context, invID string) error
func (*PgOrgStore) DeleteAgentRole ¶
func (s *PgOrgStore) DeleteAgentRole(ctx context.Context, roleID string) error
DeleteAgentRole deletes a role. Caller must check dependents first.
func (*PgOrgStore) DeleteInvitation ¶
func (s *PgOrgStore) DeleteInvitation(ctx context.Context, invID string) error
func (*PgOrgStore) DeleteOrgPolicy ¶
func (s *PgOrgStore) DeleteOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey) error
func (*PgOrgStore) DeleteSSOConfig ¶
func (s *PgOrgStore) DeleteSSOConfig(ctx context.Context, orgID string) error
func (*PgOrgStore) DeleteStripeEvent ¶
func (s *PgOrgStore) DeleteStripeEvent(ctx context.Context, eventID string) error
func (*PgOrgStore) DeleteWorkspacePrompt ¶
func (s *PgOrgStore) DeleteWorkspacePrompt(ctx context.Context, workspaceID string) error
DeleteWorkspacePrompt removes the user-level prompt override.
func (*PgOrgStore) DemoteOrgAdminIfNotLast ¶
func (*PgOrgStore) FindSSOConfigByDomain ¶
func (s *PgOrgStore) FindSSOConfigByDomain(ctx context.Context, domain string) (*types.OrgSSOConfig, error)
FindSSOConfigByDomain resolves a claimed email domain (without leading "@") to the owning org's SSO config. Returns (nil, nil) when no org has claimed the domain.
NOTE: this matches on claimed_domains (NOT verified_domains). It is intended for internal lookups where the org has been identified by other means and its full config is needed regardless of verification status. The login-page auto-routing path uses ListSSODomains (which filters on verified_domains). If you wire this into login routing, you MUST add a verified_domains check or you bypass the DNS verification gate this migration introduces.
func (*PgOrgStore) GetAgentRole ¶
GetAgentRole retrieves a single agent role by ID.
func (*PgOrgStore) GetInvitationByID ¶
func (s *PgOrgStore) GetInvitationByID(ctx context.Context, invID string) (*types.OrgInvitation, error)
func (*PgOrgStore) GetInvitationByTokenHash ¶
func (s *PgOrgStore) GetInvitationByTokenHash(ctx context.Context, tokenHash string) (*types.OrgInvitation, error)
func (*PgOrgStore) GetOrg ¶
func (s *PgOrgStore) GetOrg(ctx context.Context, orgID string) (*types.Organization, error)
func (*PgOrgStore) GetOrgBySlug ¶
func (s *PgOrgStore) GetOrgBySlug(ctx context.Context, slug string) (*types.Organization, error)
func (*PgOrgStore) GetOrgIDByStripeCustomer ¶
func (*PgOrgStore) GetOrgMember ¶
func (*PgOrgStore) GetOrgPolicies ¶
func (*PgOrgStore) GetPlatformSetting ¶
func (s *PgOrgStore) GetPlatformSetting(ctx context.Context, key types.PlatformSettingKey) (*types.PlatformSetting, error)
GetPlatformSetting retrieves a single platform-wide setting by key. Returns nil (not an error) when the key does not exist.
func (*PgOrgStore) GetRoleDependents ¶
func (s *PgOrgStore) GetRoleDependents(ctx context.Context, roleID string) ([]*types.AgentRole, error)
GetRoleDependents returns roles that extend the given role.
func (*PgOrgStore) GetSSOConfig ¶
func (s *PgOrgStore) GetSSOConfig(ctx context.Context, orgID string) (*types.OrgSSOConfig, error)
func (*PgOrgStore) GetStripeCustomerID ¶
func (*PgOrgStore) GetUserEmail ¶
func (*PgOrgStore) GetUserIDByEmail ¶
func (*PgOrgStore) GetUserOrgID ¶
func (*PgOrgStore) GetUserPlan ¶ added in v0.7.0
GetUserPlan reads users.plan_id for an individual user. Used by the MCP-servers user-scope handler to resolve the MaxPersonalMcpServers quota (Epic 53 D12). Returns PlanFree on any error (fail-safe: a solo user whose plan can't be read gets the free-tier quota, not unlimited).
func (*PgOrgStore) GetWorkspaceAgentRole ¶
func (s *PgOrgStore) GetWorkspaceAgentRole(ctx context.Context, workspaceID string) (*types.AgentRole, error)
GetWorkspaceAgentRole retrieves the role assigned to a workspace.
func (*PgOrgStore) GetWorkspaceOrgID ¶
GetWorkspaceOrgID returns the org_id for a workspace, or "" if the workspace has no org (standalone user).
func (*PgOrgStore) GetWorkspacePrompt ¶
func (s *PgOrgStore) GetWorkspacePrompt(ctx context.Context, workspaceID string) (*types.WorkspacePrompt, error)
GetWorkspacePrompt retrieves the user-level prompt override for a workspace. Returns nil (not an error) when no override exists.
func (*PgOrgStore) HardDeleteOrg ¶
func (s *PgOrgStore) HardDeleteOrg(ctx context.Context, orgID string) error
func (*PgOrgStore) HasRoleWorkspaceUsage ¶
HasRoleWorkspaceUsage checks if any workspace references this role.
func (*PgOrgStore) IsOrgAdmin ¶
func (*PgOrgStore) IsOrgMember ¶
func (*PgOrgStore) ListAgentRoles ¶
func (s *PgOrgStore) ListAgentRoles(ctx context.Context, scope string, orgID string) ([]*types.AgentRole, error)
ListAgentRoles lists roles by scope and optional org_id.
func (*PgOrgStore) ListAllAudit ¶
func (s *PgOrgStore) ListAllAudit(ctx context.Context, filters types.AuditFilters) ([]*types.AuditEntry, *types.PaginationMetadata, error)
ListAllAudit returns audit_log rows across every org, narrowed by filters. The WHERE clause is built from conditional ANDs over a parameterised args slice — no user input is ever interpolated into the SQL text.
func (*PgOrgStore) ListAllOrgs ¶
func (s *PgOrgStore) ListAllOrgs(ctx context.Context, limit, offset int, statusFilter *string) ([]types.OrgSummary, *types.PaginationMetadata, error)
ListAllOrgs returns every non-deleted organization with aggregated member and workspace counts for the platform-admin dashboard. The optional statusFilter narrows the result to a single OrgStatus (e.g. "suspended"); an empty/nil filter returns all statuses. Results are ordered by created_at DESC.
The two counts are correlated subqueries on the same row, so a single round trip returns the full summary without an N+1 fan-out. The COUNT(*) total is fetched first so an empty page short-circuits the SELECT.
func (*PgOrgStore) ListOrgAudit ¶
func (s *PgOrgStore) ListOrgAudit(ctx context.Context, orgID string, limit, offset int) ([]*types.AuditEntry, *types.PaginationMetadata, error)
func (*PgOrgStore) ListOrgMembers ¶
func (*PgOrgStore) ListOrgWorkspaces ¶
func (s *PgOrgStore) ListOrgWorkspaces(ctx context.Context, orgID string, limit, offset int) ([]*types.WorkspaceMetadata, *types.PaginationMetadata, error)
func (*PgOrgStore) ListOrgsForUser ¶
func (s *PgOrgStore) ListOrgsForUser(ctx context.Context, userID string) ([]*types.OrgResponse, error)
func (*PgOrgStore) ListPendingInvitations ¶
func (s *PgOrgStore) ListPendingInvitations(ctx context.Context, orgID string) ([]*types.OrgInvitation, error)
func (*PgOrgStore) ListPendingOrgsOlderThan ¶
func (s *PgOrgStore) ListPendingOrgsOlderThan(ctx context.Context, maxAge time.Duration) ([]PendingOrgCleanup, error)
func (*PgOrgStore) ListSSODomains ¶
func (*PgOrgStore) LogAuditEvent ¶
func (s *PgOrgStore) LogAuditEvent(ctx context.Context, domain, actorID, action, targetID string, orgID *string, metadata map[string]any) error
LogAuditEvent inserts a row into audit_log with an explicit domain and an optional org scope. It is the general audit writer used by both org-scoped events (domain='org', orgID non-nil) and platform-admin events (domain='admin', orgID nil). The domain must be one of the values allowed by the audit_log_domain_chk CHECK constraint (billing/secrets/admin/org).
func (*PgOrgStore) LogOrgEvent ¶
func (*PgOrgStore) MarkUserEmailVerified ¶
func (s *PgOrgStore) MarkUserEmailVerified(ctx context.Context, userID string) error
MarkUserEmailVerified sets users.email_verified=true for the given user, bypassing the email-verification token flow. Used by the org-admin "Verify" action (POST /orgs/:id/members/:userID/verify) when an admin has confirmed the member's identity out-of-band. The membership is verified by the caller (OrgAdminGuard + GetOrgMember) before this is invoked, so a bare userID is safe here. Idempotent: re-verifying an already-verified user is a no-op.
func (*PgOrgStore) OrgsWhereUserIsLastActiveAdmin ¶
func (s *PgOrgStore) OrgsWhereUserIsLastActiveAdmin(ctx context.Context, userID string) ([]types.LastAdminOrg, error)
OrgsWhereUserIsLastActiveAdmin returns every organization where the given user is an admin AND no OTHER active admin exists. Suspending such a user (D19) would orphan the org — no remaining admin could manage it (promote members, change policies, manage billing). The user-suspend path refuses with 409 when this returns a non-empty slice (unless force=true).
func (*PgOrgStore) RecordStripeEvent ¶
func (*PgOrgStore) RemoveOrgAdminIfNotLast ¶
func (*PgOrgStore) RemoveOrgMember ¶
func (s *PgOrgStore) RemoveOrgMember(ctx context.Context, orgID, userID string) error
func (*PgOrgStore) RotateVerificationToken ¶
RotateVerificationToken replaces the org's verification token with a fresh random 32-hex value and returns it. Used for both initial creation (when verification_token is NULL) and rotation. Returns the new token.
func (*PgOrgStore) SetBillingAccountSubscription ¶
func (s *PgOrgStore) SetBillingAccountSubscription(ctx context.Context, ownerID, ownerType, provider, subscriptionID string) error
func (*PgOrgStore) SetDomainVerified ¶
SetDomainVerified atomically appends a domain to verified_domains. The domain must already be in claimed_domains (the WHERE clause enforces this); a domain not claimed is a no-op. Idempotent: re-verifying an already- verified domain returns (false, nil) without error. Returns (true, nil) only when the domain was newly promoted.
func (*PgOrgStore) SetOrgDefaultRole ¶
func (s *PgOrgStore) SetOrgDefaultRole(ctx context.Context, orgID, roleID string) error
SetOrgDefaultRole atomically sets one role as default and clears all others.
func (*PgOrgStore) SetOrgPolicy ¶
func (s *PgOrgStore) SetOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey, value json.RawMessage, updatedBy string) error
func (*PgOrgStore) SetPlatformSetting ¶
func (s *PgOrgStore) SetPlatformSetting(ctx context.Context, key types.PlatformSettingKey, value json.RawMessage, updatedBy string) error
SetPlatformSetting upserts a platform-wide setting.
func (*PgOrgStore) SetWorkspaceAgentRole ¶
func (s *PgOrgStore) SetWorkspaceAgentRole(ctx context.Context, workspaceID, roleID, userID string) error
SetWorkspaceAgentRole sets the role for a workspace.
func (*PgOrgStore) SetWorkspacePrompt ¶
func (s *PgOrgStore) SetWorkspacePrompt(ctx context.Context, workspaceID string, prompt string, updatedBy string) error
SetWorkspacePrompt upserts the user-level prompt override for a workspace.
func (*PgOrgStore) SoftDeleteOrg ¶
func (s *PgOrgStore) SoftDeleteOrg(ctx context.Context, orgID string) error
func (*PgOrgStore) SuspendUserGuardedByLastAdmin ¶
func (s *PgOrgStore) SuspendUserGuardedByLastAdmin(ctx context.Context, userID string, force bool) (*types.LastAdminOrg, error)
SuspendUserGuardedByLastAdmin atomically refuses to suspend the user when they are the sole active admin of any org (unless force), and otherwise sets the user's status to suspended. The SELECT … FOR UPDATE on the admin membership rows of every org the user administers plus the UPDATE on users run in a single transaction, closing the TOCTOU window of the prior read-then-write sequence (F7, US-43.19): two concurrent admin suspensions or a suspend racing a demote can no longer both pass the last-admin check and leave the org adminless. `active` is mirrored to `false` so the legacy column cannot drift from `status` (F6).
Returns a non-nil *LastAdminOrg when the suspend was refused (last admin); the caller surfaces this as 409. Returns (nil, nil) on a successful suspension.
func (*PgOrgStore) UpdateAgentRole ¶
func (s *PgOrgStore) UpdateAgentRole(ctx context.Context, roleID string, role *types.AgentRole, configJSON []byte) (*types.AgentRole, error)
UpdateAgentRole updates an agent role.
func (*PgOrgStore) UpdateOrg ¶
func (s *PgOrgStore) UpdateOrg(ctx context.Context, orgID string, req types.UpdateOrgRequest) (*types.Organization, error)
func (*PgOrgStore) UpdateOrgMemberRole ¶
func (*PgOrgStore) UpdateOrgStatus ¶
func (*PgOrgStore) UpsertSSOConfig ¶
func (s *PgOrgStore) UpsertSSOConfig(ctx context.Context, config *types.OrgSSOConfig) error
type Service ¶
Service handles database operations
func (*Service) BeginTx ¶
BeginTx starts a new database transaction. Used by handlers that need multi-statement atomicity (e.g., AgentReloadHandler's SELECT FOR UPDATE + UPSERT).
func (*Service) CheckPermission ¶
func (s *Service) CheckPermission(ctx context.Context, userID, resourceType, resourceID, action string) (bool, error)
CheckPermission checks if a user has permission to perform an action on a resource
func (*Service) CheckResourceOwnership ¶
func (s *Service) CheckResourceOwnership(ctx context.Context, userID, resourceType, resourceID string) (bool, error)
CheckResourceOwnership checks if a user owns a resource
func (*Service) CountActiveWorkspacesByUserAndOrg ¶
func (*Service) CountUsers ¶
CountUsers returns the total number of users in the system. Used by the auth Register flow to detect a fresh installation (count == 0) and auto-promote the first user to admin so a brand-new install has at least one administrator.
func (*Service) CountWorkspacesByUserAndOrg ¶
func (*Service) CreateAPIKey ¶
func (*Service) CreateBuild ¶ added in v0.7.1
func (*Service) CreateConfig ¶ added in v0.7.1
func (*Service) CreateConfigAndBuild ¶ added in v0.7.1
func (s *Service) CreateConfigAndBuild(ctx context.Context, c *imagefactory.Config, b *imagefactory.Build) error
CreateConfigAndBuild inserts both rows in a single transaction so a failure in either insert rolls back the other — no orphaned config at 'building' with no build, no build row with no config. The handler calls this after a successful dispatch (design/0046 #17).
func (*Service) CreateUser ¶
CreateUser creates a new user
func (*Service) CreateWorkspace ¶
CreateWorkspace inserts a new workspace record.
func (*Service) DeleteAPIKey ¶
func (*Service) DeleteBase ¶ added in v0.7.1
func (*Service) DeleteConfig ¶ added in v0.8.8
DeleteConfig deletes a config row and its build history. Returns ErrNotFound if the config doesn't exist. Builds are deleted first (the FK has no ON DELETE CASCADE), then the config — both in a single tx so a partial delete can't leave orphaned rows.
func (*Service) DeleteKnownFailure ¶ added in v0.7.1
func (*Service) DeleteSessionIndex ¶
func (*Service) DeleteSessionTree ¶
func (*Service) DeleteUser ¶
DeleteUser deletes a user
func (*Service) DeleteWorkspace ¶
DeleteWorkspace removes a workspace record.
func (*Service) GetAPIKeyRecordByHash ¶
func (*Service) GetAllInstanceSettings ¶
GetAllInstanceSettings returns all rows from instance_settings.
func (*Service) GetAllUserSettings ¶
func (s *Service) GetAllUserSettings(ctx context.Context, userID string) (map[string]json.RawMessage, error)
GetAllUserSettings returns all settings for a specific user.
func (*Service) GetBuildByGHRunID ¶ added in v0.7.1
func (*Service) GetConfigByHash ¶ added in v0.7.1
func (s *Service) GetConfigByHash(ctx context.Context, hash string, scope imagefactory.ConfigScope, ownerID, orgID *string) (imagefactory.Config, error)
func (*Service) GetDefaultModel ¶
GetDefaultModel returns the workspace's configured default model, or "" if unset.
func (*Service) GetExtension ¶ added in v0.7.1
func (*Service) GetInFlightOrSuccessfulBuild ¶ added in v0.7.1
func (s *Service) GetInFlightOrSuccessfulBuild(ctx context.Context, hash, baseVersion string) (*imagefactory.Build, error)
GetInFlightOrSuccessfulBuild is the coalescing probe (design/0046 #16). Returns a successful build if one exists for (hash, base_version); otherwise an in-flight (dispatched) one; otherwise nil. Prefers success so a new config immediately links to a Ready build rather than waiting on an in-flight one that might fail.
func (*Service) GetKnownFailure ¶ added in v0.7.1
func (s *Service) GetKnownFailure(ctx context.Context, selectionHash, baseName string) (imagefactory.KnownFailure, error)
func (*Service) GetLastCredentialChangedAt ¶
func (s *Service) GetLastCredentialChangedAt(ctx context.Context, workspaceID string) (time.Time, error)
GetLastCredentialChangedAt returns the most recent credential-changed timestamp for the workspace, or the zero time if no row exists.
func (*Service) GetLaunchableConfigByHash ¶ added in v0.8.5
func (s *Service) GetLaunchableConfigByHash(ctx context.Context, hash string, scope imagefactory.ConfigScope, ownerID, orgID *string) (imagefactory.Config, string, error)
GetLaunchableConfigByHash implements ImageFactoryStore.GetLaunchableConfigByHash. It joins image_factory_configs with its successful build to return both the config and the image_ref in one round-trip, and enforces the Ready + scope constraints needed by the workspace launch path. The query filters:
- config.status = 'ready' (design/0046 #15 — only Ready configs are launchable)
- scope/owner/org match (authorization: caller must own the config)
- a joined build row with status='succeeded' AND image_ref <> ” exists
All selected columns are qualified with `c.` (or `b.`) because config and build tables share column names (hash, base_name, base_version, etc.) — an unqualified SELECT would be ambiguous and fail at query time.
The build's image_ref is what the controller's runtime_resolver will use verbatim as the pod image (any '/'-containing runtime value is a passthrough).
func (*Service) GetPlatformConfig ¶ added in v0.7.1
func (s *Service) GetPlatformConfig(ctx context.Context) (imagefactory.PlatformConfig, error)
func (*Service) GetUserByAPIKey ¶
GetUserByAPIKey gets the user associated with an API key
func (*Service) GetUserByEmail ¶
GetUserByEmail gets a user by email address
func (*Service) GetWorkspace ¶
func (s *Service) GetWorkspace(ctx context.Context, workspaceID string) (*types.WorkspaceMetadata, error)
GetWorkspace gets a workspace by ID.
func (*Service) InsertInstanceSettingIfMissing ¶
func (s *Service) InsertInstanceSettingIfMissing(ctx context.Context, key string, value json.RawMessage) (bool, error)
InsertInstanceSettingIfMissing inserts a setting only if the key doesn't exist. Returns true if inserted, false if already existed.
func (*Service) ListAPIKeys ¶
func (*Service) ListAPIKeysWithDecrypt ¶
func (*Service) ListAllUsers ¶
func (s *Service) ListAllUsers(ctx context.Context, limit, offset int, statusFilter *string) ([]types.UserListEntry, *types.PaginationMetadata, error)
ListAllUsers returns every user for the platform-admin dashboard (US-43.18). The optional statusFilter narrows to a single UserStatus; an empty/nil filter returns all users. Each entry carries the user's single org membership (org_id/org_name) resolved via a LEFT JOIN — under single-org enforcement (D8) a user belongs to at most one org, so this adds no row fan-out.
Password hashes and other sensitive columns are never selected. limit is clamped to [1, adminListMaxLimit]; offset defaults to 0.
func (*Service) ListAllWorkspaceOwners ¶
func (*Service) ListAllWorkspacesForBilling ¶
func (s *Service) ListAllWorkspacesForBilling(ctx context.Context) ([]WorkspaceBillingRecord, error)
func (*Service) ListConfigs ¶ added in v0.7.1
func (s *Service) ListConfigs(ctx context.Context, scope imagefactory.ConfigScope, ownerID, orgID *string) ([]imagefactory.Config, error)
func (*Service) ListExtensions ¶ added in v0.7.1
func (*Service) ListKnownFailures ¶ added in v0.7.1
func (s *Service) ListKnownFailures(ctx context.Context) ([]imagefactory.KnownFailure, error)
func (*Service) ListPendingReloadWorkspaces ¶
func (s *Service) ListPendingReloadWorkspaces(ctx context.Context, userID string) ([]*types.WorkspaceMetadata, error)
ListPendingReloadWorkspaces returns workspaces with pending_refresh=TRUE for the given user.
func (*Service) ListRejectedConfigsForFailure ¶ added in v0.7.1
func (*Service) ListSessionIndex ¶
func (*Service) ListVisibleConfigs ¶ added in v0.7.1
func (s *Service) ListVisibleConfigs(ctx context.Context, ownerID, orgID *string) ([]imagefactory.Config, error)
ListVisibleConfigs returns the configs a member can see: their own member-scope, their org's org-scope (if any), plus platform-scope.
func (*Service) ListWorkspaces ¶
func (s *Service) ListWorkspaces(ctx context.Context, userID string, limit, offset int) ([]*types.WorkspaceMetadata, *types.PaginationMetadata, error)
ListWorkspaces lists workspaces owned by the user with pagination.
Per Epic 43 decision D6, this returns only workspaces the user created (w.user_id = $1). The prior LEFT JOIN org_memberships + OR clause that let any org member see every other member's org workspace has been removed: members now see only their own workspaces. Org admins who need to see all org workspaces use the dedicated GET /orgs/:id/workspaces endpoint (OrgStore.ListOrgWorkspaces).
func (*Service) MarkAgentReloaded ¶
func (s *Service) MarkAgentReloaded(ctx context.Context, tx *sql.Tx, workspaceID string, priorChangedAt time.Time) (time.Time, error)
MarkAgentReloaded clears pending_refresh after a successful dispose. Uses SELECT FOR UPDATE to serialize against concurrent MarkCredentialChanged. priorChangedAt is captured BEFORE dispose; if a new credential was staged during the dispose window, pending_refresh stays true. Returns the DB-clock timestamp written to last_agent_disposed_at.
func (*Service) MarkBuildFailed ¶ added in v0.7.1
func (*Service) MarkBuildSucceeded ¶ added in v0.7.1
func (*Service) MarkCredentialChanged ¶
MarkCredentialChanged flips a workspace into "credentials staged, reload needed" state. Uses a single auto-commit UPSERT (no external transaction parameter) because the binding write (PgSecretStore, pgxpool) and this write (*sql.DB) use incompatible connection pools — cross-pool transactions are impossible.
func (*Service) MarkWorkspaceDeleted ¶
MarkWorkspaceDeleted soft-deletes a workspace by setting deleted_at and purges any user_secret_bindings rows pointing at it within a single transaction. The bindings table has no FK to workspaces.id (the column types differ historically) so a soft delete leaves orphan binding rows behind unless we clean up here explicitly. See Bug 11 in worklog 0085.
The two writes are wrapped in a single transaction so an API-process crash between them cannot leave a soft-deleted workspace with orphan bindings (validator finding on Bug 11 follow-up).
func (*Service) PublishExtension ¶ added in v0.7.1
func (*Service) PurgeUserSecrets ¶
PurgeUserSecrets deletes every user-owned secret row for a user: provider_credentials (LLM provider keys) and user_secrets. It is called from the email password-reset flow to make the "your saved keys will be deleted" guarantee literal.
The DEK reinitialisation that precedes this call already makes the old ciphertext cryptographically undecryptable; deleting the rows removes them outright and guarantees no future materialization can resurrect them. Both tables' dependents (workspace_credential_bindings, user_secret_bindings) reference the parent with ON DELETE CASCADE, so no orphaned binding rows remain. Rows deleted before this call by the DEK reinit's UPSERT (user_keys) are unaffected.
Best-effort at the caller: a failure here does not undo the reset because the cryptographic erasure has already happened.
func (*Service) RecordKnownFailure ¶ added in v0.7.1
func (s *Service) RecordKnownFailure(ctx context.Context, kf imagefactory.KnownFailure) error
func (*Service) RenameConfig ¶ added in v0.8.8
RenameConfig updates the friendly name. Returns ErrNotFound if the config doesn't exist, or ErrConflict (via pq unique violation) if the name collides within the same scope.
func (*Service) RetireExtension ¶ added in v0.7.1
func (*Service) SetConfigStatus ¶ added in v0.7.1
func (s *Service) SetConfigStatus(ctx context.Context, id string, status imagefactory.ConfigStatus) error
func (*Service) SetExtensionReviewRequested ¶ added in v0.7.1
func (*Service) SetInstanceSetting ¶
SetInstanceSetting upserts a single instance setting.
func (*Service) SetKnownFailureRetriable ¶ added in v0.7.1
func (*Service) SetPlatformConfig ¶ added in v0.7.1
func (s *Service) SetPlatformConfig(ctx context.Context, pc imagefactory.PlatformConfig) error
func (*Service) SetUserSetting ¶
func (s *Service) SetUserSetting(ctx context.Context, userID, key string, value json.RawMessage) error
SetUserSetting upserts a single user setting.
func (*Service) SetUserStatus ¶
SetUserStatus sets the authoritative operational status of a user account (D19). status='suspended' blocks the user across all contexts via the auth middleware; 'active' restores access.
F6 (US-43.19): the legacy `active` boolean is mirrored from `status` (active = (status='active')) so the two columns cannot drift apart. The auth middleware authorizes on `status`; Login historically checks `active`. Before this fix, any path that wrote `active` independently of `status` would leave the user blocked at Login but not at the middleware (or vice-versa). Keeping them in lockstep removes that divergence vector.
func (*Service) SyncWorkspaceVersionInfo ¶
func (s *Service) SyncWorkspaceVersionInfo(ctx context.Context, workspaceID, imageTag, agentVersion string)
SyncWorkspaceVersionInfo updates image_tag and/or agent_version in the DB. Only non-empty values are written; passing an empty string for either field leaves the existing DB value untouched. This allows the CRD watcher to sync imageTag without clobbering agentVersion, which is sourced separately from agentd health checks.
func (*Service) TransitionBuildFailed ¶ added in v0.7.1
func (s *Service) TransitionBuildFailed(ctx context.Context, buildID, configID string, kf imagefactory.KnownFailure) error
TransitionBuildFailed atomically marks a build failed, records the known failure, and flips the config to rejected. Single tx — no partial state.
func (*Service) TransitionBuildSucceeded ¶ added in v0.7.1
func (s *Service) TransitionBuildSucceeded(ctx context.Context, buildID, configID, imageRef, digest string) error
TransitionBuildSucceeded atomically marks a build succeeded and its config ready. Single tx — no partial state if one write fails.
func (*Service) UpdateAPIKeyDEK ¶
func (*Service) UpdateSessionLastSeen ¶
func (*Service) UpdateUser ¶
UpdateUser updates specific fields on a user record. Only non-nil fields are applied.
func (*Service) UpdateWorkspace ¶
func (s *Service) UpdateWorkspace(ctx context.Context, workspaceID string, updates types.WorkspaceUpdates) error
UpdateWorkspace updates specific fields on a workspace record.
func (*Service) UpsertBase ¶ added in v0.7.1
func (*Service) UpsertSessionContextUsed ¶
func (s *Service) UpsertSessionContextUsed(ctx context.Context, workspaceID, sessionID string, contextUsed int64) error
UpsertSessionContextUsed persists the prompt token count for the most recent LLM step in this session. Called by the API proxy on every session.next.step.ended SSE event. Idempotent: concurrent writes of the same value are safe (both replicas receive the same event and write the same data).
func (*Service) UpsertSessionMessage ¶
func (*Service) UpsertSessionParent ¶
func (s *Service) UpsertSessionParent(ctx context.Context, workspaceID, sessionID, parentID string) error
UpsertSessionParent records (or refreshes) the parent_session_id for a session. Used to mirror opencode subagent (subtask) parent links into the sidebar's session_index so the UI can render the hierarchy without round-tripping the agent.
Idempotent: passing the same parentID is a no-op. We deliberately do not guard against parentID changes — opencode never re-parents a session in practice, and an UPDATE-on-conflict path costs less than a SELECT-then- UPDATE round trip.