# backend/middleware/lockfile_guard.py # This middleware is the backend half of the Crime 2 causal chain. # Without this, the Python backend keeps serving requests even during lockdown. # JARVIS would still respond to API calls even with the lockfile present. # That means lockdown is cosmetic only. That is Crime 2. # # REGISTRATION REQUIREMENT: # This middleware MUST be registered in EVERY FastAPI/Flask app in this codebase. # Check every app's startup file. If this middleware is missing from any server, # that server violates Crime 2. No exceptions. import os import json from pathlib import Path from fastapi import Request from fastapi.responses import JSONResponse def get_lock_path(): data_dir = os.environ.get("JARVIS_APP_DATA_DIR") if not data_dir: return None return Path(data_dir) / "jarvis.lock" async def lockfile_middleware(request: Request, call_next): # Exempt the unlock endpoint — otherwise the user can never unlock if request.url.path == "/api/unlock": return await call_next(request) lock_path = get_lock_path() if lock_path and lock_path.exists(): try: lock_data = json.loads(lock_path.read_text()) except Exception: lock_data = {} return JSONResponse( status_code=423, content={ "error": "SYSTEM_LOCKED", "reason": "Unauthorized device detected. Authenticate to resume.", "device_serial": lock_data.get("device_serial", "unknown"), "locked_at": lock_data.get("timestamp", 0), } ) return await call_next(request) # ── REGISTRATION TEMPLATE ──────────────────────────────────────────────────── # Copy this into every FastAPI app's main startup file: # # from backend.middleware.lockfile_guard import lockfile_middleware # app = FastAPI() # app.middleware("http")(lockfile_middleware) # # For Flask apps: # from backend.middleware.lockfile_guard import flask_lockfile_guard # app.before_request(flask_lockfile_guard) # ───────────────────────────────────────────────────────────────────────────── def flask_lockfile_guard(): from flask import request, jsonify if request.path == "/api/unlock": return None lock_path = get_lock_path() if lock_path and lock_path.exists(): try: lock_data = json.loads(lock_path.read_text()) except Exception: lock_data = {} return jsonify({ "error": "SYSTEM_LOCKED", "reason": "Unauthorized device detected. Authenticate to resume.", "device_serial": lock_data.get("device_serial", "unknown"), "locked_at": lock_data.get("timestamp", 0), }), 423