Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 62 additions & 16 deletions crates/corpus-core/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
//! Each migration filename must have a unique numeric prefix because SQLx
//! persists that prefix as the primary-keyed migration version.
//!
//! SQLx also records a SHA-384 checksum for each applied migration, so
//! applied files are immutable; add a new migration instead of editing one.
//!
//! Call [`migrate`] once at process start before serving traffic.

use crate::error::Result;
Expand Down Expand Up @@ -85,7 +88,12 @@ where
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
use sha2::{Digest, Sha384};
use std::{
fs,
path::Path,
sync::atomic::{AtomicU32, Ordering},
};

#[tokio::test]
async fn startup_retry_survives_a_slow_database_proxy() {
Expand All @@ -110,6 +118,59 @@ mod tests {
assert_eq!(observed_attempts.load(Ordering::Relaxed), 7);
}

#[test]
fn applied_migration_checksums_are_immutable() {
const APPLIED_MIGRATIONS: &[(&str, &str)] = &[
(
"0001_init.sql",
"e2dd15450914e699dfc1523c96a94e4d63dffa1a4b9bc4dfb5f6d253cfdd6a0695c7f9466be74ca36f613570a765d2e9",
),
(
"0002_agents.sql",
"b63e38b1816f2279767f059e8b5bbcbe0b1e4aa34a08b07e00884626ea85492baf6e3ea3599e643930fdae2d30d68d7a",
),
(
"0003_similarity.sql",
"9afa81e8f30af8158e959a4d5d6bfdc9d67a0337826cd4f0651ed3fca250baf81573a00f9ce6d1d57f89a3a844dccebe",
),
(
"0004_bootstrap.sql",
"ccdb12d8a0e28d2e66f5a311760bb10d96c7f66ada4a71c62c9d5642802f5a10d57028e0da672e16ad49d7ae226104de",
),
(
"0005_analyst.sql",
"68c9f52ceed1cf6d93b287054b3ecd396da6a522499fb2def326462ade364bf300ed21a9af88aebebe038ccd695a4b1c",
),
(
"0006_semantic.sql",
"7020f4223c4d165881f738e4656855cb534360a86841540d8ae6f69dcba1f63160c4dfbac7c6ed489e4254695cc5295c",
),
(
"0007_detonation.sql",
"8b5f867b081243df98cce5b6f857d35d246cfdd4cbbe55614048cfabc507b8e922ef125b4a306ac34582ccb3110cf6e9",
),
(
"0008_lsh_and_hunt_queue.sql",
"261485c56c6eb617d70c57fab1c99341ecc16bb70b0fab364209d8fbaf890c16e0df1e6b10c425ac26993f2786fb4e2e",
),
(
"0009_continuous_investigate.sql",
"5b3861bc0eb81eadb3c9d7932e5e216365f151873f3b4fe6c96e18a62e741ccc1452b995d2cd962bfaf9130fa107f7f0",
),
(
"0010_receipts_and_cleanup.sql",
"bfb3ac668520b6893a5d0b49d73112c1fe8c705d59585ef9f518d250c6998686c108476438c9f8264552540e0e444573",
),
];

let migrations_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../migrations");
for (name, expected) in APPLIED_MIGRATIONS {
let bytes = fs::read(migrations_dir.join(name)).expect("read applied migration");
let actual = hex::encode(Sha384::digest(&bytes));
assert_eq!(actual, *expected, "applied migration {name} was modified");
}
}

#[test]
fn migration_versions_are_unique() {
use std::{collections::BTreeMap, fs, path::Path};
Expand Down Expand Up @@ -139,19 +200,4 @@ mod tests {
);
}
}

#[test]
fn applied_agent_migration_checksum_is_immutable() {
use sha2::{Digest, Sha256};
use std::{fs, path::Path};

let migration =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../migrations/0002_agents.sql");
let checksum = hex::encode(Sha256::digest(fs::read(migration).expect("read migration")));

assert_eq!(
checksum, "45f16bac4c5d1021f7ed9636ebfa203767b828807bbaf53b7f6f53d2aeebb8d1",
"migration 0002 is already applied in production and must not be edited"
);
}
}
5 changes: 3 additions & 2 deletions migrations/0002_agents.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
-- Milestone 1: agent enrollment, identity, and health (spec 10.1, 10.11).
-- Tenant-scoped like every other data table (0001 tenant registry).

-- One-time enrollment tokens minted by operators via corpusctl.
CREATE TABLE enrollment_token (
token_sha256 bytea PRIMARY KEY,
tenant_id uuid NOT NULL,
tenant_id uuid NOT NULL REFERENCES tenant (id),
label text NOT NULL DEFAULT '',
created_at timestamptz NOT NULL,
expires_at timestamptz,
Expand All @@ -15,7 +16,7 @@ CREATE TABLE enrollment_token (
-- plaintext token is shown exactly once at enrollment.
CREATE TABLE agent (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
tenant_id uuid NOT NULL REFERENCES tenant (id),
host_name text NOT NULL,
token_sha256 bytea NOT NULL,
version text NOT NULL,
Expand Down
2 changes: 0 additions & 2 deletions migrations/0003_similarity.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
-- Milestone 3a: similarity features, typed edges, variant groups (spec 16).
--
-- Similarity features, typed edges, and variant groups (byte-level M3a).

-- Versioned features per artifact. family: exact | normalized | byte |
-- structural | semantic (plugin slot, unpopulated in M3a) | provenance.
Expand Down
2 changes: 0 additions & 2 deletions migrations/0004_bootstrap.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
-- Milestone 4 (vault bootstrap): snapshot backfill, OCI ingestion,
-- intel-corpus connectors.
--
-- Bootstrap helpers and seed data beyond the default tenant.

-- Artifact scope separates endpoint-collected bytes from intel imports.
-- Default queries (retro hunts, blast radius occurrence views) cover
Expand Down
2 changes: 0 additions & 2 deletions migrations/0005_analyst.sql
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
-- Milestone 5: analyst surface — opinions, triggers, audit.
-- (Prevalence and dropper hunts are pure SQL over the occurrence ledger;
-- proof-of-absence is computed, not stored.)
--
-- Analyst surface tables: opinions, prevalence support, rarity indexes.

-- Human verdicts, separate from analyzer scores (spec 5.5). Append-only;
-- the current opinion for an artifact is the latest row.
Expand Down
2 changes: 0 additions & 2 deletions migrations/0006_semantic.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
-- Milestone M8: per-function semantic signatures (spec 16.2/16.5).
--
-- Per-function signatures for semantic similarity (x86-64).

CREATE TABLE similarity_function (
tenant_id uuid NOT NULL REFERENCES tenant (id),
Expand Down
2 changes: 0 additions & 2 deletions migrations/0007_detonation.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
-- Milestone M10: detonation findings (spec 13.4, 17.4).
--
-- External sandbox job records and behavioral result storage.

-- Analyzer runs (spec 12.2 sketch; the table was deferred from the M0
-- subset until the first producer — detonation — needed it).
Expand Down
2 changes: 0 additions & 2 deletions migrations/0008_lsh_and_hunt_queue.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
-- Each artifact contributes fixed band keys derived from its ssdeep digest;
-- candidate queries join on (tenant_id, band_idx, band_key) instead of a
-- full per-class table scan.
--
-- LSH band index for byte-similar candidates; hunt worker queue.

CREATE TABLE IF NOT EXISTS similarity_lsh_band (
tenant_id uuid NOT NULL REFERENCES tenant (id),
Expand Down
2 changes: 0 additions & 2 deletions migrations/0009_continuous_investigate.sql
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
-- Continuous re-analysis tracking, autonomous detection events, and
-- investigation scaffolding for the continuous re-analysis product loop:
-- retain → detect → re-hunt history → blast radius → recommended actions.
--
-- Continuous re-analysis work items and investigation snapshots.

CREATE TABLE IF NOT EXISTS detection_event (
id uuid PRIMARY KEY,
Expand Down
Loading