Database Schema
The database currently has 47 migrations in pkg/storage/migration/sql/. The server binary auto-runs pending migrations on startup.
Timestamps: All timestamp columns use
TIMESTAMPTZ(timestamp with time zone). Data is stored in UTC. Migration 035 standardized the 64 columns that previously usedTIMESTAMP(without timezone). The database-level timezone is set to UTC viaALTER DATABASE ... SET timezone = 'UTC'.
Scope Annotations: All sqlc queries carry
-- scope:annotations validated by CI (TestQueryScopeCoverage). These document the tenant isolation intent of each query (org, global, system, resource, join).
Tables
Core Tables
| Table | Description |
|---|---|
organizations | Multi-tenant boundary |
users | User accounts (including system users); is_approved gates beta access |
organization_members | Multi-org membership with role-based access |
organization_invites | Organization invite tokens and tracking |
api_keys | API authentication (Argon2id hashed) |
services | Service registry |
service_instances | Health check tracking |
settings | Organization-aware settings with global defaults |
casbin_rule | Casbin authorization policy storage (policy rules p, org-scoped role assignments g, global role assignments g2) |
role_metadata | Role display info (name, description, system flag) linked to casbin_rule via role_key; client_visible boolean controls visibility in Client API (superadmin is hidden) |
Job System Tables
| Table | Description |
|---|---|
gather_jobs | Upstream release fetch jobs (GitHub, Helm, OCI, endoflife.date); has labels JSONB column for categorization, sync_state JSONB column for incremental sync tracking, and suggest_global boolean for global pool contribution nominations |
scrape_jobs | Repository version scraping jobs |
helm_sync_jobs | Helm repository auto-sync jobs that create child gather jobs; has labels JSONB column for categorization and suggest_global boolean for global pool contribution nominations |
task_executions | Individual job run tracking with execution tokens |
agent_submission_logs | Debugging logs for agent task submissions |
Release & Version Tables
| Table | Description |
|---|---|
upstream_releases | Releases discovered by gather jobs |
version_snapshots | Versions extracted by scrape jobs |
Rules & Alerts Tables
| Table | Description |
|---|---|
monitoring_rules | Rule definitions |
alert_configs | Links scrape jobs + gather jobs + rules |
alerts | Generated alerts (supports soft delete via resolved_at) |
audit_trail | Polymorphic audit trail for entity state changes (alert actions, future: job actions) |
Notification System Tables
| Table | Description |
|---|---|
notification_channels | Webhook endpoints and channel config |
notification_rules | Severity/event routing rules |
notification_deliveries | Delivery tracking with retry logic |
notification_deliveries_archive | Archive for old deliveries |
notification_ack_tokens | Inbound acknowledgment tokens for webhooks |
EOL Sync Tables
| Table | Description |
|---|---|
eol_sync_status | endoflife.date sync progress tracking |
eol_products | Cached product list from endoflife.date; status enum (approved, denied, pending) controls sync eligibility. State transitions: pending → approved, pending → denied, denied → approved. approved is terminal (cannot be denied — denying would orphan gather jobs and releases) |
Feedback Table
| Table | Description |
|---|---|
user_feedback | User feedback collection (bug reports, feature requests) |
Separate Schema
| Schema.Table | Description |
|---|---|
earlyaccess.waitlist_requests | Early access waitlist entries (separate PostgreSQL schema) |
Key Enums
job_status: 'pending', 'queued', 'in_progress', 'completed', 'failed'
source_type: 'github_releases', 'helm_repository', 'endoflife_date', 'oci_registry'
parse_type: 'yq', 'jq', 'regex', 'manual'
rule_type: 'days_behind', 'majors_behind', 'minors_behind'
alert_severity: 'moderate', 'high', 'critical'
notification_channel_type: 'webhook', 'pagerduty', 'telegram', 'smtp'
notification_delivery_status: 'pending', 'in_progress', 'succeeded', 'failed', 'dead_letter'
org_role: 'owner', 'admin', 'member'
feedback_category: 'bug_report', 'feature_request', 'general', 'ui_ux'
audit_entity_type: 'alert'
audit_action_type: 'created', 'escalated', 'acknowledge', 'unacknowledge', 'resolve'
audit_source: 'api', 'ui', 'webhook', 'system'
eol_product_status: 'approved', 'denied', 'pending'
Warning: Only
webhookhas an actual sender implementation inpkg/notifications/webhook/. Thepagerduty,telegram, andsmtpchannel types exist in the database enum but have no sender implementation yet. They are schema placeholders for future development.
Global Seed Data
On first startup, the system creates:
-- Default organization for global release data
INSERT INTO organizations (name, description, is_active)
VALUES ('Global', 'Default Organization that houses all default data', TRUE);
-- System users
INSERT INTO users (organization_id, email, first_name, is_system, metadata)
VALUES
(1, 'system@planekeeper.com', 'Global System', TRUE, '{}'),
(1, 'agent@planekeeper.com', 'Global Agent', TRUE, '{}');
-- Service registry
INSERT INTO services (id, service_name) OVERRIDING SYSTEM VALUE VALUES
(1, 'server'),
(2, 'serveragent'),
(3, 'taskengine'),
(4, 'eolsync'),
(5, 'clientui'),
(6, 'internalui'),
(7, 'notifier'),
(8, 'clientagent');
Pre-Seeded Global Jobs (Migration 034)
Migration 034 seeds ~173 global gather/helm sync jobs covering common infrastructure software. These provide immediate upstream release data for new organizations.
| Category | Count | Description |
|---|---|---|
| Helm Sync Jobs | ~29 | Full repository syncs (ingress-nginx, cert-manager, Argo, etc.) |
| Individual Helm Charts | ~11 | Cherry-picked charts from large repos (Prometheus, Grafana stacks) |
| GitHub Releases | ~85 | Direct release tracking (Kubernetes, Terraform, Go, Node.js, etc.) |
| OCI Registry | ~34 | Container image tags (Alpine, PostgreSQL, Nginx, k8s components) |
All seeded jobs use organization_id = 0 (global scope), have staggered cron schedules across 1:00-7:38 AM UTC, and carry labels for categorization (e.g., {"category": "kubernetes", "ecosystem": "cncf"}).
The migration also:
- Adds
labels JSONB NOT NULL DEFAULT '{}'columns togather_jobsandhelm_sync_jobs - Migrates existing artifact names to unified host/path format (
github.com/owner/repo,endoflife.date/product) - Creates partial unique indexes per source type for global job deduplication
- Curates EOL products from ~431 to ~65 active products
Upstream Release Metadata
The upstream_releases.metadata JSONB column stores source-specific metadata per release:
| Source Type | Metadata Keys | Description |
|---|---|---|
github_releases | release_name, author, target_branch, assets_count | GitHub release metadata |
helm_repository | app_version, description, deprecated, kube_version, home, sources | Helm chart metadata |
oci_registry | digest, platforms | Container manifest metadata |
endoflife_date | cycle, eol, lts, support_end, latest_release_date, link, codename, extended_support | EOL lifecycle metadata |
Gather Job Sync State
The gather_jobs.sync_state JSONB column (added by migration 038) tracks incremental sync progress for GitHub gather jobs:
| Key | Type | Description |
|---|---|---|
full_sync_complete | boolean | Whether the last full sync fetched all releases without hitting the page limit |
releases_fetched | integer | Number of releases fetched in the last run |
last_full_sync_at | string (RFC3339) | Timestamp of the last full (non-incremental) sync |
The dispatcher uses this state to inject an _incremental_since hint into the agent’s source config, allowing the GitHub gatherer to stop pagination early when it reaches already-known releases. Sync state is reset when a gather job is updated or its releases are cleared.
Source Types (Gatherers)
The agent supports four data source types for upstream releases:
| Source Type | Implementation | Artifact Name Format | Description |
|---|---|---|---|
github_releases | pkg/gatherer/github.go | github.com/owner/repo | Fetches releases from GitHub repositories |
helm_repository | pkg/gatherer/helm.go | host/path/chart-name | Fetches chart versions from Helm repositories |
endoflife_date | pkg/gatherer/endoflife.go | endoflife.date/product | Fetches EOL data from endoflife.date API |
oci_registry | pkg/gatherer/oci.go | registry/image | Fetches tags from OCI container registries |
Version Extraction Methods
The agent supports three parsing strategies for scrape jobs, plus a manual entry mode:
| Type | Use Case | Example Expression |
|---|---|---|
| YQ | YAML files (Chart.yaml) | .version |
| JQ | JSON files (package.json) | .version |
| Regex | Any text file | version:\s*([\d.]+) |
| Manual | User-entered versions (no agent needed) | - (placeholder) |
Manual scrape jobs use placeholder values (repository_url="manual://", target_file="-", parse_expression="-") and are never assigned to agents. Versions are set via the POST /scrape-jobs/{id}/set-version API endpoint.
Rule Types
| Type | Description | Parameters |
|---|---|---|
days_behind | Alert if version is N days old | max_days |
majors_behind | Alert if N major versions behind | max_majors |
minors_behind | Alert if N minor versions behind (counts actual releases) | max_minors |
All rules support the stable_only parameter to ignore prereleases.