cloudstore

package
v2.0.0-rc.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	PrincipalKindHuman          = "human"
	PrincipalKindServiceAccount = "service_account"

	PrincipalRoleAdmin  = "admin"
	PrincipalRoleMember = "member"
)
View Source
const AuditActionChunkPush = "chunk_push"

AuditActionChunkPush discriminates chunk push rejections.

View Source
const AuditActionMutationPush = "mutation_push"

AuditActionMutationPush discriminates mutation push rejections.

View Source
const AuditOutcomeRejectedProjectPaused = "rejected_project_paused"

AuditOutcomeRejectedProjectPaused is the single outcome constant for v1. Used as the `outcome` column value when a push is rejected because the project sync is paused.

Variables

View Source
var (
	ErrInvalidPrincipalKind            = errors.New("cloudstore: invalid principal kind")
	ErrInvalidPrincipalRole            = errors.New("cloudstore: invalid principal role")
	ErrLastActiveAdmin                 = errors.New("cloudstore: cannot remove last active admin")
	ErrSensitiveAuditMetadata          = errors.New("cloudstore: sensitive auth audit metadata is not allowed")
	ErrAuthAuditInsertFailed           = errors.New("cloudstore: auth audit insert failed")
	ErrAdminAlreadyExists              = errors.New("cloudstore: a managed admin already exists")
	ErrStrandedAdminRecoveryIneligible = errors.New("cloudstore: stranded admin token recovery is not eligible")
	ErrPrincipalNotFound               = errors.New("cloudstore: principal not found")
	ErrPrincipalDisabled               = errors.New("cloudstore: principal is disabled")
	ErrPrincipalTokenNotFound          = errors.New("cloudstore: principal token not found")
)
View Source
var ErrChunkConflict = errors.New("cloudstore: chunk id conflict")
View Source
var ErrChunkNotFound = errors.New("cloudstore: chunk not found")
View Source
var ErrDashboardContributorNotFound = errors.New("cloudstore: dashboard contributor not found")

ErrDashboardContributorNotFound is returned when GetContributorDetail cannot find the named contributor. R4-7: Use a dedicated error so classifyStoreError can return "Contributor not found" instead of "Project not found".

View Source
var ErrDashboardObservationNotFound = errors.New("cloudstore: dashboard observation not found")
View Source
var ErrDashboardProjectForbidden = errors.New("cloudstore: dashboard project is outside allowed scope")
View Source
var ErrDashboardProjectInvalid = errors.New("cloudstore: dashboard project is invalid")
View Source
var ErrDashboardProjectNotFound = errors.New("cloudstore: dashboard project not found")
View Source
var ErrDashboardPromptNotFound = errors.New("cloudstore: dashboard prompt not found")
View Source
var ErrDashboardSessionNotFound = errors.New("cloudstore: dashboard session not found")

R5-4: Dedicated not-found errors for session, observation, and prompt detail lookups. Using project-not-found for these was misleading — users would see "Project not found" when actually only the session/observation/prompt was missing within a valid project.

Functions

func NormalizeProjectGrant

func NormalizeProjectGrant(project string) string

NormalizeProjectGrant exposes normalizeCloudProjectGrant's normalization rules to callers outside this package (specifically, the cmd/engram PrincipalProjectAuthorizer adapter wired into cloudserver at runtime) so a caller-presented project name can be compared against stored cloud_project_grants rows using the exact same normalization that CreateProjectGrant already applies when persisting a grant. Duplicating this normalization logic in another package instead of reusing it here would risk the two falling out of sync and silently breaking grant enforcement.

Types

type AuditEntry

type AuditEntry struct {
	Contributor string
	Project     string
	Action      string // use AuditAction* constants
	Outcome     string // use AuditOutcome* constants
	EntryCount  int
	ReasonCode  string
	Metadata    map[string]any // reserved for future use; nil is fine (stored as NULL)
}

AuditEntry is the write-side struct for inserting an audit log row.

type AuditFilter

type AuditFilter struct {
	Contributor    string
	Project        string
	Outcome        string
	OccurredAtFrom time.Time // zero value = no lower bound
	OccurredAtTo   time.Time // zero value = no upper bound
}

AuditFilter holds optional filter fields for ListAuditEntriesPaginated. All fields are independently optional; zero values mean "no filter".

type AuthAuditEvent

type AuthAuditEvent struct {
	ID                string
	OccurredAt        time.Time
	ActorPrincipalID  string
	ActorSource       string
	TargetPrincipalID string
	Project           string
	Action            string
	Outcome           string
	ReasonCode        string
	Metadata          map[string]any
}

type AuthAuditQuery

type AuthAuditQuery struct {
	Limit int
}

type CloudStore

type CloudStore struct {
	// contains filtered or unexported fields
}

func New

func New(cfg cloud.Config) (*CloudStore, error)

func (*CloudStore) AdminOverview

func (cs *CloudStore) AdminOverview() (DashboardAdminOverview, error)

func (*CloudStore) BackfillMutationChunks

func (cs *CloudStore) BackfillMutationChunks(ctx context.Context, project string, apply bool) (MutationChunkBackfillReport, error)

func (*CloudStore) Close

func (cs *CloudStore) Close() error

func (*CloudStore) CreateFirstAdminHumanUser

func (cs *CloudStore) CreateFirstAdminHumanUser(ctx context.Context, params CreateHumanUserParams) (HumanUser, error)

CreateFirstAdminHumanUser atomically checks for an existing active admin and creates the first managed admin human user within a single transaction, using the same transaction-scoped advisory lock as guardLastActiveAdminTx (key "engram_cloud_active_admin_guard"). This closes a check-then-act (TOCTOU) race: two callers (the CLI bootstrap command and/or the dashboard first-admin bootstrap route) invoking HasActiveAdmin then CreateHumanUser as two separate calls could both observe "no active admin" and both create a first admin. Callers MUST use this method instead of that check-then-act sequence for first-admin bootstrap.

Returns ErrAdminAlreadyExists (no mutation) if an active admin already exists. params.Role is ignored — this method always creates an admin-role principal, matching its sole purpose.

func (*CloudStore) CreateHumanUser

func (cs *CloudStore) CreateHumanUser(ctx context.Context, params CreateHumanUserParams) (HumanUser, error)

func (*CloudStore) CreatePrincipal

func (cs *CloudStore) CreatePrincipal(ctx context.Context, params CreatePrincipalParams) (Principal, error)

func (*CloudStore) CreatePrincipalToken

func (cs *CloudStore) CreatePrincipalToken(ctx context.Context, params CreatePrincipalTokenParams) (PrincipalToken, error)

func (*CloudStore) CreatePrincipalTokenWithAudit

func (cs *CloudStore) CreatePrincipalTokenWithAudit(ctx context.Context, params CreatePrincipalTokenParams, audit AuthAuditEvent) (PrincipalToken, error)

func (*CloudStore) CreateProjectGrant

func (cs *CloudStore) CreateProjectGrant(ctx context.Context, params CreateProjectGrantParams) (ProjectGrant, error)

func (*CloudStore) CreateUser

func (cs *CloudStore) CreateUser(username, email, _ string) (*User, error)

func (*CloudStore) FindPrincipalTokenByHash

func (cs *CloudStore) FindPrincipalTokenByHash(ctx context.Context, tokenHash string) (PrincipalToken, Principal, error)

FindPrincipalTokenByHash looks up a managed token record and its owning principal by the token's HMAC verifier hash. It is the storage-only production lookup consumed (through a package-boundary-safe adapter, since internal/cloud/auth already imports cloudstore and a direct cloudstore -> auth import would cycle) by cloudauth.ManagedTokenLookup at runtime, so managed cloud tokens can actually authenticate against a running `engram cloud serve` process rather than only in tests.

Returns ErrPrincipalTokenNotFound (not sql.ErrNoRows) when no token matches tokenHash, so callers can map it to the auth package's own "unknown token" sentinel without leaking a database-specific error type. The returned PrincipalToken's TokenHash field is intentionally left blank, matching the same "never return the stored hash to a caller" convention already used by ListPrincipalTokens/CreatePrincipalToken — the caller already holds the hash it looked up with and never needs it echoed back.

func (*CloudStore) GetContributorDetail

GetContributorDetail returns the contributor row plus all sessions, observations, and prompts that belong to projects the contributor has created chunks in. Purely in-memory scan of dashboardReadModel. Satisfies (h).

func (*CloudStore) GetObservationDetail

func (cs *CloudStore) GetObservationDetail(project, sessionID, syncID string) (DashboardObservationRow, DashboardSessionRow, []DashboardObservationRow, error)

GetObservationDetail returns an observation with its parent session and related observations. The third parameter is syncID — the unique per-observation identifier (map key). Using syncID (not chunkID) is the correct lookup since one chunk can contain multiple observations.

func (*CloudStore) GetPrincipal

func (cs *CloudStore) GetPrincipal(ctx context.Context, id string) (Principal, error)

func (*CloudStore) GetProjectSyncControl

func (cs *CloudStore) GetProjectSyncControl(project string) (*ProjectSyncControl, error)

GetProjectSyncControl returns the control record for a project, or nil if absent.

func (*CloudStore) GetPromptDetail

func (cs *CloudStore) GetPromptDetail(project, sessionID, syncID string) (DashboardPromptRow, DashboardSessionRow, []DashboardPromptRow, error)

GetPromptDetail returns a prompt with its parent session and related prompts. The third parameter is syncID — the unique per-prompt identifier (map key).

func (*CloudStore) GetSessionDetail

func (cs *CloudStore) GetSessionDetail(project, sessionID string) (DashboardSessionRow, []DashboardObservationRow, []DashboardPromptRow, error)

GetSessionDetail returns session detail with its observations and prompts.

func (*CloudStore) GetUserByEmail

func (cs *CloudStore) GetUserByEmail(email string) (*User, error)

func (*CloudStore) GetUserByUsername

func (cs *CloudStore) GetUserByUsername(username string) (*User, error)

func (*CloudStore) HasActiveAdmin

func (cs *CloudStore) HasActiveAdmin(ctx context.Context) (bool, error)

func (*CloudStore) InsertAuditEntry

func (cs *CloudStore) InsertAuditEntry(ctx context.Context, entry AuditEntry) error

InsertAuditEntry synchronously inserts one audit log row. On DB error the error is returned to the caller; do NOT suppress it. The caller is responsible for logging at WARN and deciding HTTP response. JW5: Metadata field is included in the INSERT via json.Marshal so that future-proofing data is not silently dropped. N5: nil or empty Metadata map is stored as NULL in the DB (not as "{}").

func (*CloudStore) InsertAuthAuditEvent

func (cs *CloudStore) InsertAuthAuditEvent(ctx context.Context, event AuthAuditEvent) error

func (*CloudStore) InsertMutationBatch

func (cs *CloudStore) InsertMutationBatch(ctx context.Context, batch []MutationEntry) ([]int64, error)

InsertMutationBatch inserts a batch of mutations into the cloud_mutations journal. Returns the sequence numbers assigned to each entry. BW3: The entire batch is wrapped in a transaction — partial failures roll back all prior entries so the client can retry the full batch without creating duplicates.

func (*CloudStore) IsProjectSyncEnabled

func (cs *CloudStore) IsProjectSyncEnabled(project string) (bool, error)

IsProjectSyncEnabled returns whether sync is enabled for the project. An absent row defaults to enabled=true (safe default).

func (*CloudStore) KnownSessionIDs

func (cs *CloudStore) KnownSessionIDs(ctx context.Context, project string) (map[string]struct{}, error)

func (*CloudStore) ListAuditEntriesPaginated

func (cs *CloudStore) ListAuditEntriesPaginated(ctx context.Context, filter AuditFilter, limit, offset int) ([]DashboardAuditRow, int, error)

ListAuditEntriesPaginated returns a page of audit rows matching the filter, sorted by occurred_at DESC, plus the total matching count. limit and offset are SQL LIMIT/OFFSET values.

func (*CloudStore) ListAuthAuditEvents

func (cs *CloudStore) ListAuthAuditEvents(ctx context.Context, query AuthAuditQuery) ([]AuthAuditEvent, error)

func (*CloudStore) ListContributors

func (cs *CloudStore) ListContributors(query string) ([]DashboardContributorRow, error)

func (*CloudStore) ListContributorsPaginated

func (cs *CloudStore) ListContributorsPaginated(query string, limit, offset int) ([]DashboardContributorRow, int, error)

ListContributorsPaginated returns a page of contributors.

func (*CloudStore) ListDistinctTypes

func (cs *CloudStore) ListDistinctTypes() ([]string, error)

ListDistinctTypes returns a sorted list of distinct, non-empty observation types from the in-memory read model. Satisfies (m).

func (*CloudStore) ListHumanUsers

func (cs *CloudStore) ListHumanUsers(ctx context.Context) ([]HumanUser, error)

func (*CloudStore) ListMutationsSince

func (cs *CloudStore) ListMutationsSince(ctx context.Context, sinceSeq int64, limit int, allowedProjects []string) ([]StoredMutation, bool, int64, error)

ListMutationsSince returns mutations with seq > sinceSeq, filtered to allowedProjects. If allowedProjects is nil, no project filter is applied (returns all). If allowedProjects is non-nil (even empty), only those projects are returned. Returns (mutations, hasMore, latestSeq, error).

func (*CloudStore) ListPrincipalTokens

func (cs *CloudStore) ListPrincipalTokens(ctx context.Context, principalID string) ([]PrincipalToken, error)

func (*CloudStore) ListPrincipals

func (cs *CloudStore) ListPrincipals(ctx context.Context) ([]Principal, error)

func (*CloudStore) ListProjectGrants

func (cs *CloudStore) ListProjectGrants(ctx context.Context, principalID string) ([]ProjectGrant, error)

func (*CloudStore) ListProjectSyncControls

func (cs *CloudStore) ListProjectSyncControls() ([]ProjectSyncControl, error)

ListProjectSyncControls returns all project controls UNION DISTINCT projects known from cloud_chunks (projects with no explicit control row default to enabled).

func (*CloudStore) ListProjects

func (cs *CloudStore) ListProjects(query string) ([]DashboardProjectRow, error)

func (*CloudStore) ListProjectsPaginated

func (cs *CloudStore) ListProjectsPaginated(query string, limit, offset int) ([]DashboardProjectRow, int, error)

ListProjectsPaginated returns a page of projects filtered by query. Satisfies Design Decision 1 (in-memory slicing, no SQL LIMIT/OFFSET).

func (*CloudStore) ListRecentObservations

func (cs *CloudStore) ListRecentObservations(project string, query string, limit int) ([]DashboardObservationRow, error)

func (*CloudStore) ListRecentObservationsPaginated

func (cs *CloudStore) ListRecentObservationsPaginated(project, query, obsType string, limit, offset int) ([]DashboardObservationRow, int, error)

ListRecentObservationsPaginated returns a page of observations.

func (*CloudStore) ListRecentPrompts

func (cs *CloudStore) ListRecentPrompts(project string, query string, limit int) ([]DashboardPromptRow, error)

func (*CloudStore) ListRecentPromptsPaginated

func (cs *CloudStore) ListRecentPromptsPaginated(project, query string, limit, offset int) ([]DashboardPromptRow, int, error)

ListRecentPromptsPaginated returns a page of prompts.

func (*CloudStore) ListRecentSessions

func (cs *CloudStore) ListRecentSessions(project string, query string, limit int) ([]DashboardSessionRow, error)

func (*CloudStore) ListRecentSessionsPaginated

func (cs *CloudStore) ListRecentSessionsPaginated(project, query string, limit, offset int) ([]DashboardSessionRow, int, error)

ListRecentSessionsPaginated returns a page of sessions.

func (*CloudStore) ProjectDetail

func (cs *CloudStore) ProjectDetail(project string) (DashboardProjectDetail, error)

func (*CloudStore) ReadChunk

func (cs *CloudStore) ReadChunk(ctx context.Context, project, chunkID string) ([]byte, error)

func (*CloudStore) ReadManifest

func (cs *CloudStore) ReadManifest(ctx context.Context, project string) (*engramsync.Manifest, error)

func (*CloudStore) RecoverStrandedAdminTokenWithAudit

func (cs *CloudStore) RecoverStrandedAdminTokenWithAudit(ctx context.Context, params RecoverStrandedAdminTokenParams, audit AuthAuditEvent) (PrincipalToken, error)

RecoverStrandedAdminTokenWithAudit atomically issues a token to the sole enabled managed human admin only when no principal token exists anywhere in the deployment. It is intentionally narrower than normal token issuance: it repairs only the partial state left by the historical bootstrap audit failure, preserves all grants, and cannot be repeated after a token exists.

func (*CloudStore) RevokePrincipalToken

func (cs *CloudStore) RevokePrincipalToken(ctx context.Context, tokenID, revokedByPrincipalID, reason string) error

func (*CloudStore) RevokeProjectGrant

func (cs *CloudStore) RevokeProjectGrant(ctx context.Context, principalID, project string) error

func (*CloudStore) SetDashboardAllowedProjects

func (cs *CloudStore) SetDashboardAllowedProjects(projects []string)

func (*CloudStore) SetHumanUserEnabled

func (cs *CloudStore) SetHumanUserEnabled(ctx context.Context, principalID string, enabled bool) error

func (*CloudStore) SetProjectSyncEnabled

func (cs *CloudStore) SetProjectSyncEnabled(project string, enabled bool, updatedBy, reason string) error

SetProjectSyncEnabled upserts the project sync control record.

func (*CloudStore) SystemHealth

func (cs *CloudStore) SystemHealth() (DashboardSystemHealth, error)

SystemHealth returns aggregate metrics from the in-memory read model plus a DB ping. Satisfies REQ-105 / AD-3.

func (*CloudStore) UpdatePrincipal

func (cs *CloudStore) UpdatePrincipal(ctx context.Context, id string, params UpdatePrincipalParams) error

func (*CloudStore) WouldRemoveLastActiveAdmin

func (cs *CloudStore) WouldRemoveLastActiveAdmin(ctx context.Context, principalID string) (bool, error)

func (*CloudStore) WriteChunk

func (cs *CloudStore) WriteChunk(ctx context.Context, project, chunkID, createdBy, clientCreatedAt string, payload []byte) error

type CreateHumanUserParams

type CreateHumanUserParams struct {
	Username    string
	Email       string
	DisplayName string
	Role        string
}

type CreatePrincipalParams

type CreatePrincipalParams struct {
	Kind        string
	DisplayName string
	Role        string
	Enabled     *bool
}

type CreatePrincipalTokenParams

type CreatePrincipalTokenParams struct {
	PrincipalID          string
	TokenPrefix          string
	TokenHash            string
	Name                 string
	CreatedByPrincipalID string
}

type CreateProjectGrantParams

type CreateProjectGrantParams struct {
	PrincipalID          string
	Project              string
	GrantedByPrincipalID string
}

type DashboardAdminOverview

type DashboardAdminOverview struct {
	Projects     int
	Contributors int
	Chunks       int
}

type DashboardAuditRow

type DashboardAuditRow struct {
	ID          int64
	OccurredAt  string // RFC3339 UTC
	Contributor string
	Project     string
	Action      string
	Outcome     string
	EntryCount  int
	ReasonCode  string
	Metadata    map[string]any // nil when NULL in DB
}

DashboardAuditRow is the read-side struct returned from ListAuditEntriesPaginated. N6: Metadata is now included so callers have the full audit row. In v1 UI the field is present but not rendered; the API contract is complete.

type DashboardContributorRow

type DashboardContributorRow struct {
	CreatedBy   string
	Chunks      int
	Projects    int
	LastChunkAt string
}

type DashboardObservationRow

type DashboardObservationRow struct {
	Project   string
	SessionID string
	SyncID    string // unique map key — used for detail page URL segment
	ChunkID   string // chunk the observation was first written in (preserved across mutations)
	Type      string
	Title     string
	Content   string // NEW — materialized from chunk payload
	TopicKey  string // NEW — from observation payload
	ToolName  string // NEW — from observation payload
	CreatedAt string
}

type DashboardProjectDetail

type DashboardProjectDetail struct {
	Project      string
	Stats        DashboardProjectRow
	Contributors []DashboardContributorRow
	Sessions     []DashboardSessionRow
	Observations []DashboardObservationRow
	Prompts      []DashboardPromptRow
}

type DashboardProjectRow

type DashboardProjectRow struct {
	Project      string
	Chunks       int
	Sessions     int
	Observations int
	Prompts      int
}

type DashboardPromptRow

type DashboardPromptRow struct {
	Project   string
	SessionID string
	SyncID    string // unique map key — used for detail page URL segment
	ChunkID   string // chunk the prompt was first written in (preserved across mutations)
	Content   string
	CreatedAt string
}

type DashboardSessionRow

type DashboardSessionRow struct {
	Project   string
	SessionID string
	StartedAt string
	EndedAt   string // NEW — populated from session close chunk if available
	Summary   string // NEW — from session chunk summary field
	Directory string // NEW — from session chunk directory field
}

type DashboardSystemHealth

type DashboardSystemHealth struct {
	DBConnected  bool
	Projects     int
	Contributors int
	Sessions     int
	Observations int
	Prompts      int
	Chunks       int
}

DashboardSystemHealth holds aggregate metrics for the admin health page. Satisfies REQ-105 / AD-3.

type HumanUser

type HumanUser struct {
	PrincipalID string    `json:"principal_id"`
	Username    string    `json:"username"`
	Email       string    `json:"email"`
	DisplayName string    `json:"display_name"`
	Role        string    `json:"role"`
	Enabled     bool      `json:"enabled"`
	CreatedAt   time.Time `json:"created_at"`
}

type MutationChunkBackfillReport

type MutationChunkBackfillReport struct {
	Project             string `json:"project"`
	Applied             bool   `json:"applied"`
	CandidateMutations  int    `json:"candidate_mutations"`
	AlreadyMaterialized int    `json:"already_materialized"`
	InvalidMutations    int    `json:"invalid_mutations"`
	ChunksPlanned       int    `json:"chunks_planned"`
	ChunksInserted      int    `json:"chunks_inserted"`
}

type MutationEntry

type MutationEntry struct {
	Project   string          `json:"project"`
	Entity    string          `json:"entity"`
	EntityKey string          `json:"entity_key"`
	Op        string          `json:"op"`
	Payload   json.RawMessage `json:"payload"`
	CreatedBy string          `json:"-"`
}

MutationEntry mirrors cloudserver.MutationEntry to avoid a circular import.

type Principal

type Principal struct {
	ID          string
	Kind        string
	DisplayName string
	Role        string
	Enabled     bool
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

type PrincipalToken

type PrincipalToken struct {
	ID                   string
	PrincipalID          string
	TokenPrefix          string
	TokenHash            string
	Name                 string
	CreatedByPrincipalID string
	CreatedAt            time.Time
	LastUsedAt           *time.Time
	RevokedAt            *time.Time
	RevokedByPrincipalID string
	RevocationReason     string
}

type ProjectGrant

type ProjectGrant struct {
	PrincipalID          string    `json:"principal_id"`
	Project              string    `json:"project"`
	GrantedByPrincipalID string    `json:"granted_by_principal_id"`
	CreatedAt            time.Time `json:"created_at"`
}

type ProjectSyncControl

type ProjectSyncControl struct {
	Project      string
	SyncEnabled  bool
	PausedReason *string
	UpdatedAt    string
	UpdatedBy    *string
}

ProjectSyncControl holds the per-project sync enable/pause record. The backing table is cloud_project_controls (Postgres, added in migrate()).

type RecoverStrandedAdminTokenParams

type RecoverStrandedAdminTokenParams struct {
	TokenPrefix string
	TokenHash   string
	Name        string
}

RecoverStrandedAdminTokenParams contains the non-secret token data needed to recover the single admin left without a token by a failed bootstrap.

type StoredMutation

type StoredMutation struct {
	Seq        int64           `json:"seq"`
	Project    string          `json:"project"`
	Entity     string          `json:"entity"`
	EntityKey  string          `json:"entity_key"`
	Op         string          `json:"op"`
	Payload    json.RawMessage `json:"payload"`
	OccurredAt string          `json:"occurred_at"`
}

StoredMutation mirrors cloudserver.StoredMutation to avoid a circular import.

type UpdatePrincipalParams

type UpdatePrincipalParams struct {
	Role    string
	Enabled bool
}

type User

type User struct {
	ID           string
	Username     string
	Email        string
	PasswordHash string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL