cloudstore

package
v1.18.0 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2026 License: MIT Imports: 16 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")
	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 added in v1.18.0

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 added in v1.13.0

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 added in v1.13.0

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 added in v1.18.0

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 added in v1.18.0

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 added in v1.13.0

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

func (*CloudStore) BackfillMutationChunks added in v1.15.5

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

func (*CloudStore) Close

func (cs *CloudStore) Close() error

func (*CloudStore) CreateFirstAdminHumanUser added in v1.18.0

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 added in v1.18.0

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

func (*CloudStore) CreatePrincipal added in v1.18.0

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

func (*CloudStore) CreatePrincipalToken added in v1.18.0

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

func (*CloudStore) CreatePrincipalTokenWithAudit added in v1.18.0

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

func (*CloudStore) CreateProjectGrant added in v1.18.0

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 added in v1.18.0

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 added in v1.13.0

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 added in v1.13.0

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 added in v1.18.0

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 added in v1.13.0

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 added in v1.13.0

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 added in v1.18.0

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

func (*CloudStore) InsertAuditEntry added in v1.13.0

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 added in v1.18.0

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

func (*CloudStore) InsertMutationBatch added in v1.13.0

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 added in v1.13.0

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

func (*CloudStore) ListAuditEntriesPaginated added in v1.13.0

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 added in v1.18.0

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

func (*CloudStore) ListContributors added in v1.13.0

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

func (*CloudStore) ListContributorsPaginated added in v1.13.0

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

ListContributorsPaginated returns a page of contributors.

func (*CloudStore) ListDistinctTypes added in v1.13.0

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 added in v1.18.0

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

func (*CloudStore) ListMutationsSince added in v1.13.0

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 added in v1.18.0

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

func (*CloudStore) ListPrincipals added in v1.18.0

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

func (*CloudStore) ListProjectGrants added in v1.18.0

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 added in v1.13.0

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

func (*CloudStore) ListProjectsPaginated added in v1.13.0

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 added in v1.13.0

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

func (*CloudStore) ListRecentObservationsPaginated added in v1.13.0

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

ListRecentObservationsPaginated returns a page of observations.

func (*CloudStore) ListRecentPrompts added in v1.13.0

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

func (*CloudStore) ListRecentPromptsPaginated added in v1.13.0

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

ListRecentPromptsPaginated returns a page of prompts.

func (*CloudStore) ListRecentSessions added in v1.13.0

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

func (*CloudStore) ListRecentSessionsPaginated added in v1.13.0

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

ListRecentSessionsPaginated returns a page of sessions.

func (*CloudStore) ProjectDetail added in v1.13.0

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

func (*CloudStore) ReadChunk added in v1.13.0

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

func (*CloudStore) ReadManifest added in v1.13.0

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

func (*CloudStore) RevokePrincipalToken added in v1.18.0

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

func (*CloudStore) RevokeProjectGrant added in v1.18.0

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

func (*CloudStore) SetDashboardAllowedProjects added in v1.13.0

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

func (*CloudStore) SetHumanUserEnabled added in v1.18.0

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 added in v1.18.0

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

func (*CloudStore) WouldRemoveLastActiveAdmin added in v1.18.0

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

func (*CloudStore) WriteChunk added in v1.13.0

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

type CreateHumanUserParams added in v1.18.0

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

type CreatePrincipalParams added in v1.18.0

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

type CreatePrincipalTokenParams added in v1.18.0

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

type CreateProjectGrantParams added in v1.18.0

type CreateProjectGrantParams struct {
	PrincipalID          string
	Project              string
	GrantedByPrincipalID string
}

type DashboardAdminOverview added in v1.13.0

type DashboardAdminOverview struct {
	Projects     int
	Contributors int
	Chunks       int
}

type DashboardAuditRow added in v1.13.0

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 added in v1.13.0

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

type DashboardObservationRow added in v1.13.0

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 added in v1.13.0

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

type DashboardProjectRow added in v1.13.0

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

type DashboardPromptRow added in v1.13.0

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 added in v1.13.0

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 added in v1.13.0

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 added in v1.18.0

type HumanUser struct {
	PrincipalID string
	Username    string
	Email       string
	DisplayName string
	Role        string
	Enabled     bool
	CreatedAt   time.Time
}

type MutationChunkBackfillReport added in v1.15.5

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 added in v1.13.0

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"`
}

MutationEntry mirrors cloudserver.MutationEntry to avoid a circular import.

type Principal added in v1.18.0

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

type PrincipalToken added in v1.18.0

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 added in v1.18.0

type ProjectGrant struct {
	PrincipalID          string
	Project              string
	GrantedByPrincipalID string
	CreatedAt            time.Time
}

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 StoredMutation added in v1.13.0

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 added in v1.18.0

type UpdatePrincipalParams struct {
	Role    string
	Enabled bool
}

type User added in v1.13.0

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