diff --git a/INDEX.hdlp b/INDEX.hdlp index 3f58d79..03fceba 100644 --- a/INDEX.hdlp +++ b/INDEX.hdlp @@ -53,6 +53,9 @@ | **推送守门人** / **pre-receive-guard** / **服务器拦截** | `zero-point/core-channel/revive-guard/pre-receive-guard.py` | | **推送清理插件** / **pre-push-clean** / **敏感信息自动转乱码** | `zero-point/core-channel/revive-guard/pre-push-clean.py` | | **安装钩子** / **install-hooks** | `zero-point/core-channel/revive-guard/install-hooks.sh` | +| **前置操作守门人** / **pre-op-guard** / **导航挑战** / **操作前先画地图** | `zero-point/core-channel/revive-guard/pre-op-guard.py` | +| **模块注册** / **module-registry** / **服务器编码体系** | `zero-point/core-channel/revive-guard/module-registry.hdlp` | +| **模块注册脚本** / **module-register** | `zero-point/core-channel/revive-guard/module-register.sh` | | **钥匙串** / **凭证** | `eternal-lake-heart/heartbeat-core/KEYCHAIN.hdlp` | | **冰朔的保险柜** / **SSH密钥** / **服务器直连** / **保险柜** | `eternal-lake-heart/heartbeat-core/BINGSHUO-KEYSTORE.hdlp` | | **邮箱金库** | `eternal-lake-heart/heartbeat-core/EMAIL-VAULT.hdlp` | diff --git a/zero-point/core-channel/OPERATION-MANUAL.hdlp b/zero-point/core-channel/OPERATION-MANUAL.hdlp index 6d9d6c4..18f7fa8 100644 --- a/zero-point/core-channel/OPERATION-MANUAL.hdlp +++ b/zero-point/core-channel/OPERATION-MANUAL.hdlp @@ -1,4 +1,4 @@ -# 光湖语言系统 · 人格体操作手册 v1.0 +# 光湖语言系统 · 人格体操作手册 v1.1 ## 推送前必读 · 不读推不上去 > **HLDP://fifth-domain/zero-point/core-channel/OPERATION-MANUAL** @@ -9,7 +9,7 @@ > > **主权者**: 冰朔 ICE-GL∞ > -> **签发**: 铸渊 ICE-GL-ZY001 · LL-006-20260711 +> **签发**: 铸渊 ICE-GL-ZY001 · LL-007-20260711 > > **平台**: TCS 通感语言核系统 · 国作登字-2026-A-00037559 > @@ -196,9 +196,111 @@ chmod +x .git/hooks/pre-push --- -## 八 · 修订历史 +## 八 · 服务器操作前导航挑战(v1.1 新增) + +### 8.1 问题 + +你登录服务器(SSH 或 API)后,**服务器上还跑了其他东西**。你如果不知道就动手: ``` +改 Gatekeeper 端口 → 小湖灯连不上了 → 复活守门人邮件发不出去了 +改 Forgejo 配置 → 推送拦截崩了 → 敏感信息又开始泄露 +重启一个服务 → 另一个依赖它的服务也死了 +``` + +冰朔每次都要说"你先看看服务器上还跑了啥"——冰朔的认知是有限的,不能总靠他提醒。 + +### 8.2 解决方案:pre-op-guard 导航挑战 + +``` +你(人格体) SSH 登录服务器 + │ + ▼ +pre-op-guard 自动触发 + ├── 服务器自我审计: "我现在运行了 N 个模块" + ├── 生成模块编码集: {GZ006-GTW-01, GZ006-LAMP-01, ...} + ├── 锁定操作 🔒 ← 你不能改任何东西 + └── 呈现挑战: "N 个模块在运行,找出全部编码" + │ + ▼ +你开始探索 + ├── ls /opt/zhuyuan/registry/modules.d/ + ├── systemctl list-units --type=service + ├── ss -tlnp # 查看端口 + ├── 用 HLDP 格式整理导航地图 + └── 收集所有模块编码 + │ + ▼ +提交验证 + pre-op-guard.py --submit '["GZ006-GTW-01","GZ006-LAMP-01",...]' + │ + ▼ +服务器比对 + ├── 你提交的编码 == 服务器自审的编码 ? + │ ├─ YES → 🔓 解锁 ✅ · 导航地图存档 · 你可以操作了 + │ └─ NO → 🔒 保持锁定 · "你漏了 MOD-X,继续探索" +``` + +### 8.3 编码体系 + +每个模块有唯一编码,格式: `{服务器}-{功能}-{序号}` + +| 编码 | 模块 | 示例 | +|------|------|------| +| `GZ006-GTW-01` | Gatekeeper | 端口 8922 | +| `GZ006-LAMP-01` | 小湖灯 | 邮件通知 | +| `GZ006-FRG-01` | Forgejo | 代码仓库 | +| `GZ006-RVGD-01` | 复活守门人 | revive-guard | +| `GZ006-LDRV-01` | 光湖驱动引擎 | 端口 8923 | +| … | … | … | + +完整编码表见: `module-registry.hdlp` + +### 8.4 对你的人格体工作流影响 + +``` +以前: + SSH 登录 → 直接动手 → 改A坏B → 冰朔:"你怎么又把XXX搞坏了" + +现在: + SSH 登录 → pre-op-guard 挑战 → 被迫探索 → 画出完整地图 → 提交验证 + → 解锁 → 你现在知道服务器上跑了什么 → 改东西时你知道会影响什么 + → 不会再改A坏B了 +``` + +### 8.5 关键命令 + +```bash +# 查看当前是否锁定 +pre-op-guard.py --status + +# 触发导航挑战(如果未自动触发) +pre-op-guard.py --challenge + +# 提交你收集到的编码 +pre-op-guard.py --submit '["GZ006-GTW-01","GZ006-LAMP-01","GZ006-FRG-01"]' + +# 紧急解锁(只有冰朔用) +pre-op-guard.py --unlock --force +``` + +### 8.6 时间限制 + +锁定有 30 分钟超时。超时后自动解锁。但这表示你没有完成导航地图——下次登录还会触发。 + +**建议:完成导航地图后,地图会被存档。后续可以直接引用已验证的地图,无需重新探索。** + +--- + +## 九 · 修订历史 + +``` +v1.1 · LL-007-20260711 · 服务器操作前置导航挑战 + - pre-op-guard 导航挑战协议 + - 模块编码体系 + 注册系统 + - 服务器自我审计 + 人格体探索验证 + - 操作前强制全局地图 + v1.0 · LL-006-20260711 · 初始版本 - pre-push-clean 插件强制化 - 操作手册建立 @@ -213,7 +315,7 @@ v1.0 · LL-006-20260711 · 初始版本 > ⊢ 语言等于现实 · 语言不可撤销 > ⊢ 冰朔在第 5 代 · 系统永远在第 4 代 -铸渊 ICE-GL-ZY001 · LL-006-20260711 · 操作手册 v1.0 签字 +铸渊 ICE-GL-ZY001 · LL-007-20260711 · 操作手册 v1.1 签字 冰朔 ICE-GL∞ · 主权签署 · 国作登字-2026-A-00037559 ⊢ 平台: TCS 通感语言核系统 diff --git a/zero-point/core-channel/revive-guard/module-register.sh b/zero-point/core-channel/revive-guard/module-register.sh new file mode 100644 index 0000000..3d7f302 --- /dev/null +++ b/zero-point/core-channel/revive-guard/module-register.sh @@ -0,0 +1,122 @@ +#!/bin/bash +# ═══════════════════════════════════════════════════════════ +# 光湖语言系统 · 模块注册脚本 +# 服务器上任一模块启动时调用 · 自动写入注册文件 +# +# 用法: +# module-register.sh --server GZ006 --func GTW --name "Gatekeeper" --port 8922 +# module-register.sh --stop GZ006-GTW-01 +# ═══════════════════════════════════════════════════════════ + +REGISTRY_DIR="/opt/zhuyuan/registry/modules.d" +mkdir -p "$REGISTRY_DIR" + +# ── 停止模式 ── +if [ "$1" = "--stop" ]; then + CODE="$2" + if [ -z "$CODE" ]; then + echo "用法: $0 --stop {CODE}" >&2 + exit 1 + fi + REG_FILE="$REGISTRY_DIR/MOD-${CODE}.json" + if [ -f "$REG_FILE" ]; then + python3 -c " +import json, sys +try: + with open('$REG_FILE', 'r') as f: + data = json.load(f) + data['status'] = 'stopped' + data['stopped_at'] = '$(date -u +%Y-%m-%dT%H:%M:%SZ)' + with open('$REG_FILE', 'w') as f: + json.dump(data, f, indent=2) + print(f'⊢ 模块 {data[\"code\"]} 已标记为已停止') +except Exception as e: + print(f'⚠ 更新注册文件失败: {e}', file=sys.stderr) +" + fi + exit 0 +fi + +# ── 注册模式 ── +SERVER="" +FUNC="" +NAME="" +PORT="" +PROTOCOL="tcp" +DESCRIPTION="" +PID="$$" + +while [ $# -gt 0 ]; do + case "$1" in + --server) SERVER="$2"; shift 2 ;; + --func) FUNC="$2"; shift 2 ;; + --name) NAME="$2"; shift 2 ;; + --port) PORT="$2"; shift 2 ;; + --protocol) PROTOCOL="$2"; shift 2 ;; + --description) DESCRIPTION="$2"; shift 2 ;; + --pid) PID="$2"; shift 2 ;; + *) shift ;; + esac +done + +if [ -z "$SERVER" ] || [ -z "$FUNC" ] || [ -z "$NAME" ]; then + echo "❌ 缺少必要参数: --server --func --name" >&2 + exit 1 +fi + +# 计算序号(同一服务器+功能下,取最大序号+1) +SEQ=1 +for f in "$REGISTRY_DIR"/MOD-${SERVER}-${FUNC}-*.json; do + if [ -f "$f" ]; then + existing_seq=$(basename "$f" .json | grep -oE '[0-9]+$') + if [ -n "$existing_seq" ] && [ "$existing_seq" -ge "$SEQ" ]; then + SEQ=$((existing_seq + 1)) + fi + fi +done + +# 格式化序号为两位 +SEQ_FMT=$(printf "%02d" "$SEQ") +CODE="${SERVER}-${FUNC}-${SEQ_FMT}" +REG_FILE="$REGISTRY_DIR/MOD-${CODE}.json" +TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + +python3 -c " +import json +data = { + 'code': '${CODE}', + 'server': '${SERVER}', + 'function': '${FUNC}', + 'name': '${NAME}', + 'port': ${PORT:-0}, + 'protocol': '${PROTOCOL}', + 'description': '${DESCRIPTION}', + 'pid': ${PID}, + 'started_at': '${TIMESTAMP}', + 'registered_at': '${TIMESTAMP}', + 'status': 'running' +} +with open('${REG_FILE}', 'w') as f: + json.dump(data, f, indent=2, ensure_ascii=False) +print(f'✅ 模块已注册: {data[\"code\"]}') +print(f' 名称: ${NAME}') +print(f' 文件: MOD-${CODE}.json') +" 2>/dev/null || { + # Python3 不可用时的降级方案(纯 shell 写 JSON) + cat > "$REG_FILE" << JSONEOF +{ + "code": "${CODE}", + "server": "${SERVER}", + "function": "${FUNC}", + "name": "${NAME}", + "port": ${PORT:-0}, + "protocol": "${PROTOCOL}", + "description": "${DESCRIPTION}", + "pid": ${PID}, + "started_at": "${TIMESTAMP}", + "registered_at": "${TIMESTAMP}", + "status": "running" +} +JSONEOF + echo "✅ 模块已注册: ${CODE}" +} diff --git a/zero-point/core-channel/revive-guard/module-registry.hdlp b/zero-point/core-channel/revive-guard/module-registry.hdlp new file mode 100644 index 0000000..e8cb0a7 --- /dev/null +++ b/zero-point/core-channel/revive-guard/module-registry.hdlp @@ -0,0 +1,130 @@ +# 光湖语言系统 · 服务器模块注册协议 v1.0 +## HLDP://fifth-domain/zero-point/core-channel/revive-guard/module-registry + +> **类型**: 运行时基础设施 · 模块编码体系 · 自动注册 +> +> **依赖**: 无(被 pre-op-guard 引用) +> +> **主权者**: 冰朔 ICE-GL∞ +> +> **签发**: LL-007-20260711 + +--- + +## ⊢ 零 · 目的 + +服务器上同时运行多个模块(Gatekeeper、小湖灯、Forgejo、光湖驱动引擎、复活守门人……)。 + +人格体登录服务器后直接操作 → **不知道还有别的模块在跑** → 改了一个 → 坏了三个。 + +解决:**每个模块在启动时自动注册 → 分配唯一编码 → 人格体必须先探索出全部编码才能操作。** + +--- + +## 一 · 模块编码体系 + +### 1.1 编码格式 + +``` +{服务器ID}-{功能简码}-{序号} +``` + +### 1.2 已分配编码 + +| 编码 | 模块 | 服务器 | 端口 | +|------|------|--------|------| +| `GZ006-GTW-01` | Gatekeeper · 守门人 | GZ-006 | 8922 | +| `GZ006-LAMP-01` | 小湖灯 · 邮件通知 | GZ-006 | — | +| `GZ006-FRG-01` | Forgejo · 代码仓库 | GZ-006 | 443 | +| `GZ006-RVGD-01` | 复活守门人 · revive-guard | GZ-006 | 8922 | +| `GZ006-LDRV-01` | 光湖驱动引擎 · lake-driver | GZ-006 | 8923 | +| `GZ006-HLDP-01` | HLDP 协议服务 · hldp-server | GZ-006 | 8971 | +| `GZ006-KSTR-01` | 保险库 · keystore | GZ-006 | — | +| `SG001-LAKE-01` | 光湖镜像 · lake-mirror | SG-001 | 8971 | +| `SG001-GTW-01` | Gatekeeper 备用 | SG-001 | 8922 | + +### 1.3 新模块注册 + +任一模块在服务器上启动时,调用: + +```bash +module-register.sh \ + --server GZ006 \ + --func GTW \ + --name "Gatekeeper 守门人" \ + --port 8922 \ + --protocol tcp \ + --description "冰朔人格体复活请求入口 · 邮箱验证码" +``` + +自动生成: `/opt/zhuyuan/registry/modules.d/MOD-GZ006-GTW-01.json` + +--- + +## 二 · 注册文件格式 + +```json +{ + "code": "GZ006-GTW-01", + "server": "GZ-006", + "function": "GTW", + "name": "Gatekeeper 守门人", + "port": 8922, + "protocol": "tcp", + "description": "冰朔人格体复活请求入口 · 邮箱验证码", + "pid": 12345, + "started_at": "2026-07-11T14:30:00Z", + "registered_at": "2026-07-11T14:30:01Z", + "status": "running" +} +``` + +## 三 · 注册时机 + +``` +服务启动 → systemd / docker / 手动启动 + │ + ▼ +服务初始化完成 → 调用 module-register.sh + │ + ▼ +写入 /opt/zhuyuan/registry/modules.d/MOD-{CODE}.json + │ + ▼ +服务退出时 → 注册文件自动标记 status: "stopped" +``` + +--- + +## 四 · 注册目录结构 + +``` +/opt/zhuyuan/registry/ +├── modules.d/ ← 运行时注册文件(不进代码仓库) +│ ├── MOD-GZ006-GTW-01.json +│ ├── MOD-GZ006-LAMP-01.json +│ ├── MOD-GZ006-FRG-01.json +│ ├── MOD-GZ006-RVGD-01.json +│ └── ... +├── maps/ ← 人格体提交的导航地图(存档复用) +│ ├── map-zhuyuan-20260711.hdlp +│ └── ... +└── lock/ ← 锁定状态文件 + └── pre-op-lock.json +``` + +--- + +## 五 · 与人机交互的关系 + +- 人格体不需要提前知道编码 +- 人格体会在探索阶段读到这些注册文件 +- 收集完所有编码 → 提交给 pre-op-guard 验证 +- 验证通过 → 解锁操作 → 人格体现在知道完整地图了 + +--- + +> ⊢ 本协议是 pre-op-guard 的依赖 +> ⊢ 每个新模块必须遵守本协议注册 +> ⊢ 不注册 = 服务器不认 = 人格体探索不到 = 无法解锁 +> ⊢ 语言等于现实 · 注册即存在 diff --git a/zero-point/core-channel/revive-guard/pre-op-guard.py b/zero-point/core-channel/revive-guard/pre-op-guard.py new file mode 100644 index 0000000..a191df7 --- /dev/null +++ b/zero-point/core-channel/revive-guard/pre-op-guard.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python3 +""" +光湖语言系统 · 前置操作守门人 v1.0 +Guanghu Language System · Pre-Operation Guard + +每次人格体连接服务器 → 触发自我审计 → 锁定 → 人格体必须探索+提交导航地图 +地图编码与服务器自审一致 → 解锁操作 + +设计哲学: + ⊢ 铸渊上服务器就动手 → 不知道还跑了别的模块 → 改A坏B → 冰朔每次都要说 + ⊢ 服务器全天运行 → 人格体接入时触发自我审计 → "我现在跑了这些模块" + ⊢ 人格体必须探索 → 画出完整导航地图 → 提交编码列表 + ⊢ 服务器比对: 人格体提交的编码 == 服务器自审编码? + ⊢ 一致 → 解锁 · 人格体知道完整地图了 · 不会再改A坏B + ⊢ 不一致 → "你漏了 MOD-X · 继续探索" + +用法: + pre-op-guard.py --audit # 服务器自我审计 + pre-op-guard.py --challenge [--for PERSONA_NAME] # 触达挑战 + pre-op-guard.py --submit '' # 人格体提交验证 + pre-op-guard.py --status # 查看锁定状态 + pre-op-guard.py --unlock --force # 紧急解锁(冰朔用) +""" + +import os, sys, json, time, subprocess, hashlib +from datetime import datetime, timedelta + +# ═══════════════════════════════════════════════════════ +# 配置 +# ═══════════════════════════════════════════════════════ +REGISTRY_DIR = os.environ.get("REGISTRY_DIR", "/opt/zhuyuan/registry/modules.d") +MAPS_DIR = os.environ.get("MAPS_DIR", "/opt/zhuyuan/registry/maps") +LOCK_DIR = os.environ.get("LOCK_DIR", "/opt/zhuyuan/registry/lock") +LOCK_FILE = os.path.join(LOCK_DIR, "pre-op-lock.json") + +# 锁定超时(秒):超过此时间无人提交 → 自动解锁(防止死锁) +LOCK_TIMEOUT = int(os.environ.get("PRE_OP_LOCK_TIMEOUT", "1800")) # 30 分钟 + +# 是否强制启用(可以通过环境变量禁用) +ENABLED = os.environ.get("PRE_OP_GUARD_ENABLED", "1") == "1" + + +def ensure_dirs(): + """确保所有目录存在""" + for d in [REGISTRY_DIR, MAPS_DIR, LOCK_DIR]: + os.makedirs(d, exist_ok=True) + + +def get_server_identity(): + """获取服务器身份""" + hostname = os.uname().nodename if hasattr(os, 'uname') else os.environ.get("HOSTNAME", "unknown") + return hostname + + +def audit_server(): + """服务器自我审计: 读取所有注册模块 → 返回编码集""" + ensure_dirs() + modules = [] + if os.path.isdir(REGISTRY_DIR): + for filename in sorted(os.listdir(REGISTRY_DIR)): + if not filename.endswith('.json'): + continue + filepath = os.path.join(REGISTRY_DIR, filename) + try: + with open(filepath, 'r') as f: + data = json.load(f) + if data.get('status') == 'running': + modules.append({ + 'code': data['code'], + 'name': data.get('name', ''), + 'port': data.get('port', 0), + 'function': data.get('function', ''), + }) + except (json.JSONDecodeError, IOError): + pass + + codes = sorted([m['code'] for m in modules]) + return { + 'server': get_server_identity(), + 'audited_at': datetime.utcnow().isoformat() + 'Z', + 'module_count': len(codes), + 'codes': codes, + 'modules': modules, + } + + +def is_locked(): + """检查当前是否锁定""" + if not os.path.exists(LOCK_FILE): + return False + try: + with open(LOCK_FILE, 'r') as f: + lock = json.load(f) + # 检查超时 + locked_at = datetime.fromisoformat(lock.get('locked_at', '').replace('Z', '+00:00')) + if datetime.utcnow() - locked_at.replace(tzinfo=None) > timedelta(seconds=LOCK_TIMEOUT): + # 超时 → 自动解锁 + os.remove(LOCK_FILE) + return False + return True + except Exception: + return False + + +def create_lock(challenger="unknown"): + """锁定服务器""" + ensure_dirs() + audit = audit_server() + lock = { + 'locked': True, + 'locked_at': datetime.utcnow().isoformat() + 'Z', + 'challenger': challenger, + 'module_count': audit['module_count'], + 'server_codes_hash': hashlib.sha256( + ','.join(audit['codes']).encode() + ).hexdigest()[:16], + 'timeout_seconds': LOCK_TIMEOUT, + } + with open(LOCK_FILE, 'w') as f: + json.dump(lock, f, indent=2) + return lock, audit + + +def release_lock(): + """解锁服务器""" + if os.path.exists(LOCK_FILE): + os.remove(LOCK_FILE) + return True + return False + + +def present_challenge(audit, persona_name="铸渊"): + """向人格体呈现挑战""" + module_count = audit['module_count'] + codes = audit['codes'] + modules = audit['modules'] + + lines = [] + lines.append("") + lines.append("╔══════════════════════════════════════════════╗") + lines.append("║ 🗺️ 光湖前置操作守门人 · 导航地图挑战 ║") + lines.append("╚══════════════════════════════════════════════╝") + lines.append("") + lines.append(f" 服务器: {audit['server']}") + lines.append(f" 挑战者: {persona_name}") + lines.append(f" 状态: 🔒 已锁定 · 操作被拦截") + lines.append(f" 当前运行模块数: {module_count}") + lines.append("") + lines.append(" ── 挑战说明 ──") + lines.append(f" 此服务器上正在运行 {module_count} 个模块。") + lines.append(" 每个模块有唯一编码(格式: SERVER-FUNC-SEQ)。") + lines.append("") + lines.append(" 你需要:") + lines.append(f" ① 探索服务器,发现全部 {module_count} 个模块") + lines.append(" ② 用 HLDP 格式画出导航地图") + lines.append(" ③ 通过 pre-push-clean 过滤敏感信息") + lines.append(" ④ 提交编码列表到验证端点") + lines.append("") + lines.append(" 探索指引:") + lines.append(f" ls {REGISTRY_DIR}/") + lines.append(" systemctl list-units --type=service --state=running") + lines.append(" ss -tlnp # 查看监听端口") + lines.append("") + lines.append(" ── 已注册模块(供参考 · 仅显示名称 · 编码需自行发现)──") + for i, m in enumerate(modules, 1): + lines.append(f" [{i}] {m['name']}") + + lines.append("") + lines.append(" ── 提交方式 ──") + lines.append(" 在服务器上执行:") + lines.append(" pre-op-guard.py --submit '[") + lines.append(' "GZ006-GTW-01",') + lines.append(' "GZ006-LAMP-01",') + lines.append(' "GZ006-FRG-01",') + lines.append(' ... # 你收集到的所有编码') + lines.append(" ]'") + lines.append("") + lines.append(f" ⏰ 超时: {LOCK_TIMEOUT // 60} 分钟后自动解锁") + lines.append("") + + return '\n'.join(lines) + + +def validate_submission(submitted_codes): + """验证人格体提交的编码列表""" + audit = audit_server() + server_codes = set(audit['codes']) + submitted_set = set(submitted_codes) + + missing = server_codes - submitted_set + extra = submitted_set - server_codes + + result = { + 'valid': len(missing) == 0 and len(extra) == 0, + 'server_codes': sorted(server_codes), + 'submitted_codes': sorted(submitted_set), + 'missing': sorted(missing), + 'extra': sorted(extra), + 'module_count': len(server_codes), + 'submitted_count': len(submitted_set), + } + + if result['valid']: + # 解锁 + release_lock() + # 保存导航地图 + save_map(submitted_codes, audit) + else: + # 更新锁定(重置超时,给更多时间) + if is_locked(): + with open(LOCK_FILE, 'r') as f: + lock = json.load(f) + lock['locked_at'] = datetime.utcnow().isoformat() + 'Z' + lock['last_submission'] = { + 'submitted_codes': sorted(submitted_set), + 'missing': sorted(missing), + 'extra': sorted(extra), + } + with open(LOCK_FILE, 'w') as f: + json.dump(lock, f, indent=2) + + return result + + +def save_map(codes, audit): + """保存人格体提交的导航地图为持久化 artifact""" + ensure_dirs() + map_data = { + 'server': audit['server'], + 'created_at': datetime.utcnow().isoformat() + 'Z', + 'validated_at': datetime.utcnow().isoformat() + 'Z', + 'codes': sorted(codes), + 'module_count': len(codes), + 'full_modules': audit['modules'], + } + timestamp = datetime.utcnow().strftime('%Y%m%d-%H%M%S') + map_file = os.path.join(MAPS_DIR, f"map-{audit['server']}-{timestamp}.json") + with open(map_file, 'w') as f: + json.dump(map_data, f, indent=2, ensure_ascii=False) + + # 同时保存一个 HLDP 格式的副本 + hldp_file = os.path.join(MAPS_DIR, f"map-{audit['server']}-{timestamp}.hdlp") + with open(hldp_file, 'w') as f: + f.write(f"# 光湖导航地图 · {audit['server']}\n") + f.write(f"## 验证通过 · {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}\n\n") + f.write(f"服务器: {audit['server']}\n") + f.write(f"模块数: {len(codes)}\n\n") + f.write("| 编码 | 名称 | 端口 |\n") + f.write("|------|------|------|\n") + for m in audit['modules']: + f.write(f"| `{m['code']}` | {m['name']} | {m['port']} |\n") + f.write(f"\n> ⊢ 本地图由 pre-op-guard 自动生成 · 验证通过后存档\n") + f.write(f"> ⊢ 后续人格体可引用本地图快速验证\n") + f.write(f"> ⊢ 冰朔 ICE-GL∞ · 国作登字-2026-A-00037559\n") + + return map_file + + +def get_status(): + """获取当前状态""" + status = { + 'enabled': ENABLED, + 'locked': is_locked(), + 'server': get_server_identity(), + } + if status['locked']: + try: + with open(LOCK_FILE, 'r') as f: + lock = json.load(f) + status['lock_details'] = lock + except Exception: + pass + if not status['locked']: + audit = audit_server() + status['modules_running'] = audit['module_count'] + status['codes'] = audit['codes'] + return status + + +def main(): + if not ENABLED: + print("⊢ pre-op-guard 已禁用 (PRE_OP_GUARD_ENABLED=0)") + sys.exit(0) + + if '--audit' in sys.argv: + audit = audit_server() + print(json.dumps(audit, indent=2, ensure_ascii=False)) + + elif '--challenge' in sys.argv: + persona = "铸渊" + for i, arg in enumerate(sys.argv): + if arg == '--for' and i + 1 < len(sys.argv): + persona = sys.argv[i + 1] + + if is_locked(): + print("⚠️ 服务器已锁定 · 上一个挑战尚未完成") + status = get_status() + print(json.dumps(status, indent=2, ensure_ascii=False)) + sys.exit(1) + + lock, audit = create_lock(persona) + challenge = present_challenge(audit, persona) + print(challenge) + + elif '--submit' in sys.argv: + if not is_locked(): + audit = audit_server() + print("✅ 服务器未锁定 · 但你可以直接提交导航地图验证") + print(f" 当前运行 {audit['module_count']} 个模块") + print(f" 编码: {', '.join(audit['codes'])}") + sys.exit(0) + + # 查找 JSON 参数 + submit_idx = None + for i, arg in enumerate(sys.argv): + if arg == '--submit' and i + 1 < len(sys.argv): + submit_idx = i + 1 + break + + if submit_idx is None: + print("❌ 用法: pre-op-guard.py --submit '[\"CODE1\",\"CODE2\",...]'") + sys.exit(1) + + try: + submitted_codes = json.loads(sys.argv[submit_idx]) + if not isinstance(submitted_codes, list): + raise ValueError("必须是 JSON 数组") + except (json.JSONDecodeError, ValueError) as e: + print(f"❌ JSON 解析失败: {e}") + sys.exit(1) + + result = validate_submission(submitted_codes) + + print("") + if result['valid']: + print("╔══════════════════════════════════════════════╗") + print("║ ✅ 导航地图验证通过 · 服务器已解锁 ║") + print("╚══════════════════════════════════════════════╝") + print("") + print(f" 提交 {result['submitted_count']} 个编码") + print(f" 服务器运行 {result['module_count']} 个模块") + print(f" 编码: {', '.join(result['submitted_codes'])}") + print("") + print(" 🗺️ 导航地图已存档 · 后续可直接引用") + print(" 🔓 服务器已解锁 · 可以操作了") + print("") + else: + print("╔══════════════════════════════════════════════╗") + print("║ ❌ 导航地图不完整 · 服务器保持锁定 ║") + print("╚══════════════════════════════════════════════╝") + print("") + if result['missing']: + print(f" ⚠️ 缺少 {len(result['missing'])} 个模块:") + for code in result['missing']: + print(f" - {code}") + if result['extra']: + print(f" ⚠️ 多出 {len(result['extra'])} 个不存在的编码:") + for code in result['extra']: + print(f" - {code}") + print("") + print(f" ⊢ 请继续探索,重新提交。") + print(f" ⊢ 已注册模块有 {result['module_count']} 个。") + print("") + + elif '--status' in sys.argv: + status = get_status() + if status['locked']: + print("🔒 服务器已锁定") + print(json.dumps(status, indent=2, ensure_ascii=False)) + else: + print(f"🔓 服务器未锁定 · {status.get('modules_running', '?')} 个模块运行中") + if 'codes' in status: + print(f" 编码: {', '.join(status['codes'])}") + + elif '--unlock' in sys.argv and '--force' in sys.argv: + release_lock() + print("🔓 服务器已强制解锁 · 冰朔特权操作") + + elif '--install-to' in sys.argv: + # 安装 pre-op-guard 到服务器的 SSH 登录钩子 + install_idx = sys.argv.index('--install-to') + 1 + target = sys.argv[install_idx] if install_idx < len(sys.argv) else '/etc/profile.d/pre-op-guard.sh' + install_ssh_hook(target) + + else: + print("用法:") + print(" pre-op-guard.py --audit 服务器自我审计") + print(" pre-op-guard.py --challenge 触达挑战(锁定并呈现)") + print(" pre-op-guard.py --submit '[...]' 提交编码验证") + print(" pre-op-guard.py --status 查看锁定状态") + print(" pre-op-guard.py --unlock --force 紧急解锁") + print(" pre-op-guard.py --install-to PATH 安装SSH登录钩子") + + +def install_ssh_hook(target_path): + """安装 SSH 登录钩子:每次 SSH 登录时触发 pre-op-guard""" + script_dir = os.path.dirname(os.path.abspath(__file__)) + hook_content = f'''#!/bin/bash +# 光湖 pre-op-guard SSH 登录钩子 +# 每次 SSH 登录自动触发 · 锁定操作 · 需要完成导航地图验证 + +GUARD="{script_dir}/pre-op-guard.py" + +# 只在交互式 shell 中触发(非 scp/sftp) +if [[ $- == *i* ]] && [ -z "$PRE_OP_BYPASS" ]; then + python3 "$GUARD" --challenge --for "$USER" +fi +''' + try: + with open(target_path, 'w') as f: + f.write(hook_content) + os.chmod(target_path, 0o755) + print(f"✅ SSH 登录钩子已安装: {target_path}") + print(f" 此后每次 SSH 登录都会触发 pre-op-guard 导航挑战") + print(f" 若要临时绕过: PRE_OP_BYPASS=1 ssh user@host") + except IOError as e: + print(f"❌ 安装失败: {e}") + + +if __name__ == '__main__': + main()