PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS crack_methods ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS crack_method_versions ( method_id TEXT NOT NULL, version INTEGER NOT NULL, definition_json TEXT NOT NULL, created_at TEXT NOT NULL, PRIMARY KEY (method_id, version), FOREIGN KEY (method_id) REFERENCES crack_methods(id), CHECK (version > 0) ); CREATE TABLE IF NOT EXISTS crack_clients ( id TEXT PRIMARY KEY, name TEXT NOT NULL, token_hash TEXT, status TEXT NOT NULL, created_at TEXT NOT NULL, last_seen_at TEXT, metadata_json TEXT, CHECK (status IN ('ACTIVE', 'DISABLED')) ); CREATE TABLE IF NOT EXISTS crack_jobs ( id TEXT PRIMARY KEY, method_id TEXT NOT NULL, method_version INTEGER NOT NULL, status TEXT NOT NULL, created_at TEXT NOT NULL, queued_at TEXT, started_at TEXT, completed_at TEXT, cancelled_at TEXT, imported_at TEXT, client_id TEXT, hash_file_name TEXT NOT NULL, hash_file_sha256 TEXT NOT NULL, hash_count INTEGER NOT NULL, step_count INTEGER NOT NULL, result_sha256 TEXT, error TEXT, FOREIGN KEY (method_id, method_version) REFERENCES crack_method_versions(method_id, version), FOREIGN KEY (client_id) REFERENCES crack_clients(id), CHECK ( status IN ( 'CREATED', 'QUEUED', 'RUNNING', 'PARTIAL', 'COMPLETED', 'FAILED', 'CANCELLED' ) ), CHECK (hash_count >= 0), CHECK (step_count > 0) ); CREATE TABLE IF NOT EXISTS crack_job_handshakes ( job_id TEXT NOT NULL, handshake_id INTEGER NOT NULL, hash22000 TEXT NOT NULL, access_point_id INTEGER, PRIMARY KEY (job_id, handshake_id), FOREIGN KEY (job_id) REFERENCES crack_jobs(id), FOREIGN KEY (handshake_id) REFERENCES handshakes(id), FOREIGN KEY (access_point_id) REFERENCES access_points(id) ); CREATE TABLE IF NOT EXISTS crack_job_steps ( job_id TEXT NOT NULL, step_no INTEGER NOT NULL, step_id TEXT NOT NULL, status TEXT NOT NULL, session_name TEXT NOT NULL, definition_json TEXT NOT NULL, started_at TEXT, completed_at TEXT, exit_code INTEGER, restore_seen INTEGER NOT NULL DEFAULT 0, error TEXT, PRIMARY KEY (job_id, step_no), UNIQUE (job_id, step_id), FOREIGN KEY (job_id) REFERENCES crack_jobs(id), CHECK (step_no > 0), CHECK ( status IN ( 'PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'SKIPPED' ) ), CHECK (restore_seen IN (0, 1)) ); CREATE TABLE IF NOT EXISTS crack_reservations ( id TEXT PRIMARY KEY, handshake_id INTEGER NOT NULL, method_id TEXT NOT NULL, method_version INTEGER NOT NULL, job_id TEXT NOT NULL, client_id TEXT, status TEXT NOT NULL, created_at TEXT NOT NULL, expires_at TEXT, released_at TEXT, FOREIGN KEY (handshake_id) REFERENCES handshakes(id), FOREIGN KEY (method_id, method_version) REFERENCES crack_method_versions(method_id, version), FOREIGN KEY (job_id) REFERENCES crack_jobs(id), FOREIGN KEY (client_id) REFERENCES crack_clients(id), CHECK ( status IN ( 'ACTIVE', 'RELEASED', 'EXPIRED', 'CANCELLED' ) ) ); CREATE TABLE IF NOT EXISTS crack_attempts ( handshake_id INTEGER NOT NULL, method_id TEXT NOT NULL, method_version INTEGER NOT NULL, job_id TEXT NOT NULL, status TEXT NOT NULL, completed_at TEXT, PRIMARY KEY (handshake_id, method_id, method_version), FOREIGN KEY (handshake_id) REFERENCES handshakes(id), FOREIGN KEY (method_id, method_version) REFERENCES crack_method_versions(method_id, version), FOREIGN KEY (job_id) REFERENCES crack_jobs(id), CHECK (status = 'COMPLETED') ); CREATE TABLE IF NOT EXISTS crack_results ( job_id TEXT PRIMARY KEY, status TEXT NOT NULL, result_json TEXT NOT NULL, imported_at TEXT NOT NULL, FOREIGN KEY (job_id) REFERENCES crack_jobs(id), CHECK ( status IN ( 'COMPLETED', 'PARTIAL', 'FAILED', 'CANCELLED' ) ) ); CREATE TABLE IF NOT EXISTS crack_audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TEXT NOT NULL, event_type TEXT NOT NULL, job_id TEXT, client_id TEXT, handshake_id INTEGER, method_id TEXT, method_version INTEGER, details_json TEXT, FOREIGN KEY (job_id) REFERENCES crack_jobs(id), FOREIGN KEY (client_id) REFERENCES crack_clients(id), FOREIGN KEY (handshake_id) REFERENCES handshakes(id), FOREIGN KEY (method_id, method_version) REFERENCES crack_method_versions(method_id, version) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_crack_reservations_active ON crack_reservations(handshake_id, method_id, method_version) WHERE status = 'ACTIVE'; CREATE INDEX IF NOT EXISTS idx_crack_method_versions_method ON crack_method_versions(method_id, version); CREATE INDEX IF NOT EXISTS idx_crack_jobs_status ON crack_jobs(status); CREATE INDEX IF NOT EXISTS idx_crack_jobs_method_status ON crack_jobs(method_id, method_version, status); CREATE INDEX IF NOT EXISTS idx_crack_jobs_client_status ON crack_jobs(client_id, status); CREATE INDEX IF NOT EXISTS idx_crack_job_handshakes_handshake ON crack_job_handshakes(handshake_id); CREATE INDEX IF NOT EXISTS idx_crack_job_handshakes_job ON crack_job_handshakes(job_id); CREATE INDEX IF NOT EXISTS idx_crack_job_steps_status ON crack_job_steps(job_id, status); CREATE INDEX IF NOT EXISTS idx_crack_reservations_handshake ON crack_reservations(handshake_id, method_id, method_version); CREATE INDEX IF NOT EXISTS idx_crack_reservations_job ON crack_reservations(job_id, status); CREATE INDEX IF NOT EXISTS idx_crack_reservations_client ON crack_reservations(client_id, status); CREATE INDEX IF NOT EXISTS idx_crack_attempts_method ON crack_attempts(method_id, method_version, status); CREATE INDEX IF NOT EXISTS idx_crack_attempts_job ON crack_attempts(job_id); CREATE INDEX IF NOT EXISTS idx_crack_attempts_handshake ON crack_attempts(handshake_id); CREATE INDEX IF NOT EXISTS idx_crack_audit_job ON crack_audit_log(job_id, created_at); CREATE INDEX IF NOT EXISTS idx_crack_audit_client ON crack_audit_log(client_id, created_at); CREATE INDEX IF NOT EXISTS idx_crack_audit_handshake ON crack_audit_log(handshake_id, created_at); CREATE INDEX IF NOT EXISTS idx_crack_audit_event ON crack_audit_log(event_type, created_at);