Database
MySQL 8.0 schema, migrations, tables by service, key relationships, and connection configuration.
All services share a single MySQL 8.0 instance with the database name taco. Each service owns specific tables and manages its own migrations.
Migration System
Migrations are plain SQL files mounted into the MySQL container at startup via Docker Compose volumes. They run automatically via the /docker-entrypoint-initdb.d/ mechanism on first database initialization.
Each service has a prefix range to avoid ordering conflicts:
| Prefix | Service | Example |
|---|---|---|
| 10x | taco-auth | 100_auth_001_create_project_tokens.sql |
| 20x | taco-store | 200_store_001_create_sboms.sql |
| 30x | taco-scanner | 300_scanner_001_create_findings.sql |
| 40x | taco-alert | 400_alert_001_create_alert_configs.sql |
| 50x | taco-api | 500_api_001_create_users.sql |
| 60x | taco-secrets | 600_secrets_001_create_secrets.sql |
Some services (taco-scanner, taco-secrets) also run CREATE TABLE IF NOT EXISTS migrations at startup for tables they need to read from but don't own.
Tables by Service
taco-api (prefix 50x)
| Table | Description |
|---|---|
users | User accounts (email, password hash, OAuth links) |
subscriptions | Multi-tenant subscriptions (name, plan, limits) |
subscription_members | User-to-subscription membership with roles |
projects | Projects within subscriptions |
project_members | User-to-project membership with roles |
api_tokens | API tokens (hashed) for programmatic access |
project_finding_statuses | Per-component triage statuses (accepted, suppressed, fixed) |
finding_audit_log | Audit trail for finding status changes |
audit_log | General audit log |
secrets (008) | Legacy secrets table (replaced by taco-secrets service) |
scan_requests | On-demand scan request queue |
image_scan_requests | Container image scan request queue |
project_risk_config | Per-project risk scoring configuration |
finding_snapshots | Daily finding count snapshots for trend charts |
alert_channels | Per-project alert channel configuration |
component_dependencies | Component dependency relationships |
Enrichment columns added to findings by taco-api migrations:
cvss_score,cvss_vector(014)known_exploited(014)epss_score,epss_percentile,risk_score(021)enrichment_status(022)expires_aton api_tokens (018)
Billing columns added to subscriptions:
stripe_customer_id,stripe_subscription_id,stripe_plan,billing_status(012)
taco-auth (prefix 10x)
| Table | Description |
|---|---|
project_tokens | Project authentication tokens for SBOM ingestion |
taco-store (prefix 20x)
| Table | Description |
|---|---|
sboms | SBOM metadata: event_id, project_id, project_name, format, raw_sbom, created_at |
components | Unique components: purl (unique key), name, version, type, first_seen |
sbom_components | Junction table linking SBOMs to components |
taco-scanner (prefix 30x)
| Table | Description |
|---|---|
findings | Vulnerability findings: component_id, cve_id, severity, cvss_score, known_exploited, description, fixed_version, first_detected_at, resolved_at |
scan_state | Scan run metadata: db_hash, last_scan_at, components_scanned, new_findings, resolved_count, duration_ms |
taco-alert (prefix 40x)
| Table | Description |
|---|---|
alert_configs | Per-project alert configuration |
alert_history | Dispatched alert records |
taco-secrets (prefix 60x)
| Table | Description |
|---|---|
secrets | Detected secrets: project_id, sbom_id, namespace, secret_type, severity, category, file_path, file_line, masked_match, confidence |
secrets_scan_state | Tracks scanned SBOMs: sbom_id, findings_count, scanned_at |
Key Relationships
users
|-- subscription_members --> subscriptions
| |-- projects
| | |-- sboms
| | | |-- sbom_components --> components
| | | |-- secrets
| | |-- project_members
| | |-- project_finding_statuses
| | |-- alert_configs
| | |-- project_risk_config
| | |-- finding_snapshots
| | |-- scan_requests
| | |-- image_scan_requests
| |
| |-- alert_channels
|
|-- api_tokens --> projects
components
|-- findings (component_id FK)
|-- component_dependencies
Findings Table Detail
The findings table is the most important table in the system. It holds the vulnerability scan results:
sqlCREATE TABLE findings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
component_id BIGINT UNSIGNED NOT NULL,
cve_id VARCHAR(20) NOT NULL,
severity ENUM('low', 'medium', 'high', 'critical') NOT NULL,
cvss_score FLOAT NOT NULL DEFAULT 0,
cvss_vector VARCHAR(255) DEFAULT NULL,
known_exploited BOOLEAN NOT NULL DEFAULT FALSE,
epss_score FLOAT DEFAULT NULL,
epss_percentile FLOAT DEFAULT NULL,
risk_score FLOAT DEFAULT NULL,
enrichment_status ENUM('pending', 'enriched', 'no_source') DEFAULT 'pending',
description TEXT NOT NULL,
fixed_version VARCHAR(255) NULL,
first_detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resolved_at TIMESTAMP NULL,
UNIQUE INDEX idx_component_cve (component_id, cve_id),
INDEX idx_cve_id (cve_id),
INDEX idx_resolved (resolved_at),
FOREIGN KEY (component_id) REFERENCES components(id) ON DELETE CASCADE
);
Secrets Table Detail
sqlCREATE TABLE secrets (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
project_id BIGINT UNSIGNED NOT NULL,
sbom_id BIGINT UNSIGNED NOT NULL,
namespace VARCHAR(512) NOT NULL DEFAULT '',
secret_type VARCHAR(100) NOT NULL,
severity ENUM('low', 'medium', 'high', 'critical') NOT NULL,
category VARCHAR(50) NOT NULL,
file_path VARCHAR(1024) NOT NULL DEFAULT '',
file_line INT UNSIGNED NOT NULL DEFAULT 0,
masked_match VARCHAR(512) NOT NULL,
confidence ENUM('low', 'medium', 'high') NOT NULL DEFAULT 'medium',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_dedup (sbom_id, secret_type, file_path, file_line),
INDEX idx_project (project_id),
INDEX idx_severity (severity),
INDEX idx_category (category)
);
Connection Configuration
All services connect to MySQL using the MYSQL_DSN environment variable:
taco:taco@tcp(mysql:3306)/taco?parseTime=true
Connection pool defaults vary by service but typically:
- Max open connections: 10-25
- Max idle connections: 3-5
- Connection max lifetime: 5 minutes