Documentation
¶
Index ¶
- Variables
- 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) 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) 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) 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) DemoteOrgAdminIfNotLast(ctx context.Context, orgID, targetUserID string) (bool, error)
- func (s *PgOrgStore) FindSSOConfigByDomain(ctx context.Context, domain string) (*types.OrgSSOConfig, 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) 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) HardDeleteOrg(ctx context.Context, orgID string) 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) 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) 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) SetOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey, ...) 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) 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(userID, resourceType, resourceID, action string) (bool, error)
- func (s *Service) CheckResourceOwnership(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) 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) 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) GetDefaultModel(ctx context.Context, workspaceID string) (string, error)
- func (s *Service) GetLastCredentialChangedAt(ctx context.Context, workspaceID string) (time.Time, 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) ListPendingReloadWorkspaces(ctx context.Context, userID string) ([]*types.WorkspaceMetadata, error)
- func (s *Service) ListSessionIndex(ctx context.Context, workspaceID string) ([]types.SessionListItem, 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) 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) SetInstanceSetting(ctx context.Context, key string, value json.RawMessage) 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) 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) 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 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 ¶
This section is empty.
Types ¶
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)
// 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 (*PgOrgStore) CountInvitationsLastHour ¶
func (*PgOrgStore) CountOrgAdmins ¶
func (*PgOrgStore) CountSSOConfigs ¶
func (s *PgOrgStore) CountSSOConfigs(ctx context.Context) (int, error)
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) 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) 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) 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) 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) HardDeleteOrg ¶
func (s *PgOrgStore) HardDeleteOrg(ctx context.Context, orgID string) error
func (*PgOrgStore) IsOrgAdmin ¶
func (*PgOrgStore) IsOrgMember ¶
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) 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) SetOrgPolicy ¶
func (s *PgOrgStore) SetOrgPolicy(ctx context.Context, orgID string, key types.OrgPolicyKey, value json.RawMessage, updatedBy string) error
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) 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 ¶
CheckPermission checks if a user has permission to perform an action on a resource
func (*Service) CheckResourceOwnership ¶
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) CreateUser ¶
CreateUser creates a new user
func (*Service) CreateWorkspace ¶
CreateWorkspace inserts a new workspace record.
func (*Service) DeleteAPIKey ¶
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) GetDefaultModel ¶
GetDefaultModel returns the workspace's configured default model, or "" if unset.
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) 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) 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) ListSessionIndex ¶
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) 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) SetInstanceSetting ¶
SetInstanceSetting upserts a single instance setting.
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) 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) 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.