Data Model
Domain entities and important relationships in the Logga Prisma schema.
The Prisma schema is the best source of truth for how Logga stores information. This page translates the current models into a product-oriented mental model.
Core Hierarchy
User
-> WorkspaceMembership
-> Workspace
-> Environment
-> Project
-> Channel
-> Event
-> Session
-> TrackedUserAdditional workspace-scoped entities:
ApiKeyAlertRuleNotificationDeviceTokenReportConfig
Model Cheatsheet
| Model | Purpose | Important fields |
|---|---|---|
User |
Human account | email, name, passwordHash, avatarUrl |
Workspace |
Top-level tenant | name |
Environment |
Deployment slice inside a workspace | slug, name, defaultProjectId |
Project |
Product or app within an environment | name, workspaceId, environmentId |
Channel |
Named stream inside a project | slug, name, notificationsEnabled, notificationRule |
Event |
Immutable telemetry record | eventType, level, tags, metadata, actor, triggeredBy, trackedUserId, createdAt |
TrackedUser |
Stable identity for a product user in a project | externalId, name, email, avatarUrl, role, label, properties, firstSeenAt, lastSeenAt |
Session |
Mutable process with lifecycle | status, progress, currentStep, summary, ttlSeconds |
AlertRule |
Notification rule config | condition, isActive, optional channelId |
Notification |
Persisted alert result | title, body, ruleId, optional eventId, optional sessionId |
ReportConfig |
Scheduled aggregation config | schedule, metric, aggregateField, filters, groupBy, channelIds |
Workspace And Environment Scoping
Most operational models carry both workspaceId and environmentId.
This matters because:
- the same workspace can separate production and sandbox activity
- rules, reports, sessions, notifications, and device tokens are environment-aware
- clients should avoid assuming a workspace has only one active environment
Events
Events are the canonical telemetry primitive.
Important characteristics:
- immutable once created
- attached to a project and a channel
- optionally linked to a session
- can carry both
userIdand richeractorsnapshot data - can carry
triggeredBymetadata derived from the authenticated caller - now drives the dashboard's per-project tracked-users view
Interesting fields:
| Field | Use |
|---|---|
eventType |
Semantic event name, such as invoice_sent |
level |
Severity or tone: debug, info, success, warning, error |
tags |
Lightweight filtering labels |
metadata |
Arbitrary JSON payload |
idempotencyKey |
Deduplication handle |
Tracked Users
TrackedUser is a project-scoped identity row representing one of your product's end users. It is separate from the User model (which represents humans using the Logga dashboard).
Shape:
projectId+externalId(unique together) — the stable id your SDK sends asactor.idoruserId- reserved identity columns:
name,email,avatarUrl,role,label properties: free-form JSONB for custom key-value metadata (strings, numbers, booleans, null; ≤50 keys, key ≤64 chars, string value ≤1024 chars)firstSeenAt,lastSeenAt: aggregate timestamps maintained by ingestion
How rows get populated:
- every event with an
actor.idoruserIdupserts a matchingTrackedUserrow - identity fields from the event's
actorobject overwrite the stored values propertiesfrom the event are a patch: sent keys are written, missing keys are kept, a key sent asnullis removed. The merge runs in oneUPDATE, so concurrent events for the same user keep each other's keys, and it is refused when the result would exceed 50 keys (the event is still stored, with awarningsentry in the response)- fields the event does not carry are left untouched
- the dashboard
PATCH /v1/dashboard/projects/:projectId/usersendpoint can also edit identity fields and properties directly, replacing the wholepropertiesobject. Subsequent events withactor.*overwrite identity fields and patch the properties they carry (there is no "lock" mechanism today)
Event.trackedUserId is the FK linking each event back to its tracked user. Event.userId (the external id) and Event.actor (the point-in-time snapshot) are still populated for historical audit.
Unattributed events are events with neither actor.id nor userId and no corresponding trackedUserId.
Sessions
Sessions represent multi-step flows that evolve over time.
Common examples:
- background jobs
- deploy flows
- AI agent runs
- build pipelines
Fields worth knowing:
status: starts as running-state and can becomecompleted,failed, orclosedprogress: integer 0 through 100currentStep: short text descriptionsummary: final operator-facing outcomettlSeconds: optional expected time budget
Rules And Notifications
Rules are configuration. Notifications are outcomes.
Relationship:
AlertRule -> Notification
Notification -> Event? / Session?A rule can be:
- global to the environment
- scoped to one channel
Rules store conditions as JSON, which gives flexibility while keeping the API explicit.
Reports
Reports are scheduled, persisted query definitions rather than ad hoc saved UI state.
Important fields:
schedule: currentlydailyorweeklymetric:count,sum, oraverageaggregateField: required forsumandaveragefilters: serialized JSON objectgroupBy: currently supports actor groupingchannelIds: one or more targeted channels
Indexing And Performance Notes
The schema already includes targeted indexes on common access patterns such as:
- event creation time per project and per channel
- session status and recency
- workspace and environment recency for rules and notifications
- user plus project access patterns
Recent additions include a partial event index optimized for project-level user drilldown:
Event(projectId, userId, createdAt DESC) WHERE userId IS NOT NULLEvent(trackedUserId, createdAt)for the tracked-user detail aggregationsTrackedUser(projectId, lastSeenAt DESC)for the users listTrackedUser(projectId, email)for email-based search
If query shapes change substantially, revisit the Prisma schema rather than assuming current indexes are enough.