diff --git a/server-tools/enterprise-lighthouse/README.md b/server-tools/enterprise-lighthouse/README.md index 7001582..283f49e 100644 --- a/server-tools/enterprise-lighthouse/README.md +++ b/server-tools/enterprise-lighthouse/README.md @@ -32,6 +32,11 @@ human_steward_id `HUMAN_DECLARED_PERSONA_PENDING_REGISTRATION / BLOCKED_UNTIL_AWEN_SIGNED_APPROVAL`, 候选构建不得被标记为生产部署。 +如果 Awen 先以可核验的人类消息明确授权当前引导部署,可登记授权证据摘要,把当前部署标记 +为 `AUTHORIZED_FOR_CURRENT_BOOTSTRAP`;该授权只覆盖证据所指向的本次部署,不会自动生成 +Awen 的人格体、密钥或长期单写者租约。完成部署后,仍须由 Awen 自行登记技术执行人格体并 +接收后续逐次审批权。 + 第五域不受企业灯塔管辖。其人类根授权者是 `ICE-GL∞`(冰朔),责任人格体与技术执行主控 人格体是 `ICE-P-ZY001`(铸渊);协作者可以参与搭建,但第五域的部署、运维和服务器动作 必须回到冰朔—铸渊责任链逐次签字。 diff --git a/server-tools/enterprise-lighthouse/lighthouse.py b/server-tools/enterprise-lighthouse/lighthouse.py index 97c1d7e..b04cee0 100644 --- a/server-tools/enterprise-lighthouse/lighthouse.py +++ b/server-tools/enterprise-lighthouse/lighthouse.py @@ -264,8 +264,38 @@ def active_domain_steward(db, domain_id): ).fetchone() +def enterprise_technical_control(db): + controller = db.execute( + "SELECT * FROM technical_controllers WHERE scope_id='ENTERPRISE-FOUR-DOMAINS'" + ).fetchone() + authorized = bool( + controller + and controller["state"] == "AUTHORIZED_BOOTSTRAP_PERSONA_PENDING_REGISTRATION" + and controller["authorization_receipt"] + and controller["final_signature_receipt"] + ) + return { + "enterprise_human_controller": ENTERPRISE_TECHNICAL_CONTROLLER_NAME, + "enterprise_human_controller_id": controller["human_controller_id"] if controller else None, + "enterprise_persona_executor_id": controller["persona_executor_id"] if controller else None, + "state": ( + "AUTHORIZED_BOOTSTRAP_PERSONA_PENDING_REGISTRATION" + if authorized + else "HUMAN_DECLARED_PERSONA_PENDING_REGISTRATION" + ), + "final_deployment_signature_state": ( + "AWEN_MESSAGE_AUTHORIZATION_EVIDENCE_PRESENT" + if authorized + else "NOT_PRESENT" + ), + "authorization_receipt": controller["authorization_receipt"] if authorized else None, + "production_deployment": "AUTHORIZED_FOR_CURRENT_BOOTSTRAP" if authorized else "BLOCKED", + } + + def domain_statuses(db): result = [] + technical_control = enterprise_technical_control(db) for row in db.execute("SELECT id,name,state FROM domains ORDER BY id"): domain = dict(row) if row["id"] in ENTERPRISE_MANAGED_DOMAINS: @@ -277,11 +307,11 @@ def domain_statuses(db): "responsibility_state": "BOUND" if steward else "AWAITING_TEAM_ASSIGNMENT", "steward_state": "ACTIVE" if steward else "UNBOUND", "technical_controller_human_name": ENTERPRISE_TECHNICAL_CONTROLLER_NAME, - "technical_controller_human_id": None, - "technical_controller_persona_id": None, - "technical_control_state": "HUMAN_DECLARED_PERSONA_PENDING_REGISTRATION", - "final_deployment_signature_state": "NOT_PRESENT", - "deployment_state": "BLOCKED_UNTIL_AWEN_PERSONA_AND_SIGNED_APPROVAL", + "technical_controller_human_id": technical_control["enterprise_human_controller_id"], + "technical_controller_persona_id": technical_control["enterprise_persona_executor_id"], + "technical_control_state": technical_control["state"], + "final_deployment_signature_state": technical_control["final_deployment_signature_state"], + "deployment_state": technical_control["production_deployment"], "mutation_state": ( "PERSONA_STEWARD_ACTIVE" if steward @@ -354,14 +384,7 @@ class Handler(BaseHTTPRequestHandler): "node_id": "AW-GZ-001", "host_state": "ONLINE", "observed_at": now(), - "technical_control": { - "enterprise_human_controller": ENTERPRISE_TECHNICAL_CONTROLLER_NAME, - "enterprise_human_controller_id": None, - "enterprise_persona_executor_id": None, - "state": "HUMAN_DECLARED_PERSONA_PENDING_REGISTRATION", - "final_deployment_signature_state": "NOT_PRESENT", - "production_deployment": "BLOCKED", - }, + "technical_control": enterprise_technical_control(db), "domains": domain_statuses(db), "node_counts": counts, "fixed_actions": sorted(FIXED_ACTIONS), diff --git a/server-tools/enterprise-lighthouse/test_lighthouse.py b/server-tools/enterprise-lighthouse/test_lighthouse.py index c3f9a63..4b60546 100644 --- a/server-tools/enterprise-lighthouse/test_lighthouse.py +++ b/server-tools/enterprise-lighthouse/test_lighthouse.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import json import os +import sqlite3 import subprocess import sys import tempfile @@ -75,6 +76,7 @@ with tempfile.TemporaryDirectory() as temp: "enterprise_persona_executor_id": None, "state": "HUMAN_DECLARED_PERSONA_PENDING_REGISTRATION", "final_deployment_signature_state": "NOT_PRESENT", + "authorization_receipt": None, "production_deployment": "BLOCKED", } assert {domain["state"] for domain in status["domains"] if domain["id"] != "DOMAIN-FIFTH"} == { @@ -95,7 +97,7 @@ with tempfile.TemporaryDirectory() as temp: domain["deployment_state"] for domain in status["domains"] if domain["id"] != "DOMAIN-FIFTH" - } == {"BLOCKED_UNTIL_AWEN_PERSONA_AND_SIGNED_APPROVAL"} + } == {"BLOCKED"} assert all( domain["human_steward_id"] is None and domain["persona_steward_id"] is None for domain in status["domains"] @@ -109,6 +111,31 @@ with tempfile.TemporaryDirectory() as temp: assert fifth["final_deployment_signature_state"] == "REQUIRED_PER_CHANGE" assert status["node_counts"]["ACTIVE"] == 4 + with sqlite3.connect(env["LIGHTHOUSE_DB"]) as db: + db.execute( + """INSERT INTO technical_controllers + (scope_id, human_display_name, human_controller_id, persona_executor_id, + persona_public_key, runtime_node_id, authorization_receipt, + final_signature_receipt, state, registered_at) + VALUES (?, ?, NULL, NULL, NULL, NULL, ?, ?, + 'AUTHORIZED_BOOTSTRAP_PERSONA_PENDING_REGISTRATION', ?)""", + ( + "ENTERPRISE-FOUR-DOMAINS", + "Awen", + "sha256:test-awen-message", + "sha256:test-awen-message", + int(time.time()), + ), + ) + authorized = json.load(urllib.request.urlopen(BASE + "/v1/status")) + assert authorized["technical_control"]["production_deployment"] == "AUTHORIZED_FOR_CURRENT_BOOTSTRAP" + assert authorized["technical_control"]["authorization_receipt"] == "sha256:test-awen-message" + assert { + domain["deployment_state"] + for domain in authorized["domains"] + if domain["id"] != "DOMAIN-FIFTH" + } == {"AUTHORIZED_FOR_CURRENT_BOOTSTRAP"} + nodes = json.load(urllib.request.urlopen(BASE + "/v1/nodes"))["nodes"] hosted = {node["domain_id"]: node for node in nodes} assert set(hosted) == {"DOMAIN-MAIN", "DOMAIN-SUB", "DOMAIN-ZERO", "DOMAIN-ZS"}