Documentation
¶
Overview ¶
Package postgres provides a Postgres-backed implementation of service.Repository for the identity service.
This is an alternative to the original graph-backed repository. The two are interchangeable from the AuthService's point of view: both implement the same service.Repository interface.
Why Postgres ¶
Postgres is well understood, ubiquitous in production environments, and ships with a mature ops toolkit (pg_dump, pg_basebackup, replicas, PITR via WAL archiving). It is the right default for teams that want to run identity without taking a dependency on the tenant-shard-db stack.
Driver choice ¶
The implementation uses pgx/v5 directly (not via database/sql). pgxpool.Pool gives us a tuneable connection pool; pgx is also faster than database/sql and exposes Postgres-specific features (LISTEN / NOTIFY, COPY, JSONB native typing) that we may want later.
Migrations ¶
Schema DDL lives under migrations/ and is applied via golang-migrate/migrate using the embed.FS source. By default New() does NOT run pending migrations on connect — production deploys run migrations out-of-band as a separate Job, so a rolling rollout never races two replicas to apply the same change. Set Config.AutoMigrate to true (or GATEWAY_POSTGRES_AUTO_MIGRATE=true) only for local dev and single-replica environments.
Project isolation (ADR-0002) ¶
The Project is identity's storage shard. Every data-plane table carries a project_id text not null column (FK to projects(id)) and uniqueness constraints are scoped to (project_id, ...). A pgRepository instance is bound to a single project_id and writes/reads only that project's rows; per-request scopes are derived via WithProject. The logical-tenant tenant_id columns on the governance tables (domains, login_policies, tenant_memberships, tenant_invitations) reference tenants(id) and are a separate concept from this storage shard.
Error mapping ¶
Postgres unique-violation errors (SQLSTATE 23505) are mapped to service.ErrAlreadyExists by errors.go::wrapPgErr. ErrNoRows is surfaced as a nil result (not an error), matching the existing in-memory driver.
Index ¶
- Constants
- func Migrate(dsn string) error
- func New(ctx context.Context, cfg Config) (*pgRepository, error)
- type AutoFormStore
- type Config
- type DomainStore
- func (s *DomainStore) CreateDomain(ctx context.Context, d *service.Domain) (string, error)
- func (s *DomainStore) GetDomain(ctx context.Context, projectID, domainID string) (*service.Domain, error)
- func (s *DomainStore) GetDomainByName(ctx context.Context, projectID, domain string) (*service.Domain, error)
- func (s *DomainStore) ListDomainsByTenant(ctx context.Context, projectID, tenantID string) ([]*service.Domain, error)
- func (s *DomainStore) SetDomainStatus(ctx context.Context, projectID, domainID, status string, verifiedAtMs int64) error
- type InvitationStore
- func (s *InvitationStore) CreateInvitation(ctx context.Context, inv *service.TenantInvitation) (string, error)
- func (s *InvitationStore) GetInvitationByTokenHash(ctx context.Context, projectID, tokenHash string) (*service.TenantInvitation, error)
- func (s *InvitationStore) ListInvitationsForTenant(ctx context.Context, projectID, tenantID string) ([]*service.TenantInvitation, error)
- func (s *InvitationStore) SetInvitationStatus(ctx context.Context, projectID, invitationID, status string, ...) error
- type LoginPolicyStore
- func (s *LoginPolicyStore) DeleteLoginPolicy(ctx context.Context, projectID, tenantID string) error
- func (s *LoginPolicyStore) GetLoginPolicy(ctx context.Context, projectID, tenantID string) (*service.LoginPolicy, error)
- func (s *LoginPolicyStore) UpsertLoginPolicy(ctx context.Context, p *service.LoginPolicy) (string, error)
- type MembershipStore
- func (s *MembershipStore) GetMembership(ctx context.Context, projectID, tenantID, userID string) (*service.TenantMembership, error)
- func (s *MembershipStore) ListMembershipsForTenant(ctx context.Context, projectID, tenantID string) ([]*service.TenantMembership, error)
- func (s *MembershipStore) ListMembershipsForUser(ctx context.Context, projectID, userID string) ([]*service.TenantMembership, error)
- func (s *MembershipStore) RemoveMembership(ctx context.Context, projectID, tenantID, userID string) error
- func (s *MembershipStore) UpsertMembership(ctx context.Context, m *service.TenantMembership) (string, error)
- type PlatformAdminStore
- type Project
- type ProjectAuthDomain
- type ProjectCredential
- type ProjectStore
- func (s *ProjectStore) CreateAuthDomain(ctx context.Context, projectID, hostname string, isPrimary bool) error
- func (s *ProjectStore) CreateProject(ctx context.Context, p *service.AdminProject) (string, error)
- func (s *ProjectStore) CreateProjectAuthDomain(ctx context.Context, d *ProjectAuthDomain) (string, error)
- func (s *ProjectStore) CreateProjectCredential(ctx context.Context, c *service.AdminProjectCredential) (string, error)
- func (s *ProjectStore) EnsureAuthDomain(ctx context.Context, projectID, hostname string, isPrimary bool, ...) error
- func (s *ProjectStore) EnsureDefaultProject(ctx context.Context, projectID, storageScopeID, name string) (*Project, error)
- func (s *ProjectStore) GetAuthDomain(ctx context.Context, projectID, hostname string) (*service.AdminProjectAuthDomain, error)
- func (s *ProjectStore) GetProjectAuthDomain(ctx context.Context, projectID, hostname string) (*ProjectAuthDomain, error)
- func (s *ProjectStore) GetProjectByAuthHostname(ctx context.Context, hostname string) (*Project, error)
- func (s *ProjectStore) GetProjectByID(ctx context.Context, projectID string) (*Project, error)
- func (s *ProjectStore) GetProjectByStorageScope(ctx context.Context, storageScopeID string) (*Project, error)
- func (s *ProjectStore) GetProjectConfig(ctx context.Context, projectID string) (string, error)
- func (s *ProjectStore) GetProjectCredentialByPublicID(ctx context.Context, publicID string) (*ProjectCredential, error)
- func (s *ProjectStore) ListAuthDomains(ctx context.Context, projectID string) ([]*service.AdminProjectAuthDomain, error)
- func (s *ProjectStore) ListProjectAuthDomains(ctx context.Context, projectID string) ([]*ProjectAuthDomain, error)
- func (s *ProjectStore) ResolveByCredential(ctx context.Context, publicID string) (*service.ResolvedProject, error)
- func (s *ProjectStore) ResolveByHostname(ctx context.Context, hostname string) (*service.ResolvedProject, error)
- func (s *ProjectStore) RevokeProjectCredential(ctx context.Context, credentialID string, atMs int64) error
- func (s *ProjectStore) SetAuthDomainVerified(ctx context.Context, projectID, hostname string, verifiedAtMs int64) error
- func (s *ProjectStore) SetPrimaryAuthDomain(ctx context.Context, projectID, hostname string) (*service.AdminProjectAuthDomain, error)
- func (s *ProjectStore) SetPrimaryProjectAuthDomain(ctx context.Context, projectID, hostname string) (*ProjectAuthDomain, error)
- func (s *ProjectStore) SetProjectAuthDomainVerified(ctx context.Context, projectID, hostname string, verifiedAtMs int64) error
- func (s *ProjectStore) UpdateProjectConfig(ctx context.Context, projectID, configJSON string) (string, error)
- type TenantStore
- func (s *TenantStore) CreateTenant(ctx context.Context, t *service.Tenant) (string, error)
- func (s *TenantStore) GetTenant(ctx context.Context, projectID, tenantID string) (*service.Tenant, error)
- func (s *TenantStore) GetTenantByPrimaryDomain(ctx context.Context, projectID, domain string) (*service.Tenant, error)
- func (s *TenantStore) ListTenants(ctx context.Context, projectID string) ([]*service.Tenant, error)
- func (s *TenantStore) SetTenantStatus(ctx context.Context, projectID, tenantID, status string) error
Constants ¶
const DefaultConnTimeout = 5 * time.Second
DefaultConnTimeout is used when Config.ConnTimeout is zero.
const DefaultMaxConns int32 = 25
DefaultMaxConns is used when Config.MaxConns is zero.
Variables ¶
This section is empty.
Functions ¶
func Migrate ¶ added in v0.15.0
Migrate applies all pending schema migrations to the Postgres database at dsn, then returns. It is idempotent — a fully-migrated database is a no-op — and safe to run concurrently with other instances: the underlying runner holds a Postgres advisory lock for the duration, so exactly one caller applies the migrations and the rest wait, then no-op. It is the entry point behind the `identity migrate` deploy step (the explicit alternative to GATEWAY_POSTGRES_AUTO_MIGRATE).
func New ¶
New constructs a Postgres-backed repository:
- Parse / validate cfg.
- Open a pgxpool with cfg.MaxConns, cfg.ConnTimeout.
- Optionally run pending migrations (cfg.AutoMigrate=true).
- Ping to fail fast on a misconfigured DSN.
The returned store implements both service.Repository and service.DB. The caller is responsible for keeping the *pgRepository alive for the lifetime of the service; pool resources are released by Close().
Types ¶
type AutoFormStore ¶ added in v0.17.0
type AutoFormStore struct {
// contains filtered or unexported fields
}
AutoFormStore turns a verified company-email signup into governance rows.
func NewAutoFormStore ¶ added in v0.17.0
func NewAutoFormStore(r *pgRepository) *AutoFormStore
NewAutoFormStore builds an auto-formation store sharing the repository's pool.
func (*AutoFormStore) EnsureTenantForDomain ¶ added in v0.17.0
func (s *AutoFormStore) EnsureTenantForDomain(ctx context.Context, projectID, domain, userID string) (string, error)
EnsureTenantForDomain idempotently ensures a latent Tenant + its email Domain exist for (projectID, domain) and records a domain-derived membership for userID. See the interface doc for the concurrency contract: tenant+domain are created in one transaction so a lost race leaves no orphan tenant.
type Config ¶
type Config struct {
DSN string
MaxConns int32
ConnTimeout time.Duration
AutoMigrate bool
ProjectID string
}
Config controls how the postgres repository connects to its database.
DSN is the libpq-style connection string, e.g.
postgres://user:pass@host:5432/dbname?sslmode=disable
MaxConns caps the underlying pgxpool. A zero value means "use the pgxpool default" (currently 4 + GOMAXPROCS-ish). 25 is the suggested default for an identity service node (see DefaultMaxConns).
ConnTimeout is the per-acquire timeout used when checking a connection out of the pool. It does NOT bound the total query time — callers are still responsible for passing a context with the appropriate deadline.
AutoMigrate controls whether New() applies pending schema migrations on first connect. In CI / dev / test we want true (the default); in strict production deploys teams may flip it to false and run `migrate ... up` from a deploy pipeline instead.
ProjectID is the project (storage shard) whose rows this repository instance writes and reads. Per ADR-0002 the Project is identity's isolation shard: every data-plane row carries project_id and the mandatory `WHERE project_id = $1` predicate is bound here. Multi-project deployments derive a per-request scope via WithProject; the common zero-config single-project boot plumbs cfg.DefaultProjectID straight through.
type DomainStore ¶ added in v0.17.0
type DomainStore struct {
// contains filtered or unexported fields
}
DomainStore persists Domains within a Project.
func NewDomainStore ¶ added in v0.17.0
func NewDomainStore(r *pgRepository) *DomainStore
NewDomainStore builds a domain store sharing the repository's pool.
func (*DomainStore) CreateDomain ¶ added in v0.17.0
CreateDomain inserts a domain. ProjectID, TenantID and Domain are required; a blank id is generated and written back. A duplicate (project_id, lower(domain)) surfaces service.ErrAlreadyExists.
func (*DomainStore) GetDomain ¶ added in v0.17.0
func (s *DomainStore) GetDomain(ctx context.Context, projectID, domainID string) (*service.Domain, error)
GetDomain returns the domain by id within a project, or (nil, nil).
func (*DomainStore) GetDomainByName ¶ added in v0.17.0
func (s *DomainStore) GetDomainByName(ctx context.Context, projectID, domain string) (*service.Domain, error)
GetDomainByName returns the domain row for a name (case-insensitive) within a project, or (nil, nil).
func (*DomainStore) ListDomainsByTenant ¶ added in v0.17.0
func (s *DomainStore) ListDomainsByTenant(ctx context.Context, projectID, tenantID string) ([]*service.Domain, error)
ListDomainsByTenant returns every domain bound to a tenant, newest first.
func (*DomainStore) SetDomainStatus ¶ added in v0.17.0
func (s *DomainStore) SetDomainStatus(ctx context.Context, projectID, domainID, status string, verifiedAtMs int64) error
SetDomainStatus transitions a domain's status and, when verifying, stamps verified_at_ms (0 defaults to now on a verify). Unknown ids are a no-op.
type InvitationStore ¶ added in v0.17.0
type InvitationStore struct {
// contains filtered or unexported fields
}
InvitationStore persists TenantInvitations within a Project.
func NewInvitationStore ¶ added in v0.17.0
func NewInvitationStore(r *pgRepository) *InvitationStore
NewInvitationStore builds a store sharing the repository's pool.
func (*InvitationStore) CreateInvitation ¶ added in v0.17.0
func (s *InvitationStore) CreateInvitation(ctx context.Context, inv *service.TenantInvitation) (string, error)
CreateInvitation atomically enforces one-open-invite: in a single transaction it revokes any existing pending invitation for the same (project, tenant, lower(email)) and inserts the new one. This is the authoritative enforcement (the partial unique index is defense-in-depth, and the memory driver — should they ever gain invitations — must match these revoke-then-insert semantics).
func (*InvitationStore) GetInvitationByTokenHash ¶ added in v0.17.0
func (s *InvitationStore) GetInvitationByTokenHash(ctx context.Context, projectID, tokenHash string) (*service.TenantInvitation, error)
GetInvitationByTokenHash resolves an invitation by its hashed token within a project, or (nil, nil).
func (*InvitationStore) ListInvitationsForTenant ¶ added in v0.17.0
func (s *InvitationStore) ListInvitationsForTenant(ctx context.Context, projectID, tenantID string) ([]*service.TenantInvitation, error)
ListInvitationsForTenant returns every invitation in a tenant, newest first.
func (*InvitationStore) SetInvitationStatus ¶ added in v0.17.0
func (s *InvitationStore) SetInvitationStatus(ctx context.Context, projectID, invitationID, status string, acceptedAtMs int64) error
SetInvitationStatus transitions an invitation's status and, when accepting, stamps accepted_at_ms (0 defaults to now on accept). Unknown ids are a no-op.
type LoginPolicyStore ¶ added in v0.17.0
type LoginPolicyStore struct {
// contains filtered or unexported fields
}
LoginPolicyStore persists per-tenant login policies within a Project.
func NewLoginPolicyStore ¶ added in v0.17.0
func NewLoginPolicyStore(r *pgRepository) *LoginPolicyStore
NewLoginPolicyStore builds a store sharing the repository's pool.
func (*LoginPolicyStore) DeleteLoginPolicy ¶ added in v1.3.0
func (s *LoginPolicyStore) DeleteLoginPolicy(ctx context.Context, projectID, tenantID string) error
DeleteLoginPolicy removes the policy for (projectID, tenantID). It is idempotent: deleting an absent policy affects no row and returns nil. Both ids are required.
func (*LoginPolicyStore) GetLoginPolicy ¶ added in v0.17.0
func (s *LoginPolicyStore) GetLoginPolicy(ctx context.Context, projectID, tenantID string) (*service.LoginPolicy, error)
GetLoginPolicy returns the policy for (projectID, tenantID), or (nil, nil) when none is set.
func (*LoginPolicyStore) UpsertLoginPolicy ¶ added in v0.17.0
func (s *LoginPolicyStore) UpsertLoginPolicy(ctx context.Context, p *service.LoginPolicy) (string, error)
UpsertLoginPolicy inserts or replaces the policy for (ProjectID, TenantID). On conflict it updates the policy fields and stamps updated_at_ms, leaving id and created_at_ms intact. Returns the id of the resulting row.
type MembershipStore ¶ added in v0.17.0
type MembershipStore struct {
// contains filtered or unexported fields
}
MembershipStore persists TenantMemberships within a Project.
func NewMembershipStore ¶ added in v0.17.0
func NewMembershipStore(r *pgRepository) *MembershipStore
NewMembershipStore builds a store sharing the repository's pool.
func (*MembershipStore) GetMembership ¶ added in v0.17.0
func (s *MembershipStore) GetMembership(ctx context.Context, projectID, tenantID, userID string) (*service.TenantMembership, error)
GetMembership returns the membership for (project, tenant, user), or (nil, nil).
func (*MembershipStore) ListMembershipsForTenant ¶ added in v0.17.0
func (s *MembershipStore) ListMembershipsForTenant(ctx context.Context, projectID, tenantID string) ([]*service.TenantMembership, error)
ListMembershipsForTenant returns every membership in a tenant, newest first.
func (*MembershipStore) ListMembershipsForUser ¶ added in v0.17.0
func (s *MembershipStore) ListMembershipsForUser(ctx context.Context, projectID, userID string) ([]*service.TenantMembership, error)
ListMembershipsForUser returns every membership a user holds across a project's tenants, newest first.
func (*MembershipStore) RemoveMembership ¶ added in v0.17.0
func (s *MembershipStore) RemoveMembership(ctx context.Context, projectID, tenantID, userID string) error
RemoveMembership deletes the membership for (project, tenant, user). Unknown rows are a no-op.
func (*MembershipStore) UpsertMembership ¶ added in v0.17.0
func (s *MembershipStore) UpsertMembership(ctx context.Context, m *service.TenantMembership) (string, error)
UpsertMembership inserts or, on a (project, tenant, user) conflict, updates source/role/status in place (keeping id + created_at_ms) and stamps updated_at_ms. Returns the surviving row id.
type PlatformAdminStore ¶ added in v1.1.0
type PlatformAdminStore struct {
// contains filtered or unexported fields
}
PlatformAdminStore is the Postgres-backed, control-plane store for platform operator accounts (the platform_admins table from migration 0013). Like the other control-plane stores it is platform-global (not project/tenant scoped) and shares its caller's connection pool.
func NewPlatformAdminStore ¶ added in v1.1.0
func NewPlatformAdminStore(r *pgRepository) *PlatformAdminStore
NewPlatformAdminStore builds a platform-admin store sharing the given repository's connection pool. The store must NOT be closed independently — closing the owning *pgRepository releases the pool for every derived store.
func (*PlatformAdminStore) CountPlatformAdmins ¶ added in v1.1.0
func (s *PlatformAdminStore) CountPlatformAdmins(ctx context.Context) (int, error)
CountPlatformAdmins returns the number of platform admins (any status).
func (*PlatformAdminStore) CreateFirstPlatformAdmin ¶ added in v1.1.0
func (s *PlatformAdminStore) CreateFirstPlatformAdmin(ctx context.Context, a *service.PlatformAdmin) (bool, error)
CreateFirstPlatformAdmin inserts the first platform admin atomically and ONLY while the table is empty. It takes a transaction-scoped advisory lock first, so concurrent bootstraps are fully serialized: the winner sees an empty table and inserts; every loser, running after the winner commits, sees a non-empty table and returns (created=false, nil) without writing.
The advisory lock — rather than relying on the table's own constraints — is what makes "empty?" race-safe: under READ COMMITTED two plain SELECT-then-INSERT transactions could each see zero rows and both insert distinct-email admins, defeating the one-time guarantee. Serializing on a single lock key closes that window.
type Project ¶ added in v0.15.0
type Project struct {
ID string
StorageScopeID string
Name string
Status string // active | suspended
ConfigJSON string // JSON object; "" is normalised to "{}".
CreatedAtMs int64
UpdatedAtMs int64
}
Project is a control-plane registry row: one logical, control-plane isolation entity (a Firebase-style project) mapped onto exactly one physical storage scope (shard) via StorageScopeID.
type ProjectAuthDomain ¶ added in v0.15.0
type ProjectAuthDomain struct {
ID string
ProjectID string
Hostname string
IsPrimary bool
VerifiedAtMs int64
CreatedAtMs int64
}
ProjectAuthDomain is a per-project serving hostname. One host resolves to exactly one project (Hostname is globally unique, case-insensitive), so the Host header alone can resolve a project.
type ProjectCredential ¶ added in v0.15.0
type ProjectCredential struct {
ID string
ProjectID string
Kind string // publishable | secret | mtls
PublicID string
SecretHash string
Status string // active | revoked
CreatedAtMs int64
LastUsedAtMs int64
RevokedAtMs int64
}
ProjectCredential is a lookup key used to resolve a project on a request, by its globally-unique PublicID.
type ProjectStore ¶ added in v0.15.0
type ProjectStore struct {
// contains filtered or unexported fields
}
ProjectStore is the Postgres-backed, control-plane registry store. It is platform-global (not project/tenant-scoped) and shares its caller's connection pool.
func NewProjectStore ¶ added in v0.15.0
func NewProjectStore(r *pgRepository, requireVerifiedAuthDomain bool) *ProjectStore
NewProjectStore builds a control-plane store that shares the given repository's connection pool. The store must NOT be closed independently — closing the owning *pgRepository releases the pool for every derived store. requireVerifiedAuthDomain, when true, restricts the resolved primary auth-domain to DNS-verified hostnames (see primaryAuthHostname).
func (*ProjectStore) CreateAuthDomain ¶ added in v1.1.0
func (s *ProjectStore) CreateAuthDomain(ctx context.Context, projectID, hostname string, isPrimary bool) error
CreateAuthDomain registers an UNVERIFIED custom serving hostname (the customer-domain flow). VerifiedAtMs is left 0 so the resolver does not resolve it until VerifyProjectAuthDomain proves ownership. A hostname already bound to any project surfaces service.ErrAlreadyExists.
func (*ProjectStore) CreateProject ¶ added in v0.15.0
func (s *ProjectStore) CreateProject(ctx context.Context, p *service.AdminProject) (string, error)
CreateProject inserts a project from the admin service's value type and returns its id. EnsureAuthDomain and the resolver read the same tables.
func (*ProjectStore) CreateProjectAuthDomain ¶ added in v0.15.0
func (s *ProjectStore) CreateProjectAuthDomain(ctx context.Context, d *ProjectAuthDomain) (string, error)
CreateProjectAuthDomain inserts a serving hostname for a project. Hostname is globally unique on lower(hostname) — a duplicate (in any case) surfaces service.ErrAlreadyExists. At most one is_primary domain is allowed per project (partial unique index); a second primary for the same project likewise surfaces service.ErrAlreadyExists. The id is caller-supplied (random hex when empty) and written back to d.ID.
func (*ProjectStore) CreateProjectCredential ¶ added in v0.15.0
func (s *ProjectStore) CreateProjectCredential(ctx context.Context, c *service.AdminProjectCredential) (string, error)
CreateProjectCredential inserts a credential from the admin service's value type and returns its id. Only the secret HASH is carried in — the raw secret never reaches the store.
func (*ProjectStore) EnsureAuthDomain ¶ added in v0.17.0
func (s *ProjectStore) EnsureAuthDomain(ctx context.Context, projectID, hostname string, isPrimary bool, verifiedAtMs int64) error
EnsureAuthDomain idempotently ensures hostname is a serving auth-domain of projectID. It is safe to call on every boot: if the hostname already exists it is a no-op when it belongs to projectID, and an error when it belongs to a DIFFERENT project (a misconfiguration the operator must resolve). verifiedAtMs marks the domain verified at seed time — used for deployer-owned domains, which need no DNS challenge. A concurrent creator is tolerated by re-reading the winner. Note: passing isPrimary=true when the project already has a different primary surfaces ErrAlreadyExists (the per-project primary partial-unique); changing the primary host is an explicit reconfiguration, not a silent re-seed.
func (*ProjectStore) EnsureDefaultProject ¶ added in v0.15.0
func (s *ProjectStore) EnsureDefaultProject(ctx context.Context, projectID, storageScopeID, name string) (*Project, error)
EnsureDefaultProject idempotently ensures the default Project exists, mapped onto the given storage scope (typically GATEWAY_DEFAULT_TENANT_ID). It is safe to call on every boot and from multiple instances at once: it returns the existing project when one is already present (looked up by id, then by storage scope), and otherwise creates it, tolerating a concurrent creator (an ErrAlreadyExists race is resolved by re-reading the row).
The default project is a logical control-plane entity that POINTS AT the storage scope via StorageScopeID — it is not the same value as the storage id, and the two must not be conflated.
If the storage scope is already mapped to a project (storage_scope_id is globally unique), that existing project is returned even when its id differs from projectID — the scope binding wins and no second project is created for the same scope.
func (*ProjectStore) GetAuthDomain ¶ added in v1.1.0
func (s *ProjectStore) GetAuthDomain(ctx context.Context, projectID, hostname string) (*service.AdminProjectAuthDomain, error)
GetAuthDomain returns a project's own auth-domain (any state) as the admin service's value type, or (nil, nil) when the project has no such hostname.
func (*ProjectStore) GetProjectAuthDomain ¶ added in v1.1.0
func (s *ProjectStore) GetProjectAuthDomain(ctx context.Context, projectID, hostname string) (*ProjectAuthDomain, error)
GetProjectAuthDomain returns a project's auth-domain by hostname (case-insensitive), or (nil, nil) when the project has no such domain. It is scoped to projectID — a hostname owned by a different project is a miss — so the custom-domain RPCs only ever read/verify a project's own domains. Unlike GetProjectByAuthHostname it does NOT filter on verified, so the caller can observe (and verify) a still-unverified domain.
func (*ProjectStore) GetProjectByAuthHostname ¶ added in v0.15.0
func (s *ProjectStore) GetProjectByAuthHostname(ctx context.Context, hostname string) (*Project, error)
GetProjectByAuthHostname resolves a project from a request's Host header. The hostname match is case-insensitive (lower(hostname)), matching the global unique index, so one host resolves to exactly one project. Only a VERIFIED auth-domain (verified_at_ms > 0) resolves: a customer-registered custom domain stays non-resolving until its DNS-TXT ownership challenge is proven, so an attacker cannot point an unverified hostname at a project. Returns (nil, nil) when no verified auth domain matches the host.
func (*ProjectStore) GetProjectByID ¶ added in v0.15.0
GetProjectByID returns the project with the given id, or (nil, nil) when no such project exists.
func (*ProjectStore) GetProjectByStorageScope ¶ added in v0.15.0
func (s *ProjectStore) GetProjectByStorageScope(ctx context.Context, storageScopeID string) (*Project, error)
GetProjectByStorageScope returns the single project mapped onto the given physical storage scope, or (nil, nil) when none is. storage_scope_id is globally unique, so this resolves at most one project.
func (*ProjectStore) GetProjectConfig ¶ added in v1.3.0
GetProjectConfig returns a project's stored config_json ("{}" when unset). An unknown project surfaces service.ErrNotFound.
func (*ProjectStore) GetProjectCredentialByPublicID ¶ added in v0.15.0
func (s *ProjectStore) GetProjectCredentialByPublicID(ctx context.Context, publicID string) (*ProjectCredential, error)
GetProjectCredentialByPublicID resolves a credential (and thus its owning project_id, kind and status) by its globally-unique public_id, or returns (nil, nil) when none matches. This is the key-based per-request project-resolution path.
func (*ProjectStore) ListAuthDomains ¶ added in v1.1.0
func (s *ProjectStore) ListAuthDomains(ctx context.Context, projectID string) ([]*service.AdminProjectAuthDomain, error)
ListAuthDomains returns every auth-domain of a project (primary-first) as the admin service's value type.
func (*ProjectStore) ListProjectAuthDomains ¶ added in v0.15.0
func (s *ProjectStore) ListProjectAuthDomains(ctx context.Context, projectID string) ([]*ProjectAuthDomain, error)
ListProjectAuthDomains returns every auth domain for a project, ordered primary-first then by creation time, so callers can pick the link-building host deterministically. An unknown project yields an empty slice.
func (*ProjectStore) ResolveByCredential ¶ added in v0.16.0
func (s *ProjectStore) ResolveByCredential(ctx context.Context, publicID string) (*service.ResolvedProject, error)
ResolveByCredential resolves the active project an active credential public id belongs to. A revoked credential, a suspended project, an unknown id, or a blank id is a clean miss (nil, nil); only an infrastructure failure returns an error.
func (*ProjectStore) ResolveByHostname ¶ added in v0.16.0
func (s *ProjectStore) ResolveByHostname(ctx context.Context, hostname string) (*service.ResolvedProject, error)
ResolveByHostname resolves the active project a serving hostname maps onto (case-insensitive). An unmapped hostname, a suspended project, or a blank hostname is a clean miss (nil, nil).
func (*ProjectStore) RevokeProjectCredential ¶ added in v0.15.0
func (s *ProjectStore) RevokeProjectCredential(ctx context.Context, credentialID string, atMs int64) error
RevokeProjectCredential marks a credential revoked at atMs (defaulting to now when zero). Revoking is idempotent: an already-revoked or non-existent credential is a no-op that returns nil. A revoked credential no longer resolves a project for new requests, though the row is retained for audit.
func (*ProjectStore) SetAuthDomainVerified ¶ added in v1.1.0
func (s *ProjectStore) SetAuthDomainVerified(ctx context.Context, projectID, hostname string, verifiedAtMs int64) error
SetAuthDomainVerified stamps verifiedAtMs on a project's own auth-domain, flipping it to resolving. A hostname the project does not own surfaces service.ErrNotFound.
func (*ProjectStore) SetPrimaryAuthDomain ¶ added in v1.2.0
func (s *ProjectStore) SetPrimaryAuthDomain(ctx context.Context, projectID, hostname string) (*service.AdminProjectAuthDomain, error)
SetPrimaryAuthDomain promotes a project's VERIFIED auth-domain to primary, atomically demoting the current primary, and returns the promoted record as the admin service's value type. An unverified target surfaces service.ErrAuthDomainNotVerified; a hostname the project does not own surfaces service.ErrNotFound.
func (*ProjectStore) SetPrimaryProjectAuthDomain ¶ added in v1.2.0
func (s *ProjectStore) SetPrimaryProjectAuthDomain(ctx context.Context, projectID, hostname string) (*ProjectAuthDomain, error)
SetPrimaryProjectAuthDomain promotes a project's VERIFIED auth-domain to its primary serving host, atomically demoting the current primary in the SAME transaction so the per-project partial-unique primary index is never violated — even under concurrent promotions (both lock the same project's rows, so they serialize rather than colliding on the index). Only a verified (verified_at_ms > 0) domain may be promoted: an unverified target is ErrAuthDomainNotVerified. A hostname the project does not own is ErrNotFound. Promoting the already-primary host is a no-op that returns its record.
func (*ProjectStore) SetProjectAuthDomainVerified ¶ added in v1.1.0
func (s *ProjectStore) SetProjectAuthDomainVerified(ctx context.Context, projectID, hostname string, verifiedAtMs int64) error
SetProjectAuthDomainVerified stamps verified_at_ms on a project's auth-domain, flipping it from non-resolving to resolving. verifiedAtMs must be > 0 (the resolver treats 0 as unverified); atMs defaulting is the caller's job. The update is scoped to projectID so one project cannot mark another's domain verified. A hostname the project does not own affects no row and surfaces ErrNotFound, so a caller can distinguish a no-op verify from a successful one.
func (*ProjectStore) UpdateProjectConfig ¶ added in v1.3.0
func (s *ProjectStore) UpdateProjectConfig(ctx context.Context, projectID, configJSON string) (string, error)
UpdateProjectConfig REPLACES a project's config_json blob and returns the stored (normalised) value. An unknown project surfaces service.ErrNotFound.
type TenantStore ¶ added in v0.17.0
type TenantStore struct {
// contains filtered or unexported fields
}
TenantStore persists Tenants within a Project.
func NewTenantStore ¶ added in v0.17.0
func NewTenantStore(r *pgRepository) *TenantStore
NewTenantStore builds a tenant store sharing the repository's pool. Do not close it independently; closing the *pgRepository releases the pool.
func (*TenantStore) CreateTenant ¶ added in v0.17.0
CreateTenant inserts a tenant. ProjectID is required; a blank id is generated and written back to t.ID.
func (*TenantStore) GetTenant ¶ added in v0.17.0
func (s *TenantStore) GetTenant(ctx context.Context, projectID, tenantID string) (*service.Tenant, error)
GetTenant returns the tenant by id within a project, or (nil, nil).
func (*TenantStore) GetTenantByPrimaryDomain ¶ added in v0.17.0
func (s *TenantStore) GetTenantByPrimaryDomain(ctx context.Context, projectID, domain string) (*service.Tenant, error)
GetTenantByPrimaryDomain returns the tenant whose primary_domain equals domain (case-insensitive) within a project, or (nil, nil). A blank domain never matches.
func (*TenantStore) ListTenants ¶ added in v0.17.0
ListTenants returns every tenant in a project, newest first.
func (*TenantStore) SetTenantStatus ¶ added in v0.17.0
func (s *TenantStore) SetTenantStatus(ctx context.Context, projectID, tenantID, status string) error
SetTenantStatus transitions a tenant's status and stamps updated_at. Unknown ids are a no-op.
Source Files
¶
- config.go
- db.go
- doc.go
- email_change.go
- email_verification.go
- errors.go
- identity_verification.go
- invitation.go
- login_challenge.go
- login_policy_store.go
- membership_store.go
- migrate.go
- migrations.go
- oauth_identity.go
- oauth_one_time_code.go
- passkey.go
- password_reset.go
- passwordless.go
- phone.go
- platform_admin.go
- project_admin.go
- project_resolver.go
- project_store.go
- qr_login.go
- recovery_code.go
- refresh_token.go
- repo.go
- rls.go
- session.go
- sweeper.go
- tenant_autoform.go
- tenant_store.go
- totp.go
- tracepool.go
- user.go