Register the SQLite blob activation bridge as a server migration

The activation module described itself as a temporary compatibility
bridge but bypassed the structure the migrations strategy prescribes: no
dated migrations/ file, no src/migrations.rs registry entry, no
REMOVAL_DEADLINE, and no removal_deadline log field. The strategy doc's
removal checklist (grep REMOVAL_DEADLINE, explicit registry ordering)
would never have surfaced it, letting the bridge silently outlive its
window as a second, parallel migration mechanism in serve.rs.

The module now lives at migrations/2026082301_sqlite_blob_activation.rs,
is registered and re-exported through src/migrations.rs like the two
existing server migrations, carries a REMOVAL_DEADLINE eligibility floor
(removal still requires the evidence and explicit approval in the module
docs), and logs removal_deadline on every activation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Scott Werner 2026-08-23 13:23:05 -04:00
parent 5629dcd7d0
commit f71d077221
4 changed files with 12 additions and 3 deletions

View file

@ -20,6 +20,12 @@ use tracing::{debug, info, warn};
use crate::server::resource_sampler;
/// Earliest date this bridge becomes eligible for removal, assuming the first
/// production activation happens no earlier than this change ships. Removal
/// additionally requires the evidence and explicit approval described in the
/// module docs; the date alone never triggers deletion.
pub(crate) const REMOVAL_DEADLINE: &str = "2026-09-22";
const DISK_HEADROOM_BYTES: u64 = 64 * 1024 * 1024;
const BACKUP_SUFFIX: &str = ".pre-blob-activation.bak";
const STAGING_SUFFIX: &str = ".tmp";
@ -204,6 +210,7 @@ pub(crate) async fn activate_blob_storage(
passive_checkpoints = import.passive_checkpoints,
backup_required,
backup_path = ?retained_backup,
removal_deadline = REMOVAL_DEADLINE,
"Activated SQLite blob storage"
);
Ok(store)

View file

@ -14,7 +14,6 @@ pub mod auth;
reason = "Automation materializer test hooks and helpers are only referenced by selected targets."
)]
mod automation_materializer;
mod blob_activation;
mod canonical_host;
mod canonical_origin;
pub mod csp;

View file

@ -7,9 +7,12 @@ use fabro_vault::SecretStore;
mod legacy_vault_entries;
#[path = "../migrations/2026052501_optional_server_env_secrets_to_vault.rs"]
mod optional_server_env_secrets_to_vault;
#[path = "../migrations/2026082301_sqlite_blob_activation.rs"]
mod sqlite_blob_activation;
pub(crate) use legacy_vault_entries::REMOVAL_DEADLINE as LEGACY_VAULT_REMOVAL_DEADLINE;
pub(crate) use optional_server_env_secrets_to_vault::REMOVAL_DEADLINE as OPTIONAL_SERVER_ENV_SECRETS_REMOVAL_DEADLINE;
pub(crate) use sqlite_blob_activation::activate_blob_storage;
pub(crate) type LegacyVaultMigrationReport = legacy_vault_entries::LegacyVaultMigrationReport;
pub(crate) type OptionalServerEnvSecretsMigrationReport =

View file

@ -40,7 +40,7 @@ use crate::server::{
};
use crate::server_secrets::{ServerSecrets, process_env_snapshot};
use crate::startup::{migrate_startup_vault, resolve_startup, validate_startup_configuration};
use crate::{blob_activation, migrations, static_files};
use crate::{migrations, static_files};
pub const DEFAULT_TCP_PORT: u16 = 32276;
type EnvLookup = Arc<dyn Fn(&str) -> Option<String> + Send + Sync>;
@ -773,7 +773,7 @@ where
} else {
None
};
let store = blob_activation::activate_blob_storage(
let store = migrations::activate_blob_storage(
&database,
&sqlite_path,
object_store,