Skip to content

Data Model

techdox edited this page Jul 14, 2026 · 2 revisions

Data Model

Trove stores its catalog in SQLite.

The schema is migration-driven. Migrations live in:

internal/store/migrations

The database is opened and migrated automatically by the server.

Main tables

Table Purpose
agents Known Trove agents, token hashes, platform/version, heartbeat state.
hosts Hosts reported by agents.
services Current and recently removed catalog entries.
events State, health, and agent transition history.
image_checks Registry freshness cache per image reference.
alert_state Observed/notified state per incident key.
alert_channel_deliveries Temporary per-channel success records while a fan-out retry is incomplete.
meta Internal key/value metadata such as migration version and alert cursor.

Migrations

Migration Description
0001_init.sql Base schema: agents, hosts, services, events, meta.
0002_image_checks.sql Adds image freshness cache.
0003_service_parent.sql Adds nullable services.parent_id for parent/child service relationships.
0004_event_model.sql Rebuilds events around kind, agent events, and denormalized display fields.
0005_alert_notified.sql Adds alert_state.notified so alert delivery state is separate from observed value.
0006_health_detail.sql Adds short human-readable health context to services.
0007_alert_channel_deliveries.sql Tracks successful channels so retries target only failed destinations.

Agent table

Agents store:

  • name
  • token hash
  • platform
  • version
  • report interval
  • created time
  • last seen time
  • last status

Plaintext tokens are never stored. The server stores SHA-256 hashes.

Host table

Hosts belong to agents.

A host is identified by:

  • agent ID
  • hostname

The host also stores platform metadata as JSON. Examples include Docker version or other platform facts that are useful to display but not worth dedicated columns.

Service table

Services belong to hosts.

A service is correlated by:

  • host ID
  • external ID

external_id is the platform-native stable identifier, such as a container ID, Kubernetes UID, VM ID, or unit name depending on the agent.

Important fields:

  • name
  • kind
  • image
  • image_digest
  • state
  • health
  • health_detail
  • ports_json
  • labels_json
  • parent_id
  • first_seen_at
  • last_seen_at
  • updated_at

Parent-child links

services.parent_id is nullable.

It is used for relationships such as:

Deployment -> Pod
StatefulSet -> Pod
DaemonSet -> Pod

The migration deliberately avoids a strict foreign key for this column. Parent links are resolved during ingest, and dangling references are simple to handle in application code.

Events table

Events are append-only transition records within retention.

Kinds:

  • state
  • health
  • agent

Events denormalize display fields such as service name, hostname, and agent name. This is deliberate. An event should remain readable even after the service row is later pruned.

Image checks table

image_checks is a cache, not core inventory.

It stores:

  • image reference
  • latest digest
  • status
  • last error
  • checked time
  • next check time

The ingest path does not call registries. Freshness checks run in the background.

Alert state table

alert_state tracks per-incident alert delivery state.

The key idea is that Trove stores two separate facts:

  1. what bad value is currently known
  2. whether a notification was actually sent for it

That second fact lives in notified.

This avoids dropping resolved alerts during reconnect or cooldown edge cases.

alert_channel_deliveries handles a separate fan-out problem. If one channel succeeds and another fails, Trove remembers the success and retries only the failed channel. Rows are temporary and disappear when every configured destination has accepted the notification.

Full-state ingest semantics

ApplyReport runs inside one SQLite transaction.

For each report:

  1. refresh agent heartbeat and metadata
  2. upsert the host
  3. load existing services for that host
  4. upsert reported services
  5. emit state and health events for changed values
  6. resolve parent links
  7. mark missing services as removed
  8. commit

Applying the same report twice should not create duplicate transition events.

Removed services

When a service disappears from a full-state report, Trove marks it:

state = "removed"
health = "unknown"

It records a state event once.

Removed services remain visible until maintenance pruning removes them after TROVE_REMOVED_RETENTION.

Clone this wiki locally