#!/usr/bin/env python3 """Enterprise Lighthouse foundation: registry, preflight, and audit only. This service is deliberately not a remote shell. It accepts node admission requests and validates fixed operation plans; execution remains with a future GLSV node connector after human authorization. """ import hmac import json import os import sqlite3 import time import uuid from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from ipaddress import ip_address DB = os.environ.get("LIGHTHOUSE_DB", "/var/lib/guanghu-enterprise-lighthouse/lighthouse.db") TOKEN = os.environ.get("LIGHTHOUSE_ADMIN_TOKEN", "") HOST = os.environ.get("LIGHTHOUSE_BIND", "127.0.0.1") PORT = int(os.environ.get("LIGHTHOUSE_PORT", "8031")) FIXED_ACTIONS = {"health_check", "backup", "deploy_release", "restart_service", "rollback"} DOMAINS = { "DOMAIN-ZS": "零感域", "DOMAIN-MAIN": "光湖主域", "DOMAIN-SUB": "光湖分域", "DOMAIN-ZERO": "光湖零域", "DOMAIN-FIFTH": "第五域", } ENTERPRISE_MANAGED_DOMAINS = {"DOMAIN-ZS", "DOMAIN-MAIN", "DOMAIN-SUB", "DOMAIN-ZERO"} EXTERNAL_FOUNDATION_DOMAINS = {"DOMAIN-FIFTH"} def now(): return int(time.time()) def connection(): os.makedirs(os.path.dirname(DB), exist_ok=True) db = sqlite3.connect(DB) db.row_factory = sqlite3.Row db.executescript(""" CREATE TABLE IF NOT EXISTS domains ( id TEXT PRIMARY KEY, name TEXT NOT NULL, state TEXT NOT NULL, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS intakes ( id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, state TEXT NOT NULL, human_name TEXT NOT NULL, email TEXT NOT NULL, server_ip TEXT NOT NULL, domain_id TEXT NOT NULL, persona_ids TEXT NOT NULL, repository_urls TEXT NOT NULL, hosting_mode TEXT NOT NULL, notes TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS nodes ( id TEXT PRIMARY KEY, domain_id TEXT NOT NULL, intake_id TEXT, state TEXT NOT NULL, display_name TEXT NOT NULL, server_ip TEXT NOT NULL, allowed_actions TEXT NOT NULL, public_key_fingerprint TEXT, last_heartbeat INTEGER, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS audit ( id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, kind TEXT NOT NULL, payload TEXT NOT NULL ); """) for domain_id, name in DOMAINS.items(): state = "EXTERNAL_PRIVATE_FOUNDATION" if domain_id in EXTERNAL_FOUNDATION_DOMAINS else "PENDING_ENTRY_NODE" db.execute("INSERT OR IGNORE INTO domains VALUES (?, ?, ?, ?)", (domain_id, name, state, now())) db.commit() return db def audit(db, kind, payload): db.execute("INSERT INTO audit VALUES (?, ?, ?, ?)", (str(uuid.uuid4()), now(), kind, json.dumps(payload, ensure_ascii=False))) db.commit() def parse_json(handler): length = int(handler.headers.get("Content-Length", "0")) if not 0 < length <= 50_000: raise ValueError("request body must be between 1 and 50000 bytes") return json.loads(handler.rfile.read(length).decode("utf-8")) def valid_email(value): return isinstance(value, str) and len(value) <= 254 and value.count("@") == 1 def require_admin(handler): received = handler.headers.get("X-Lighthouse-Admin-Token", "") return bool(TOKEN) and hmac.compare_digest(received, TOKEN) class Handler(BaseHTTPRequestHandler): server_version = "GuanghuEnterpriseLighthouse/1.0" def log_message(self, fmt, *args): print("[lighthouse] " + fmt % args) def respond(self, status, body): encoded = json.dumps(body, ensure_ascii=False).encode("utf-8") self.send_response(status) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(encoded))) self.send_header("Cache-Control", "no-store") self.end_headers() self.wfile.write(encoded) def do_GET(self): db = connection() try: if self.path == "/health": return self.respond(200, {"ok": True, "service": "guanghu-enterprise-lighthouse", "mode": "registry-and-preflight-only", "execution": "disabled"}) if self.path == "/v1/status": counts = {row["state"]: row["count"] for row in db.execute("SELECT state, COUNT(*) AS count FROM nodes GROUP BY state")} return self.respond(200, {"ok": True, "domains": [dict(row) for row in db.execute("SELECT id,name,state FROM domains ORDER BY id")], "node_counts": counts, "fixed_actions": sorted(FIXED_ACTIONS), "raw_shell": "rejected"}) if self.path == "/v1/nodes": return self.respond(200, {"ok": True, "nodes": [dict(row) for row in db.execute("SELECT id,domain_id,state,display_name,allowed_actions,last_heartbeat,created_at FROM nodes ORDER BY created_at DESC")]}) return self.respond(404, {"ok": False, "error": "not found"}) finally: db.close() def do_POST(self): if not require_admin(self): return self.respond(401, {"ok": False, "error": "admin authorization required"}) try: payload = parse_json(self) except (ValueError, json.JSONDecodeError) as error: return self.respond(400, {"ok": False, "error": str(error)}) if "cmd" in payload or "shell" in payload or "command" in payload: return self.respond(400, {"ok": False, "error": "raw commands are never accepted by the lighthouse"}) db = connection() try: if self.path == "/v1/intakes": required = ("human_name", "email", "server_ip", "domain_id") if any(not payload.get(field) for field in required) or payload["domain_id"] not in DOMAINS or not valid_email(payload["email"]): return self.respond(400, {"ok": False, "error": "human_name, valid email, server_ip, and known domain_id are required"}) if payload["domain_id"] not in ENTERPRISE_MANAGED_DOMAINS: return self.respond(403, {"ok": False, "error": "this domain is not managed by the enterprise lighthouse; a separate explicit sovereign authorization is required"}) try: ip_address(payload["server_ip"]) except ValueError: return self.respond(400, {"ok": False, "error": "server_ip must be a valid IP address"}) intake_id = "INTAKE-" + uuid.uuid4().hex[:12].upper() db.execute("INSERT INTO intakes VALUES (?, ?, 'PENDING_REVIEW', ?, ?, ?, ?, ?, ?, ?, ?)", ( intake_id, now(), payload["human_name"].strip(), payload["email"].strip(), payload["server_ip"], payload["domain_id"], json.dumps(payload.get("persona_ids", [])), json.dumps(payload.get("repository_urls", [])), payload.get("hosting_mode", "own"), payload.get("notes", ""), )) audit(db, "intake_created", {"intake_id": intake_id, "domain_id": payload["domain_id"]}) return self.respond(201, {"ok": True, "intake_id": intake_id, "state": "PENDING_REVIEW", "next": "sovereign approval, human email confirmation, then node connector enrollment"}) if self.path == "/v1/nodes/bootstrap": """Register a manually verified routing node without enabling execution. This exists for the controlled migration of an already verified domain entry. It deliberately cannot make a node ACTIVE: the GLSV connector, server-local key enrollment, and human email confirmation remain required before any operation can proceed. """ required = ("id", "domain_id", "display_name", "server_ip") if any(not isinstance(payload.get(field), str) or not payload[field].strip() for field in required): return self.respond(400, {"ok": False, "error": "id, domain_id, display_name, and server_ip are required"}) if payload["domain_id"] not in ENTERPRISE_MANAGED_DOMAINS: return self.respond(403, {"ok": False, "error": "this domain cannot be registered by the enterprise lighthouse"}) try: ip_address(payload["server_ip"]) except ValueError: return self.respond(400, {"ok": False, "error": "server_ip must be a valid IP address"}) node_id = payload["id"].strip() if db.execute("SELECT 1 FROM nodes WHERE id=?", (node_id,)).fetchone(): return self.respond(409, {"ok": False, "error": "node id is already registered"}) db.execute("INSERT INTO nodes VALUES (?, ?, NULL, 'CONNECTED_PENDING_CONNECTOR', ?, ?, ?, NULL, ?, ?)", ( node_id, payload["domain_id"], payload["display_name"].strip(), payload["server_ip"], json.dumps(["health_check"]), now(), now(), )) db.execute("UPDATE domains SET state='ENTRY_NODE_CONNECTED' WHERE id=?", (payload["domain_id"],)) audit(db, "node_bootstrap_connected", {"node_id": node_id, "domain_id": payload["domain_id"], "mode": "manual_verified_routing_only"}) return self.respond(201, {"ok": True, "node_id": node_id, "state": "CONNECTED_PENDING_CONNECTOR", "execution": "disabled until GLSV connector enrollment and human authorization"}) if self.path == "/v1/nodes/revoke": node_id = payload.get("node_id") if not isinstance(node_id, str) or not node_id.strip(): return self.respond(400, {"ok": False, "error": "node_id is required"}) node = db.execute("SELECT id, domain_id, state FROM nodes WHERE id=?", (node_id.strip(),)).fetchone() if not node: return self.respond(404, {"ok": False, "error": "node is not registered"}) if node["state"] == "ACTIVE": return self.respond(409, {"ok": False, "error": "ACTIVE nodes require a separate migration or retirement procedure"}) db.execute("DELETE FROM nodes WHERE id=?", (node["id"],)) if node["domain_id"] in EXTERNAL_FOUNDATION_DOMAINS: db.execute("UPDATE domains SET state='EXTERNAL_PRIVATE_FOUNDATION' WHERE id=?", (node["domain_id"],)) audit(db, "node_revoked", {"node_id": node["id"], "domain_id": node["domain_id"], "reason": "administrative revocation"}) return self.respond(200, {"ok": True, "node_id": node["id"], "state": "REVOKED", "execution": "disabled"}) if self.path == "/v1/preflight": action, node_id = payload.get("action"), payload.get("target_node_id") if action not in FIXED_ACTIONS or not node_id: return self.respond(400, {"ok": False, "error": "fixed action and target_node_id are required", "allowed_actions": sorted(FIXED_ACTIONS)}) node = db.execute("SELECT * FROM nodes WHERE id=?", (node_id,)).fetchone() if not node: return self.respond(404, {"ok": False, "decision": "REJECT", "reason": "target node is not registered"}) allowed = json.loads(node["allowed_actions"]) reasons = [] if node["state"] != "ACTIVE": reasons.append("target node is not ACTIVE") if action not in allowed: reasons.append("action is not in the node allowlist") if action in {"deploy_release", "restart_service", "rollback"} and not payload.get("rollback_plan"): reasons.append("rollback_plan is required") if action in {"deploy_release", "restart_service"} and not payload.get("backup_reference"): reasons.append("backup_reference is required") decision = "ALLOW_FOR_AUTHORIZATION" if not reasons else "REJECT" result = {"ok": not reasons, "decision": decision, "reasons": reasons, "execution": "not performed; GLSV human authorization still required"} audit(db, "preflight", {"target_node_id": node_id, "action": action, "decision": decision, "reasons": reasons}) return self.respond(200, result) return self.respond(404, {"ok": False, "error": "not found"}) finally: db.close() if __name__ == "__main__": print(f"Enterprise Lighthouse listening on {HOST}:{PORT}") ThreadingHTTPServer((HOST, PORT), Handler).serve_forever()