Documentation
¶
Index ¶
- Constants
- Variables
- func AuthzListSkipOncePending(ctx context.Context) bool
- func AuthzListUnrestricted(ctx context.Context) bool
- func ConsumeAuthzListSkipOnce(ctx context.Context) bool
- func SyncUserTeamMembershipEdge(ctx context.Context, edges AuthzMembershipEdgeStatements, ...) error
- func WithAuthzListFilter(ctx context.Context, filter AuthzListFilter) context.Context
- func WithAuthzListSkipOnce(ctx context.Context) context.Context
- func WithAuthzListUnrestricted(ctx context.Context) context.Context
- type AllStatements
- type AuthAttemptService
- type AuthAttemptStatements
- type AuthzAssignmentStatements
- type AuthzCatalogStatements
- type AuthzListFilter
- type AuthzMembershipEdgeStatements
- type AuthzResolverStatements
- type BeginPasskeyEnrollmentInput
- type BeginPasskeyEnrollmentOutput
- type BrandingService
- func (s *BrandingService) Create(ctx context.Context, input CreateBrandingInput) (*domain.Branding, error)
- func (s *BrandingService) Get(ctx context.Context, projectID, id string) (*domain.Branding, error)
- func (s *BrandingService) GetLatest(ctx context.Context, projectID string) (*domain.Branding, error)
- func (s *BrandingService) List(ctx context.Context, projectID string) ([]*domain.Branding, error)
- type BrandingStatements
- type BuiltinSchemaProvider
- type Challenge
- type ClaimCompleteResult
- type ClaimInitResult
- type ClaimService
- type ClaimStatements
- type ClaimStatusResult
- type CreateAuthAttemptInput
- type CreateBrandingInput
- type CreateGrantInput
- type CreateSchemaByURLInput
- type CreateSchemaInput
- type CreateSessionInput
- type CreateTeamInput
- type CreateUserAction
- type CreateUserInput
- type CryptoKeyStatements
- type DB
- type DeleteSessionInput
- type DeleteUserAction
- type DeleteUserInput
- type EnvironmentService
- func (s *EnvironmentService) GetByName(ctx context.Context, projectID, name string) (*domain.Environment, error)
- func (s *EnvironmentService) List(ctx context.Context, input ListEnvironmentsInput) (*ListEnvironmentsOutput, error)
- func (s *EnvironmentService) SeedDefaults(ctx context.Context, stmts AllStatements, projectID string) error
- type EnvironmentStatements
- type EventExportAdapter
- func (a EventExportAdapter) DeleteEventsOlderThan(ctx context.Context, createdBefore time.Time) (int64, error)
- func (a EventExportAdapter) EnsureSink(ctx context.Context, sink *domain.EventSink) error
- func (a EventExportAdapter) GetEventSinkCursor(ctx context.Context, sinkID, projectID string) (*domain.EventSinkCursor, error)
- func (a EventExportAdapter) InsertEvents(ctx context.Context, events []*domain.Event) error
- func (a EventExportAdapter) ListClaimedProjectIDs(ctx context.Context) ([]string, error)
- func (a EventExportAdapter) ListEventsAfterCursor(ctx context.Context, projectID string, afterCreatedAt time.Time, ...) ([]*domain.Event, error)
- func (a EventExportAdapter) ListProjectIDs(ctx context.Context) ([]string, error)
- func (a EventExportAdapter) UpsertEventSinkCursor(ctx context.Context, cursor *domain.EventSinkCursor) error
- type EventService
- type EventStatements
- type ExchangeInput
- type Filter
- type FinishPasskeyEnrollmentInput
- type FinishPasskeyEnrollmentOutput
- type FlowAuthAttemptAdapter
- func (a *FlowAuthAttemptAdapter) Handoff(ctx context.Context, in domain.FlowHandoffInput) (domain.FlowHandoffOutput, error)
- func (a *FlowAuthAttemptAdapter) IssuePasskeyChallenge(ctx context.Context, in domain.FlowIssuePasskeyChallengeInput) (domain.FlowPasskeyChallengeOutput, error)
- func (a *FlowAuthAttemptAdapter) IssuePasskeyRegistrationChallenge(ctx context.Context, in domain.FlowIssuePasskeyRegistrationChallengeInput) (domain.FlowPasskeyRegistrationChallengeOutput, error)
- func (a *FlowAuthAttemptAdapter) Start(ctx context.Context, in domain.FlowCreateAttemptInput) (string, error)
- func (a *FlowAuthAttemptAdapter) SubmitIdentifier(ctx context.Context, in domain.FlowSubmitIdentifierInput) (string, error)
- func (a *FlowAuthAttemptAdapter) SubmitPasskey(ctx context.Context, in domain.FlowSubmitPasskeyInput) (string, error)
- func (a *FlowAuthAttemptAdapter) SubmitPasskeyRegistration(ctx context.Context, in domain.FlowSubmitPasskeyRegistrationInput) error
- func (a *FlowAuthAttemptAdapter) SubmitPassword(ctx context.Context, in domain.FlowSubmitPasswordInput) error
- type FlowCreateUserWithPasswordHandler
- type FlowDefinitionRequest
- type FlowDefinitionService
- type FlowDefinitionStatements
- type FlowService
- type GetFlowStepRequest
- type GetMyUserInput
- type GetSessionInput
- type GetUserInput
- type GrantService
- type HandoffInput
- type IssueChallengeInput
- type JSONSchemaQueryOptions
- type JSONSchemaStatements
- type KeyService
- type ListEnvironmentsInput
- type ListEnvironmentsOutput
- type ListEventsRequest
- type ListEventsResponse
- type ListFlowDefinitionsRequest
- type ListFlowDefinitionsResponse
- type ListPasskeysInput
- type ListProjectsRequest
- type ListProjectsResponse
- type ListSchemasInput
- type ListSchemasOutput
- type ListSessionInput
- type ListSessionsResponse
- type ListTeamsRequest
- type ListTeamsResponse
- type ListUserTeamsInput
- type ListUserTeamsOutput
- type ListUsersInput
- type ListUsersOutput
- type ManagedIDGenerator
- type PasskeyChallenge
- type PasskeyProof
- type PasskeyRegistrationChallenge
- type PasskeyRegistrationProof
- type PasswordChallenge
- type PasswordProof
- type PersonalTeamEnsurer
- type Pool
- type ProjectService
- type ProjectStatements
- type Proof
- type ResolveFlowHint
- type ResolveFlowRequest
- type ResourceScopeStatements
- type SchemaGetter
- type SchemaService
- func (s *SchemaService) CreateSchema(ctx context.Context, input CreateSchemaInput) (_ *domain.JSONSchema, err error)
- func (s *SchemaService) CreateSchemaByUrl(ctx context.Context, input CreateSchemaByURLInput) (*domain.JSONSchema, error)
- func (s *SchemaService) GetSchema(ctx context.Context, projectID string, teamID string, schemaID string) (*domain.JSONSchema, error)
- func (s *SchemaService) ListSchemas(ctx context.Context, input ListSchemasInput) (*ListSchemasOutput, error)
- type SessionConfig
- type SessionResolver
- type SessionService
- type SessionStatements
- type SessionStatementsResolver
- type SetPasswordInput
- type SetPasswordUserAction
- type Sorting
- type StartFlowRequest
- type StatementPool
- type Statementer
- type Statements
- type StatementsUserRefResolver
- type SubmitFlowRequest
- type TeamMembershipStatements
- type TeamService
- func (s *TeamService) Create(ctx context.Context, input CreateTeamInput) (*domain.Team, error)
- func (s *TeamService) Delete(ctx context.Context, projectID, teamID string) error
- func (s *TeamService) Get(ctx context.Context, projectID string, teamID string) (*domain.Team, error)
- func (s *TeamService) List(ctx context.Context, req ListTeamsRequest) (*ListTeamsResponse, error)
- func (s *TeamService) Update(ctx context.Context, input UpdateTeamInput) (*domain.Team, error)
- type TeamStatements
- type TokenService
- type TokenStatements
- type Transactioner
- type UpdateTeamInput
- type UserAction
- type UserChallenge
- type UserLookup
- type UserPasskeyStatements
- type UserPasswordStatements
- type UserProof
- type UserQueryOptions
- type UserRecoveryCodesStatements
- type UserRefResolver
- type UserService
- type UserStatements
- type UserStatementsLookup
- type UserTOTPStatements
- type VerifyProofInput
Constants ¶
const DefaultUserTeamsLimit = 10
DefaultUserTeamsLimit caps an embedded membership list. Embedded collections are not paginated, so without a cap one user on thousands of teams would dominate a page whose limit promised twenty users. Past the cap the user is flagged truncated and the full list is read from ListUserTeams.
Variables ¶
var ErrListFilterRequired = errors.New("authz list filter missing")
ErrListFilterRequired means a management list ran without AuthzListFilter (or an unrestricted / one-shot skip marker) on the context. HTTP maps this to a 500 with message "authz list filter missing" so it is not triaged as a DB outage.
Functions ¶
func AuthzListSkipOncePending ¶
AuthzListSkipOncePending reports whether a one-shot Allow skip is still available (not yet consumed).
func AuthzListUnrestricted ¶
AuthzListUnrestricted reports whether WithAuthzListUnrestricted is set.
func ConsumeAuthzListSkipOnce ¶
ConsumeAuthzListSkipOnce returns true once for a WithAuthzListSkipOnce context, then false. compileList / ListUsers call this from RequireManagementListFilter.
func SyncUserTeamMembershipEdge ¶
func SyncUserTeamMembershipEdge(ctx context.Context, edges AuthzMembershipEdgeStatements, projectID, teamID, userID string, status domain.MembershipStatus) error
SyncUserTeamMembershipEdge projects a team_memberships status change onto authz_membership_edges: authz-active statuses upsert the user→team edge; otherwise the edge is deleted.
func WithAuthzListFilter ¶
func WithAuthzListFilter(ctx context.Context, filter AuthzListFilter) context.Context
WithAuthzListFilter attaches a list authz conjunct for dialect list SQL.
func WithAuthzListSkipOnce ¶
WithAuthzListSkipOnce is the project-wide Allow skip: the next compileList or ListUsers consumes it and omits EXISTS. A later management list on the same request without a filter still fails closed (#838). Do not use this for nested uniqueness / resolve — those must use WithAuthzListUnrestricted.
func WithAuthzListUnrestricted ¶
WithAuthzListUnrestricted marks a nested non-HTTP list (create uniqueness, login Resolve, branding GetLatest, stmttest). It skips the #838 tripwire and ignores an inherited AuthzListFilter so a nested query cannot see zero rows from the outer kind's EXISTS.
Types ¶
type AllStatements ¶
type AllStatements interface {
ManagedIDGenerator
ProjectStatements
FlowDefinitionStatements
CryptoKeyStatements
JSONSchemaStatements
EnvironmentStatements
TeamStatements
TeamMembershipStatements
TokenStatements
SessionStatements
AuthAttemptStatements
UserStatements
UserPasswordStatements
UserTOTPStatements
UserPasskeyStatements
UserRecoveryCodesStatements
BrandingStatements
ClaimStatements
ResourceScopeStatements
AuthzAssignmentStatements
AuthzMembershipEdgeStatements
AuthzCatalogStatements
AuthzResolverStatements
EventStatements
Statements
}
type AuthAttemptService ¶
type AuthAttemptService interface {
// Create and store a new auth attempt.
//
// If input.SessionID is set, the existing session's verified checks are copied
// into the attempt for step-up authentication against an existing session.
//
// If input.RequiredChecks are nil, the project's default required checks are used.
//
// errors: domain.ErrAuthAttemptInvalidRequest, domain.ErrInternal
Create(ctx context.Context, input CreateAuthAttemptInput) (*domain.AuthAttempt, error)
// GetByID retrieves an auth attempt including all factors and challenges by its ID.
//
// errors: domain.ErrAuthAttemptNotFound, domain.ErrInternal
GetByID(ctx context.Context, projectID, attemptID string) (*domain.AuthAttempt, error)
// IssueChallenge creates and returns a new challenge for the given check type.
// An existing challenge of the same type will be overwritten.
//
// For passkey challenges, the user check must already be verified, so the user ID is known
// for retrieving registered credentials.
//
// errors: domain.ErrAuthAttemptNotFound, domain.ErrAuthAttemptInvalidRequest, domain.ErrAuthAttemptInvalidState, domain.ErrAuthAttemptAlreadyHandedOff, domain.ErrInternal
IssueChallenge(ctx context.Context, input IssueChallengeInput) (*domain.AuthAttempt, error)
// VerifyProof verifies the submitted proof against the challenge identified by ChallengeID.
//
// On success, it persists the verification and marks the attempt complete if all
// required checks are now satisfied.
//
// On failure, it records the failed attempt for rate-limiting purposes.
//
// The ChallengeID must match the ID returned by IssueChallenge to prevent use of
// stale proofs after a challenge is re-issued.
//
// errors: domain.ErrAuthAttemptNotFound, domain.ErrAuthAttemptStaleChallenge, domain.ErrAuthAttemptInvalidRequest, domain.ErrAuthAttemptProofRejected, domain.ErrAuthAttemptInvalidState, domain.ErrAuthAttemptAlreadyHandedOff, domain.ErrInternal
VerifyProof(ctx context.Context, input VerifyProofInput) (*domain.AuthAttempt, error)
// Handoff mints a single-use handoff token for a completed attempt.
//
// The client exchanges the token at POST /sessions/exchange to create
// a new session or add factors to an existing session.
//
// The attempt must be in a completed state and not expired.
//
// errors: domain.ErrAuthAttemptNotFound, domain.ErrAuthAttemptInvalidState, domain.ErrAuthAttemptNotCompleted, domain.ErrAuthAttemptAlreadyHandedOff, domain.ErrInternal
Handoff(ctx context.Context, input HandoffInput) (*domain.AuthAttempt, error)
// BeginPasskeyEnrollment starts a management-plane enrollment ceremony on
// an internal attempt: the target user is pinned as a verified user
// factor and a registration challenge is issued. RegistrationID is the
// internal attempt's id, but the attempt is invisible on the attempt
// surface — internal attempts cannot be read, handed off, or exchanged.
//
// errors: domain.ErrAuthAttemptInvalidRequest, domain.ErrInternal
BeginPasskeyEnrollment(ctx context.Context, input BeginPasskeyEnrollmentInput) (*BeginPasskeyEnrollmentOutput, error)
// FinishPasskeyEnrollment verifies the attestation against the ceremony
// and persists the credential; the internal attempt is consumed in the
// same transaction, so a completed ceremony cannot be replayed and no
// best-effort cleanup is needed.
//
// errors: domain.ErrAuthAttemptNotFound, domain.ErrAuthAttemptStaleChallenge, domain.ErrAuthAttemptInvalidRequest, domain.ErrAuthAttemptProofRejected, domain.ErrInternal
FinishPasskeyEnrollment(ctx context.Context, input FinishPasskeyEnrollmentInput) (*FinishPasskeyEnrollmentOutput, error)
}
AuthAttemptService is the primary port for auth attempt operations. REST handler, OIDC server, SAML server and the flow engine all depend on this interface.
func NewAuthAttemptService ¶
func NewAuthAttemptService( stmts StatementPool, sessions SessionResolver, users UserLookup, passwordVerifier crypto.HashVerifier, ) AuthAttemptService
type AuthAttemptStatements ¶
type AuthAttemptStatements interface {
Statements
CreateAuthAttempt(ctx context.Context, entity *domain.AuthAttempt) error
GetAuthAttemptByID(ctx context.Context, projectID, authAttemptID string) (*domain.AuthAttempt, error)
GetAuthAttemptByHandoffToken(ctx context.Context, projectID string, handoffToken []byte) (*domain.AuthAttempt, error)
DeleteAuthAttemptByID(ctx context.Context, projectID, authAttemptID string) error
HandoffAuthAttempt(ctx context.Context, attempt *domain.AuthAttempt) error
SetAuthAttemptChallenge(ctx context.Context, projectID, authAttemptID string, challenge domain.AuthChallenge) error
// SetAuthAttemptFactor upserts a verified factor directly, without a
// challenge/proof cycle: the caller has already established the fact by
// other means (e.g. the user row was just created in the same transaction).
// An existing check row of the same type is overwritten and its challenge
// state cleared. Returns the check row's id for audit emits.
SetAuthAttemptFactor(ctx context.Context, projectID, authAttemptID string, factor domain.AuthFactor) (checkID string, err error)
AuthAttemptChallengeSucceeded(ctx context.Context, projectID, authAttemptID string, factor domain.AuthFactor, challengeID string) error
AuthAttemptChallengeFailed(ctx context.Context, projectID, authAttemptID string, challenge domain.AuthChallenge) error
}
type AuthzAssignmentStatements ¶
type AuthzAssignmentStatements interface {
Statements
CreateAuthzAssignment(ctx context.Context, assignment *domain.AuthzAssignment) error
GetAuthzAssignment(ctx context.Context, projectID, id string) (*domain.AuthzAssignment, error)
ListAuthzAssignments(ctx context.Context, projectID string, principalType domain.AuthzPrincipalType, principalID string, includeRevoked bool) ([]*domain.AuthzAssignment, error)
RevokeAuthzAssignment(ctx context.Context, projectID, id string) error
// GetActiveOwningTeamGrant returns the project's active owning-team grant
// (object project, relation team) or NoRowFoundError when the project is
// unclaimed. The unique active grant is the claim source of truth
// (ADR 046 / ADR 054 §2); at most one active row exists per project,
// enforced by authz_assignments_one_owning_team.
GetActiveOwningTeamGrant(ctx context.Context, projectID string) (*domain.AuthzAssignment, error)
// ListClaimedProjectIDs returns project ids holding an active owning-team
// grant (ADR 049 export visibility), ordered by project_id after afterID
// (empty starts at the beginning).
ListClaimedProjectIDs(ctx context.Context, afterID string, limit uint32) ([]string, error)
}
AuthzAssignmentStatements persists grants (principal → catalog relation at a scope).
Use cases: grant/revoke product APIs and resolver reads — not dual-write from CreateUser. Create/Revoke are the write path; Get/List support admin and check-time lookup.
type AuthzCatalogStatements ¶
type AuthzCatalogStatements interface {
Statements
PersistCatalogVersion(ctx context.Context, meta domain.AuthzCatalogVersion, mutations compiler.CatalogMutations) error
GetAuthzCatalog(ctx context.Context, catalogID string) (*domain.AuthzCatalog, error)
LoadCatalogMutations(ctx context.Context, catalogID string) (compiler.PersistedCatalog, error)
}
AuthzCatalogStatements persists a compiled catalog version (#720 → Wave 1 tables).
PersistCatalogVersion inserts authz_catalogs plus relations, relation references, expression edges, and closure. It does not write RSI, assignments, membership edges, or bundles. Retires any previously active catalog for the same (catalog_kind, owner_id).
GetAuthzCatalog loads one catalog version and its projected child rows by id.
LoadCatalogMutations reads the child rows PersistCatalogVersion wrote as compiler.PersistedCatalog (stmttest round-trip and L4 oracle catalog load).
type AuthzListFilter ¶
type AuthzListFilter = domain.AuthzListObjectsParams
AuthzListFilter is the portable authz conjunct injected into resource list SQL. Management compileList requires this on context; compileRead and nested lists omit it on purpose.
func AuthzListFilterFromContext ¶
func AuthzListFilterFromContext(ctx context.Context) (AuthzListFilter, bool)
AuthzListFilterFromContext returns the list authz filter when present. An unrestricted wrap wins over an inherited filter so nested uniqueness / resolve / GetLatest queries are not EXISTS-narrowed to the outer kind.
type AuthzMembershipEdgeStatements ¶
type AuthzMembershipEdgeStatements interface {
Statements
UpsertAuthzMembershipEdge(ctx context.Context, edge *domain.AuthzMembershipEdge) error
GetAuthzMembershipEdge(ctx context.Context, key domain.AuthzMembershipEdgeKey) (*domain.AuthzMembershipEdge, error)
ListAuthzMembershipEdgesByMember(ctx context.Context, projectID string, memberType domain.AuthzMemberType, memberID string) ([]*domain.AuthzMembershipEdge, error)
DeleteAuthzMembershipEdges(ctx context.Context, filter database.Filter[domain.AuthzMembershipEdgeField]) error
DeleteAuthzMembershipEdgesForTeamDeactivate(ctx context.Context, projectID, teamID string) error
}
AuthzMembershipEdgeStatements persists the authz projection of set membership. The resolver reads these edges, not team_memberships (membership/lifecycle stays separate).
Use cases:
- Upsert: low-level row ops. Prefer SyncUserTeamMembershipEdge for membership status changes.
- DeleteAuthzMembershipEdges: column-shaped deletes via Filter (single edge, by member, by set).
- DeleteAuthzMembershipEdgesForTeamDeactivate: team deactivate (team set + lifecycle-owned users).
- Get/ListByMember: resolver / dual-write test reads.
type AuthzResolverStatements ¶
type AuthzResolverStatements interface {
Statements
ActiveSystemCatalogID(ctx context.Context) (string, error)
HasAuthzProjectFoothold(ctx context.Context, projectID string, principalType domain.AuthzPrincipalType, principalID string) (bool, error)
CheckAuthz(ctx context.Context, params domain.AuthzCheckParams) (allowed bool, foothold bool, err error)
ListAuthzObjectIDs(ctx context.Context, params domain.AuthzListObjectsParams) ([]string, error)
}
AuthzResolverStatements is the storage surface for permission Check / list (#423).
Use cases:
- ActiveSystemCatalogID: active system catalog (kind=system, owner=system).
- HasAuthzProjectFoothold: any active assignment or membership edge in project.
- CheckAuthz: allowed + foothold in one round-trip (assignments, closure, membership, TTU).
- ListAuthzObjectIDs: L4/oracle helper materializing authorized RSI ids for one kind. List *endpoints* compose an injectable SQL predicate (ADR 033); that lands with HTTP wiring.
type BeginPasskeyEnrollmentInput ¶
type BeginPasskeyEnrollmentInput struct {
ProjectID string
UserID string
Username string
DisplayName string
RPID string
RPOrigins []url.URL
}
BeginPasskeyEnrollmentInput starts a management-plane enrollment ceremony for an existing user.
type BrandingService ¶
type BrandingService struct {
// contains filtered or unexported fields
}
BrandingService publishes and resolves immutable branding revisions (ADR 040). Revisions are create-only; flow responses resolve the latest revision per project.
func NewBrandingService ¶
func NewBrandingService(v2Pool *DB) *BrandingService
func (*BrandingService) Create ¶
func (s *BrandingService) Create(ctx context.Context, input CreateBrandingInput) (*domain.Branding, error)
Create validates and publishes a new branding revision.
type BrandingStatements ¶
type BrandingStatements interface {
Statements
CreateBranding(ctx context.Context, entity *domain.Branding) error
GetBrandingByID(ctx context.Context, projectID, id string) (*domain.Branding, error)
ListBrandings(ctx context.Context, filter *database.ListOptions[domain.BrandingField]) (*database.ListResult[*domain.Branding], error)
}
type BuiltinSchemaProvider ¶
type BuiltinSchemaProvider interface {
GetBuiltinSchema(uri string) (*jsonschema.Schema, error)
LatestSchemaURI(kind domain.KnownSchemaKind) (string, error)
}
type Challenge ¶
type Challenge interface {
ChallengeCheckType() domain.AuthCheckType
}
Challenge is implemented by each check-type-specific challenge value.
type ClaimCompleteResult ¶
type ClaimInitResult ¶
type ClaimInitResult struct {
// ClaimURL is the browser URL the developer opens; it embeds the plaintext
// challenge token so the browser leg never needs the project secret.
ClaimURL string
// ChallengeID is the plaintext challenge token the CLI polls with.
ChallengeID string
ExpiresAt time.Time
}
type ClaimService ¶
type ClaimService interface {
// Init mints a single-use challenge for an unclaimed project. secretHash
// is the hex SHA-256 of the presented project-secret bearer, computed by
// the handler; it is stored so Status can verify proof of possession.
Init(ctx context.Context, projectID, secretHash string) (*ClaimInitResult, error)
// Status reports the challenge state. challengeID is the plaintext token
// from Init; secretHash must match the initiating secret's hash.
Status(ctx context.Context, projectID, challengeID, secretHash string) (*ClaimStatusResult, error)
// Complete spends the challenge and attaches the project to userID's
// personal team in one transaction. userID comes from the platform-session
// check (verifyClaimSession, C2).
Complete(ctx context.Context, projectID, challengeID, userID string) (*ClaimCompleteResult, error)
}
ClaimService is the claim use-case surface (ADR 046): the server side of the init/poll/complete dance that attaches an unclaimed project to the claiming user's personal team. Handler wiring is Claim E2 (#612).
func NewClaimService ¶
func NewClaimService(v2Pool *DB, consoleBaseURL, platformProjectID string) ClaimService
NewClaimService builds the claim service. platformProjectID is the project hosting the claiming humans' accounts and personal teams (ADR 046 §2).
type ClaimStatements ¶
type ClaimStatements interface {
Statements
// CreateChallenge inserts a pending claim challenge; entity.ID is the
// SHA-256 hash of the challenge token, minted by the caller.
CreateChallenge(ctx context.Context, entity *domain.ClaimChallenge) error
GetChallengeByID(ctx context.Context, projectID, id string) (*domain.ClaimChallenge, error)
// MarkChallengeCompleted flips pending -> completed; a challenge that is
// absent, in another project, or already completed returns NoRowFoundError.
MarkChallengeCompleted(ctx context.Context, projectID, id string) error
// GetPersonalTeamForUser resolves the user's earliest membership as the
// personal team and returns NoRowFoundError when that membership or its team
// is not active. It never falls back to a later membership: a deactivated
// personal team is not silently replaced by another team the user belongs to.
GetPersonalTeamForUser(ctx context.Context, projectID, userID string) (*domain.Team, error)
// GetEarliestTeamMembership returns the same earliest membership
// GetPersonalTeamForUser resolves from, but regardless of its status and
// without joining the team. It exists to tell apart the two states
// GetPersonalTeamForUser deliberately collapses into NoRowFoundError:
// a user who holds no membership at all (NoRowFoundError here too) from one
// whose personal team is deactivated (a row with a non-active status).
// Provisioning must create a team for the first and leave the second alone.
GetEarliestTeamMembership(ctx context.Context, projectID, userID string) (*domain.TeamMembership, error)
}
type ClaimStatusResult ¶
type CreateAuthAttemptInput ¶
type CreateAuthAttemptInput struct {
ProjectID string
// SessionID is set for step-up auth against an existing session.
SessionID *string
// RequiredChecks overrides the project's default policy.
// Used by the OIDC adapter (acr_values) and flow engine.
// If nil the project's DefaultRequiredChecks are used.
RequiredChecks []domain.AuthCheckType
// Internal marks a server-orchestrated ceremony (see
// [domain.AuthAttempt.Internal]). Never settable through the REST surface.
Internal bool
}
type CreateBrandingInput ¶
type CreateGrantInput ¶
type CreateSchemaByURLInput ¶
type CreateSchemaInput ¶
type CreateSessionInput ¶
type CreateTeamInput ¶
type CreateUserAction ¶
type CreateUserAction struct {
CreateUserInput
CreateUser *domain.CreateUser
// contains filtered or unexported fields
}
func NewCreateUserAction ¶
func NewCreateUserAction(input CreateUserInput, schemaStore domain.JSONSchemaStore) *CreateUserAction
func (*CreateUserAction) Apply ¶
func (o *CreateUserAction) Apply(ctx context.Context, stmts AllStatements) error
type CreateUserInput ¶
type CryptoKeyStatements ¶
type CryptoKeyStatements interface {
Statements
GetEncryptionKey(ctx context.Context, filter database.Filter[domain.EncryptionKeyField]) (*domain.EncryptionKey, error)
ListEncryptionKeys(ctx context.Context, opts *database.ListOptions[domain.EncryptionKeyField]) (*database.ListResult[*domain.EncryptionKey], error)
CreateEncryptionKey(ctx context.Context, key *domain.EncryptionKey) error
UpdateKey(ctx context.Context, id string, key string) error
GetSigningKey(ctx context.Context, filter database.Filter[domain.SigningKeyField]) (*domain.SigningKey, error)
CreateSigningKey(ctx context.Context, key *domain.SigningKey) error
}
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
func (*DB) Transaction ¶
func (p *DB) Transaction(ctx context.Context, callback func(ctx context.Context, tx Statementer[AllStatements]) error) error
type DeleteSessionInput ¶
type DeleteUserAction ¶
type DeleteUserAction struct {
DeleteUserInput
}
func NewDeleteUserAction ¶
func NewDeleteUserAction(input DeleteUserInput) *DeleteUserAction
func (*DeleteUserAction) Apply ¶
func (o *DeleteUserAction) Apply(ctx context.Context, stmts AllStatements) error
type DeleteUserInput ¶
type EnvironmentService ¶
type EnvironmentService struct {
// contains filtered or unexported fields
}
func NewEnvironmentService ¶
func NewEnvironmentService(v2Pool *DB) *EnvironmentService
func (*EnvironmentService) GetByName ¶
func (s *EnvironmentService) GetByName(ctx context.Context, projectID, name string) (*domain.Environment, error)
func (*EnvironmentService) List ¶
func (s *EnvironmentService) List(ctx context.Context, input ListEnvironmentsInput) (*ListEnvironmentsOutput, error)
func (*EnvironmentService) SeedDefaults ¶
func (s *EnvironmentService) SeedDefaults(ctx context.Context, stmts AllStatements, projectID string) error
SeedDefaults creates domain.DefaultEnvironmentNames for the project, in order, on the statements of an already open transaction.
It takes stmts rather than opening its own transaction because project creation seeds environments inside the transaction that creates the project: a project that committed without its runtime slots would be a project nothing can ever be deployed to, and no later code path would repair it.
type EnvironmentStatements ¶
type EnvironmentStatements interface {
Statements
CreateEnvironment(ctx context.Context, entity *domain.Environment) error
GetEnvironmentByName(ctx context.Context, projectID, name string) (*domain.Environment, error)
ListEnvironments(ctx context.Context, filter *database.ListOptions[domain.EnvironmentField]) (*database.ListResult[*domain.Environment], error)
}
type EventExportAdapter ¶
type EventExportAdapter struct {
Pool *DB
}
EventExportAdapter adapts AllStatements to audit retention/shipper interfaces.
func (EventExportAdapter) DeleteEventsOlderThan ¶
func (EventExportAdapter) EnsureSink ¶
func (EventExportAdapter) GetEventSinkCursor ¶
func (a EventExportAdapter) GetEventSinkCursor(ctx context.Context, sinkID, projectID string) (*domain.EventSinkCursor, error)
func (EventExportAdapter) InsertEvents ¶
InsertEvents persists a Path A batch in one transaction.
func (EventExportAdapter) ListClaimedProjectIDs ¶
func (a EventExportAdapter) ListClaimedProjectIDs(ctx context.Context) ([]string, error)
ListClaimedProjectIDs returns project IDs that have completed claim (active owning-team grant, ADR 054 §2), matching ADR 049 export visibility.
func (EventExportAdapter) ListEventsAfterCursor ¶
func (EventExportAdapter) ListProjectIDs ¶
func (a EventExportAdapter) ListProjectIDs(ctx context.Context) ([]string, error)
func (EventExportAdapter) UpsertEventSinkCursor ¶
func (a EventExportAdapter) UpsertEventSinkCursor(ctx context.Context, cursor *domain.EventSinkCursor) error
type EventService ¶
type EventService struct {
// contains filtered or unexported fields
}
EventService lists and loads audit events (ADR 049).
func NewEventService ¶
func NewEventService(v2Pool *DB) *EventService
func (*EventService) Get ¶
Get loads one event by (project_id, id). Pre-claim projects and misses return not found (no existence leak).
func (*EventService) List ¶
func (s *EventService) List(ctx context.Context, req ListEventsRequest) (*ListEventsResponse, error)
List returns events for a claimed project with optional filters and keyset pagination on (created_at, id). Pre-claim projects yield an empty page.
type EventStatements ¶
type EventStatements interface {
Statements
InsertEvent(ctx context.Context, event *domain.Event) error
GetEventByID(ctx context.Context, projectID, id string) (*domain.Event, error)
ListEvents(ctx context.Context, filter *database.ListOptions[domain.EventField]) (*database.ListResult[*domain.Event], error)
// DeleteEventsOlderThan removes events with created_at before cutoff across
// all projects (retention job; includes orphaned project_id rows).
DeleteEventsOlderThan(ctx context.Context, createdBefore time.Time) (int64, error)
EnsureEventSink(ctx context.Context, sink *domain.EventSink) error
// GetEventSinkCursor returns (nil, nil) when no cursor row exists.
GetEventSinkCursor(ctx context.Context, sinkID, projectID string) (*domain.EventSinkCursor, error)
UpsertEventSinkCursor(ctx context.Context, cursor *domain.EventSinkCursor) error
ListEventsAfterCursor(ctx context.Context, projectID string, afterCreatedAt time.Time, afterID string, limit uint32) ([]*domain.Event, error)
}
EventStatements persists append-only wide events (ADR 048).
type ExchangeInput ¶
type FlowAuthAttemptAdapter ¶
type FlowAuthAttemptAdapter struct {
// contains filtered or unexported fields
}
FlowAuthAttemptAdapter adapts the broad AuthAttemptService surface to the narrow domain.FlowAuthAttemptService the flow state machine consumes. Each Submit* collapses the underlying issue-then-verify cycle so the state machine never deals with challenge IDs.
func NewFlowAuthAttemptAdapter ¶
func NewFlowAuthAttemptAdapter(attempts AuthAttemptService, schemaStore domain.JSONSchemaStore) *FlowAuthAttemptAdapter
func (*FlowAuthAttemptAdapter) Handoff ¶
func (a *FlowAuthAttemptAdapter) Handoff(ctx context.Context, in domain.FlowHandoffInput) (domain.FlowHandoffOutput, error)
func (*FlowAuthAttemptAdapter) IssuePasskeyChallenge ¶
func (a *FlowAuthAttemptAdapter) IssuePasskeyChallenge(ctx context.Context, in domain.FlowIssuePasskeyChallengeInput) (domain.FlowPasskeyChallengeOutput, error)
IssuePasskeyChallenge issues a WebAuthn assertion challenge and returns its id plus the PublicKeyCredentialRequestOptions the browser hands to navigator.credentials.get(). Unlike the other Submit* methods it does not verify — the assertion arrives on a later submit (see [SubmitPasskey]).
func (*FlowAuthAttemptAdapter) IssuePasskeyRegistrationChallenge ¶
func (a *FlowAuthAttemptAdapter) IssuePasskeyRegistrationChallenge(ctx context.Context, in domain.FlowIssuePasskeyRegistrationChallengeInput) (domain.FlowPasskeyRegistrationChallengeOutput, error)
IssuePasskeyRegistrationChallenge issues a WebAuthn registration challenge on the attempt and returns its id, the (possibly minted) user handle, and the PublicKeyCredentialCreationOptions the browser hands to navigator.credentials.create().
func (*FlowAuthAttemptAdapter) Start ¶
func (a *FlowAuthAttemptAdapter) Start(ctx context.Context, in domain.FlowCreateAttemptInput) (string, error)
func (*FlowAuthAttemptAdapter) SubmitIdentifier ¶
func (a *FlowAuthAttemptAdapter) SubmitIdentifier(ctx context.Context, in domain.FlowSubmitIdentifierInput) (string, error)
func (*FlowAuthAttemptAdapter) SubmitPasskey ¶
func (a *FlowAuthAttemptAdapter) SubmitPasskey(ctx context.Context, in domain.FlowSubmitPasskeyInput) (string, error)
SubmitPasskey verifies the assertion against the challenge identified by ChallengeID and returns the authenticated user id (resolved from the assertion for a discoverable login, or the already-pinned user otherwise).
func (*FlowAuthAttemptAdapter) SubmitPasskeyRegistration ¶
func (a *FlowAuthAttemptAdapter) SubmitPasskeyRegistration(ctx context.Context, in domain.FlowSubmitPasskeyRegistrationInput) error
SubmitPasskeyRegistration verifies the attestation against the challenge identified by ChallengeID. The create-user factory rides along; the attempt service invokes it with the persisted challenge's authoritative user handle, and only when that challenge is provisional.
func (*FlowAuthAttemptAdapter) SubmitPassword ¶
func (a *FlowAuthAttemptAdapter) SubmitPassword(ctx context.Context, in domain.FlowSubmitPasswordInput) error
type FlowCreateUserWithPasswordHandler ¶
type FlowCreateUserWithPasswordHandler struct {
// contains filtered or unexported fields
}
FlowCreateUserWithPasswordHandler implements the `create_user` on_success: persist a new user from validated identifier + password fields.
func NewFlowCreateUserHandler ¶
func NewFlowCreateUserHandler( hasher crypto.Hasher, userService UserService, schemaStore domain.JSONSchemaStore, db StatementPool, ) *FlowCreateUserWithPasswordHandler
func (*FlowCreateUserWithPasswordHandler) Handle ¶
func (h *FlowCreateUserWithPasswordHandler) Handle(ctx context.Context, in domain.FlowOnSuccessInput) (domain.FlowOnSuccessResult, error)
type FlowDefinitionRequest ¶
type FlowDefinitionRequest struct {
FlowDefinitionID string
ProjectID string
Name string
Status string
SchemaVersion string // todo (grvijayan): currently empty as the request does not contain schema version
FlowSchemaURI string // todo (grvijayan): schema_version (semver) stored in the db vs schema_uri needed for validation
UserSchema string
Purposes map[string]string
Audience domain.FlowDefinitionAudience
Steps []domain.FlowDefinitionStep
}
type FlowDefinitionService ¶
type FlowDefinitionService interface {
Create(ctx context.Context, req FlowDefinitionRequest) (*domain.FlowDefinition, error)
Update(ctx context.Context, req FlowDefinitionRequest) (*domain.FlowDefinition, error)
Get(ctx context.Context, projectID, id string) (*domain.FlowDefinition, error)
List(ctx context.Context, req ListFlowDefinitionsRequest) (*ListFlowDefinitionsResponse, error)
Delete(ctx context.Context, projectID string, id string) error
}
func NewFlowDefinitionService ¶
func NewFlowDefinitionService( v2Pool *DB, schemaGetter SchemaGetter, schemaProvider BuiltinSchemaProvider, flowDefinitionValidatorFn flowDefinitionValidatorFunc, ) FlowDefinitionService
type FlowDefinitionStatements ¶
type FlowDefinitionStatements interface {
Statements
CreateFlowDefinition(ctx context.Context, entity *domain.FlowDefinition) error
GetFlowDefinitionByID(ctx context.Context, projectID, id string) (*domain.FlowDefinition, error)
UpdateFlowDefinition(ctx context.Context, entity *domain.FlowDefinition) error
ListFlowDefinitions(ctx context.Context, filter *database.ListOptions[domain.FlowDefinitionField]) (*database.ListResult[*domain.FlowDefinition], error)
DeleteFlowDefinitionByID(ctx context.Context, projectID, id string) error
}
type FlowService ¶
type FlowService interface {
// Resolve returns the [domain.FlowDefinition] to run. Direct lookup
// when Name is set; audience match otherwise.
Resolve(ctx context.Context, req ResolveFlowRequest) (*domain.FlowDefinition, error)
// Start mints a fresh flow on the resolved definition.
Start(ctx context.Context, req StartFlowRequest) (domain.FlowStepResult, error)
// Submit advances the state machine. Re-fetches the definition
// from FlowState.DefinitionID.
Submit(ctx context.Context, req SubmitFlowRequest) (domain.FlowStepResult, error)
// GetStep re-emits the current step without advancing.
GetStep(ctx context.Context, req GetFlowStepRequest) (domain.FlowStepResult, error)
}
FlowService is the flow engine's use-case surface. The API handler depends only on this interface.
func NewFlowService ¶
func NewFlowService( v2Pool *DB, stateMachine domain.FlowStateMachine, ) FlowService
type GetFlowStepRequest ¶
type GetMyUserInput ¶
type GetSessionInput ¶
type GetUserInput ¶
type GrantService ¶
type GrantService struct {
// contains filtered or unexported fields
}
func NewGrantService ¶
func NewGrantService(v2Pool *DB, platformProjectID string) *GrantService
func (*GrantService) Create ¶
func (s *GrantService) Create(ctx context.Context, input CreateGrantInput) (*domain.AuthzAssignment, error)
func (*GrantService) Get ¶
func (s *GrantService) Get(ctx context.Context, projectID, id string) (*domain.AuthzAssignment, error)
type HandoffInput ¶
type IssueChallengeInput ¶
type JSONSchemaQueryOptions ¶
type JSONSchemaQueryOptions struct {
// LatestRevisionPerObjectType keeps only the newest revision of each
// object_type. Rows without an object_type are revisions of nothing and
// pass through untouched.
LatestRevisionPerObjectType bool
}
JSONSchemaQueryOptions carries query modes for ListJSONSchemas that are not column predicates. Column predicates stay in Filter / ListOptions.
type JSONSchemaStatements ¶
type JSONSchemaStatements interface {
Statements
CreateJSONSchema(ctx context.Context, entity *domain.JSONSchema) error
GetJSONSchemaByID(ctx context.Context, projectID, schemaID string) (*domain.JSONSchema, error)
ListJSONSchemas(ctx context.Context, filter *database.ListOptions[domain.JSONSchemaField], opts JSONSchemaQueryOptions) (*database.ListResult[*domain.JSONSchema], error)
DeleteJSONSchemaByID(ctx context.Context, projectID, schemaID string) error
}
type KeyService ¶
type KeyService interface {
GetEncryptionKey(ctx context.Context, keyID string, algorithm jose.ContentEncryption) (*domain.EncryptionKey, error)
GetCrypter(ctx context.Context, keyID string, algorithm jose.ContentEncryption) (op.Crypto, error)
GetProjectEncryptionKey(ctx context.Context, projectID string, purpose domain.EncryptionKeyPurpose) (*domain.EncryptionKey, error)
GetProjectCrypter(ctx context.Context, projectID string, purpose domain.EncryptionKeyPurpose) (op.Crypto, error)
GetProjectSigningKey(ctx context.Context, projectID string, purpose domain.SigningKeyPurpose) (*domain.SigningKey, error)
GetProjectSigner(ctx context.Context, projectID string, purpose domain.SigningKeyPurpose) (jose.Signer, error)
GetMasterKeyCrypter(ctx context.Context) (op.Crypto, error)
MigrateToLatestMasterKey(ctx context.Context) error
}
func NewKeyService ¶
func NewKeyService( db *DB, masterKeys domain.MasterKeys, ) KeyService
type ListEnvironmentsInput ¶
type ListEnvironmentsOutput ¶
type ListEnvironmentsOutput struct {
Items []*domain.Environment
NextPageToken string
}
type ListEventsRequest ¶
type ListEventsRequest struct {
ProjectID string
Categories []string
EventTypes []string
ActorID string
ClientID string
SessionID string
FlowID string
RequestID string
Fingerprint string
EntityType string
EntityID string
TeamID string
CreatedAfter *time.Time
CreatedBefore *time.Time
Order string
Limit int
PageToken string
}
ListEventsRequest is the input for listing project audit events (ADR 049).
type ListEventsResponse ¶
ListEventsResponse is a cursor page of events.
type ListFlowDefinitionsResponse ¶
type ListFlowDefinitionsResponse struct {
Items []*domain.FlowDefinition
NextPageToken string
}
type ListPasskeysInput ¶
type ListProjectsRequest ¶
type ListProjectsRequest struct {
// ProjectID restricts results to that single project. Handlers set it from
// the caller's scope, and every credential today is bound to one project.
// Required.
ProjectID string
Limit int
PageToken string
Sorting *Sorting // optional; defaults to createdAt asc
Filters []Filter
}
ListProjectsRequest is the input for listing projects.
type ListProjectsResponse ¶
ListProjectsResponse is the output for listing projects.
type ListSchemasInput ¶
type ListSchemasInput struct {
ProjectID string
// IDs narrows the list to the given schema ids. Empty means no id filter.
// Not exposed on the wire: it serves in-process batch resolution, such as
// the flow-definition expand hydrator.
IDs []string
ObjectType string
// Kind is nil when the caller did not filter by one. It is a pointer rather
// than a zero value because JSONSchemaKindUnknown is a real stored kind, so
// it cannot double as "no filter".
Kind *domain.JSONSchemaKind
// LatestRevisionPerObjectType asks for the newest revision of each object
// type instead of the full revision history.
LatestRevisionPerObjectType bool
PageToken string
Limit int
}
type ListSchemasOutput ¶
type ListSchemasOutput struct {
Items []*domain.JSONSchema
NextPageToken string
}
type ListSessionInput ¶
type ListSessionsResponse ¶
ListSessionsResponse is the output for listing sessions.
type ListTeamsRequest ¶
type ListTeamsRequest struct {
// ProjectID restricts results to that project. Required: a team only has
// meaning inside its project, so an unscoped team list is never valid.
ProjectID string
Limit int
PageToken string
Sorting *Sorting // optional; defaults to createdAt asc
Filters []Filter
}
ListTeamsRequest is the input for listing the teams of a project.
type ListTeamsResponse ¶
ListTeamsResponse is the output for listing teams.
type ListUserTeamsInput ¶
type ListUserTeamsOutput ¶
type ListUsersInput ¶
type ListUsersInput struct {
ProjectID string
PageToken string
Limit int
Sorting *Sorting // optional; defaults to createdAt desc
Filters []Filter
// IncludeTeams embeds each user's team memberships (ADR 059).
IncludeTeams bool
// IncludeLifecycleOwnerTeam embeds the team that owns each user's
// lifecycle (ADR 059).
IncludeLifecycleOwnerTeam bool
}
type ListUsersOutput ¶
type ManagedIDGenerator ¶
ManagedIDGenerator mints prefixed managed resource IDs.
type PasskeyChallenge ¶
PasskeyChallenge carries the raw WebAuthn assertion response bytes.
func (PasskeyChallenge) ChallengeCheckType ¶
func (PasskeyChallenge) ChallengeCheckType() domain.AuthCheckType
type PasskeyProof ¶
type PasskeyProof struct {
AssertionResponse []byte
}
PasskeyProof carries the raw WebAuthn assertion response bytes.
type PasskeyRegistrationChallenge ¶
type PasskeyRegistrationChallenge struct {
UserID string
Username string
DisplayName string
RPID string
RPOrigins []url.URL
}
PasskeyRegistrationChallenge starts a WebAuthn credential-enrollment ceremony. UserID targets an existing user (must match a pinned user factor when one exists); empty UserID on an attempt without a user factor makes the ceremony provisional: a user handle is minted and the user row is created when the attestation is verified.
func (PasskeyRegistrationChallenge) ChallengeCheckType ¶
func (PasskeyRegistrationChallenge) ChallengeCheckType() domain.AuthCheckType
type PasskeyRegistrationProof ¶
type PasskeyRegistrationProof struct {
AttestationResponse []byte
// Name labels the credential in passkey management surfaces. Optional:
// when empty, a name is derived from the credential itself
// ([domain.GeneratePasskeyCredentialName]).
Name string
// CreateUser, when set, builds the actions that create the ceremony's
// user inside the success transaction, so user creation, credential
// persistence, and check success land atomically. The service invokes it
// with the stored challenge's authoritative user handle — a caller cannot
// create a row under a different id than the credential binds to. Only
// consulted for provisional ceremonies.
CreateUser func(userID string) []UserAction
}
PasskeyRegistrationProof carries the raw WebAuthn attestation response bytes.
type PasswordChallenge ¶
type PasswordChallenge struct{}
PasswordChallenge carries the plaintext password for verification.
func (PasswordChallenge) ChallengeCheckType ¶
func (PasswordChallenge) ChallengeCheckType() domain.AuthCheckType
type PasswordProof ¶
type PasswordProof struct {
Password string
}
PasswordProof carries the plaintext password for verification.
type PersonalTeamEnsurer ¶
type PersonalTeamEnsurer interface {
// EnsurePersonalTeam is a no-op outside the platform project and for users
// that already hold an active membership. Otherwise it creates the team
// and its membership in one transaction.
EnsurePersonalTeam(ctx context.Context, projectID, userID string) error
}
PersonalTeamEnsurer guarantees a platform-project user a team — the team `claim/complete` attaches claimed projects to (ADR 046 §2, #527).
The requirement is only that a registered user *has* a team. The team is an ordinary team: it may later be renamed, shared, or end up carrying other members, and that is fine. "Personal team" is not a modeled concept — it resolves to the user's earliest active membership (`personalTeamForUserStmt`, shared with the claim service) — so a user who already holds any membership is already provisioned and the call is a no-op.
An idempotent *ensure* rather than a create: it runs on session exchange — the one credential-agnostic point every registration and every sign-in passes through before any authenticated call — best-effort, so users created before the effect existed converge on their next sign-in too.
func NewPersonalTeamService ¶
func NewPersonalTeamService( v2Pool StatementPool, platformProjectID string, ) PersonalTeamEnsurer
NewPersonalTeamService wires the ensurer.
type Pool ¶
type Pool interface {
Transactioner[AllStatements]
Statementer[AllStatements]
}
type ProjectService ¶
type ProjectService interface {
// Create generates a new project with server-minted secrets and persists it.
// If seedDefaults is true, server fallback schema and flow resources are
// created with the project for non-CLI creation paths.
// Returns the stored project including timestamps.
Create(ctx context.Context, name string, previewOrigins []string, seedDefaults bool) (*domain.Project, error)
// CreateWithID is Create under a caller-supplied id, for the one project
// whose id the server owns rather than mints: the platform project
// (domain.PlatformProjectID). Reports the same already-exists error as
// Create when the id is taken.
CreateWithID(ctx context.Context, id, name string, previewOrigins []string, seedDefaults bool) (*domain.Project, error)
// Get retrieves a project by ID.
// Returns [database.NoRowFoundError] when no project with the given ID exists.
Get(ctx context.Context, id string) (*domain.Project, error)
// DefaultProject resolves the transitional standalone default retained by
// Console ADR 0004 §2's bootstrap cutover rule: the configured project when
// cfgProjectID is set — which must exist; a missing configured project is a
// configuration error — otherwise the deployment's first-created
// project. Returns (nil, nil) while no project exists yet: the server
// never creates the default project, the customer's integration
// (`zitadel setup` → POST /projects) does.
DefaultProject(ctx context.Context, cfgProjectID string) (*domain.Project, error)
// Update updates the name of a project.
// Returns domain.ErrProjectMissingID or domain.ErrProjectNameInvalid for validation failures.
// Returns domain.ErrProjectNotFound when no project with the given ID exists; other failures return domain.ErrInternal.
Update(ctx context.Context, id, name string) (*domain.Project, error)
// List returns projects matching the request, ordered and paginated with an
// opaque cursor token. The returned NextPageToken is empty when the last page
// has been reached.
// Returns domain.ErrProjectMissingID when the request carries no project.
List(ctx context.Context, req ListProjectsRequest) (*ListProjectsResponse, error)
// Delete hard-deletes a project, cascading to its child resources through the
// storage delete. Deleting a project that does not exist is a no-op.
Delete(ctx context.Context, id string) error
}
ProjectService is the project use-case surface.
func NewProjectService ¶
func NewProjectService( v2Pool *DB, serverURL string, schemaValidator *domain.SchemaValidator, keyService KeyService, ) ProjectService
NewProjectService returns a ProjectService backed by the given repository.
type ProjectStatements ¶
type ProjectStatements interface {
Statements
CreateProject(ctx context.Context, entity *domain.Project) error
GetProjectByID(ctx context.Context, id string) (*domain.Project, error)
UpdateProject(ctx context.Context, entity *domain.Project) error
ListProjects(ctx context.Context, filter *database.ListOptions[domain.ProjectField]) (*database.ListResult[*domain.Project], error)
// DeleteProjectByID removes the project. changed is false when no row matched.
DeleteProjectByID(ctx context.Context, id string) (changed bool, err error)
}
type Proof ¶
type Proof interface {
// contains filtered or unexported methods
}
Proof is implemented by each check-type-specific proof value.
type ResolveFlowHint ¶
type ResolveFlowRequest ¶
type ResolveFlowRequest struct {
ProjectID string
Purpose domain.FlowDefinitionPurpose
Name *string // direct-lookup slug
SchemaVersion *string // nil = latest active
AuthRequestID *string
// Hint scopes audience resolution (ignored when Name is set) — see
// resolveByAudience for the scoring rules.
Hint ResolveFlowHint
}
type ResourceScopeStatements ¶
type ResourceScopeStatements interface {
Statements
UpsertResourceScope(ctx context.Context, scope *domain.ResourceScope) error
GetResourceScope(ctx context.Context, resourceID string) (*domain.ResourceScope, error)
GetResourceScopeInProject(ctx context.Context, kind domain.ResourceKind, projectID, resourceID string) (*domain.ResourceScope, error)
GetResourceScopeByIDInProject(ctx context.Context, projectID, resourceID string) (*domain.ResourceScope, error)
ExistsResourceScopeElsewhere(ctx context.Context, kind domain.ResourceKind, resourceID, excludeProjectID string) (bool, error)
DeleteResourceScope(ctx context.Context, kind domain.ResourceKind, projectID, resourceID string) error
}
ResourceScopeStatements persists resource_scope_index rows (path.id → project/team).
Use cases:
- UpsertResourceScope: dual-write on project/team/user/schema/branding/ flow_definition/session create (build via domain.New*ResourceScope).
- GetResourceScope: tests/oracle only when resource_id is known globally unique; must not be used by the HTTP management gate (schema $id URLs are not).
- GetResourceScopeInProject: gate lookup by kind + credential project + path.id.
- GetResourceScopeByIDInProject: gate wrong-kind fallback (same project, any kind).
- ExistsResourceScopeElsewhere: gate delete path — foreign presence without requiring a unique global resource_id.
- DeleteResourceScope: explicit cleanup where FK cascade does not apply (user / schema / flow_definition / session delete today; branding relies on project cascade; project delete cascades RSI via project_id FK).
type SchemaGetter ¶
type SchemaService ¶
type SchemaService struct {
// contains filtered or unexported fields
}
func NewSchemaService ¶
func NewSchemaService( v2Pool *DB, schemaResolver *domain.JSONSchemaResolver, schemaValidator *domain.SchemaValidator, ) *SchemaService
func (*SchemaService) CreateSchema ¶
func (s *SchemaService) CreateSchema(ctx context.Context, input CreateSchemaInput) (_ *domain.JSONSchema, err error)
func (*SchemaService) CreateSchemaByUrl ¶
func (s *SchemaService) CreateSchemaByUrl(ctx context.Context, input CreateSchemaByURLInput) (*domain.JSONSchema, error)
func (*SchemaService) GetSchema ¶
func (s *SchemaService) GetSchema(ctx context.Context, projectID string, teamID string, schemaID string) (*domain.JSONSchema, error)
func (*SchemaService) ListSchemas ¶
func (s *SchemaService) ListSchemas(ctx context.Context, input ListSchemasInput) (*ListSchemasOutput, error)
type SessionConfig ¶
type SessionConfig struct {
DefaultTTL time.Duration `mapstructure:"default_ttl"`
MaxTTL time.Duration `mapstructure:"max_ttl"`
}
SessionConfig holds server-wide session TTL settings for exchange.
func (SessionConfig) Validate ¶
func (c SessionConfig) Validate() error
Validate checks session TTL configuration at startup.
type SessionResolver ¶
type SessionService ¶
type SessionService interface {
Create(ctx context.Context, input CreateSessionInput) (*domain.Session, error)
Exchange(ctx context.Context, input ExchangeInput) (*domain.Session, error)
// Get retrieves a session by ID within a project. When the input sets
// WithUserIdentity, the linked user's resolved reference (ADR 058 §3) is
// hydrated onto Session.User (absent users degrade to a session without
// a ref). errors: domain.ErrSessionNotFound, domain.ErrInternal
Get(ctx context.Context, input GetSessionInput) (*domain.Session, error)
// List returns the project's sessions matching the input's filters,
// errors: domain.ErrProjectMissingID, domain.ErrRequestInvalid, domain.ErrNotImplemented, domain.ErrInternal
List(ctx context.Context, input ListSessionInput) (*ListSessionsResponse, error)
Delete(ctx context.Context, input DeleteSessionInput) error
}
func NewSessionService ¶
func NewSessionService(v2Pool StatementPool, refs UserRefResolver, cfg SessionConfig) SessionService
type SessionStatements ¶
type SessionStatements interface {
Statements
// CreateSession inserts user_agent (optional), session, and session token.
// It wraps the multi-write steps in withTransaction.
CreateSession(ctx context.Context, entity *domain.Session) error
// ExchangeSession promotes verified auth-attempt checks onto a session
// (create or upgrade), rotates the session token, and deletes the attempt.
// It wraps the multi-write steps in withTransaction.
// idempotencyKey is accepted for API compatibility and currently ignored.
ExchangeSession(ctx context.Context, projectID, handoffToken string, idempotencyKey *string, ttl time.Duration) (*domain.Session, error)
GetSessionByID(ctx context.Context, projectID, sessionID string) (*domain.Session, error)
ListSessions(ctx context.Context, filter *database.ListOptions[domain.SessionField]) (*database.ListResult[*domain.Session], error)
DeleteSessionByID(ctx context.Context, projectID, sessionID string) error
}
type SessionStatementsResolver ¶
type SessionStatementsResolver struct {
Pool StatementPool
}
SessionStatementsResolver adapts StatementPool to SessionResolver.
type SetPasswordInput ¶
type SetPasswordUserAction ¶
type SetPasswordUserAction struct {
SetPasswordInput
// contains filtered or unexported fields
}
func NewSetUserPasswordAction ¶
func NewSetUserPasswordAction(input SetPasswordInput, hasher crypto.Hasher) *SetPasswordUserAction
func (*SetPasswordUserAction) Apply ¶
func (o *SetPasswordUserAction) Apply(ctx context.Context, stmts AllStatements) error
type StartFlowRequest ¶
type StartFlowRequest struct {
Definition *domain.FlowDefinition
Purpose domain.FlowDefinitionPurpose
RedirectURI *string
AuthRequestID *string
SessionID *string
UserAgent *domain.UserAgent
}
type StatementPool ¶
type StatementPool interface {
Statementer[AllStatements]
Transactioner[AllStatements]
}
type Statementer ¶
type Statementer[S any] interface { Statements() S }
type Statements ¶
type Statements interface {
IsStatements()
}
type StatementsUserRefResolver ¶
type StatementsUserRefResolver struct {
Pool StatementPool
}
StatementsUserRefResolver resolves refs against the statement surface.
func (StatementsUserRefResolver) ResolveRefsForUsers ¶
func (r StatementsUserRefResolver) ResolveRefsForUsers(ctx context.Context, projectID string, users []*domain.User) (map[string]domain.UserRef, error)
ResolveRefsForUsers maps already-listed users through their own schema's designations (ADR 058 §3a). The callers' read paths hydrate the full attribute document, a superset of the designated keys, so resolution needs only the schema documents — one schema-list query per page, zero additional user queries (§4: list endpoints must not resolve per row). Users whose schema URL has no stored document resolve to a bare id ref.
func (StatementsUserRefResolver) ResolveUserRefs ¶
func (r StatementsUserRefResolver) ResolveUserRefs(ctx context.Context, projectID string, userIDs []string) (map[string]domain.UserRef, error)
ResolveUserRefs loads the project's user-schema documents (every stored revision — users pin the schema URL they were created under, so each revision's own designations govern its users), lists the requested users in one call hydrating only the designated attribute keys, and maps each user through its schema's designations.
type SubmitFlowRequest ¶
type SubmitFlowRequest struct {
State *domain.FlowState
Action string
Fields map[string]any
GateProofs map[string]string
SSOProviderID *string
// ChallengeResponse carries the client's answer to a pending ceremony
// (e.g. a passkey assertion). Nil unless the step issued a challenge.
ChallengeResponse *domain.FlowChallengeResponse
// PasskeyRP carries the WebAuthn relying-party params derived from the
// request, needed when issuing a passkey challenge.
PasskeyRP *domain.FlowPasskeyRP
}
type TeamMembershipStatements ¶
type TeamMembershipStatements interface {
Statements
CreateTeamMembership(ctx context.Context, membership *domain.TeamMembership) error
GetTeamMembership(ctx context.Context, projectID, teamID, userID string) (*domain.TeamMembership, error)
ListTeamMemberships(ctx context.Context, filter *database.ListOptions[domain.TeamMembershipField]) (*database.ListResult[*domain.TeamMembership], error)
ListUserTeams(ctx context.Context, filter *database.ListOptions[domain.UserTeamField]) (*database.ListResult[*domain.UserTeam], error)
UpdateTeamMembershipStatus(ctx context.Context, projectID, teamID, userID string, status domain.MembershipStatus) error
}
type TeamService ¶
type TeamService struct {
// contains filtered or unexported fields
}
func NewTeamService ¶
func NewTeamService(v2Pool *DB) *TeamService
func (*TeamService) Create ¶
func (s *TeamService) Create(ctx context.Context, input CreateTeamInput) (*domain.Team, error)
func (*TeamService) Delete ¶
func (s *TeamService) Delete(ctx context.Context, projectID, teamID string) error
Delete deactivates the team and cascades to its memberships and the users whose lifecycle it owns (ADR 024). The team is tombstoned, not erased, so it stays readable through TeamService.Get with status deactivated.
Delete is idempotent: DeactivateTeam only touches an active team, so an unknown or already-deactivated team reports success without persisting any changes.
func (*TeamService) List ¶
func (s *TeamService) List(ctx context.Context, req ListTeamsRequest) (*ListTeamsResponse, error)
List returns the teams of a project, ordered and paginated with an opaque cursor token. The returned NextPageToken is empty when the last page has been reached.
func (*TeamService) Update ¶
func (s *TeamService) Update(ctx context.Context, input UpdateTeamInput) (*domain.Team, error)
type TeamStatements ¶
type TeamStatements interface {
Statements
CreateTeam(ctx context.Context, entity *domain.Team) error
GetTeamByID(ctx context.Context, projectID, id string) (*domain.Team, error)
GetTeam(ctx context.Context, filter database.Filter[domain.TeamField]) (*domain.Team, error)
UpdateTeam(ctx context.Context, entity *domain.Team) error
ListTeams(ctx context.Context, filter *database.ListOptions[domain.TeamField]) (*database.ListResult[*domain.Team], error)
// DeactivateTeam tombs the team and cascades membership/user lifecycle
// updates. It wraps the multi-write steps in withTransaction (opens a tx
// via Statements(), joins an outer pool.Transaction when already nested).
//
// Only an active team is deactivated: the team UPDATE is guarded on the status,
// and a zero-row result skips the cascade and reports success with changed=false.
// An unknown or already-deactivated team is therefore a no-op;
// updated_at records when the team was first deactivated.
DeactivateTeam(ctx context.Context, projectID, id string) (changed bool, err error)
}
type TokenService ¶
type TokenService interface {
GenerateJWE(ctx context.Context, data *domain.Token) (string, error)
RevokeToken(ctx context.Context, projectID, tokenID string) error
IntrospectToken(ctx context.Context, token string) (*domain.Token, error)
GetActivePreviewToken(ctx context.Context, projectID string) (*domain.Token, error)
}
func NewTokenService ¶
func NewTokenService( keys KeyService, v2Pool StatementPool, ) TokenService
type TokenStatements ¶
type TokenStatements interface {
Statements
CreateToken(ctx context.Context, entity *domain.Token) error
GetTokenByID(ctx context.Context, projectID, tokenID string) (*domain.Token, error)
ListTokens(ctx context.Context, filter *database.ListOptions[domain.TokenField]) (*database.ListResult[*domain.Token], error)
DeleteTokenByID(ctx context.Context, projectID, tokenID string) error
DeleteTokensBySessionID(ctx context.Context, projectID, sessionID string) error
}
type Transactioner ¶
type UpdateTeamInput ¶
type UserAction ¶
type UserChallenge ¶
type UserChallenge struct{}
UserChallenge identifies the user by login name (email, username, phone).
func (UserChallenge) ChallengeCheckType ¶
func (UserChallenge) ChallengeCheckType() domain.AuthCheckType
type UserLookup ¶
type UserPasskeyStatements ¶
type UserPasskeyStatements interface {
Statements
CreateUserPasskey(ctx context.Context, passkey *domain.CreateUserPasskey) error
GetUserPasskey(ctx context.Context, filter database.Filter[domain.UserPasskeyField]) (*domain.UserPasskey, error)
ListUserPasskeys(ctx context.Context, filter *database.ListOptions[domain.UserPasskeyField]) (*database.ListResult[*domain.UserPasskey], error)
UpdateUserPasskey(ctx context.Context, filter database.Filter[domain.UserPasskeyField], updates ...domain.UserPasskeyUpdate) error
DeleteUserPasskey(ctx context.Context, filter database.Filter[domain.UserPasskeyField]) error
}
type UserPasswordStatements ¶
type UserPasswordStatements interface {
Statements
SetUserPassword(ctx context.Context, pw *domain.SetUserPassword) error
GetUserPassword(ctx context.Context, filter database.Filter[domain.UserPasswordField]) (*domain.UserPassword, error)
ListUserPasswords(ctx context.Context, filter *database.ListOptions[domain.UserPasswordField]) (*database.ListResult[*domain.UserPassword], error)
UpdateUserPassword(ctx context.Context, filter database.Filter[domain.UserPasswordField], updates ...domain.UserPasswordUpdate) error
DeleteUserPassword(ctx context.Context, filter database.Filter[domain.UserPasswordField]) error
}
type UserQueryOptions ¶
type UserQueryOptions struct {
// AttributeKeys limits hydrated EAV keys; empty means all attributes.
AttributeKeys []string
// Attributes, when non-empty, restricts to users matching all key/value pairs.
Attributes []domain.Attribute
// UniqueAttributesOnly restricts Attributes matching to values recorded
// in the unique-attributes registry. Identifier lookups set it so an
// equal value in a non-unique property of another user (for example a
// notification address) cannot make the lookup ambiguous.
UniqueAttributesOnly bool
// MembershipTeamID, when set, requires an active team membership.
MembershipTeamID *string
// IncludeTeams hydrates each user's team memberships (ADR 059). The list is loaded
// by a batched second query keyed on the page's ids, never by joining it
// into the user query: a join multiplies rows, which would make LIMIT
// count memberships instead of users and corrupt the keyset cursor.
IncludeTeams bool
// TeamsLimit caps how many membership entries each user carries when
// IncludeTeams is set. Zero means [DefaultUserTeamsLimit].
TeamsLimit int
// IncludeLifecycleOwnerTeam hydrates the team that owns each user's
// lifecycle (ADR 059). Like IncludeTeams it is a batched second query,
// keyed on the distinct owner ids the page returned; self-owned users
// contribute no id and stay nil.
IncludeLifecycleOwnerTeam bool
}
UserQueryOptions carries EAV match/hydrate options for GetUser / ListUsers. Column predicates stay in Filter / ListOptions.
type UserRecoveryCodesStatements ¶
type UserRecoveryCodesStatements interface {
Statements
CreateUserRecoveryCodes(ctx context.Context, codes *domain.CreateRecoveryCodes) error
GetUserRecoveryCodes(ctx context.Context, filter database.Filter[domain.UserRecoveryCodesField]) (*domain.UserRecoveryCodes, error)
ListUserRecoveryCodes(ctx context.Context, filter *database.ListOptions[domain.UserRecoveryCodesField]) (*database.ListResult[*domain.UserRecoveryCodes], error)
UpdateUserRecoveryCodes(ctx context.Context, filter database.Filter[domain.UserRecoveryCodesField], updates ...domain.UserRecoveryCodesUpdate) error
DeleteUserRecoveryCodes(ctx context.Context, filter database.Filter[domain.UserRecoveryCodesField]) error
}
type UserRefResolver ¶
type UserRefResolver interface {
ResolveUserRefs(ctx context.Context, projectID string, userIDs []string) (map[string]domain.UserRef, error)
// ResolveRefsForUsers resolves refs for users the caller already holds
// fully hydrated (ADR 058 §3a) — one schema-list query, no user query.
ResolveRefsForUsers(ctx context.Context, projectID string, users []*domain.User) (map[string]domain.UserRef, error)
}
UserRefResolver is the batch resolution port of ADR 058 §4: it hydrates user references for a page of user IDs with one batched attribute query — list endpoints must not resolve per row. Missing users are simply absent from the result; their refs degrade to the user id at the caller.
type UserService ¶
type UserService interface {
ApplyActions(ctx context.Context, actions ...UserAction) (err error)
CreateUser(ctx context.Context, input CreateUserInput) (*domain.User, error)
DeleteUser(ctx context.Context, input DeleteUserInput) error
ListUsers(ctx context.Context, input ListUsersInput) (*ListUsersOutput, error)
ListPasskeys(ctx context.Context, input ListPasskeysInput) (passkeys []*domain.UserPasskey, nextPage string, err error)
ListUserTeams(ctx context.Context, input ListUserTeamsInput) (*ListUserTeamsOutput, error)
GetUserByID(ctx context.Context, input GetUserInput) (*domain.User, error)
SetPassword(ctx context.Context, input SetPasswordInput) (err error)
GetMyUser(ctx context.Context, input GetMyUserInput) (*domain.User, error)
}
func NewUserService ¶
func NewUserService( v2Pool StatementPool, schemaStore domain.JSONSchemaStore, hasher crypto.Hasher, refs UserRefResolver, ) UserService
NewUserService returns the interface rather than *userService, deliberately against "accept interfaces, return structs". The OpenAPI error generator reflects the API handler's fields to find which services an operation calls, and only an interface-typed field gives it something to key on — a handler holding *userService drops this service out of the reflected floor that backstops the call-graph walk.
type UserStatements ¶
type UserStatements interface {
Statements
CreateUser(ctx context.Context, user *domain.CreateUser) error
GetUser(ctx context.Context, filter database.Filter[domain.UserField], opts UserQueryOptions) (*domain.User, error)
ListUsers(ctx context.Context, filter *database.ListOptions[domain.UserField], opts UserQueryOptions) (*database.ListResult[*domain.User], error)
DeactivateUser(ctx context.Context, projectID, userID string) error
DeleteUserByID(ctx context.Context, projectID, userID string) error
UserExists(ctx context.Context, projectID, userID string) (bool, error)
}
type UserStatementsLookup ¶
type UserStatementsLookup struct {
Pool StatementPool
}
UserStatementsLookup adapts UserStatements to UserLookup for AuthAttemptService.
func (UserStatementsLookup) GetByAttributes ¶
func (l UserStatementsLookup) GetByAttributes(ctx context.Context, projectID string, attrs []domain.Attribute) (*domain.User, error)
GetByAttributes resolves a login identifier, so it matches only uniquely registered values: an equal value in a non-unique property of another user must not make the lookup ambiguous and reject the sign-in.
type UserTOTPStatements ¶
type UserTOTPStatements interface {
Statements
CreateUserTOTP(ctx context.Context, totp *domain.CreateUserTOTP) error
GetUserTOTP(ctx context.Context, filter database.Filter[domain.UserTOTPField]) (*domain.UserTOTP, error)
ListUserTOTPs(ctx context.Context, filter *database.ListOptions[domain.UserTOTPField]) (*database.ListResult[*domain.UserTOTP], error)
UpdateUserTOTP(ctx context.Context, filter database.Filter[domain.UserTOTPField], updates ...domain.UserTOTPUpdate) error
DeleteUserTOTP(ctx context.Context, filter database.Filter[domain.UserTOTPField]) error
}
type VerifyProofInput ¶
Source Files
¶
- auth_attempt.go
- authz_list.go
- branding.go
- claim.go
- database.go
- environment.go
- errors.go
- event.go
- event_claim.go
- event_export.go
- flow.go
- flow_auth_attempt.go
- flow_create_user_with_password_handler.go
- flow_definition.go
- grant.go
- keys.go
- list.go
- logging.go
- personal_team.go
- project.go
- schema_service.go
- session.go
- session_config.go
- statement.go
- team.go
- token.go
- user.go
- user_ref.go