Documentation
¶
Overview ¶
Package db owns the postgres connection pool, migrations, and per-table repositories.
Index ¶
- Variables
- func AddMember(ctx context.Context, p *Pool, orgID, userID uuid.UUID) error
- func ApproveDeviceCode(ctx context.Context, p *Pool, userCode string, userID uuid.UUID) error
- func ConsumeDeviceCode(ctx context.Context, p *Pool, deviceCode string) (uuid.UUID, error)
- func DeletePipe(ctx context.Context, p *Pool, sourceID uuid.UUID, name string) error
- func GeneratePlaintextKey() (string, error)
- func HashAPIKey(plaintext string) (string, error)
- func IsMemberOrOwner(ctx context.Context, p *Pool, orgID, userID uuid.UUID) bool
- func KeyPrefix(plaintext string) string
- func Migrate(ctx context.Context, p *Pool) error
- func RemoveMember(ctx context.Context, p *Pool, orgID, userID uuid.UUID) error
- func RevokeAPIKey(ctx context.Context, p *Pool, id uuid.UUID) error
- func SetNATSAccount(ctx context.Context, p *Pool, orgID uuid.UUID, ...) error
- func UpdateLastBroadcast(ctx context.Context, p *Pool, orgID uuid.UUID, handle string, at time.Time, ...) error
- func VerifyAPIKey(plaintext, stored string) bool
- type APIKey
- type DeviceCode
- type Member
- type OAuthToken
- type Organisation
- func FirstOwnedOrgFor(ctx context.Context, p *Pool, userID uuid.UUID) (Organisation, error)
- func GetOrganisation(ctx context.Context, p *Pool, id uuid.UUID) (Organisation, error)
- func GetOrganisationByName(ctx context.Context, p *Pool, name string) (Organisation, error)
- func InsertOrganisation(ctx context.Context, p *Pool, name string, ownerUserID uuid.UUID) (Organisation, error)
- func ListOrganisations(ctx context.Context, p *Pool) ([]Organisation, error)
- func ListOrganisationsForUser(ctx context.Context, p *Pool, userID uuid.UUID) ([]Organisation, error)
- type Pipe
- type Pool
- type Source
- func GetSourceByHandle(ctx context.Context, p *Pool, orgID uuid.UUID, handle string) (Source, error)
- func InsertSource(ctx context.Context, p *Pool, orgID uuid.UUID, handle string, kind SourceKind) (Source, error)
- func ListSourcesForOrg(ctx context.Context, p *Pool, orgID uuid.UUID) ([]Source, error)
- type SourceKind
- type User
- func GetUser(ctx context.Context, p *Pool, id uuid.UUID) (User, error)
- func GetUserByUsername(ctx context.Context, p *Pool, username string) (User, error)
- func InsertUser(ctx context.Context, p *Pool, username, email string, mode UserMode) (User, error)
- func ListMembers(ctx context.Context, p *Pool, orgID uuid.UUID) ([]User, error)
- func UpsertUserByGitHubID(ctx context.Context, p *Pool, githubID int64, ...) (User, bool, error)
- type UserMode
Constants ¶
This section is empty.
Variables ¶
var ( ErrDeviceCodePending = errors.New("device_code not yet verified") ErrDeviceCodeExpired = errors.New("device_code expired") )
var ErrCannotRemoveOwner = errors.New("cannot remove the organisation's owner")
ErrCannotRemoveOwner is returned by RemoveMember when the caller tries to remove the org's owner. v1 has no transfer-ownership path; the owner has to stay on the org until v2 lands transfer.
var ErrHandleTaken = errors.New("handle taken")
ErrHandleTaken is returned when a (org, handle) row already exists.
var ErrInvalidUserMode = errors.New("user mode must be 'github' or 'internal'")
ErrInvalidUserMode is returned when a caller passes a Mode value outside the {github, internal} CHECK constraint.
var ErrNotFound = errors.New("not found")
ErrNotFound is returned by lookups when the row does not exist.
var ErrPipeNameTaken = errors.New("pipe name taken")
ErrPipeNameTaken — (source_id, name) collision on insert.
Functions ¶
func AddMember ¶
AddMember records a user as a non-owner member of an org. Idempotent — re-adding an existing member is a no-op (UPSERT-style).
func ApproveDeviceCode ¶
ApproveDeviceCode marks the user_code as verified by userID. Idempotent. Returns ErrNotFound if user_code doesn't exist or is already expired.
func ConsumeDeviceCode ¶
ConsumeDeviceCode is the CLI poll path. Returns the user_id and marks the code consumed (single-use). State machine errors:
- ErrDeviceCodePending if not yet verified
- ErrDeviceCodeExpired if past TTL
- ErrNotFound if unknown / already consumed
func DeletePipe ¶
DeletePipe removes the row. Returns ErrNotFound when (source, name) doesn't exist. Stream cleanup is the caller's responsibility (server-side).
func GeneratePlaintextKey ¶
GeneratePlaintextKey returns a fresh plaintext API key. Format: "ppz_<26 hex chars>" (a UUIDv7 hex without dashes, prefixed). 30 chars total makes the 8-char display prefix human-meaningful while leaving plenty of entropy.
func HashAPIKey ¶
HashAPIKey produces a self-describing argon2id hash:
$argon2id$v=19$m=65536,t=1,p=4$<base64-salt>$<base64-tag>
func IsMemberOrOwner ¶
IsMemberOrOwner returns true if userID owns orgID or is a member. Used by /auth/exchange (Phase 3.5) to validate that a multi-org user is actually entitled to the org they're requesting a JWT for.
func KeyPrefix ¶
KeyPrefix is the first 8 characters AFTER the "ppz_" sentinel. Used for display in the GUI and the `ppz status` line. Never use the prefix for auth.
func Migrate ¶
Migrate runs every migration file in order, lexicographically by name. Each file uses IF NOT EXISTS / IF NOT EXISTS COLUMN clauses so re-running is idempotent.
func RemoveMember ¶
RemoveMember drops a non-owner from the org. Returns ErrCannotRemoveOwner when targetUserID matches the org's owner — caller surfaces it as 409. ErrNotFound when the user wasn't a member.
func RevokeAPIKey ¶
RevokeAPIKey marks the key revoked. Idempotent: revoking an already-revoked key is a no-op (returns nil). Returns ErrNotFound if no row matches the id.
func SetNATSAccount ¶
func SetNATSAccount(ctx context.Context, p *Pool, orgID uuid.UUID, accountPub, accountJWT, signingSeed string) error
SetNATSAccount persists the Operator-signed Account JWT + the account's signing seed for an org. Called once (lazily) on first /auth/exchange after Phase 3.5 — subsequent calls find the row already populated and skip.
func UpdateLastBroadcast ¶
func UpdateLastBroadcast(ctx context.Context, p *Pool, orgID uuid.UUID, handle string, at time.Time, payload string) error
UpdateLastBroadcast records the most recent broadcast for this source. Called by the server-side subscriber on every message. Idempotent on identical inputs.
func VerifyAPIKey ¶
VerifyAPIKey checks plaintext against a stored hash in constant time.
Types ¶
type APIKey ¶
type APIKey struct {
ID uuid.UUID
OrganisationID uuid.UUID
KeyHash string
KeyPrefix string
Label string
CreatedAt time.Time
// RevokedAt is nil for active keys, set to the revoke time once
// `POST /api/v1/keys/<id>/revoke` flips it. LookupAPIKey filters
// out revoked rows; the GUI shows them with strikethrough so the
// audit trail stays visible.
RevokedAt *time.Time
}
func InsertAPIKey ¶
func ListAPIKeysForOrg ¶
ListAPIKeysForOrg returns every key for the org, including revoked ones — the GUI shows revoked keys (with strikethrough) so the audit trail stays visible. Sorted active-first by creation time, then revoked rows.
func LookupAPIKey ¶
LookupAPIKey resolves a plaintext key to its row by scanning all keys with the matching 8-char prefix and verifying the hash. ErrNotFound when no match (including when the matching key has been revoked).
type DeviceCode ¶
type DeviceCode struct {
DeviceCode string
UserCode string
ClientName string
UserID *uuid.UUID
ExpiresAt time.Time
VerifiedAt *time.Time
ConsumedAt *time.Time
CreatedAt time.Time
}
DeviceCode is the row stored in oauth_device_codes.
func CreateDeviceCode ¶
func CreateDeviceCode(ctx context.Context, p *Pool, ttl time.Duration, clientName string) (DeviceCode, error)
CreateDeviceCode mints a fresh pair, inserts the row, returns it. clientName is a free-form label the CLI sends so the verify page can name the calling app (e.g. "ppz CLI 0.15.0"); empty string is fine — the page falls back to generic copy.
func LookupDeviceCode ¶
type OAuthToken ¶
type OAuthToken struct {
ID uuid.UUID
UserID uuid.UUID
TokenHash string
Prefix string
ExpiresAt time.Time
RevokedAt *time.Time
CreatedAt time.Time
LastUsedAt *time.Time
}
func IssueBearerToken ¶
func LookupBearerToken ¶
type Organisation ¶
type Organisation struct {
ID uuid.UUID
Name string
OwnerUserID uuid.UUID
CreatedAt time.Time
// Auth V2 §Phase 3.5 — per-org NATS account. NULL until the org's
// account is provisioned (lazy on first /auth/exchange).
NATSAccountPub string
NATSAccountJWT string
NATSAccountSigningSeed string
}
func FirstOwnedOrgFor ¶
FirstOwnedOrgFor returns the org owned by userID. If they own multiple, returns the oldest. If they own none, returns ErrNotFound. Used by the OAuth path of requireAPIKey to pick a default org for callers who haven't yet specified one (Auth V2 Phase 2 interim; proper org-selection UX is V3).
func GetOrganisation ¶
func GetOrganisationByName ¶
GetOrganisationByName looks up an org by its unique name (used as a slug alias in the GUI: /orgs/alpha resolves the same as /orgs/<uuid>).
func InsertOrganisation ¶
func InsertOrganisation(ctx context.Context, p *Pool, name string, ownerUserID uuid.UUID) (Organisation, error)
InsertOrganisation creates a new org owned by ownerUserID. If ownerUserID is uuid.Nil, the org defaults to the seeded unauthenticated user — preserves back-compat for tests + GUI callers that don't supply an owner yet.
func ListOrganisations ¶
func ListOrganisations(ctx context.Context, p *Pool) ([]Organisation, error)
func ListOrganisationsForUser ¶
func ListOrganisationsForUser(ctx context.Context, p *Pool, userID uuid.UUID) ([]Organisation, error)
ListOrganisationsForUser returns the orgs userID owns or is a member of, ordered by name. Used by the GUI dashboard so each user sees only their own tenants instead of every org in the system.
type Pipe ¶
type Pipe struct {
ID uuid.UUID
SourceID uuid.UUID
Name string
TTLSeconds *int // nil = use server default (86400 s)
MaxMsgs *int // nil = use server default (1000)
MaxBytes *int64 // nil = use server default (64 MiB)
CreatedAt time.Time
}
Pipe is one user-creatable sub-bucket on a source. Auto-provisioned pipes (broadcast, stdin, stdout) are NOT stored here — they're derived from the source's kind and joined in at API response time.
func GetPipeByName ¶
GetPipeByName returns one pipe row or ErrNotFound.
func InsertPipe ¶
func InsertPipe(ctx context.Context, p *Pool, sourceID uuid.UUID, name string, ttl *int, maxMsgs *int, maxBytes *int64) (Pipe, error)
InsertPipe inserts a row. Retention overrides are NULL when the pointer arg is nil — the server provisions the JetStream stream with default values for any nil fields.
type Pool ¶
Pool is the public handle other packages use. It wraps pgxpool.Pool so the rest of the codebase doesn't import pgx directly.
type Source ¶
type Source struct {
ID uuid.UUID
OrganisationID uuid.UUID
Handle string
Kind SourceKind
CreatedAt time.Time
LastBroadcastAt *time.Time
LastBroadcastPayload *string
}
func GetSourceByHandle ¶
func InsertSource ¶
func ListSourcesForOrg ¶
func (Source) Pipes ¶
Pipes returns the pipe set for a source based on its kind. pty sources have three pipes:
- broadcast: user-level messages (same as message-kind sources)
- stdin: input fed to the wrapped child via `ppz send`
- stdout: byte-faithful capture of the PTY master's output (ANSI escapes intact); both `ppz read` and `ppz terminal view` consume this pipe.
type SourceKind ¶
type SourceKind string
SourceKind enumerates the supported source shapes.
"message" — default; one pipe: broadcast. "pty" — terminal source; three pipes: broadcast, stdin, stdout.
const ( SourceKindMessage SourceKind = "message" SourceKindPTY SourceKind = "pty" )
type User ¶
type User struct {
ID uuid.UUID
Username string
Email string
Mode UserMode
GitHubID *int64 // nil for mode=internal users
AvatarURL string
CreatedAt time.Time
}
func GetUserByUsername ¶
func InsertUser ¶
func ListMembers ¶
ListMembers returns the non-owner members of an org, ordered by when they were added.
func UpsertUserByGitHubID ¶
func UpsertUserByGitHubID(ctx context.Context, p *Pool, githubID int64, username, email, avatarURL string) (User, bool, error)
UpsertUserByGitHubID inserts a brand new mode=github user, or updates the existing row matching the GitHub numeric id. Returns the resolved User row plus a bool indicating whether the row was freshly created (true) vs already existed (false). Callers use the bool to decide whether to auto-create the user's first org.