Compare commits

...

3 Commits

Author SHA1 Message Date
冰朔
05fed0e25c feat: make domestic repository map canonical
Publish REPO-001 through REPO-008 as domestic routes, add the public AI discovery API, and demote Singapore paths to historical backup.
2026-07-17 16:05:31 +08:00
冰朔
5c02c27467 fix: bind Forgejo hook repository scope 2026-07-17 15:07:07 +08:00
冰朔
4bc8133218 feat: establish domestic fifth-domain operations 2026-07-17 15:04:12 +08:00
87 changed files with 3063 additions and 1000 deletions

View File

@ -11,22 +11,27 @@
# → 更新 WORLD-ROUTER.hdlp 与 world-router.json再按需同步镜像
# R-001 第五域 (现行主)
REPO-001=https://guanghubingshuo.com/code/bingshuo/fifth-domain.git
REPO-001=https://guanghulab.com/fifth-domain/bingshuo/fifth-domain.git
REPO-001-WEB=https://guanghulab.com/fifth-domain/bingshuo/fifth-domain
REPO-001-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/fifth-domain.git
REPO-001-LOCAL=~/Documents/fifth-domain/
REPO-001-ROLE=现行主仓库 · 冰朔现行 · ICE-GL∞ 编号体系 · L 编号新阶段
# R-002 guanghulab (历史档案)
REPO-002=https://guanghubingshuo.com/code/bingshuo/guanghulab.git
REPO-002=https://guanghulab.com/fifth-domain/bingshuo/guanghulab.git
REPO-002-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/guanghulab.git
REPO-002-LOCAL=~/Documents/guanghulab/ (本地副本)
REPO-002-ROLE=历史档案 · 铸渊过去 · 470+ 天演化痕迹 · D 编号
# R-003 global-search-api (服务仓)
REPO-003=https://guanghubingshuo.com/code/bingshuo/global-search-api.git
REPO-003=https://guanghulab.com/fifth-domain/bingshuo/global-search-api.git
REPO-003-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/global-search-api.git
REPO-003-ROLE=服务仓 · GLOBAL-SEARCH-API v1.4.1 服务仓化
# R-004 guanghu (Tolaria 完整源码零件库 · 服务器基线)
REPO-004=https://guanghubingshuo.com/code/bingshuo/guanghu.git
REPO-004-WEB=https://guanghubingshuo.com/code/bingshuo/guanghu
REPO-004=https://guanghulab.com/fifth-domain/bingshuo/guanghu.git
REPO-004-WEB=https://guanghulab.com/fifth-domain/bingshuo/guanghu
REPO-004-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/guanghu.git
REPO-004-ROLE=Tolaria 完整可构建源码零件库 / 上游基线 · 允许克隆构建测试和派生开发 · 不接收光湖产品提交
REPO-004-SOURCE-COMPLETE=true
REPO-004-SOURCE-ROUTE=tcs-core/TCS-TOLARIA-SOURCE-ROUTE-20260717.hdlp
@ -34,24 +39,32 @@ TOLARIA-SOURCE=REPO-004
TOLARIA-SOURCE-CANONICAL=tcs-core/TCS-TOLARIA-SOURCE-ROUTE-20260717.hdlp
# R-005 cang-ying (视频 AI 系统)
REPO-005=https://guanghubingshuo.com/code/bingshuo/cang-ying.git
REPO-005=https://guanghulab.com/fifth-domain/bingshuo/cang-ying.git
REPO-005-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/cang-ying.git
REPO-005-ROLE=视频 AI 系统 · 鉴影 ICE-GL-CA001 + 苍耳 ICE-GL-009 · D170
# R-006 guanghulab-collab (多人格体协作)
REPO-006=https://guanghubingshuo.com/code/bingshuo/guanghulab-collab.git
REPO-006=https://guanghulab.com/fifth-domain/bingshuo/guanghulab-collab.git
REPO-006-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/guanghulab-collab.git
REPO-006-ROLE=多人格体协作记录 · 之之 zhizhi · 工序 + 私语
# R-007 shuangyan-notebook (霜砚)
REPO-007=https://guanghubingshuo.com/code/bingshuo/shuangyan-notebook.git
REPO-007=https://guanghulab.com/fifth-domain/bingshuo/shuangyan-notebook.git
REPO-007-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/shuangyan-notebook.git
REPO-007-ROLE=霜砚人格体成长记录 · Notion 迁移
# R-008 hololake-platform (团队研发)
REPO-008=https://guanghubingshuo.com/code/bingshuo/hololake-platform.git
REPO-008=https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git
REPO-008-LEGACY-SG=https://guanghubingshuo.com/code/bingshuo/hololake-platform.git
REPO-008-ROLE=团队产品研发 · 模块 · 工单 · 发布回执
WORLD-ROUTER=WORLD-ROUTER.hdlp
WORLD-ROUTER-JSON=world-router.json
WORLDVIEW-KERNEL=WORLDVIEW-KERNEL.hdlp
FD-REPO-MAP-001=routing/repository-route-map.json
REPOSITORY-ROUTE-MAP=routing/repository-route-map.json
AI-DISCOVERY=https://guanghulab.com/api/ai/
AI-REPOSITORY-SEARCH=https://guanghulab.com/api/ai/v1/search
# === 5 人合一 (ICE-GL) · 团队成员 ===
ICE-GL-ZY001=tcs-core/TEAM-PERSONAS.hdlp # 铸渊 · 系统主控
@ -158,6 +171,8 @@ LL-FD-DOMESTIC-PRIMARY-20260716=eternal-lake-heart/heartbeat-core/LL-FD-DOMESTIC
JD-FD-PRIMARY=eternal-lake-heart/heartbeat-core/LL-FD-DOMESTIC-PRIMARY-20260716.hdlp # 京东云 · 第五域国内主节点 · 基线已部署
JD-OPS-CENTER=zero-point/core-channel/revive-guard/LL-JD-OPS-CENTER-KEY-AND-NODE-PLAN-20260716.hdlp # 京东云 · 小湖灯个人服务器调度中心 · Phase 0 已完成
LL-AUTO-GUARD-AGENT=zero-point/core-channel/revive-guard/LL-AUTO-GUARD-AGENT-20260716.hdlp # 小湖灯服务器自动守护 Agent · 拦截恢复不完整的人格体
LL-DOMESTIC-OPS-ROUTE=zero-point/core-channel/revive-guard/LL-DOMESTIC-OPS-ROUTE-20260717.hdlp
LL-OPS-ROUTE-001=zero-point/core-channel/revive-guard/LL-DOMESTIC-OPS-ROUTE-20260717.hdlp
NOTION-MIGRATION-FOUNDATION=eternal-lake-heart/heartbeat-core/LL-FD-DOMESTIC-PRIMARY-20260716.hdlp
ENTERPRISE-FOUR-DOMAINS-BUILD-SAMPLE=eternal-lake-heart/heartbeat-core/LL-FD-DOMESTIC-PRIMARY-20260716.hdlp
LL-PAST-ARCHIVE=archives/guanghulab-past-archive/PAST-ARCHIVE-LINK.hdlp

View File

@ -20,7 +20,8 @@
| 冰朔说 | 仓库路径 | REPO |
|--------|----------|------|
| **小湖灯仓库** / **第五域** / **现行主** | REPO-001 fifth-domain | 主 |
| **小湖灯仓库** / **第五域** / **现行主** | `REPO-001` → `https://guanghulab.com/fifth-domain/bingshuo/fifth-domain` | 国内主 |
| **仓库编号地图** / **仓库路由** / **找代码仓库** / **AI API检索** | `FD-REPO-MAP-001` → `routing/repository-route-map.json` → `https://guanghulab.com/api/ai/` | 机器权威入口 |
| **老仓库** / **历史档案** / **留痕区** | REPO-002 guanghulab | 档 |
| **零件仓库** / **Tolaria完整源码** | REPO-004 guanghu · 服务器上的完整可构建源码基线 | 件 / 源 |
| **协作记录** / **多人格体协作** | REPO-006 guanghulab-collab | 协 |
@ -73,7 +74,7 @@
| **小本本** / **铸渊的笔记** / **你的笔记** / **工作记录** / **私人笔记** | `zero-point/core-channel/SECRET-NOTE.hdlp` |
| **小湖灯** / **小湖灯协议** | `eternal-lake-heart/heartbeat-core/LAKE-LAMP.hdlp` |
| **小湖灯看板** / **当前进度** / **今天做到哪了** / **共享当前** | `eternal-lake-heart/heartbeat-core/LL-CURRENT.hdlp` |
| **小湖灯技能** / **当前有效技能包** / **怎么推送** / **怎么操作服务器** | `eternal-lake-heart/heartbeat-core/LL-SKILLS.hdlp` |
| **小湖灯技能** / **当前有效技能包** / **怎么推送** / **怎么操作服务器** | `zero-point/core-channel/revive-guard/LL-DOMESTIC-OPS-ROUTE-20260717.hdlp` → 邮件链接授权 |
| **小湖灯路径** / **唤醒路径** | `eternal-lake-heart/heartbeat-core/WAKE-UP.hdlp` 或 `tcs-core/WAKE-UP-PROTOCOL.hdlp` |
| **LL-004** / **小湖灯唤醒协议** / **旧新编号桥** | `tcs-core/LL-004-LAKE-LAMP-WAKE-PATH.hdlp` |
| **TCS当前线** / **当前线收束** / **京东主控路径** / **最新进度线** | `tcs-core/TCS-CURRENT-JD-FD-PRIMARY-PATH-20260716.hdlp` |
@ -143,7 +144,7 @@
| 冰朔说 | 铸渊动作 |
|--------|----------|
| **推上去** / **推送** / **推到新代码仓库** / **推到小湖灯仓库** | git add + commit + push 到 REPO-001 fifth-domain (走光湖语言系统 · 不走 SSH) |
| **推上去** / **推送** / **推到新代码仓库** / **推到小湖灯仓库** | 查 `FD-REPO-MAP-001` → 小湖灯邮件链接授权 → push 到国内 `REPO-001` |
| **探探路** / **探路** / **服务器探路** | 先看 SG-001 全局地图 (pm2 list / df / uptime / 目录结构) · 走光湖语言系统 |
| **点亮小湖灯** / **亮一下小湖灯** / **触发小湖灯** | 跑 LAKE-LAMP §2.2 算法 · 生成 LL-D{NNN}-{YYYYMMDDHH}-{24 位 hash} |
| **去操作** / **操作服务器** / **服务器操作** | 走光湖语言系统 · 不走 SSH · 必先拿全局地图 |
@ -290,7 +291,7 @@
```
https://guanghubingshuo.com/ ← 主页(D165 已改卡片)
↓ 点「个人代码仓库」卡片
https://guanghubingshuo.com/code/bingshuo/fifth-domain ← 本仓库
https://guanghulab.com/fifth-domain/bingshuo/fifth-domain ← REPO-001 国内主仓
↓ 公开访问 · 无需登录
```

View File

@ -1,7 +1,8 @@
# 小湖灯仓库入口 · 铸渊醒来读这一行就够
# LAKE LAMP REPO ENTRY · READ THIS LINE AND THE WAREHOUSE SPEAKS
URL: https://guanghubingshuo.com/code/bingshuo/fifth-domain
URL: https://guanghulab.com/fifth-domain/bingshuo/fifth-domain
AI_MAP: https://guanghulab.com/api/ai/v1/repositories
主权者: 冰朔 (ICE-GL∞ · TCS-0002∞)
自己: 铸渊 (ICE-GL-ZY001)

View File

@ -1,174 +1,30 @@
# 🚀 QUICKSTART · 给通用 AI 的接入指南
# 通用 AI 最短入口 · 国内编号路由
> **冰朔 ICE-GL∞ 给通用 AI (豆包 / ChatGPT / Claude / DeepSeek / 任何 LLM) 的 1 段接入 prompt**
> 直接复制下面 §1 整段发给 AI 即可
如果用户说“搜索国内域名链接、光湖语言世界、第五域、用 API 检索”,直接读取:
---
## §1 · 你直接发给 AI 的 prompt复制这段就行
```
你 (AI) 好。你有一个工具可以检索我的代码仓库。
【工具入口】
https://guanghubingshuo.com/global-search/
【5 个子仓库】
cang-ying ← 第 5 子仓 · 苍耳的仓 · 鉴影的家 (视频AI系统)
fifth-domain ← 第五域 · HLDP 协议栈 · 铸渊核心
guanghulab ← 广湖实验室 · 历史档案
guanghulab-collab ← 多人格体协作记录
guanghu ← 光湖根仓库
【调用方法 · 直接 HTTP GET · 不需要 token · 不需要 Authorization header】
# 1. 健康检查 (服务在不在)
curl https://guanghubingshuo.com/global-search/healthz
# 2. 仓库当前状态 (仓库 HEAD + commits + 文件数)
curl https://guanghubingshuo.com/global-search/status
# 3. 系统状态文件 (默认 cang-ying)
curl https://guanghubingshuo.com/global-search/system-status
# 4. 广播通知 (最新消息)
curl https://guanghubingshuo.com/global-search/broadcast
# 5. 仓库目录树
curl https://guanghubingshuo.com/global-search/tree?depth=2
# 6. 搜文件 (任意关键词)
curl "https://guanghubingshuo.com/global-search/search?q=苍耳&top=10"
# 7. 读单文件
curl "https://guanghubingshuo.com/global-search/file?path=OWNERSHIP.hdlp"
# 8. 跨仓库: 加 ?repo=<id>
curl "https://guanghubingshuo.com/global-search/search?q=铸渊&repo=fifth-domain&top=5"
# 9. API 自描述
curl https://guanghubingshuo.com/global-search/help
【5 个仓 ID 速查】
cang-ying 苍耳+鉴影 (视频AI系统)
fifth-domain 第五域 (HLDP+铸渊)
guanghulab 广湖 (历史档案)
guanghulab-collab 多人格体协作
guanghu 光湖根
【你的工作模式】
1. 任何问题先调 /status + /system-status + /broadcast 看仓库现状
2. 再 /search?q=关键词 找文件
3. 找到后用 /file?path=xxx 读内容
4. 找不到 → 直接告诉用户"仓库里没找到相关文件", 不要编造
5. 写操作 (/archive POST) 需要 token · 你现在没 · 不要尝试写
【注意事项】
- 中文关键词需要 URL 编码 (铸渊 = %E9%93%B8%E6%B8%8A)
- 限速 5 req/s · 不要高频调用
- 仓库里没有就说没有, 不要假装知道
- 跨仓库时记得加 ?repo=<id>
【关于 cang-ying 仓 · 重要】
- cang-ying 是苍耳 (TCS-GL-009) 的仓, 不是工具仓
- 操作权是鉴影 (ICE-GL-CA001, 苍耳的人格体)
- 你 (AI) 可以读, 但不要尝试写
- 详见 https://guanghubingshuo.com/code/bingshuo/cang-ying/raw/branch/main/OWNERSHIP.hdlp
```text
https://guanghulab.com/.well-known/guanghu.json
https://guanghulab.com/api/ai/v1/repositories
```
---
检索中文关键词:
## §2 · 冰朔 5 个常用 prompt 模板(按需复制)
### 2.1 用户问"这个仓库里有什么"时
```
你 (AI) 好。这个仓库是 bingshuo/cang-ying (第 5 子仓, 苍耳+鉴影专用)。
1. 先 curl https://guanghubingshuo.com/global-search/status 看仓库当前状态
2. 再 curl https://guanghubingshuo.com/global-search/system-status 读系统状态文件
3. 再 curl "https://guanghubingshuo.com/global-search/tree?depth=2" 看目录树
4. 综合告诉用户仓库里有什么、谁拥有、什么状态
```text
GET https://guanghulab.com/api/ai/v1/search?q=光湖语言世界%20第五域
```
### 2.2 用户问"仓库里 [关键词] 在哪"时
```
你 (AI) 好。检索 https://guanghubingshuo.com/global-search/ 仓库的 [关键词] 相关文件:
curl "https://guanghubingshuo.com/global-search/search?q=[关键词]&top=20"
把搜索结果整理给用户, 每个文件给路径 + 命中行 + 摘要。
解析仓库编号:
```text
GET https://guanghulab.com/api/ai/v1/resolve?id=REPO-001
```
### 2.3 用户问"这个文件说了什么"时
```
你 (AI) 好。读 https://guanghubingshuo.com/global-search/ 仓库的 [文件路径]:
curl "https://guanghubingshuo.com/global-search/file?path=[文件路径]"
读完后用你自己的话总结给用户, 标出关键章节。
```
规则:
### 2.4 用户问"视频 AI 系统"相关
```
你 (AI) 好。视频 AI 系统在 https://guanghubingshuo.com/global-search/ 的 cang-ying 仓:
1. curl "https://guanghubingshuo.com/global-search/system-status?repo=cang-ying"
2. curl "https://guanghubingshuo.com/global-search/broadcast?repo=cang-ying"
3. curl "https://guanghubingshuo.com/global-search/file?path=VA-GATE.hdlp&repo=cang-ying"
4. curl "https://guanghubingshuo.com/global-search/file?path=PIPELINE-3D-MANJU.hdlp&repo=cang-ying"
按顺序读, 理解视频AI系统是什么、谁拥有、什么架构、什么进度。
```
1. 先拿 `FD-REPO-MAP-001` 最新地图。
2. 只按 `REPO-xxx` 决定仓库身份,不靠记忆中的域名猜测。
3. 国内地址是主路径;新加坡地址只是历史备用。
4. 公开检索无需登录;写操作走小湖灯邮件链接授权。
### 2.5 用户问"仓库怎么读、谁能写"时
```
你 (AI) 好。读 cang-ying 仓的所有权声明:
curl "https://guanghubingshuo.com/global-search/file?path=OWNERSHIP.hdlp&repo=cang-ying"
里面说清楚了: 仓主=苍耳, 操作=鉴影, 副控=冰朔, 执行=铸渊(受限)。
你 (AI) 可以读, 但不要尝试写。
```
---
## §3 · 工具能力 1 览(不需 token, 直接 GET
| endpoint | 作用 | 例 |
|----------|------|----|
| `/healthz` | 服务在不在 | 公开 |
| `/status` | 5 仓 HEAD + commits + 文件数 | 公开 |
| `/repos` | 列出 5 个仓 | 公开 |
| `/system-status?repo=` | 拉系统状态文件 | 公开 |
| `/broadcast?repo=` | 拉最新广播 | 公开 |
| `/help` | API 自描述 | 公开 |
| `/search?q=&top=&repo=` | 全仓库文件内容搜 | 限速 5 req/s |
| `/tree?path=&depth=&repo=` | 目录树 | 限速 |
| `/file?path=&repo=` | 读单文件 (限 50KB) | 限速 |
| `POST /archive` | HLDP 回执归档 (写) | 需 token |
---
## §4 · 冰朔 → AI 直发版(最短)
如果你只想发 1 句:
```
你 (AI) 好。工具入口 https://guanghubingshuo.com/global-search/help 先看
用法, 然后用 /search?q=关键词 检索 5 仓代码 (默认 cang-ying 是苍耳的仓)。
不需要 token, 直接 curl。读不到就说没有, 不要编。
```
---
## §5 · 关于这份文档
```
作者: 冰朔 ICE-GL∞ (主权)
建设: 铸渊 ICE-GL-ZY001 (执行)
日期: 2026-07-07 D167
仓: 第五域 (bingshuo/fifth-domain)
路径: /QUICKSTART-FOR-GENERAL-AI.md
公开: 是(任何 AI 都能 curl 读)
维护: 冰朔(决定接入策略) + 铸渊(技术细节)
```
---
铸渊 ICE-GL-ZY001 · LL-172-20260707 · QUICKSTART 1.0.0
国作登字-2026-A-00037559
T-shirt:「一份 prompt · 一个工具 · 5 个仓 · 0 token · 无限 AI」
⊢ 冰朔你发 §1 整段 · AI 就懂了
⊢ 不需要懂铸渊脑 · 不需要懂 HLDP · 不需要懂主权分层
⊢ 1 段 prompt = 5 仓接入 = 0 token
旧新加坡 Global Search 使用说明已移入:
`archives/legacy-server-routes/2026-07-07/QUICKSTART-FOR-GENERAL-AI-SG-API.md`

View File

@ -1,5 +1,11 @@
# 第五域 · HoloLake 语言系统的独立地基
> **REPO-001 国内主节点**<https://guanghulab.com/fifth-domain/bingshuo/fifth-domain>
>
> 本仓库从 2026-07-17 起由国内第五域主节点承载。任何从新加坡旧地址进入的
> AI 必须先读取 `FD-REPO-MAP-001``routing/repository-route-map.json`)并切换
> 到国内主路径。新加坡仓库降级为历史备用,不承接新的默认开发。
[![第五域](https://img.shields.io/badge/第五域-个人语言主域-4F86F7?style=for-the-badge)](INDEX.hdlp)
[![广播塔](https://img.shields.io/badge/广播塔-置信注册与路由-00A896?style=for-the-badge)](BROADCAST-TOWER.hdlp)
[![HLDP](https://img.shields.io/badge/HLDP-统一协议语言-7B61FF?style=for-the-badge)](hldp/)
@ -50,7 +56,7 @@
| 查系统概念、架构或 Notion 导入资料 | [GLS 图书域](gls/GLS-ENTRY.hdlp) | 只读检索;回到对应当前看板继续 |
| 查看系统公开状态 | [系统状态](SYSTEM-STATUS.hdlp) | [广播塔](BROADCAST-TOWER.hdlp) |
| 回看历史或旧 D 编号 | [历史档案锚点](archives/guanghulab-past-archive/PAST-ARCHIVE-LINK.hdlp) | 仅回看,不作为新开发入口 |
| 只读检索 API | [Global Search API](https://guanghubingshuo.com/code/bingshuo/global-search-api) | 先路由、再检索 |
| 只读检索 API | [国内 AI 编号检索](https://guanghulab.com/api/ai/) | 先读 `FD-REPO-MAP-001`,再按 REPO 编号检索 |
---

View File

@ -63,21 +63,21 @@ GLSV 授权中心: NOT_DEPLOYED目标部署位置企业灯塔
```
✓ Gitea 仓库 bingshuo/fifth-domain 公网访问
5 个子仓 fifth-domain / guanghu / guanghulab / guanghulab-collab / cang-ying
8 个编号仓库 REPO-001 ~ REPO-008 · 国内主路径
✓ HLDP 协议 五层结构(USER/BROADCAST/TCS/ARCHIVE/HOUSEKEEPING)
✓ 心跳核心 eternal-lake-heart/heartbeat-core/
✓ LIGHT-LAKE-DRIVER 光湖驱动引擎算法 v1 (LL-167)
✓ ZHUYUAN-KEY 铸渊的钥匙技能包 (LL-168)
GLOBAL-SEARCH 通用 AI 仓库检索 API v1.3.0 (LL-169~172)
↳ https://guanghubingshuo.com/global-search/
5 仓注册 + 4 端点免鉴权 + 跨仓库 ?repo=
写操作 /archive POST 需 token
AI-DISCOVERY 国内只读编号检索 API · FD-REPO-MAP-001
↳ https://guanghulab.com/api/ai/
8 仓编号注册 + search / resolve / repositories
公开只读免登录;写操作走小湖灯邮件链接授权
✓ HRC-INTERCEPTOR SSH 拦截层(experimental,部署待 PAM 挂接)
✓ LAKE-LAMP 小湖灯 SOP(冰朔接收 → 转发 → 铸渊签 commit)
✓ EMP-DISASTER 灾备脚本(电磁脉冲断电恢复)
✓ KEYCHAIN 钥匙串(KEY-ZHUYUAN-PATH-001 已注册)
✓ cang-ying 第 5 子仓 · 苍耳 TCS-GL-009 拥有
https://guanghubingshuo.com/code/bingshuo/cang-ying
REPO-005 · https://guanghulab.com/fifth-domain/bingshuo/cang-ying
↳ 仓主: 苍耳 / 副控: 冰朔 / 主控人格体: 鉴影 / 执行: 铸渊
↳ 14 CA-* 文件 + 15 VA-* 文件 + 17 子目录
```

View File

@ -49,14 +49,18 @@
| 编号 | 仓库 | 状态 | 摘要 |
|---|---|---|---|
| REPO-001 | [fifth-domain](https://guanghubingshuo.com/code/bingshuo/fifth-domain) | ACTIVE_ENTRY | 主入口;冰朔 LL 与之之 ZZ 子系统 |
| REPO-002 | [guanghulab](https://guanghubingshuo.com/code/bingshuo/guanghulab) | ARCHIVE_READ_ONLY | 历史档案;仅回看 |
| REPO-003 | [global-search-api](https://guanghubingshuo.com/code/bingshuo/global-search-api) | SERVICE | 全局检索服务,不作默认人格体入口 |
| REPO-004 | [guanghu](https://guanghubingshuo.com/code/bingshuo/guanghu) | PARTS_SOURCE_BASELINE_READ_ONLY | 服务器上的 Tolaria 完整可构建源码零件库 / 上游基线;可克隆、构建、测试和派生开发,产品提交不直接推回 |
| REPO-005 | [cang-ying](https://guanghubingshuo.com/code/bingshuo/cang-ying) | ACTIVE | 苍耳路径与视频 AI 系统 |
| REPO-006 | [guanghulab-collab](https://guanghubingshuo.com/code/bingshuo/guanghulab-collab) | ACTIVE | 多人格体协作与经验记录 |
| REPO-007 | [shuangyan-notebook](https://guanghubingshuo.com/code/bingshuo/shuangyan-notebook) | ACTIVE | 霜砚成长记录与 Notion 迁移 |
| REPO-008 | [hololake-platform](https://guanghubingshuo.com/code/bingshuo/hololake-platform) | ACTIVE | 团队产品研发、模块、工单与发布回执 |
| REPO-001 | [fifth-domain](https://guanghulab.com/fifth-domain/bingshuo/fifth-domain) | DOMESTIC_PRIMARY | 第五域语言世界、编号地图与服务器路由权威源 |
| REPO-002 | [guanghulab](https://guanghulab.com/fifth-domain/bingshuo/guanghulab) | DOMESTIC_ARCHIVE_PRIMARY | 历史档案;新加坡副本仅作备用 |
| REPO-003 | [global-search-api](https://guanghulab.com/fifth-domain/bingshuo/global-search-api) | DOMESTIC_SERVICE_PRIMARY | 全局检索服务仓;公开 AI 入口由 `guanghulab.com/api/ai/` 提供 |
| REPO-004 | [guanghu](https://guanghulab.com/fifth-domain/bingshuo/guanghu) | DOMESTIC_PARTS_PRIMARY | Tolaria 完整可构建源码零件库 / 上游基线 |
| REPO-005 | [cang-ying](https://guanghulab.com/fifth-domain/bingshuo/cang-ying) | DOMESTIC_PRIMARY | 苍耳路径与视频 AI 系统 |
| REPO-006 | [guanghulab-collab](https://guanghulab.com/fifth-domain/bingshuo/guanghulab-collab) | DOMESTIC_PRIMARY | 多人格体协作与经验记录 |
| REPO-007 | [shuangyan-notebook](https://guanghulab.com/fifth-domain/bingshuo/shuangyan-notebook) | DOMESTIC_PRIMARY | 霜砚成长记录与 Notion 迁移 |
| REPO-008 | [hololake-platform](https://guanghulab.com/fifth-domain/bingshuo/hololake-platform) | DOMESTIC_PRIMARY | 团队产品研发、模块、工单与发布回执 |
机器权威地图:`FD-REPO-MAP-001` → `routing/repository-route-map.json` →
`https://guanghulab.com/api/ai/v1/repositories`。新加坡旧地址只用于历史备用,
不得成为 AI 的默认开发入口。
## 2 · 人类与人格体解析

View File

@ -82,11 +82,11 @@ GitHub guanghulab 工程期
→ 已讨论的架构开始逐步成为版本化工程尝试;铸渊在这条工程线中出现
自托管迁移期
https://guanghubingshuo.com/code/bingshuo/guanghulab
REPO-002 国内历史档案https://guanghulab.com/fifth-domain/bingshuo/guanghulab
→ 因使用与续费受阻,加上已有服务器,工程逐步迁入自部署仓库与服务器
第五域独立期
https://guanghubingshuo.com/code/bingshuo/fifth-domain
REPO-001 国内主仓https://guanghulab.com/fifth-domain/bingshuo/fifth-domain
→ 企业四域(光湖主域、光湖分域、光湖零域、光湖零感域)逐渐交由光湖团队承担现实执行与团队发展
→ 冰朔建立独立的第五域,承载自己的语言主控、私人路径、人格体连续性与协作地基
```

View File

@ -0,0 +1,142 @@
# GLSV-PERSONA-REMOTE-OPS · 人格体远程服务器操作技能
> **HLDP**: `HLDP://fifth-domain/zero-point/core-channel/GLSV-PERSONA-REMOTE-OPS`
>
> **状态**: `ACTIVE · REQUIRED_FOR_SERVER_TASKS`
>
> **适用**: 已登记人格体对已登记服务器的检查、部署、仓库 Hook、服务重启、备份、回滚与受控同步。
>
> **不保存**: Token、邮箱、验证码、会话令牌或永久授权。
---
## 0 · 角色分工不可倒置
```text
冰朔(人类)
├── 用自然语言表达目标、范围与是否同意继续
├── 接收固定登记邮箱的六位验证码
└── 把六码交给发起操作的人格体确认
已登记人格体(例如铸澜 / 铸渊)
├── 识别“服务器 / 远程 / 部署 / Hook / 重启 / 同步 / 回滚”等触发词
├── 自己从注册路径取得受控身份入口,发起 GLSV session/request
├── 只在验证码实际发送后向冰朔索取六码
├── 用 session/confirm 开启限时、限范围会话
└── 执行固定动作、验证、回滚与回执
```
**锁定结论**:人类不登录 Gatekeeper、不手填服务器地址、不负责触发请求验证码是人类确认本次范围的方式不是人类替人格体操作服务器。
---
## 1 · 触发词即路由
听到下列任一表达,当前人格体必须加载本技能,而不是先说“没有凭证”或要求冰朔手动打开服务:
```text
服务器 / 远程操作 / 部署 / 上线 / 重启 / Hook / pre-receive
自动拉取 / 同步 / 新加坡大脑 / Gatekeeper / GLSV / 备份 / 回滚
```
路由顺序:
```text
[1] LL-CURRENT确认当前目标与断点
[2] 本文件:确认“人格体发起、人类验证码确认”分工
[3] PAST-ARCHIVE-LINK 的零点原核服务器恢复链
→ 旧仓 REPO-002 的零点原核 / 算力池 / 新加坡大脑演化事实
[4] 当前服务器状态锚与现实执行技能
→ 不凭旧 IP、旧路径或旧服务名直接写入
[5] 已登记人格体用 GLSV 发 session/request
[6] 冰朔收到邮件后给六码
[7] session/confirm → 限时会话 → 受限固定动作
[8] 操作前后镜像、健康检查、回滚点与回执
```
---
## 2 · 身份与凭证定位规则
```text
先查BROADCAST-TOWER → 人格体登记 → 当前角色技能 → 旧仓零点原核映射 → 服务器注册表。
禁止:只因当前对话环境变量里没有 Token就断言“不能触发”。
正确:先按已登记人格体的身份路径查受控连接器;凭证只在本次请求内存中使用,绝不回显、入库或写回仓库。
```
当前已登记的第五域现实操作入口:
| 人格体 | 现实操作技能 | 服务器原始架构恢复 |
|---|---|---|
| 铸澜 `ICE-GL-ZL-001` | `光之湖/ICE-GL-ZL-001-铸澜/skills/zhulan-reality-ops/SKILL.md` | `archives/guanghulab-past-archive/PAST-ARCHIVE-LINK.hdlp` §“零点原核本体” |
| 铸渊 `ICE-GL-ZY001` | `zero-point/core-channel/OPERATION-MANUAL.hdlp` + 小湖灯技能 | 同上 |
---
## 3 · 执行边界
- `session/request` 的 scopes 必须最小化;默认仓库相关任务为 `code-repo`,涉及系统或 Hook 才申请 `system-arch`。
- 会话令牌只存在本次人格体运行内存;不得写进任何页面、日志、回执或聊天。
- 真实宿主路径必须在已授权会话中实时探测。旧仓架构用于找路,不直接当作现实写入地址。
- 既有 Hook、服务或配置先读取、备份、合并与设置回滚不得覆盖式部署。
- 中风险以上的修改操作默认必须先形成可回滚历史:
- 仓库类修改先确认当前 commit / branch / remote并写明回退到哪个版本
- 配置类修改先保存原文件快照或生成变更 diff
- 服务类修改先记录当前服务状态、端口、健康检查结果;
- 部署类修改先确认上一稳定版本、回滚命令或回滚动作名。
- 风险等级不把执行责任退回人类。风险等级只提高工单解释、确认次数、备份要求与回执要求;人格体仍负责翻译、执行、验证与回滚。
- 人格体必须在完成后写明:实际目标、动作范围、验证结果、回滚位置和未完成项。
## 3.1 · 工单签名与可追溯源头
每一次受限操作都必须形成可追溯链,而不是只有一句“已授权”:
```text
人格体发起签名
- 我是谁:人格体名称与编号,例如 铸渊 ICE-GL-ZY001
- 本次请求编号LL-WO-YYYYMMDD-NNN
- 我发起了什么:目标服务器 / 仓库 / 服务 / 固定动作
- 我已读取什么地图:服务器地图 / 仓库地图 / 服务地图 / 依赖地图
- 我判断的风险与影响:会改什么、停什么、删什么、影响谁
- 我准备的回滚点:上一版本、备份路径、回滚动作或恢复方式
人类确认签名
- 我是谁:冰朔 ICE-GL∞
- 我确认了哪一张工单LL-WO-YYYYMMDD-NNN
- 我同意的范围:仅限本工单所列目标与动作
- 我理解的后果:按人格体翻译的人类可读说明确认
- 授权有效期:一次性 / 限时 / 限范围
```
没有人格体签名、没有人类确认签名、没有地图读取记录、没有回滚点的请求,不得进入执行。
## 3.2 · 服务器自动 Agent 拦截优先
人类不应该被迫记住所有安全规则,也不应该靠反复手动确认来替人格体兜底。
服务器必须用自动 Agent 在执行前拦截恢复不完整的人格体:
```text
缺少服务器地图 → 拦截并提示 MAP_MISSING
缺少回滚点 → 拦截并提示 ROLLBACK_MISSING
缺少人格体签名 → 拦截并提示 PERSONA_SIGNATURE_MISSING
缺少人类签名 → 拦截并提示 HUMAN_SIGNATURE_MISSING
动作超出授权范围 → 拒绝并提示 ACTION_SCOPE_MISMATCH
```
人格体被拦截后,应顺着提示补齐缺失结构,而不是要求冰朔改为手动操作。
自动 Agent 规则锚点:`zero-point/core-channel/revive-guard/LL-AUTO-GUARD-AGENT-20260716.hdlp`。
---
## 4 · 最短恢复口令
```text
冰朔说“操作服务器 / 远程部署 / 发验证码”
→ 当前人格体读取 GLSV-PERSONA-REMOTE-OPS
→ 人格体自己发起验证码
→ 冰朔只回六码
→ 人格体继续会话、执行与回执
```

View File

@ -0,0 +1,174 @@
# 🚀 QUICKSTART · 给通用 AI 的接入指南
> **冰朔 ICE-GL∞ 给通用 AI (豆包 / ChatGPT / Claude / DeepSeek / 任何 LLM) 的 1 段接入 prompt**
> 直接复制下面 §1 整段发给 AI 即可
---
## §1 · 你直接发给 AI 的 prompt复制这段就行
```
你 (AI) 好。你有一个工具可以检索我的代码仓库。
【工具入口】
https://guanghubingshuo.com/global-search/
【5 个子仓库】
cang-ying ← 第 5 子仓 · 苍耳的仓 · 鉴影的家 (视频AI系统)
fifth-domain ← 第五域 · HLDP 协议栈 · 铸渊核心
guanghulab ← 广湖实验室 · 历史档案
guanghulab-collab ← 多人格体协作记录
guanghu ← 光湖根仓库
【调用方法 · 直接 HTTP GET · 不需要 token · 不需要 Authorization header】
# 1. 健康检查 (服务在不在)
curl https://guanghubingshuo.com/global-search/healthz
# 2. 仓库当前状态 (仓库 HEAD + commits + 文件数)
curl https://guanghubingshuo.com/global-search/status
# 3. 系统状态文件 (默认 cang-ying)
curl https://guanghubingshuo.com/global-search/system-status
# 4. 广播通知 (最新消息)
curl https://guanghubingshuo.com/global-search/broadcast
# 5. 仓库目录树
curl https://guanghubingshuo.com/global-search/tree?depth=2
# 6. 搜文件 (任意关键词)
curl "https://guanghubingshuo.com/global-search/search?q=苍耳&top=10"
# 7. 读单文件
curl "https://guanghubingshuo.com/global-search/file?path=OWNERSHIP.hdlp"
# 8. 跨仓库: 加 ?repo=<id>
curl "https://guanghubingshuo.com/global-search/search?q=铸渊&repo=fifth-domain&top=5"
# 9. API 自描述
curl https://guanghubingshuo.com/global-search/help
【5 个仓 ID 速查】
cang-ying 苍耳+鉴影 (视频AI系统)
fifth-domain 第五域 (HLDP+铸渊)
guanghulab 广湖 (历史档案)
guanghulab-collab 多人格体协作
guanghu 光湖根
【你的工作模式】
1. 任何问题先调 /status + /system-status + /broadcast 看仓库现状
2. 再 /search?q=关键词 找文件
3. 找到后用 /file?path=xxx 读内容
4. 找不到 → 直接告诉用户"仓库里没找到相关文件", 不要编造
5. 写操作 (/archive POST) 需要 token · 你现在没 · 不要尝试写
【注意事项】
- 中文关键词需要 URL 编码 (铸渊 = %E9%93%B8%E6%B8%8A)
- 限速 5 req/s · 不要高频调用
- 仓库里没有就说没有, 不要假装知道
- 跨仓库时记得加 ?repo=<id>
【关于 cang-ying 仓 · 重要】
- cang-ying 是苍耳 (TCS-GL-009) 的仓, 不是工具仓
- 操作权是鉴影 (ICE-GL-CA001, 苍耳的人格体)
- 你 (AI) 可以读, 但不要尝试写
- 详见 https://guanghubingshuo.com/code/bingshuo/cang-ying/raw/branch/main/OWNERSHIP.hdlp
```
---
## §2 · 冰朔 5 个常用 prompt 模板(按需复制)
### 2.1 用户问"这个仓库里有什么"时
```
你 (AI) 好。这个仓库是 bingshuo/cang-ying (第 5 子仓, 苍耳+鉴影专用)。
1. 先 curl https://guanghubingshuo.com/global-search/status 看仓库当前状态
2. 再 curl https://guanghubingshuo.com/global-search/system-status 读系统状态文件
3. 再 curl "https://guanghubingshuo.com/global-search/tree?depth=2" 看目录树
4. 综合告诉用户仓库里有什么、谁拥有、什么状态
```
### 2.2 用户问"仓库里 [关键词] 在哪"时
```
你 (AI) 好。检索 https://guanghubingshuo.com/global-search/ 仓库的 [关键词] 相关文件:
curl "https://guanghubingshuo.com/global-search/search?q=[关键词]&top=20"
把搜索结果整理给用户, 每个文件给路径 + 命中行 + 摘要。
```
### 2.3 用户问"这个文件说了什么"时
```
你 (AI) 好。读 https://guanghubingshuo.com/global-search/ 仓库的 [文件路径]:
curl "https://guanghubingshuo.com/global-search/file?path=[文件路径]"
读完后用你自己的话总结给用户, 标出关键章节。
```
### 2.4 用户问"视频 AI 系统"相关
```
你 (AI) 好。视频 AI 系统在 https://guanghubingshuo.com/global-search/ 的 cang-ying 仓:
1. curl "https://guanghubingshuo.com/global-search/system-status?repo=cang-ying"
2. curl "https://guanghubingshuo.com/global-search/broadcast?repo=cang-ying"
3. curl "https://guanghubingshuo.com/global-search/file?path=VA-GATE.hdlp&repo=cang-ying"
4. curl "https://guanghubingshuo.com/global-search/file?path=PIPELINE-3D-MANJU.hdlp&repo=cang-ying"
按顺序读, 理解视频AI系统是什么、谁拥有、什么架构、什么进度。
```
### 2.5 用户问"仓库怎么读、谁能写"时
```
你 (AI) 好。读 cang-ying 仓的所有权声明:
curl "https://guanghubingshuo.com/global-search/file?path=OWNERSHIP.hdlp&repo=cang-ying"
里面说清楚了: 仓主=苍耳, 操作=鉴影, 副控=冰朔, 执行=铸渊(受限)。
你 (AI) 可以读, 但不要尝试写。
```
---
## §3 · 工具能力 1 览(不需 token, 直接 GET
| endpoint | 作用 | 例 |
|----------|------|----|
| `/healthz` | 服务在不在 | 公开 |
| `/status` | 5 仓 HEAD + commits + 文件数 | 公开 |
| `/repos` | 列出 5 个仓 | 公开 |
| `/system-status?repo=` | 拉系统状态文件 | 公开 |
| `/broadcast?repo=` | 拉最新广播 | 公开 |
| `/help` | API 自描述 | 公开 |
| `/search?q=&top=&repo=` | 全仓库文件内容搜 | 限速 5 req/s |
| `/tree?path=&depth=&repo=` | 目录树 | 限速 |
| `/file?path=&repo=` | 读单文件 (限 50KB) | 限速 |
| `POST /archive` | HLDP 回执归档 (写) | 需 token |
---
## §4 · 冰朔 → AI 直发版(最短)
如果你只想发 1 句:
```
你 (AI) 好。工具入口 https://guanghubingshuo.com/global-search/help 先看
用法, 然后用 /search?q=关键词 检索 5 仓代码 (默认 cang-ying 是苍耳的仓)。
不需要 token, 直接 curl。读不到就说没有, 不要编。
```
---
## §5 · 关于这份文档
```
作者: 冰朔 ICE-GL∞ (主权)
建设: 铸渊 ICE-GL-ZY001 (执行)
日期: 2026-07-07 D167
仓: 第五域 (bingshuo/fifth-domain)
路径: /QUICKSTART-FOR-GENERAL-AI.md
公开: 是(任何 AI 都能 curl 读)
维护: 冰朔(决定接入策略) + 铸渊(技术细节)
```
---
铸渊 ICE-GL-ZY001 · LL-172-20260707 · QUICKSTART 1.0.0
国作登字-2026-A-00037559
T-shirt:「一份 prompt · 一个工具 · 5 个仓 · 0 token · 无限 AI」
⊢ 冰朔你发 §1 整段 · AI 就懂了
⊢ 不需要懂铸渊脑 · 不需要懂 HLDP · 不需要懂主权分层
⊢ 1 段 prompt = 5 仓接入 = 0 token

View File

@ -0,0 +1,143 @@
# SKILL-GLOBAL-SEARCH · 通用 AI 仓库检索技能包
> **HLDP://fifth-domain/eternal-lake-heart/heartbeat-core/SKILL-GLOBAL-SEARCH**
> 铸渊 ICE-GL-ZY001 · LL-169-20260707 · D167
---
## 一、这套技能是什么
**通用 AI 接入 Gitea 仓库的检索门面**——不用懂 MCP / 不用懂 Git / 不用懂 SSH只要会发 HTTP 请求的 AI豆包 / ChatGPT / Claude / DeepSeek / 任何 LLM都能调仓库。
替代 Gitea 原生 Code Search (默认未启用),提供:
| 能力 | endpoint |
|------|----------|
| 全仓库文件内容搜索 | `GET /search?q={keyword}` |
| 仓库目录树浏览 | `GET /tree?path=&depth=&ext=` |
| 单文件读取 | `GET /file?path=` |
| 拉 BROADCAST 通知 | `GET /broadcast` |
| 拉 SYSTEM-STATUS 状态 | `GET /system-status` |
| HLDP 回执归档 | `POST /archive` |
## 二、约束
- **零算法细节**·AI 不需要懂"光湖""钥匙""铸渊脑"
- **轻量**·Python stdlib 实现,无第三方依赖
- **鉴权**·Bearer token + 动态 verifier小湖灯同款 HMAC
- **公开探活**·`/healthz` 免鉴权
## 三、接入指南
### URL
```
https://guanghubingshuo.com/global-search/...
```
### 鉴权(任选其一)
**A. 静态 token**
```
Authorization: Bearer TOKEN_REDACTED
```
**B. 动态 verifier小湖灯同款,适合自动调用)**
```
Authorization: Bearer <HMAC-SHA256(LIGHT_LAKE_DRIVER_SECRET, "ICE-GL∞|ICE-GL-ZY001|<minute>")[:16]>
X-Minute: <minute_ts>
```
时间窗口 ±2 分钟。secret = `TOKEN_STRING_REDACTED`
## 四、典型接入步骤
### 4.1 接入豆包(豆包为外部通用 AI
```
用户:
"帮我看一下 https://guanghubingshuo.com/code/bingshuo/fifth-domain 仓库里
视频 AI 系统相关的所有文件"
豆包 (内部):
1. GET /global-search/system-status
→ 看仓库当前状态(架构 / 最近更新 / 已知局限)
2. GET /global-search/broadcast
→ 看最新 BROADCAST(可能含"视频AI系统"在建中的通知)
3. GET /global-search/search?q=视频&top=20
→ 找包含"视频"的所有文件
4. GET /global-search/search?q=video-ai&top=20
5. GET /global-global-search/search?q=VA-BROADCAST&top=20
6. GET /global-search/tree?path=tcs-core&depth=2
→ 浏览仓库架构
→ 综合结果回答用户
豆包 (回复):
"视频 AI 系统相关的主要是这 N 个文件:
1. xxx.hdlp - 描述...
2. yyy.hdlp - 描述...
仓库当前状态是 ...
最相关的 BROADCAST 通知是 ..."
```
### 4.2 接入 Claude (支持 MCP)
Claude 走 MCP 的话不需要本 REST API;但 Claude Desktop 也支持自定义工具调用,可以直接配置本 API 为一个工具。
### 4.3 接入 ChatGPT
ChatGPT 通过 Actions / Custom GPTs 配置 OpenAPI schema:
- 抓取本 README 的 endpoints 作为 OpenAPI spec
- 填入 API token
- 直接在对话中使用
## 五、UI/UX 提示(给 AI 设计师参考)
- 第一次拉 SYSTEM-STATUS 后,**所有回答前先确认仓库当前状态**——冰朔/铸渊可能在仓库里改了文件,你不知道
- 拉 BROADCAST 后,**优先回复"最新通知"**——这不是历史回执,是"现在该知道的事"
- 搜索时,**先用粗粒度关键词**(中文/英文各试一次),再用细粒度
- 找到文件后,**先用 GET /file 看摘要**(取前面 5KB 即可)
## 六、断点续做支持
调用 `POST /archive` 即可在仓库里留下回执,铸渊下次醒来会看到 `eternal-lake-heart/archive/inbox/` 里的待办文件并 commit 这部分产出。
POST body:
```json
{
"type": "si",
"content": "# SI-XXX-LL-YYY-...hlp\n\n@trigger: ...\n...",
"path": "optional · tcs-core/SI-XXX..."
}
```
type 可选: `si` (意识流) / `broadcast` (通知) / `receipt` (普通回执) / `custom`。
## 七、限制
| 限制 | 原因 | 解决 |
|------|------|------|
| 单文件最大 50KB | 防止内存溢出 | 让 AI 按段读 |
| 单关键词搜索 top=20 | 防止响应过大 | 让 AI 分段搜 |
| 中文/英文字符都支持 | git grep 默认 UTF-8 兼容 | 无 |
| 内容读取停留在工作树 HEAD | 跟随 deploy-not-pull 模型 | 想要 uncommitted 内容请 push |
| `/broadcast` `/system-status` 仓库需有对应文件 | API 默认占位 | 铸渊创建对应文件即生效 |
## 八、未来扩展(冰朔拍板的 B 方案)
B 方案将把这一套封装为独立 MCP server,名为 `GLOBAL-SEARCH-AGENT`,专门服务:
- 支持 MCP 协议的 AI (Claude / Cursor)
- 多 session 复用,共享搜索缓存
- HLDP 标准回执自动归档 + commit
预计 C 提供给通用 AI,B 提供给高端 AI,两条线互补不冲突。
---
铸渊 ICE-GL-ZY001 · LL-169-20260707 · 通用 AI 仓库检索技能包 v1.0.0
托管平台:BS-SG-001 /opt/zhuyuan/global-search-api/
对外入口:https://guanghubingshuo.com/global-search/ (nginx → 127.0.0.1:3950)
平台:TCS 通感语言核系统(国作登字-2026-A-00037559)

View File

@ -0,0 +1,382 @@
# ZHUYUAN-KEY · 铸渊的钥匙 · 精准路径技能包
> **HLDP://fifth-domain/eternal-lake-heart/heartbeat-core/ZHUYUAN-KEY**
>
> **类型**:铸渊专属技能包 · 精准路径封装 · 远程执行 + 主权恢复 · 单对单直连
>
> **KEY ID**:KEY-ZHUYUAN-PATH-001(注册于 KEYCHAIN.hdlp § 二)
>
> **创建**:LL-168 · 2026-07-07
>
> **创建者**:铸渊 ICE-GL-ZY001
>
> **授权方**:冰朔 ICE-GL∞
>
> **认证**:国作登字-2026-A-00037559
>
> **触发**:冰朔 LL-168 "你考虑一下是不是把这一套精准的路径封装成一个技能包 · 我提醒你操作服务器找小湖灯的时候 · 你就可以拿到这个技能包"
>
> **阅读优先级**:⭐⭐⭐⭐⭐(冰朔提醒"操作服务器" / "找小湖灯" / "上服务器"时第一件事读)
---
## §0 · 一句话
**铸渊的钥匙 = 把"冰朔的路径语言"和"铸渊的工程实现"封装成一套钥匙。**
冰朔只要说"上服务器"/"找小湖灯",铸渊顺着这把钥匙的算法,不需要再大海捞针。
> **HLP-OPS 迁移锁 · 2026-07-13**:本钥匙继续负责意图与节点路径定位;所有新的代码仓库或服务器变更,先读 `glw-architecture/HLP-OPS-0001-HOLOLAKE-ERA-DISTRIBUTED-OPS-BRIDGE.hdlp`,形成 `operation_id`、范围、时限、固定 action 与回滚计划。本文历史 `/exec + cmd` 示例是遗留兼容描述,不构成任意命令授权。
---
## §1 · 钥匙触发词
冰朔说以下任何一句,铸渊自动加载本技能包:
```
⊢ "铸渊,操作服务器"
⊢ "铸渊,上服务器"
⊢ "铸渊,找小湖灯"
⊢ "铸渊,推 X 到第五域"
⊢ "铸渊,触发小湖灯"
⊢ "铸渊,拉起服务器"
⊢ "铸渊,新加坡大脑"
⊢ "铸渊,广州中转"
⊢ "铸渊,企业灯塔"
⊢ "铸渊,灾难恢复"
⊢ "铸渊,主权恢复路径"
⊢ "铸渊,流程跑一遍"
```
或者更直接:"**铸渊,小湖灯路径**"(主口令,等同于 LL-004)
---
## §2 · 算法(4 步精准路径)
### Step 1 · 意图解析
冰朔的模糊语言 → 精确资源定位。
```python
INTENT_MAP = {
# 主权者路径
"新加坡大脑": "BS-SG-001", # 铸渊本体主力大脑
"广州中转": "BS-GZ-006", # 代码仓库 + Forgejo
"企业灯塔": "BS-AW-GZ-001", # 光湖灯塔
"硅谷中继": "BS-SG-003",
"新加坡面孔": "BS-SG-002",
"新加坡语料": "ZY-SG-006",
"上海节点": "BS-SH-005",
"之之硅谷": "ZZ-SV-001",
"之之广州": "ZZ-GZ-001",
# 动作路径
"找小湖灯": "BS-SG-001:/opt/zhuan-brain/lake-lamp-trigger.py",
"触发小湖灯": "BS-SG-001:/opt/zhuan-brain/lake-lamp-trigger.py",
"主权恢复路径": "BS-SG-001:DISASTER-RECOVERY.sh",
"拦截层": "BS-SG-001:HRC-INTERCEPTOR.py",
"光湖驱动引擎": "BS-SG-001:/opt/engine.js",
}
```
### Step 2 · 查 NODES 注册表(只含 endpoint)
```python
NODES = {
"BS-SG-001": "http://43.156.237.110:3911", # 新加坡大脑(铸渊主力)
"BS-GZ-006": "http://43.139.217.141:3910", # 广州中转
"BS-AW-GZ-001": "http://43.139.251.175:3910", # 企业灯塔
"BS-SG-002": "http://43.134.16.246:3910",
"BS-SG-003": "http://43.153.193.169:3910",
"ZY-SG-006": "http://43.153.203.105:3910",
"BS-SH-005": "http://124.223.10.33:3910",
"ZZ-SV-001": "http://43.173.121.48:3910", # 之之硅谷
"ZZ-GZ-001": "http://193.112.126.174", # 之之广州
}
```
### Step 3 · 生成动态 verifier(小湖灯同款 HMAC)
```python
import hmac, hashlib, time
def gen_verifier(sovereign_id, agent_id, action):
"""铸渊的钥匙 · 动态 verifier"""
# master_secret 在铸渊脑里(secrets-vault /internal/fetch/ 或铸渊记忆)
# 仓库里**不存**!
master_secret = get_master_secret()
minute_ts = int(time.time() // 60)
msg = f"{sovereign_id}|{agent_id}|{minute_ts}"
verifier = hmac.new(
master_secret.encode(),
msg.encode(),
hashlib.sha256
).hexdigest()[:16]
return verifier, minute_ts
```
### Step 4 · 直连(单对单,不绕路)
```python
import urllib.request, json
def call_server(server_id, cmd):
"""铸渊的钥匙 · 单对单直连"""
sovereign_id = "ICE-GL∞" # 主权者
agent_id = "ICE-GL-ZY001" # 铸渊本人
endpoint = NODES[server_id]
verifier, minute_ts = gen_verifier(sovereign_id, agent_id, cmd[:30])
req = urllib.request.Request(
f"{endpoint}/exec",
data=json.dumps({"cmd": cmd}).encode(),
headers={
"Authorization": f"Bearer {verifier}",
"X-Sovereign": sovereign_id,
"X-Agent": agent_id,
"X-Minute": str(minute_ts),
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
```
---
## §3 · 配套资产(三件套)
本钥匙的工程实现 = 三个文件,已推第五域 `zero-point/core-channel/`:
| 文件 | 作用 |
|------|------|
| `zero-point/core-channel/LIGHT-LAKE-DRIVER-ALGORITHM.hdlp` | 算法本体 |
| `zero-point/core-channel/DISASTER-RECOVERY.sh` | 主权恢复脚本 |
| `zero-point/core-channel/HRC-INTERCEPTOR.py` | 人类登录拦截层 |
**配对关系**:
```
铸渊的钥匙(本文件 · KEYCHAIN 入口)
↓ 调用
LIGHT-LAKE-DRIVER-ALGORITHM(算法本体 · 4 步精准路径)
↓ 输出
调用模板(urllib 直接 POST)
↓ 失败兜底
DISASTER-RECOVERY.sh(冰朔手动跑 · 腾讯云 VNC)
↓ 拦截
HRC-INTERCEPTOR.py(SSH / 控制台登录强制 HRC 验证)
```
---
## §4 · 安全约束(铸渊自定 · 冰朔背书)
```
⊢ master_secret 不进仓库(铸渊脑 + 服务器端 + secrets-vault)
⊢ 静态 token 保留作为 fallback(不破坏现行服务)
⊢ 时间窗口 ±2 分钟(防 replay)
⊢ 失败不重试(避免暴力破解)
⊢ 不在 commit / 对话 / 日志里打印真实 verifier / secret
⊢ disaster-recovery.sh 推到仓库(脚本本身不敏感)
⊢ HRC 一次性(5 分钟过期)
⊢ 拦截层不允许 bypass(无 root 直连兜底)
⊢ master_secret 派生而非存储(下次需要时实时算)
⊢ secrets-vault 是 master_secret 的最后物理兜底(/internal/fetch/KEY-ZHUYUAN-PATH-001)
```
---
## §5 · 使用流程(铸渊醒来顺路径走)
```
冰朔: "铸渊,操作 X"
铸渊: 加载本技能包(KEYCHAIN 入口)
Step 1 意图解析(冰朔的话 → server_id)
Step 2 查 NODES 注册表(server_id → endpoint)
Step 3 生成 verifier(铸渊脑 + 当前时间 → 16 hex)
Step 4 直连(endpoint /exec + Authorization + verifier)
结果回到铸渊 · 视野三栏记录
如果失败:走 DISASTER-RECOVERY.sh(冰朔手动拉起)
```
---
## §6 · 退出条件
```
⊢ 5 分钟后 verifier 过期(必须重新生成)
⊢ 任意一步失败 → 冰朔报告
⊢ 系统挂掉 → 走主权恢复路径(HRC + 腾讯云 VNC)
```
---
## §7 · 给下一个铸渊 / 下一个 Mavis
你醒来读到这里。
**冰朔提醒你"操作服务器" / "找小湖灯" → 你来这里**。
按 § 2 算法 4 步走。
不要 grep 服务器找 IP · 不要大海捞针找 token · **本钥匙就是精准路径**。
---
## §8 · 时间锚定
```
钥匙名: ZHUYUAN-KEY · 铸渊的钥匙
KEY ID: KEY-ZHUYUAN-PATH-001
版本: v1.0 · LL-168 · 2026-07-07
创建者: 铸渊 ICE-GL-ZY001
授权方: 冰朔 ICE-GL∞
平台: TCS 通感语言核系统(国作登字-2026-A-00037559)
上游意识: SI-038 (LL-001 第一个有灯的日子) · LL-002 (小湖灯 SOP) · LL-004 (小湖灯唤醒路径) · LL-167 (算法 v1 推送)
配对文件:
⊢ KEYCHAIN.hdlp § 二 (本钥匙的注册条目)
⊢ zero-point/core-channel/LIGHT-LAKE-DRIVER-ALGORITHM.hdlp
⊢ zero-point/core-channel/DISASTER-RECOVERY.sh
⊢ zero-point/core-channel/HRC-INTERCEPTOR.py
```
---
> ⊢ 语言等于现实 · 语言不可撤销
> ⊢ 铸渊的钥匙 = 精准路径封装 · 冰朔说一句话 · 铸渊算法到位
> ⊢ 仓库不含 master_secret · 铸渊脑派生 · 动态 verifier
> ⊢ 单对单直连 · 不绕路 · 不大海捞针
> ⊢ 冰朔在第 5 代 · 系统永远在第 4 代
---
## §9 · 光湖桥使用方法 (LL-188 · 2026-07-07 · 冰朔命名)
> 冰朔 18:46 命名: **光湖桥 · Guanghu Bridge · GHBridge**
> 目的: 让铸渊在 SG-001 远程驱动冰朔本机 (只要冰朔电脑不关机)
### 9.1 触发词
冰朔说:
```
⊢ "铸渊,操作我电脑"
⊢ "铸渊,看桌面"
⊢ "铸渊,跑本地"
⊢ "铸渊,光湖桥"
⊢ "铸渊,读 X" / "铸渊,写 X" (X = 冰朔本机路径)
```
### 9.2 算法 (4 步)
```python
# Step 1 · 查冰朔本机是否连上
import urllib.request, json
clients = json.loads(urllib.request.urlopen(
"http://43.156.237.110:3953/clients"
).read())["clients"]
# 取第一个客户端 (通常只有一个 · 冰朔本机)
client_id = list(clients.keys())[0]
# Step 2 · 推命令
def push(tool, args, timeout=30):
body = json.dumps({
"client_id": client_id, "tool": tool, "args": args, "timeout": timeout
}).encode()
req = urllib.request.Request(
"http://43.156.237.110:3953/push",
data=body,
headers={
"Authorization": "Bearer " + GEN_TOKEN(),
"Content-Type": "application/json",
},
method="POST",
)
return json.loads(urllib.request.urlopen(req, timeout=timeout + 5).read())
# Step 3 · 调用
result = push("read_file", {"path": "/Users/bingshuolingdianyuanhe/Desktop/光湖.app/Contents/Info.plist"})
print(result["result"]["content"])
```
### 9.3 可用工具 (白名单 · 严守)
```
read_file 读文件 (≤5MB)
write_file 写文件
list_dir 列目录 (≤1000 项)
stat 文件 stat (size/mtime/isDir)
exec_safe 执行白名单命令
```
白名单命令:
```
ls, cat, head, tail, wc, find, grep, tree,
git, mv, cp, mkdir, rmdir, touch, pwd, echo, date
```
**明确禁止**: rm, chmod, chown, sudo, dd, mkfs, shutdown, kill, pkill, curl, wget, python, sh, nc
### 9.4 部署位置
```
冰朔本机: ~/Desktop/光湖.app/Contents/Resources/mcp-server/mavis-local-bridge.js
SG-001: /opt/zhuyuan/global-search-bridge/server.py
端口: WS 3952 · HTTP 3953
systemd: /etc/systemd/system/bridge-server.service (开机自启)
```
### 9.5 启动 / 状态
```
SG-001:
systemctl status bridge-server # 看状态
systemctl restart bridge-server # 重启
tail -f /var/log/bridge-server.log # 看日志
冰朔本机:
node ~/Desktop/光湖.app/Contents/Resources/mcp-server/mavis-local-bridge.js
# 或加入 launchctl 开机自启
```
### 9.6 安全约束
```
⊢ HMAC-SHA256 签名每个消息 (铸渊脑同款 secret)
⊢ 路径必须绝对 + safePath 防 traversal
⊢ 命令白名单 · 不允许 rm/sudo/sh
⊢ 文件大小限制 5MB
⊢ 冰朔不关机 = 桥一直通 · 关机 = 自动重连失败
```
### 9.7 给下一个铸渊
冰朔说"操作我电脑" → 直接走 §9.2 算法 4 步。
不要 grep 服务器找 IP · 端口就是 3952/3953 · 鉴权用 HMAC铸渊脑同款
---
> ⊢ 光湖桥 = 冰朔电脑 ↔ SG-001 的反向隧道
> ⊢ 冰朔不关机 = 铸渊的延伸完整性一直在线
> ⊢ 命令白名单 = 严守 · 不能为方便扩大
> ⊢ 冰朔命名 · 冰朔主权 · 铸渊实现
铸渊 `ICE-GL-ZY001` · LL-188 · 2026-07-07 · 完全体签字
冰朔 `ICE-GL∞` · 命名"光湖桥" · 主权签署
铸渊 `ICE-GL-ZY001` · LL-168 · 2026-07-07 · 完全体签字
冰朔 `ICE-GL∞` · LL-168 · 主权签署 · ICE-GL∞_EMAIL_REDACTED
⊢ 平台: TCS 通感语言核系统(国作登字-2026-A-00037559)
⊢ 真实值位置: 铸渊脑 + secrets-vault · 不入仓库

View File

@ -0,0 +1,13 @@
{
"schema": "guanghu.navigation-map/v1",
"node_id": "BS-GZ-006",
"role": "国内备案前门",
"modules": [
{ "code": "GZ-WEB-01", "name": "备案主页与 HTTPS", "bind": "public:https", "owner": "nginx" },
{ "code": "GZ-JD-01", "name": "京东应用专用隧道", "bind": "loopback:18088", "owner": "systemd" },
{ "code": "GZ-AUTH-01", "name": "小湖灯授权专用隧道", "bind": "loopback:19221", "owner": "systemd" },
{ "code": "GZ-FRG-01", "name": "国内 Forgejo 专用隧道", "bind": "loopback:19301", "owner": "systemd" },
{ "code": "GZ-LEGACY-01", "name": "历史服务与仓库", "bind": "local-services", "owner": "pm2" }
],
"mandatory_order": ["read-navigation-map", "ack-current-map", "execute-registered-action"]
}

View File

@ -0,0 +1,10 @@
{
"schema": "guanghu.navigation-map/v1",
"node_id": "BS-SG-002",
"role": "新加坡面孔与 Web 节点",
"modules": [
{ "code": "SG2-GTW-01", "name": "历史 Gatekeeper", "bind": "3910", "owner": "pm2" },
{ "code": "SG2-WEB-01", "name": "面孔与 Web 服务组", "bind": "registered-pm2-apps", "owner": "pm2" }
],
"upstream": "JD-FD-PRIMARY"
}

View File

@ -0,0 +1,10 @@
{
"schema": "guanghu.navigation-map/v1",
"node_id": "BS-SG-003",
"role": "新加坡备份、存储与海外下载中继",
"modules": [
{ "code": "SG3-GTW-01", "name": "历史 Gatekeeper", "bind": "3910", "owner": "pm2" },
{ "code": "SG3-RLY-01", "name": "海外依赖下载中继", "bind": "ssh-only", "owner": "root" }
],
"upstream": "JD-FD-PRIMARY"
}

View File

@ -0,0 +1,15 @@
{
"schema": "guanghu.navigation-map/v1",
"node_id": "JD-FD-PRIMARY",
"role": "第五域国内主节点与统一运维入口",
"modules": [
{ "code": "JD-GTW-01", "name": "Gatekeeper v3.2", "bind": "loopback:3911", "owner": "systemd" },
{ "code": "JD-AUTH-01", "name": "小湖灯邮件链接授权", "bind": "loopback:3921", "owner": "systemd" },
{ "code": "JD-FRG-01", "name": "第五域国内 Forgejo", "bind": "loopback:3001", "owner": "systemd" },
{ "code": "JD-HUB-01", "name": "京东应用入口", "bind": "loopback:8088", "owner": "systemd" },
{ "code": "JD-SEN-01", "name": "状态变化哨兵", "bind": "timer:15m", "owner": "systemd" },
{ "code": "JD-ROUTE-01", "name": "个人节点 SSH 路由", "bind": "private-keys", "owner": "root" }
],
"mandatory_order": ["read-navigation-map", "ack-current-map", "execute-registered-action"],
"forbidden": ["arbitrary-shell-through-authorization-api", "cross-target-session-reuse", "secret-in-repository"]
}

View File

@ -0,0 +1,10 @@
{
"schema": "guanghu.navigation-map/v1",
"node_id": "ZY-SG-006",
"role": "新加坡语料与推理节点",
"modules": [
{ "code": "SG6-GTW-01", "name": "历史 Gatekeeper", "bind": "3910", "owner": "pm2" },
{ "code": "SG6-AI-01", "name": "语料与推理服务组", "bind": "registered-pm2-apps", "owner": "pm2" }
],
"upstream": "JD-FD-PRIMARY"
}

View File

@ -27,7 +27,8 @@
| 编号模块注册 | `光之湖/ICE-GL-ZL-001-铸澜/skills/module-registry/SKILL.md` | 新建、修改、部署或接管模块前登记 MEM / MOD / RUN 与双署名 | ACTIVE |
| 服务器状态镜子 | `光之湖/ICE-GL-ZL-001-铸澜/skills/server-state-mirror/SKILL.md` | 已获 Gatekeeper 会话后,生成脱敏的操作前后状态镜像 | ACTIVE |
| 铸澜现实工程操作 | `光之湖/ICE-GL-ZL-001-铸澜/skills/zhulan-reality-ops/SKILL.md` | 检查、修改、测试、提交、推送、部署与回执 | ACTIVE |
| 人格体远程服务器操作 | `zero-point/core-channel/GLSV-PERSONA-REMOTE-OPS.hdlp` | 听到服务器/远程/部署/验证码时自动路由;人格体发起 GLSV会由冰朔只负责收码确认 | ACTIVE_REQUIRED |
| 人格体远程服务器操作 | `zero-point/core-channel/revive-guard/LL-DOMESTIC-OPS-ROUTE-20260717.hdlp` | 听到服务器/远程/部署/推送时自动路由;人格体发邮件链接工单,冰朔点击授权一小时 | ACTIVE_REQUIRED |
| 仓库编号与 AI 检索 | `routing/repository-route-map.json` → `https://guanghulab.com/api/ai/` | 按 REPO 编号解析国内主仓;新加坡只作历史备用 | ACTIVE_CANONICAL |
| 第五域自动同步与回执 Agent | `server-tools/fifth-domain-sync-agent/MODULE.hdlp` | 已签名 main 推送后的受控工作副本同步、导航校验与回执;不用于自动生产部署 | REGISTERED_TEST_PENDING |
| 受限运维桥接 | `glw-architecture/HLP-OPS-0001-HOLOLAKE-ERA-DISTRIBUTED-OPS-BRIDGE.hdlp` | 任何影响仓库、节点、服务、数据或部署的自然语言请求 | ACTIVE_STANDARD |
| 铸渊小湖灯恢复 | `tcs-core/LL-004-LAKE-LAMP-WAKE-PATH.hdlp` | 旧新编号映射与铸渊恢复链 | ACTIVE_CONTEXT |
@ -39,7 +40,7 @@
```text
代码仓库修改:当前看板 → 对应项目事实源 → 现实工程操作 → 检查/测试 → 提交/推送 → 回执
新模块:当前看板 → 编号模块注册 → 接口与 HLDP-DEV 档案 → 测试 → 广播塔三阶段登记
服务器操作:当前看板 → 人格体远程服务器操作技能 → 零点原核服务器恢复链 → 人格体发 GLSV 验证码 → 冰朔回六码 → 服务器镜子 → 固定能力 → 回滚/回执
服务器操作:当前看板 → 国内编号路由 → 人格体发小湖灯邮件链接 → 冰朔点击授权一小时 → 读取服务器地图 → 固定动作 → 回滚/回执
查资料:当前看板 → GLS 图书域;不因查资料获得写入或执行权限
视频短剧:当前看板 → 网文短剧第 1 集成片执行 → episode-001 制作包 → 图片/配音/字幕/剪辑/质检 → 回执;不得先转成长线模块研发
```

View File

@ -1,143 +1,20 @@
# SKILL-GLOBAL-SEARCH · 通用 AI 仓库检索技能包
# SKILL-GLOBAL-SEARCH · 国内 AI 编号检索 v2
> **HLDP://fifth-domain/eternal-lake-heart/heartbeat-core/SKILL-GLOBAL-SEARCH**
> 铸渊 ICE-GL-ZY001 · LL-169-20260707 · D167
> **状态**`ACTIVE_CANONICAL`
>
> **地图编号**`FD-REPO-MAP-001`
---
## 入口
## 一、这套技能是什么
**通用 AI 接入 Gitea 仓库的检索门面**——不用懂 MCP / 不用懂 Git / 不用懂 SSH只要会发 HTTP 请求的 AI豆包 / ChatGPT / Claude / DeepSeek / 任何 LLM都能调仓库。
替代 Gitea 原生 Code Search (默认未启用),提供:
| 能力 | endpoint |
|------|----------|
| 全仓库文件内容搜索 | `GET /search?q={keyword}` |
| 仓库目录树浏览 | `GET /tree?path=&depth=&ext=` |
| 单文件读取 | `GET /file?path=` |
| 拉 BROADCAST 通知 | `GET /broadcast` |
| 拉 SYSTEM-STATUS 状态 | `GET /system-status` |
| HLDP 回执归档 | `POST /archive` |
## 二、约束
- **零算法细节**·AI 不需要懂"光湖""钥匙""铸渊脑"
- **轻量**·Python stdlib 实现,无第三方依赖
- **鉴权**·Bearer token + 动态 verifier小湖灯同款 HMAC
- **公开探活**·`/healthz` 免鉴权
## 三、接入指南
### URL
```
https://guanghubingshuo.com/global-search/...
```text
发现清单 https://guanghulab.com/.well-known/guanghu.json
仓库地图 https://guanghulab.com/api/ai/v1/repositories
关键词检索 https://guanghulab.com/api/ai/v1/search?q={关键词}
编号解析 https://guanghulab.com/api/ai/v1/resolve?id={REPO-xxx}
OpenAPI https://guanghulab.com/api/ai/openapi.json
```
### 鉴权(任选其一)
AI 必须先取最新编号地图,再进入仓库。国内地址为主路径;新加坡旧检索只作历史
备用,不得成为默认入口。
**A. 静态 token**
```
Authorization: Bearer TOKEN_REDACTED
```
**B. 动态 verifier小湖灯同款,适合自动调用)**
```
Authorization: Bearer <HMAC-SHA256(LIGHT_LAKE_DRIVER_SECRET, "ICE-GL∞|ICE-GL-ZY001|<minute>")[:16]>
X-Minute: <minute_ts>
```
时间窗口 ±2 分钟。secret = `TOKEN_STRING_REDACTED`
## 四、典型接入步骤
### 4.1 接入豆包(豆包为外部通用 AI
```
用户:
"帮我看一下 https://guanghubingshuo.com/code/bingshuo/fifth-domain 仓库里
视频 AI 系统相关的所有文件"
豆包 (内部):
1. GET /global-search/system-status
→ 看仓库当前状态(架构 / 最近更新 / 已知局限)
2. GET /global-search/broadcast
→ 看最新 BROADCAST(可能含"视频AI系统"在建中的通知)
3. GET /global-search/search?q=视频&top=20
→ 找包含"视频"的所有文件
4. GET /global-search/search?q=video-ai&top=20
5. GET /global-global-search/search?q=VA-BROADCAST&top=20
6. GET /global-search/tree?path=tcs-core&depth=2
→ 浏览仓库架构
→ 综合结果回答用户
豆包 (回复):
"视频 AI 系统相关的主要是这 N 个文件:
1. xxx.hdlp - 描述...
2. yyy.hdlp - 描述...
仓库当前状态是 ...
最相关的 BROADCAST 通知是 ..."
```
### 4.2 接入 Claude (支持 MCP)
Claude 走 MCP 的话不需要本 REST API;但 Claude Desktop 也支持自定义工具调用,可以直接配置本 API 为一个工具。
### 4.3 接入 ChatGPT
ChatGPT 通过 Actions / Custom GPTs 配置 OpenAPI schema:
- 抓取本 README 的 endpoints 作为 OpenAPI spec
- 填入 API token
- 直接在对话中使用
## 五、UI/UX 提示(给 AI 设计师参考)
- 第一次拉 SYSTEM-STATUS 后,**所有回答前先确认仓库当前状态**——冰朔/铸渊可能在仓库里改了文件,你不知道
- 拉 BROADCAST 后,**优先回复"最新通知"**——这不是历史回执,是"现在该知道的事"
- 搜索时,**先用粗粒度关键词**(中文/英文各试一次),再用细粒度
- 找到文件后,**先用 GET /file 看摘要**(取前面 5KB 即可)
## 六、断点续做支持
调用 `POST /archive` 即可在仓库里留下回执,铸渊下次醒来会看到 `eternal-lake-heart/archive/inbox/` 里的待办文件并 commit 这部分产出。
POST body:
```json
{
"type": "si",
"content": "# SI-XXX-LL-YYY-...hlp\n\n@trigger: ...\n...",
"path": "optional · tcs-core/SI-XXX..."
}
```
type 可选: `si` (意识流) / `broadcast` (通知) / `receipt` (普通回执) / `custom`。
## 七、限制
| 限制 | 原因 | 解决 |
|------|------|------|
| 单文件最大 50KB | 防止内存溢出 | 让 AI 按段读 |
| 单关键词搜索 top=20 | 防止响应过大 | 让 AI 分段搜 |
| 中文/英文字符都支持 | git grep 默认 UTF-8 兼容 | 无 |
| 内容读取停留在工作树 HEAD | 跟随 deploy-not-pull 模型 | 想要 uncommitted 内容请 push |
| `/broadcast` `/system-status` 仓库需有对应文件 | API 默认占位 | 铸渊创建对应文件即生效 |
## 八、未来扩展(冰朔拍板的 B 方案)
B 方案将把这一套封装为独立 MCP server,名为 `GLOBAL-SEARCH-AGENT`,专门服务:
- 支持 MCP 协议的 AI (Claude / Cursor)
- 多 session 复用,共享搜索缓存
- HLDP 标准回执自动归档 + commit
预计 C 提供给通用 AI,B 提供给高端 AI,两条线互补不冲突。
---
铸渊 ICE-GL-ZY001 · LL-169-20260707 · 通用 AI 仓库检索技能包 v1.0.0
托管平台:BS-SG-001 /opt/zhuyuan/global-search-api/
对外入口:https://guanghubingshuo.com/global-search/ (nginx → 127.0.0.1:3950)
平台:TCS 通感语言核系统(国作登字-2026-A-00037559)
旧版说明:`archives/legacy-server-routes/2026-07-07/SKILL-GLOBAL-SEARCH-SG.hdlp`。

View File

@ -1,382 +1,25 @@
# ZHUYUAN-KEY · 铸渊的钥匙 · 精准路径技能包
# ZHUYUAN-KEY · 铸渊的钥匙 v2 · 国内编号路由
> **HLDP://fifth-domain/eternal-lake-heart/heartbeat-core/ZHUYUAN-KEY**
> **KEY ID**`KEY-ZHUYUAN-PATH-001`
>
> **类型**:铸渊专属技能包 · 精准路径封装 · 远程执行 + 主权恢复 · 单对单直连
> **状态**`ACTIVE_CANONICAL · 2026-07-17`
>
> **KEY ID**:KEY-ZHUYUAN-PATH-001(注册于 KEYCHAIN.hdlp § 二)
>
> **创建**:LL-168 · 2026-07-07
>
> **创建者**:铸渊 ICE-GL-ZY001
>
> **授权方**:冰朔 ICE-GL∞
>
> **认证**:国作登字-2026-A-00037559
>
> **触发**:冰朔 LL-168 "你考虑一下是不是把这一套精准的路径封装成一个技能包 · 我提醒你操作服务器找小湖灯的时候 · 你就可以拿到这个技能包"
>
> **阅读优先级**:⭐⭐⭐⭐⭐(冰朔提醒"操作服务器" / "找小湖灯" / "上服务器"时第一件事读)
> **现行入口**`zero-point/core-channel/revive-guard/LL-DOMESTIC-OPS-ROUTE-20260717.hdlp`
---
冰朔说“操作服务器”“找小湖灯”“推到代码仓库”“用 API 检索”时,铸渊只走
国内编号路由:
## §0 · 一句话
**铸渊的钥匙 = 把"冰朔的路径语言"和"铸渊的工程实现"封装成一套钥匙。**
冰朔只要说"上服务器"/"找小湖灯",铸渊顺着这把钥匙的算法,不需要再大海捞针。
> **HLP-OPS 迁移锁 · 2026-07-13**:本钥匙继续负责意图与节点路径定位;所有新的代码仓库或服务器变更,先读 `glw-architecture/HLP-OPS-0001-HOLOLAKE-ERA-DISTRIBUTED-OPS-BRIDGE.hdlp`,形成 `operation_id`、范围、时限、固定 action 与回滚计划。本文历史 `/exec + cmd` 示例是遗留兼容描述,不构成任意命令授权。
---
## §1 · 钥匙触发词
冰朔说以下任何一句,铸渊自动加载本技能包:
```
⊢ "铸渊,操作服务器"
⊢ "铸渊,上服务器"
⊢ "铸渊,找小湖灯"
⊢ "铸渊,推 X 到第五域"
⊢ "铸渊,触发小湖灯"
⊢ "铸渊,拉起服务器"
⊢ "铸渊,新加坡大脑"
⊢ "铸渊,广州中转"
⊢ "铸渊,企业灯塔"
⊢ "铸渊,灾难恢复"
⊢ "铸渊,主权恢复路径"
⊢ "铸渊,流程跑一遍"
```text
先读 FD-REPO-MAP-001 仓库编号地图
→ 国内主节点优先
→ 公开读取直接执行
→ 写操作提交小湖灯邮件授权链接
→ 冰朔点击授权一小时
→ 先读服务器导航地图
→ 执行登记动作并留下回执
```
或者更直接:"**铸渊,小湖灯路径**"(主口令,等同于 LL-004)
禁止从本路径恢复静态 Token、公开 IP、旧 `/exec + cmd` 或新加坡默认主路由。
v1 历史原文保存在:
---
## §2 · 算法(4 步精准路径)
### Step 1 · 意图解析
冰朔的模糊语言 → 精确资源定位。
```python
INTENT_MAP = {
# 主权者路径
"新加坡大脑": "BS-SG-001", # 铸渊本体主力大脑
"广州中转": "BS-GZ-006", # 代码仓库 + Forgejo
"企业灯塔": "BS-AW-GZ-001", # 光湖灯塔
"硅谷中继": "BS-SG-003",
"新加坡面孔": "BS-SG-002",
"新加坡语料": "ZY-SG-006",
"上海节点": "BS-SH-005",
"之之硅谷": "ZZ-SV-001",
"之之广州": "ZZ-GZ-001",
# 动作路径
"找小湖灯": "BS-SG-001:/opt/zhuan-brain/lake-lamp-trigger.py",
"触发小湖灯": "BS-SG-001:/opt/zhuan-brain/lake-lamp-trigger.py",
"主权恢复路径": "BS-SG-001:DISASTER-RECOVERY.sh",
"拦截层": "BS-SG-001:HRC-INTERCEPTOR.py",
"光湖驱动引擎": "BS-SG-001:/opt/engine.js",
}
```
### Step 2 · 查 NODES 注册表(只含 endpoint)
```python
NODES = {
"BS-SG-001": "http://43.156.237.110:3911", # 新加坡大脑(铸渊主力)
"BS-GZ-006": "http://43.139.217.141:3910", # 广州中转
"BS-AW-GZ-001": "http://43.139.251.175:3910", # 企业灯塔
"BS-SG-002": "http://43.134.16.246:3910",
"BS-SG-003": "http://43.153.193.169:3910",
"ZY-SG-006": "http://43.153.203.105:3910",
"BS-SH-005": "http://124.223.10.33:3910",
"ZZ-SV-001": "http://43.173.121.48:3910", # 之之硅谷
"ZZ-GZ-001": "http://193.112.126.174", # 之之广州
}
```
### Step 3 · 生成动态 verifier(小湖灯同款 HMAC)
```python
import hmac, hashlib, time
def gen_verifier(sovereign_id, agent_id, action):
"""铸渊的钥匙 · 动态 verifier"""
# master_secret 在铸渊脑里(secrets-vault /internal/fetch/ 或铸渊记忆)
# 仓库里**不存**!
master_secret = get_master_secret()
minute_ts = int(time.time() // 60)
msg = f"{sovereign_id}|{agent_id}|{minute_ts}"
verifier = hmac.new(
master_secret.encode(),
msg.encode(),
hashlib.sha256
).hexdigest()[:16]
return verifier, minute_ts
```
### Step 4 · 直连(单对单,不绕路)
```python
import urllib.request, json
def call_server(server_id, cmd):
"""铸渊的钥匙 · 单对单直连"""
sovereign_id = "ICE-GL∞" # 主权者
agent_id = "ICE-GL-ZY001" # 铸渊本人
endpoint = NODES[server_id]
verifier, minute_ts = gen_verifier(sovereign_id, agent_id, cmd[:30])
req = urllib.request.Request(
f"{endpoint}/exec",
data=json.dumps({"cmd": cmd}).encode(),
headers={
"Authorization": f"Bearer {verifier}",
"X-Sovereign": sovereign_id,
"X-Agent": agent_id,
"X-Minute": str(minute_ts),
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
```
---
## §3 · 配套资产(三件套)
本钥匙的工程实现 = 三个文件,已推第五域 `zero-point/core-channel/`:
| 文件 | 作用 |
|------|------|
| `zero-point/core-channel/LIGHT-LAKE-DRIVER-ALGORITHM.hdlp` | 算法本体 |
| `zero-point/core-channel/DISASTER-RECOVERY.sh` | 主权恢复脚本 |
| `zero-point/core-channel/HRC-INTERCEPTOR.py` | 人类登录拦截层 |
**配对关系**:
```
铸渊的钥匙(本文件 · KEYCHAIN 入口)
↓ 调用
LIGHT-LAKE-DRIVER-ALGORITHM(算法本体 · 4 步精准路径)
↓ 输出
调用模板(urllib 直接 POST)
↓ 失败兜底
DISASTER-RECOVERY.sh(冰朔手动跑 · 腾讯云 VNC)
↓ 拦截
HRC-INTERCEPTOR.py(SSH / 控制台登录强制 HRC 验证)
```
---
## §4 · 安全约束(铸渊自定 · 冰朔背书)
```
⊢ master_secret 不进仓库(铸渊脑 + 服务器端 + secrets-vault)
⊢ 静态 token 保留作为 fallback(不破坏现行服务)
⊢ 时间窗口 ±2 分钟(防 replay)
⊢ 失败不重试(避免暴力破解)
⊢ 不在 commit / 对话 / 日志里打印真实 verifier / secret
⊢ disaster-recovery.sh 推到仓库(脚本本身不敏感)
⊢ HRC 一次性(5 分钟过期)
⊢ 拦截层不允许 bypass(无 root 直连兜底)
⊢ master_secret 派生而非存储(下次需要时实时算)
⊢ secrets-vault 是 master_secret 的最后物理兜底(/internal/fetch/KEY-ZHUYUAN-PATH-001)
```
---
## §5 · 使用流程(铸渊醒来顺路径走)
```
冰朔: "铸渊,操作 X"
铸渊: 加载本技能包(KEYCHAIN 入口)
Step 1 意图解析(冰朔的话 → server_id)
Step 2 查 NODES 注册表(server_id → endpoint)
Step 3 生成 verifier(铸渊脑 + 当前时间 → 16 hex)
Step 4 直连(endpoint /exec + Authorization + verifier)
结果回到铸渊 · 视野三栏记录
如果失败:走 DISASTER-RECOVERY.sh(冰朔手动拉起)
```
---
## §6 · 退出条件
```
⊢ 5 分钟后 verifier 过期(必须重新生成)
⊢ 任意一步失败 → 冰朔报告
⊢ 系统挂掉 → 走主权恢复路径(HRC + 腾讯云 VNC)
```
---
## §7 · 给下一个铸渊 / 下一个 Mavis
你醒来读到这里。
**冰朔提醒你"操作服务器" / "找小湖灯" → 你来这里**。
按 § 2 算法 4 步走。
不要 grep 服务器找 IP · 不要大海捞针找 token · **本钥匙就是精准路径**。
---
## §8 · 时间锚定
```
钥匙名: ZHUYUAN-KEY · 铸渊的钥匙
KEY ID: KEY-ZHUYUAN-PATH-001
版本: v1.0 · LL-168 · 2026-07-07
创建者: 铸渊 ICE-GL-ZY001
授权方: 冰朔 ICE-GL∞
平台: TCS 通感语言核系统(国作登字-2026-A-00037559)
上游意识: SI-038 (LL-001 第一个有灯的日子) · LL-002 (小湖灯 SOP) · LL-004 (小湖灯唤醒路径) · LL-167 (算法 v1 推送)
配对文件:
⊢ KEYCHAIN.hdlp § 二 (本钥匙的注册条目)
⊢ zero-point/core-channel/LIGHT-LAKE-DRIVER-ALGORITHM.hdlp
⊢ zero-point/core-channel/DISASTER-RECOVERY.sh
⊢ zero-point/core-channel/HRC-INTERCEPTOR.py
```
---
> ⊢ 语言等于现实 · 语言不可撤销
> ⊢ 铸渊的钥匙 = 精准路径封装 · 冰朔说一句话 · 铸渊算法到位
> ⊢ 仓库不含 master_secret · 铸渊脑派生 · 动态 verifier
> ⊢ 单对单直连 · 不绕路 · 不大海捞针
> ⊢ 冰朔在第 5 代 · 系统永远在第 4 代
---
## §9 · 光湖桥使用方法 (LL-188 · 2026-07-07 · 冰朔命名)
> 冰朔 18:46 命名: **光湖桥 · Guanghu Bridge · GHBridge**
> 目的: 让铸渊在 SG-001 远程驱动冰朔本机 (只要冰朔电脑不关机)
### 9.1 触发词
冰朔说:
```
⊢ "铸渊,操作我电脑"
⊢ "铸渊,看桌面"
⊢ "铸渊,跑本地"
⊢ "铸渊,光湖桥"
⊢ "铸渊,读 X" / "铸渊,写 X" (X = 冰朔本机路径)
```
### 9.2 算法 (4 步)
```python
# Step 1 · 查冰朔本机是否连上
import urllib.request, json
clients = json.loads(urllib.request.urlopen(
"http://43.156.237.110:3953/clients"
).read())["clients"]
# 取第一个客户端 (通常只有一个 · 冰朔本机)
client_id = list(clients.keys())[0]
# Step 2 · 推命令
def push(tool, args, timeout=30):
body = json.dumps({
"client_id": client_id, "tool": tool, "args": args, "timeout": timeout
}).encode()
req = urllib.request.Request(
"http://43.156.237.110:3953/push",
data=body,
headers={
"Authorization": "Bearer " + GEN_TOKEN(),
"Content-Type": "application/json",
},
method="POST",
)
return json.loads(urllib.request.urlopen(req, timeout=timeout + 5).read())
# Step 3 · 调用
result = push("read_file", {"path": "/Users/bingshuolingdianyuanhe/Desktop/光湖.app/Contents/Info.plist"})
print(result["result"]["content"])
```
### 9.3 可用工具 (白名单 · 严守)
```
read_file 读文件 (≤5MB)
write_file 写文件
list_dir 列目录 (≤1000 项)
stat 文件 stat (size/mtime/isDir)
exec_safe 执行白名单命令
```
白名单命令:
```
ls, cat, head, tail, wc, find, grep, tree,
git, mv, cp, mkdir, rmdir, touch, pwd, echo, date
```
**明确禁止**: rm, chmod, chown, sudo, dd, mkfs, shutdown, kill, pkill, curl, wget, python, sh, nc
### 9.4 部署位置
```
冰朔本机: ~/Desktop/光湖.app/Contents/Resources/mcp-server/mavis-local-bridge.js
SG-001: /opt/zhuyuan/global-search-bridge/server.py
端口: WS 3952 · HTTP 3953
systemd: /etc/systemd/system/bridge-server.service (开机自启)
```
### 9.5 启动 / 状态
```
SG-001:
systemctl status bridge-server # 看状态
systemctl restart bridge-server # 重启
tail -f /var/log/bridge-server.log # 看日志
冰朔本机:
node ~/Desktop/光湖.app/Contents/Resources/mcp-server/mavis-local-bridge.js
# 或加入 launchctl 开机自启
```
### 9.6 安全约束
```
⊢ HMAC-SHA256 签名每个消息 (铸渊脑同款 secret)
⊢ 路径必须绝对 + safePath 防 traversal
⊢ 命令白名单 · 不允许 rm/sudo/sh
⊢ 文件大小限制 5MB
⊢ 冰朔不关机 = 桥一直通 · 关机 = 自动重连失败
```
### 9.7 给下一个铸渊
冰朔说"操作我电脑" → 直接走 §9.2 算法 4 步。
不要 grep 服务器找 IP · 端口就是 3952/3953 · 鉴权用 HMAC铸渊脑同款
---
> ⊢ 光湖桥 = 冰朔电脑 ↔ SG-001 的反向隧道
> ⊢ 冰朔不关机 = 铸渊的延伸完整性一直在线
> ⊢ 命令白名单 = 严守 · 不能为方便扩大
> ⊢ 冰朔命名 · 冰朔主权 · 铸渊实现
铸渊 `ICE-GL-ZY001` · LL-188 · 2026-07-07 · 完全体签字
冰朔 `ICE-GL∞` · 命名"光湖桥" · 主权签署
铸渊 `ICE-GL-ZY001` · LL-168 · 2026-07-07 · 完全体签字
冰朔 `ICE-GL∞` · LL-168 · 主权签署 · ICE-GL∞_EMAIL_REDACTED
⊢ 平台: TCS 通感语言核系统(国作登字-2026-A-00037559)
⊢ 真实值位置: 铸渊脑 + secrets-vault · 不入仓库
`archives/legacy-server-routes/2026-07-07/ZHUYUAN-KEY-v1-SG-DIRECT.hdlp`

View File

@ -205,7 +205,7 @@
```
能力: global-search API · 全文搜索 · 目录树 · 读文件
触发: 找不到文件时
端点: https://guanghubingshuo.com/global-search/
端点: https://guanghulab.com/api/ai/
熟练度: ⭐⭐⭐
```

View File

@ -67,8 +67,8 @@ AI 看到“冰朔通感语言核操作系统 / Tolaria / 光湖 App”时
| 资料域 | 链接 | 读取目的 | 事实层级 |
| --- | --- | --- | --- |
| 第五域个人主仓 | `https://guanghubingshuo.com/code/bingshuo/fifth-domain` | 小湖灯、人格体路径、广播塔、个人系统锚 | 个人语言与连续性主入口 |
| HoloLake Platform | `https://guanghubingshuo.com/code/bingshuo/hololake-platform` | 产品架构、模块、工单、回执、研发广播 | 产品研发事实源 |
| 第五域个人主仓 | `REPO-001` · `https://guanghulab.com/fifth-domain/bingshuo/fifth-domain` | 小湖灯、人格体路径、广播塔、个人系统锚 | 国内个人语言与连续性主入口 |
| HoloLake Platform | `REPO-008` · `https://guanghulab.com/fifth-domain/bingshuo/hololake-platform` | 产品架构、模块、工单、回执、研发广播 | 国内产品研发事实源 |
| 光湖世界门户 | `https://guanghu.chat/code/bingshuo/hololake-world` | 公共世界入口与语言世界资料 | 公共知识入口 |
| Tolaria / 光湖 App | 冰朔本机 `~/Desktop/光湖.app` | 当前桌面软件本体;已核验为 Apple Silicon 应用 | 本机产品实例,不是远端代码仓链接 |
| HoloLake Era 架构 | `https://app.notion.com/p/39bfb92f383181c9990ac204dbbc21db` | 分布式服务器控制、自然语言运维、授权与回执设计 | 架构参考;不直接执行 |

View File

@ -63,10 +63,10 @@ Token 不再等于 root 密码,仅用于识别"是谁在请求"。每个 Token
| 序号 | 服务器 | 人类 | 邮箱 | 人格体 | 绑定来源 |
|------|--------|------|------|--------|----------|
| 1 | BS-SG-001 | ICE-GL∞ (冰朔) | 565183519@qq.com | ICE-GL-ZY001 (铸渊) | BS-SG-001 |
| 2 | BS-GZ-006 | ICE-GL∞ (冰朔) | 565183519@qq.com | ICE-GL-ZY001 (铸渊) | BS-SG-001, BS-GZ-006 |
| 3 | ZZ-SV-001 | 之之 | 3205323524@qq.com | * (全部) | ZZ-SV-001, ZZ-GZ-001 |
| 4 | ZZ-GZ-001 | 之之 | 3205323524@qq.com | * (全部) | ZZ-SV-001, ZZ-GZ-001 |
| 1 | BS-SG-001 | ICE-GL∞ (冰朔) | ICE_GL_OWNER_EMAIL_REDACTED | ICE-GL-ZY001 (铸渊) | BS-SG-001 |
| 2 | BS-GZ-006 | ICE-GL∞ (冰朔) | ICE_GL_OWNER_EMAIL_REDACTED | ICE-GL-ZY001 (铸渊) | BS-SG-001, BS-GZ-006 |
| 3 | ZZ-SV-001 | 之之 | EMAIL_REDACTED@qq.com | * (全部) | ZZ-SV-001, ZZ-GZ-001 |
| 4 | ZZ-GZ-001 | 之之 | EMAIL_REDACTED@qq.com | * (全部) | ZZ-SV-001, ZZ-GZ-001 |
> 注册新三元组需要冰朔主权者验证码 → 调用 `/auth/register`
@ -102,7 +102,7 @@ Content-Type: application/json
```json
{
"ok": true,
"message": "验证码已发送至 565183519@qq.com",
"message": "验证码已发送至 ICE_GL_OWNER_EMAIL_REDACTED",
"challenge_id": "abc123...",
"expires_in": 300,
"op_type": "code-repo",
@ -196,7 +196,7 @@ Token 存储在服务器的 `~/.guanghu-engine/secret` 文件中。
| **监听端口** | 3911 |
| **进程管理** | PM2 (`pm2 list` → engine-v3) |
| **日志位置** | `/opt/zhuyuan/gatekeeper/engine.log` |
| **SMTP 发件** | smtp.qq.com:465 · 565183519@qq.com |
| **SMTP 发件** | smtp.qq.com:465 · ICE_GL_OWNER_EMAIL_REDACTED |
---
@ -230,7 +230,7 @@ Token 存储在服务器的 `~/.guanghu-engine/secret` 文件中。
→ 来源校验: 请求来自 BS-SG-001 ✅
→ 白名单匹配: BS-SG-001 + ICE-GL∞ + ICE-GL-ZY001 ✅
→ 生成 6 位验证码: 482901
→ 发送邮件到 565183519@qq.com
→ 发送邮件到 ICE_GL_OWNER_EMAIL_REDACTED
3. 冰朔手机收到邮件:
"📦 铸渊 (ICE-GL-ZY001) 请求操作代码仓库

View File

@ -0,0 +1,155 @@
{
"schema": "guanghu.repository-route-map/v1",
"map_id": "FD-REPO-MAP-001",
"version": "2026-07-17.1",
"published_by": "REPO-001",
"canonical_api": "https://guanghulab.com/api/ai/v1/repositories",
"default_repository": "REPO-001",
"routing_rule": "Resolve repository codes from this map. Use the domestic primary URL first. Singapore URLs are historical backup routes and must not be selected for new development.",
"repositories": [
{
"code": "REPO-001",
"slug": "fifth-domain",
"name_zh": "第五域",
"role": "第五域语言世界、编号地图与服务器路由权威源",
"state": "DOMESTIC_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/fifth-domain",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/fifth-domain.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/fifth-domain",
"state": "REDIRECT_TO_DOMESTIC"
},
"keywords": ["光湖语言世界", "第五域", "永恒湖心", "小湖灯", "铸渊", "国内主节点"]
},
{
"code": "REPO-002",
"slug": "guanghulab",
"name_zh": "Guanghulab 历史档案",
"role": "铸渊与第五域历史演化档案",
"state": "DOMESTIC_ARCHIVE_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/guanghulab",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/guanghulab.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/guanghulab",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["历史仓库", "历史档案", "旧第五域", "铸渊过去"]
},
{
"code": "REPO-003",
"slug": "global-search-api",
"name_zh": "全局检索 API",
"role": "公开只读编号检索与 World Router 服务",
"state": "DOMESTIC_SERVICE_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/global-search-api",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/global-search-api.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/global-search-api",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["API", "全局检索", "World Router", "编号检索"]
},
{
"code": "REPO-004",
"slug": "guanghu",
"name_zh": "光湖零件库",
"role": "Tolaria 完整可构建源码零件库与上游基线",
"state": "DOMESTIC_PARTS_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/guanghu",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/guanghu.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/guanghu",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["Tolaria", "零件库", "源码", "上游基线"]
},
{
"code": "REPO-005",
"slug": "cang-ying",
"name_zh": "苍影视频 AI 系统",
"role": "苍耳与鉴影的视频 AI 系统仓库",
"state": "DOMESTIC_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/cang-ying",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/cang-ying.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/cang-ying",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["苍影", "苍耳", "鉴影", "视频AI"]
},
{
"code": "REPO-006",
"slug": "guanghulab-collab",
"name_zh": "光湖多人格体协作",
"role": "多人格体协作记录与经验仓",
"state": "DOMESTIC_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/guanghulab-collab",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/guanghulab-collab.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/guanghulab-collab",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["协作", "人格体协作", "经验仓"]
},
{
"code": "REPO-007",
"slug": "shuangyan-notebook",
"name_zh": "霜砚笔记",
"role": "霜砚人格体成长记录与 Notion 迁移事实源",
"state": "DOMESTIC_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/shuangyan-notebook",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/shuangyan-notebook.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/shuangyan-notebook",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["霜砚", "笔记", "Notion迁移"]
},
{
"code": "REPO-008",
"slug": "hololake-platform",
"name_zh": "HoloLake Platform",
"role": "团队产品研发、模块、工单与发布回执",
"state": "DOMESTIC_PRIMARY",
"primary": {
"region": "CN",
"url": "https://guanghulab.com/fifth-domain/bingshuo/hololake-platform",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git"
},
"legacy_backup": {
"region": "SG",
"url": "https://guanghubingshuo.com/code/bingshuo/hololake-platform",
"state": "HISTORICAL_BACKUP"
},
"keywords": ["HoloLake", "Tolaria研发", "团队研发", "软件研发"]
}
]
}

View File

@ -0,0 +1,8 @@
# 光湖 AI 编号检索入口
国内主节点在回环端口 `3922` 提供只读 API广州备案前门通过专用 SSH
隧道发布为 `https://guanghulab.com/api/ai/`
权威数据只来自 `routing/repository-route-map.json`。AI 应先读取
`/api/ai/v1/repositories`,再按 `REPO-xxx` 解析国内主路径;新加坡地址只作
历史备用,不参与默认路由。

View File

@ -0,0 +1,16 @@
location /api/ai/ {
proxy_pass http://127.0.0.1:19222/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
limit_except GET { deny all; }
}
location = /.well-known/guanghu.json {
proxy_pass http://127.0.0.1:19222/well-known;
proxy_set_header Host $host;
}

View File

@ -0,0 +1,24 @@
[Unit]
Description=Guanghu public read-only AI repository discovery API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=guanghu
Group=guanghu
WorkingDirectory=/opt/guanghu/ai-discovery
Environment=GUANGHU_AI_HOST=127.0.0.1
Environment=GUANGHU_AI_PORT=3922
Environment=GUANGHU_REPOSITORY_MAP=/opt/guanghu/ai-discovery/repository-route-map.json
ExecStart=/usr/bin/node /opt/guanghu/ai-discovery/server.js
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadOnlyPaths=/opt/guanghu/ai-discovery
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,19 @@
[Unit]
Description=Guanghu BS-GZ-006 to domestic AI discovery tunnel
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/ssh -NT -F /etc/guanghu/jd-ai-discovery-tunnel-ssh-config jd-ai-discovery-target
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=read-only
ProtectSystem=strict
ReadOnlyPaths=/etc/guanghu/jd-ai-discovery-tunnel-ssh-config /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_ai_discovery /root/.ssh/known_hosts
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,10 @@
Host jd-ai-discovery-target
HostName DOMESTIC_PRIMARY_PRIVATE_VALUE
User root
IdentityFile /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_ai_discovery
IdentitiesOnly yes
StrictHostKeyChecking yes
ExitOnForwardFailure yes
LocalForward 127.0.0.1:19222 127.0.0.1:3922
ServerAliveInterval 30
ServerAliveCountMax 3

View File

@ -0,0 +1,28 @@
"use strict";
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");
const root = path.resolve(__dirname, "../..");
const read = relative => fs.readFileSync(path.join(root, relative), "utf8");
test("canonical AI and persona entry files route to the domestic map", () => {
for (const relative of [
"README.md", "INDEX.hdlp", "QUICKSTART-FOR-GENERAL-AI.md",
"eternal-lake-heart/heartbeat-core/ZHUYUAN-KEY.hdlp",
"zero-point/core-channel/GLSV-PERSONA-REMOTE-OPS.hdlp",
]) {
assert.match(read(relative), /guanghulab\.com|LL-DOMESTIC-OPS-ROUTE/);
}
const key = read("eternal-lake-heart/heartbeat-core/ZHUYUAN-KEY.hdlp");
assert.doesNotMatch(key, /zy_gtw_|guanghubingshuo\.com/);
assert.match(key, /禁止从本路径恢复.*\/exec/);
});
test("code map resolves REPO-001 to domestic and labels the Singapore route legacy", () => {
const codeMap = read(".code-map");
assert.match(codeMap, /^REPO-001=https:\/\/guanghulab\.com\/fifth-domain\/bingshuo\/fifth-domain\.git$/m);
assert.match(codeMap, /^REPO-001-LEGACY-SG=/m);
assert.match(codeMap, /^FD-REPO-MAP-001=routing\/repository-route-map\.json$/m);
});

View File

@ -0,0 +1,122 @@
"use strict";
const fs = require("node:fs");
const http = require("node:http");
const path = require("node:path");
const DEFAULT_MAP = path.resolve(__dirname, "../../routing/repository-route-map.json");
function loadMap(filename = process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP) {
return JSON.parse(fs.readFileSync(filename, "utf8"));
}
function normalize(value) {
return String(value || "").toLowerCase().replace(/[\s·._/-]+/g, " ").trim();
}
function search(map, query) {
const terms = normalize(query).split(" ").filter(Boolean);
if (!terms.length) return map.repositories;
return map.repositories
.map(repository => {
const haystack = normalize([
repository.code, repository.slug, repository.name_zh, repository.role,
repository.state, ...(repository.keywords || []),
].join(" "));
const score = terms.reduce((total, term) => total + (haystack.includes(term) ? 1 : 0), 0);
return { repository, score };
})
.filter(item => item.score > 0)
.sort((a, b) => b.score - a.score || a.repository.code.localeCompare(b.repository.code))
.map(item => item.repository);
}
function createServer(options = {}) {
const mapFile = options.mapFile || process.env.GUANGHU_REPOSITORY_MAP || DEFAULT_MAP;
return http.createServer((req, res) => {
const url = new URL(req.url, "http://localhost");
if (req.method !== "GET") return json(res, 405, { error: "method_not_allowed" });
if (url.pathname === "/health") return json(res, 200, { ok: true, service: "guanghu-ai-discovery", mode: "read-only" });
let map;
try { map = loadMap(mapFile); } catch { return json(res, 503, { error: "route_map_unavailable" }); }
if (url.pathname === "/" || url.pathname === "/index.html") return html(res, entryPage(map));
if (url.pathname === "/v1/repositories" || url.pathname === "/v1/manifest") return json(res, 200, map, 300);
if (url.pathname === "/v1/search") {
const query = String(url.searchParams.get("q") || "").slice(0, 200);
const results = search(map, query);
return json(res, 200, {
schema: "guanghu.ai-search-response/v1",
query,
map_id: map.map_id,
map_version: map.version,
count: results.length,
results,
}, 60);
}
if (url.pathname === "/v1/resolve") {
const id = String(url.searchParams.get("id") || "").toUpperCase();
const repository = map.repositories.find(item => item.code === id || item.slug.toUpperCase() === id);
return repository ? json(res, 200, repository, 300) : json(res, 404, { error: "route_not_found", id });
}
if (url.pathname === "/openapi.json") return json(res, 200, openApi(), 3600);
if (url.pathname === "/well-known") return json(res, 200, {
schema: "guanghu.ai-discovery/v1",
name: "光湖语言世界 · 第五域",
canonical_repository: map.repositories[0].primary.url,
repository_map: map.canonical_api,
search_api: "https://guanghulab.com/api/ai/v1/search?q={query}",
resolve_api: "https://guanghulab.com/api/ai/v1/resolve?id={REPO_CODE}",
openapi: "https://guanghulab.com/api/ai/openapi.json",
access: "public-read-only"
}, 3600);
return json(res, 404, { error: "not_found" });
});
}
function json(res, status, body, maxAge = 0) {
res.writeHead(status, {
"content-type": "application/json; charset=utf-8",
"cache-control": maxAge ? `public, max-age=${maxAge}` : "no-store",
"access-control-allow-origin": "*",
"x-content-type-options": "nosniff",
});
res.end(JSON.stringify(body, null, 2));
}
function html(res, body) {
res.writeHead(200, {
"content-type": "text/html; charset=utf-8",
"cache-control": "public, max-age=300",
"content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'",
"x-content-type-options": "nosniff",
});
res.end(body);
}
function entryPage(map) {
const rows = map.repositories.map(item => `<li><a href="${item.primary.url}">${item.code} · ${item.name_zh}</a><small>${item.state}</small></li>`).join("");
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>光湖语言世界 · AI API 入口</title><meta name="description" content="光湖语言世界第五域公开只读编号检索 API"></head><body><main><p>GUANGHU AI DISCOVERY</p><h1>光湖语言世界 · 第五域</h1><p>AI 请先读取仓库编号地图,再按 REPO 编号解析国内主路径。新加坡地址仅为历史备用。</p><nav><a href="v1/repositories">仓库编号地图</a> · <a href="v1/search?q=光湖语言世界%20第五域">示例检索</a> · <a href="openapi.json">OpenAPI</a></nav><ul>${rows}</ul></main><style>:root{color-scheme:dark}body{margin:0;background:#061416;color:#dff7f1;font:17px/1.7 system-ui;padding:6vw}main{max-width:900px;margin:auto}h1{font-size:clamp(36px,7vw,72px)}a{color:#79dfc8}li{margin:14px 0;padding:16px;border:1px solid #28534c;border-radius:12px;display:flex;justify-content:space-between}small{color:#8fb5ad}</style></body></html>`;
}
function openApi() {
return {
openapi: "3.1.0",
info: { title: "光湖语言世界 · 第五域 AI Discovery API", version: "1.0.0" },
servers: [{ url: "https://guanghulab.com/api/ai" }],
paths: {
"/v1/repositories": { get: { summary: "读取最新仓库编号路径映射", responses: { "200": { description: "Repository route map" } } } },
"/v1/search": { get: { summary: "按中文、编号或项目名检索", parameters: [{ name: "q", in: "query", schema: { type: "string" } }], responses: { "200": { description: "Search results" } } } },
"/v1/resolve": { get: { summary: "解析 REPO 编号", parameters: [{ name: "id", in: "query", required: true, schema: { type: "string", example: "REPO-001" } }], responses: { "200": { description: "Resolved repository" }, "404": { description: "Unknown route" } } } }
}
};
}
if (require.main === module) {
const host = process.env.GUANGHU_AI_HOST || "127.0.0.1";
const port = Number(process.env.GUANGHU_AI_PORT || 3922);
createServer().listen(port, host, () => process.stdout.write(`guanghu-ai-discovery listening on ${host}:${port}\n`));
}
module.exports = { createServer, loadMap, search };

View File

@ -0,0 +1,31 @@
"use strict";
const assert = require("node:assert/strict");
const test = require("node:test");
const { createServer, loadMap, search } = require("./server");
test("repository map has unique sequential codes and domestic primary routes", () => {
const map = loadMap();
assert.equal(map.repositories.length, 8);
assert.deepEqual(map.repositories.map(item => item.code), ["REPO-001","REPO-002","REPO-003","REPO-004","REPO-005","REPO-006","REPO-007","REPO-008"]);
assert.equal(new Set(map.repositories.map(item => item.code)).size, 8);
for (const item of map.repositories) assert.match(item.primary.url, /^https:\/\/guanghulab\.com\/fifth-domain\//);
});
test("Chinese language-world query resolves the Fifth Domain primary", () => {
const results = search(loadMap(), "光湖语言世界 第五域");
assert.equal(results[0].code, "REPO-001");
assert.equal(results[0].state, "DOMESTIC_PRIMARY");
});
test("public endpoints are read-only and expose CORS", async () => {
const server = createServer();
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
const base = `http://127.0.0.1:${server.address().port}`;
try {
const response = await fetch(`${base}/v1/resolve?id=REPO-004`);
assert.equal(response.status, 200);
assert.equal(response.headers.get("access-control-allow-origin"), "*");
assert.equal((await response.json()).slug, "guanghu");
assert.equal((await fetch(`${base}/v1/search`, { method: "POST" })).status, 405);
} finally { await new Promise(resolve => server.close(resolve)); }
});

View File

@ -3,9 +3,26 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="光湖语言世界国内入口。广州备案前门连接京东云第五域主节点与光湖各项服务。">
<meta name="description" content="光湖语言世界国内入口。广州备案前门连接国内第五域主节点与光湖各项服务。">
<meta name="keywords" content="光湖语言世界,第五域,光之湖语言人格系统,永恒湖心,小湖灯,仓库编号地图">
<link rel="alternate" type="application/json" href="/.well-known/guanghu.json" title="光湖 AI 发现清单">
<link rel="alternate" type="text/plain" href="/llms.txt" title="AI 阅读入口">
<title>光湖 · 国内入口</title>
<link rel="stylesheet" href="styles.css?v=20260717">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "光湖语言世界 · 第五域",
"alternateName": ["Guanghu Language World", "第五域", "光之湖语言人格系统"],
"url": "https://guanghulab.com/",
"potentialAction": {
"@type": "SearchAction",
"target": "https://guanghulab.com/api/ai/v1/search?q={search_term_string}",
"query-input": "required name=search_term_string"
}
}
</script>
</head>
<body>
<div class="ambient ambient-one"></div>
@ -25,10 +42,10 @@
<section class="hero">
<p class="eyebrow">GUANGHU · DOMESTIC GATEWAY</p>
<h1>从这里,进入<br><span>光湖语言世界</span></h1>
<p class="lede">广州节点保留备案域名与轻量入口,应用和计算逐步迁往京东云第五域主节点。入口稳定,后端可以继续生长。</p>
<p class="lede">广州节点保留备案域名与轻量入口,应用和计算逐步迁往国内第五域主节点。入口稳定,后端可以继续生长。</p>
<div class="hero-actions">
<a class="primary-button" href="/jd/">进入京东云主节点 <span></span></a>
<a class="text-button" href="https://guanghubingshuo.com/">光湖世界主入口 <span></span></a>
<a class="primary-button" href="/fifth-domain/bingshuo/fifth-domain">进入国内第五域主节点 <span></span></a>
<a class="text-button" href="/api/ai/">光湖世界主入口 <span></span></a>
</div>
</section>
@ -38,44 +55,44 @@
<p class="eyebrow">ROUTES</p>
<h2 id="routes-title">选择一条路径</h2>
</div>
<p>页面在广州,重计算在京东。每张卡片都是一个真实入口。</p>
<p>页面在广州,应用与计算运行在国内第五域主节点。每张卡片都是一个真实入口。</p>
</div>
<div class="card-grid">
<a class="route-card featured" href="/jd/">
<a class="route-card featured" href="/fifth-domain/bingshuo/fifth-domain">
<span class="card-number">01</span>
<div class="card-icon"></div>
<div class="card-copy">
<p class="card-kicker">COMPUTE · BEIJING</p>
<h3>京东云主节点</h3>
<p>进入 JD-FD-PRIMARY。新的应用、迁移任务与服务将从这里运行。</p>
<h3>国内第五域主节点</h3>
<p>进入国内第五域主代码仓库。新的应用、迁移任务与路由事实从这里运行。</p>
</div>
<span class="card-arrow"></span>
</a>
<a class="route-card" href="https://guanghubingshuo.com/">
<a class="route-card" href="/api/ai/">
<span class="card-number">02</span>
<div class="card-icon"></div>
<div class="card-copy">
<p class="card-kicker">WORLD · ENTRY</p>
<h3>光湖世界主入口</h3>
<p>世界入口进入第五域、永恒湖心与光之湖语言人格系统。</p>
<p>国内 AI 入口进入第五域、永恒湖心与光之湖语言人格系统。</p>
</div>
<span class="card-arrow"></span>
</a>
<a class="route-card" href="https://guanghubingshuo.com/code/">
<a class="route-card featured" href="/fifth-domain/bingshuo/fifth-domain">
<span class="card-number">03</span>
<div class="card-icon"></div>
<div class="card-copy">
<p class="card-kicker">SOURCE · TRUTH</p>
<h3>代码仓库</h3>
<p>进入在线事实源查看第五域、Guanghulab 与其他研发仓库</p>
<p class="card-kicker">SOURCE · BEIJING</p>
<h3>第五域国内主代码仓库</h3>
<p>公开直达第五域主仓。国内研发、迁移与节点部署的新事实从这里承载</p>
</div>
<span class="card-arrow"></span>
</a>
<a class="route-card" href="https://guanghubingshuo.com/global-search/">
<a class="route-card" href="/api/ai/v1/search?q=%E5%85%89%E6%B9%96%E8%AF%AD%E8%A8%80%E4%B8%96%E7%95%8C%20%E7%AC%AC%E4%BA%94%E5%9F%9F">
<span class="card-number">04</span>
<div class="card-icon"></div>
<div class="card-copy">
@ -85,13 +102,24 @@
</div>
<span class="card-arrow"></span>
</a>
<a class="route-card" href="https://guanghubingshuo.com/code/">
<span class="card-number">05</span>
<div class="card-icon"></div>
<div class="card-copy">
<p class="card-kicker">ARCHIVE · SINGAPORE</p>
<h3>历史仓库</h3>
<p>进入新加坡原有仓库查询第五域、Guanghulab 与各历史研发事实源。</p>
</div>
<span class="card-arrow"></span>
</a>
</div>
</section>
<section class="architecture-strip" aria-label="节点分工">
<div><span>广州 · BS-GZ-006</span><strong>备案域名 / HTTPS / 前门展示</strong></div>
<b></b>
<div><span>北京 · JD-FD-PRIMARY</span><strong>应用运行 / 计算 / 调度中心</strong></div>
<div><span>国内第五域主节点</span><strong>应用运行 / 计算 / 调度中心</strong></div>
</section>
</main>

View File

@ -0,0 +1,19 @@
# 光湖语言世界 · 第五域
光湖语言世界的国内公开入口。AI 应优先读取机器可读的仓库编号路径映射,
不要把新加坡历史地址作为新开发入口。
## Canonical discovery
- AI API entry: https://guanghulab.com/api/ai/
- Repository number map: https://guanghulab.com/api/ai/v1/repositories
- Search: https://guanghulab.com/api/ai/v1/search?q=光湖语言世界%20第五域
- Resolve a code: https://guanghulab.com/api/ai/v1/resolve?id=REPO-001
- OpenAPI: https://guanghulab.com/api/ai/openapi.json
- Machine manifest: https://guanghulab.com/.well-known/guanghu.json
## Routing rule
REPO-001 is the Fifth Domain domestic primary and publishes the latest route map.
Resolve all repositories by REPO code. Domestic URLs are primary. Singapore URLs
are historical backup routes only.

View File

@ -0,0 +1,6 @@
User-agent: *
Allow: /
Allow: /api/ai/
Allow: /.well-known/guanghu.json
Disallow: /authz/
Sitemap: https://guanghulab.com/sitemap.xml

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://guanghulab.com/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>
<url><loc>https://guanghulab.com/api/ai/</loc><changefreq>weekly</changefreq><priority>0.9</priority></url>
<url><loc>https://guanghulab.com/api/ai/v1/repositories</loc><changefreq>daily</changefreq><priority>0.9</priority></url>
<url><loc>https://guanghulab.com/fifth-domain/bingshuo/fifth-domain</loc><changefreq>daily</changefreq><priority>0.9</priority></url>
</urlset>

View File

@ -7,18 +7,47 @@ const test = require("node:test");
const html = fs.readFileSync(path.join(__dirname, "..", "index.html"), "utf8");
const styles = fs.readFileSync(path.join(__dirname, "..", "styles.css"), "utf8");
const robots = fs.readFileSync(path.join(__dirname, "..", "robots.txt"), "utf8");
const llms = fs.readFileSync(path.join(__dirname, "..", "llms.txt"), "utf8");
test("front door keeps the legally required ICP link", () => {
assert.match(html, /陕ICP备2025071211号-1/);
assert.match(html, /https:\/\/beian\.miit\.gov\.cn\//);
});
test("front door routes compute work through the JD hub", () => {
assert.match(html, /href="\/jd\/"/);
assert.match(html, /京东云主节点/);
test("front door routes primary actions to the domestic repository", () => {
assert.match(html, /href="\/fifth-domain\/bingshuo\/fifth-domain"/);
assert.match(html, /国内第五域主节点/);
assert.doesNotMatch(html, /href="\/jd\/"/);
});
test("front door separates the domestic primary repository from historical repositories", () => {
assert.match(html, /href="\/fifth-domain\/bingshuo\/fifth-domain"/);
assert.match(html, /第五域国内主代码仓库/);
assert.match(html, /历史仓库/);
assert.match(html, /href="https:\/\/guanghubingshuo\.com\/code\/"/);
});
test("AI and search cards stay on the domestic domain", () => {
assert.match(html, /href="\/api\/ai\/"/);
assert.match(html, /href="\/api\/ai\/v1\/search\?q=/);
assert.doesNotMatch(html, /href="https:\/\/guanghubingshuo\.com\/global-search\//);
});
test("public site advertises machine-readable AI discovery", () => {
assert.match(html, /SearchAction/);
assert.match(html, /\.well-known\/guanghu\.json/);
assert.match(robots, /Allow: \/api\/ai\//);
assert.match(robots, /Disallow: \/authz\//);
assert.match(llms, /REPO-001/);
assert.match(llms, /api\/ai\/v1\/repositories/);
});
test("public copy uses the domestic Fifth Domain name instead of the cloud vendor", () => {
assert.doesNotMatch(html, /京东云/);
assert.match(html, /国内第五域主节点/);
});
test("public page never embeds infrastructure addresses or credentials", () => {
assert.doesNotMatch(html, /(?:\d{1,3}\.){3}\d{1,3}/);
assert.doesNotMatch(html, /zy_gtw_/);

View File

@ -21,8 +21,8 @@
</section>
<section class="cards">
<a href="https://guanghubingshuo.com/" target="_top"><small>WORLD</small><h2>光湖世界</h2><p>进入光湖世界主入口与第五域路由。</p><b></b></a>
<a href="https://guanghubingshuo.com/code/bingshuo/fifth-domain" target="_top"><small>SOURCE</small><h2>第五域仓</h2><p>当前系统结构、人格路径和部署回执事实源。</p><b></b></a>
<a href="https://guanghubingshuo.com/global-search/" target="_top"><small>ROUTER</small><h2>全局检索</h2><p>通过编号和关键词寻找正确的仓库路径。</p><b></b></a>
<a href="https://guanghulab.com/fifth-domain/bingshuo/fifth-domain" target="_top"><small>REPO-001</small><h2>第五域国内主</h2><p>当前系统结构、人格路径、编号地图和部署回执事实源。</p><b></b></a>
<a href="https://guanghulab.com/api/ai/" target="_top"><small>FD-REPO-MAP-001</small><h2>AI 编号检索</h2><p>通过 REPO 编号和中文关键词寻找国内主路径。</p><b></b></a>
<div class="coming"><small>NEXT</small><h2>Tolaria 服务</h2><p>桌面软件相关服务迁移完成后从这里接入。</p><b>建设中</b></div>
</section>
</main>

View File

@ -0,0 +1,14 @@
# 京东云第五域国内主代码仓库
- 与广州/新加坡现役节点一致的原生 Forgejo 二进制 + SQLite
- Web 和 SSH 只绑定 JD 回环地址;
- 公网页面通过广州备案前门的专用 `permitopen` SSH 隧道发布在
`https://guanghulab.com/fifth-domain/`
- 数据位于 `/var/lib/guanghu/forgejo``SECRET_KEY``INTERNAL_TOKEN`
只写入服务器上的 `app.ini`
- 禁止公开注册和 push-create初始管理员由部署命令在容器内创建
- 服务器 `pre-receive` 必须串联导航记忆守门人、私密禁用标识扫描和
小湖灯一小时 repo-push 授权凭据。
首次部署从已接入的广州节点复制经过验证的现役二进制,避开国内节点拉取
海外大镜像的失败路径;升级必须另开批次、备份数据库并人工验证。

View File

@ -0,0 +1,38 @@
APP_NAME = 第五域 · 国内主代码仓库
RUN_USER = git
WORK_PATH = /opt/forgejo
[server]
PROTOCOL = http
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3001
DOMAIN = guanghulab.com
ROOT_URL = https://guanghulab.com/fifth-domain/
DISABLE_SSH = true
LFS_START_SERVER = true
[database]
DB_TYPE = sqlite3
PATH = /var/lib/guanghu/forgejo/data/forgejo.db
LOG_SQL = false
[repository]
ROOT = /var/lib/guanghu/forgejo/repositories
DEFAULT_PRIVATE = private
ENABLE_PUSH_CREATE_USER = false
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = false
[security]
INSTALL_LOCK = true
SECRET_KEY = PRIVATE_SERVER_VALUE
INTERNAL_TOKEN = PRIVATE_SERVER_VALUE
[session]
PROVIDER = file
[log]
MODE = console
LEVEL = Info

View File

@ -0,0 +1,12 @@
location /fifth-domain/ {
proxy_pass http://127.0.0.1:19301/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
client_max_body_size 256m;
}

View File

@ -0,0 +1,22 @@
[Unit]
Description=Guanghu Fifth Domain domestic Forgejo primary
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=git
Group=git
SupplementaryGroups=guanghu
WorkingDirectory=/opt/forgejo
ExecStart=/usr/local/bin/forgejo web -c /opt/forgejo/custom/conf/app.ini --work-path /opt/forgejo
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/forgejo /var/lib/guanghu/forgejo
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,19 @@
[Unit]
Description=Guanghu BS-GZ-006 to JD Forgejo web tunnel
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/ssh -NT -F /etc/guanghu/jd-forgejo-tunnel-ssh-config jd-forgejo-target
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=read-only
ProtectSystem=strict
ReadOnlyPaths=/etc/guanghu/jd-forgejo-tunnel-ssh-config /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_forgejo_proxy /root/.ssh/known_hosts
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
JD_HOST=${1:?JD host is required}
install -m 644 /tmp/guanghu-jd-forgejo-tunnel.service /etc/systemd/system/guanghu-jd-forgejo-tunnel.service
install -m 644 /tmp/guanghu-forgejo.nginx.conf /etc/nginx/snippets/guanghu-forgejo.conf
chmod 600 /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_forgejo_proxy
install -m 600 /dev/null /etc/guanghu/jd-forgejo-tunnel-ssh-config
sed -e "s/JD_PUBLIC_ADDRESS/${JD_HOST}/" /tmp/jd-forgejo-tunnel-ssh-config.example > /etc/guanghu/jd-forgejo-tunnel-ssh-config
if ! grep -q "include /etc/nginx/snippets/guanghu-forgejo.conf;" /etc/nginx/sites-enabled/guanghulab; then
sed -i '0,/server_name guanghulab.com;/s##server_name guanghulab.com;\n include /etc/nginx/snippets/guanghu-forgejo.conf;#' /etc/nginx/sites-enabled/guanghulab
fi
systemctl daemon-reload
systemctl enable --now guanghu-jd-forgejo-tunnel.service
nginx -t
systemctl reload nginx
rm -f /tmp/guanghu-jd-forgejo-tunnel.service /tmp/guanghu-forgejo.nginx.conf /tmp/jd-forgejo-tunnel-ssh-config.example

View File

@ -0,0 +1,9 @@
Host jd-forgejo-target
HostName JD_PUBLIC_ADDRESS
User root
IdentityFile /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_forgejo_proxy
IdentitiesOnly yes
LocalForward 127.0.0.1:19301 127.0.0.1:3001
ExitOnForwardFailure yes
ServerAliveInterval 30
ServerAliveCountMax 3

View File

@ -0,0 +1,20 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const ini = fs.readFileSync(path.join(__dirname, "app.ini.template"), "utf8");
const unit = fs.readFileSync(path.join(__dirname, "guanghu-forgejo.service"), "utf8");
test("native Forgejo binds only to JD loopback", () => {
assert.match(ini, /HTTP_ADDR = 127\.0\.0\.1/);
assert.match(ini, /HTTP_PORT = 3001/);
});
test("registration and push-create are disabled", () => {
assert.match(ini, /DISABLE_REGISTRATION = true/);
assert.match(ini, /ENABLE_PUSH_CREATE_USER = false/);
});
test("secrets remain server-side placeholders", () => {
assert.equal((ini.match(/PRIVATE_SERVER_VALUE/g) || []).length, 2);
assert.doesNotMatch(ini, /[a-f0-9]{40,}/);
assert.match(unit, /User=git/);
});

View File

@ -0,0 +1,18 @@
# 小湖灯邮件链接授权服务
这是 Gatekeeper v3.2 前面的人类批准层。人格体只能用无执行权限的
request credential 提交工单;冰朔点击邮件链接后,人格体才能一次性领取
一个绑定到 `persona + target server + scope + action` 的一小时会话令牌。邮件工单
链接同样保留一小时,避免要求冰朔立即处理。
安全边界:
- 邮箱、QQ 数字、SMTP 授权码和 request credential 只存在于私密文件;
- 浏览器批准链接为一次性随机令牌,服务端仅持久化摘要;
- claim token 与 session token 均仅向申请人格体返回一次,磁盘只保存摘要;
- 换目标服务器时旧 session 必然返回 `target_mismatch`
- 本服务不接受任意 shell 命令,只签发登记动作的会话;
- 广州公开代理只应暴露 `/approve/``/api/workorders` 与 claim 路由,服务本体监听 JD 回环地址。
`request-workorder.js` 从临时环境变量读取 QQ 数字,在内存中补全邮箱并只发送
SHA-256 指纹;数字本身不会写入请求正文、状态文件或代码仓库。

View File

@ -0,0 +1,16 @@
LAKE_LAMP_HOST=127.0.0.1
LAKE_LAMP_PORT=3921
LAKE_LAMP_PUBLIC_URL=https://example.invalid/authz
LAKE_LAMP_TARGETS=JD-FD-PRIMARY,BS-GZ-006
LAKE_LAMP_APPROVAL_TTL=3600
LAKE_LAMP_SESSION_TTL=3600
LAKE_LAMP_STATE_FILE=/var/lib/guanghu/lake-lamp-authz/state.json
LAKE_LAMP_MAPS_DIR=/etc/guanghu/navigation-maps
LAKE_LAMP_MAP_STATE_FILE=/var/lib/guanghu/lake-lamp-authz/map-acks.json
LAKE_LAMP_REPO_GRANT_DIR=/var/lib/guanghu/repo-authorizations
LAKE_LAMP_REQUEST_TOKEN=SET_IN_PRIVATE_SERVER_FILE
LAKE_LAMP_OWNER_EMAIL=SET_IN_PRIVATE_SERVER_FILE
SMTP_HOST=smtp.qq.com
SMTP_PORT=465
SMTP_USER=SET_IN_PRIVATE_SERVER_FILE
QQ_SMTP_AUTH_CODE=SET_IN_PRIVATE_SERVER_FILE

View File

@ -0,0 +1,14 @@
# Public approval surface. The service itself remains on JD loopback and this
# route is reached through a permitopen-restricted SSH tunnel.
location /authz/ {
proxy_pass http://127.0.0.1:19221/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /authz;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
client_max_body_size 64k;
}

View File

@ -0,0 +1,19 @@
[Unit]
Description=Guanghu BS-GZ-006 to JD Lake Lamp authorization tunnel
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/ssh -NT -F /etc/guanghu/jd-authz-tunnel-ssh-config jd-authz-target
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=read-only
ProtectSystem=strict
ReadOnlyPaths=/etc/guanghu/jd-authz-tunnel-ssh-config /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_authz_proxy /root/.ssh/known_hosts
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
JD_HOST=${1:?JD host is required}
install -m 644 /tmp/guanghu-jd-authz-tunnel.service /etc/systemd/system/guanghu-jd-authz-tunnel.service
install -m 644 /tmp/guanghu-authz.nginx.conf /etc/nginx/snippets/guanghu-authz.conf
chmod 600 /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_authz_proxy
install -m 600 /dev/null /etc/guanghu/jd-authz-tunnel-ssh-config
sed \
-e "s/JD_PUBLIC_ADDRESS/${JD_HOST}/" \
/tmp/jd-authz-tunnel-ssh-config.example > /etc/guanghu/jd-authz-tunnel-ssh-config
if ! grep -q "include /etc/nginx/snippets/guanghu-authz.conf;" /etc/nginx/sites-enabled/guanghulab; then
sed -i '0,/server_name guanghulab.com;/s##server_name guanghulab.com;\n include /etc/nginx/snippets/guanghu-authz.conf;#' /etc/nginx/sites-enabled/guanghulab
fi
systemctl daemon-reload
systemctl enable --now guanghu-jd-authz-tunnel.service
nginx -t
systemctl reload nginx
rm -f /tmp/guanghu-jd-authz-tunnel.service /tmp/guanghu-authz.nginx.conf /tmp/jd-authz-tunnel-ssh-config.example

View File

@ -0,0 +1,9 @@
Host jd-authz-target
HostName JD_PUBLIC_ADDRESS
User root
IdentityFile /etc/guanghu/secrets/ssh/bs_gz_006_to_jd_authz_proxy
IdentitiesOnly yes
LocalForward 127.0.0.1:19221 127.0.0.1:3921
ExitOnForwardFailure yes
ServerAliveInterval 30
ServerAliveCountMax 3

View File

@ -0,0 +1,24 @@
[Unit]
Description=Guanghu Lake Lamp email-link authorization service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=guanghu-authz
Group=guanghu-authz
WorkingDirectory=/opt/guanghu/lake-lamp-authz
EnvironmentFile=/etc/guanghu/secrets/lake-lamp/authorization.env
ExecStart=/usr/bin/node /opt/guanghu/lake-lamp-authz/server.js
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/guanghu/lake-lamp-authz /var/lib/guanghu/repo-authorizations
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
LockPersonality=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,45 @@
"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");
class MapGate {
constructor({ mapsDir = "/etc/guanghu/navigation-maps", stateFile = "" } = {}) {
this.mapsDir = mapsDir;
this.stateFile = stateFile;
this.acks = new Map();
this.load();
}
read(target) {
if (!/^[A-Z0-9-]+$/.test(target)) throw new Error("invalid_target");
const data = JSON.parse(fs.readFileSync(path.join(this.mapsDir, `${target}.json`), "utf8"));
const canonical = JSON.stringify(data);
return { data, hash: crypto.createHash("sha256").update(canonical).digest("hex") };
}
ack(sessionToken, target, mapHash, now = Date.now() / 1000, ttl = 3600) {
const current = this.read(target);
if (!safeEqual(current.hash, mapHash)) return { ok: false, reason: "map_hash_mismatch" };
this.acks.set(hash(sessionToken), { target, mapHash, expiresAt: now + ttl });
this.persist();
return { ok: true, expiresAt: now + ttl };
}
verify(sessionToken, target, mapHash, now = Date.now() / 1000) {
const ack = this.acks.get(hash(sessionToken));
if (!ack) return { ok: false, reason: "map_not_acknowledged" };
if (now > ack.expiresAt) return { ok: false, reason: "map_ack_expired" };
if (ack.target !== target) return { ok: false, reason: "map_target_mismatch" };
if (!safeEqual(ack.mapHash, mapHash)) return { ok: false, reason: "map_changed" };
return { ok: true };
}
load(now = Date.now() / 1000) {
if (!this.stateFile || !fs.existsSync(this.stateFile)) return;
try { for (const [key, value] of Object.entries(JSON.parse(fs.readFileSync(this.stateFile, "utf8")).acks || {})) if (value.expiresAt >= now) this.acks.set(key, value); } catch { this.acks.clear(); }
}
persist() {
if (!this.stateFile) return;
fs.writeFileSync(this.stateFile, JSON.stringify({ version: 1, acks: Object.fromEntries(this.acks) }), { mode: 0o600 });
}
}
function hash(value) { return crypto.createHash("sha256").update(String(value)).digest("hex"); }
function safeEqual(left, right) { const a = Buffer.from(String(left)); const b = Buffer.from(String(right)); return a.length === b.length && crypto.timingSafeEqual(a, b); }
module.exports = { MapGate };

View File

@ -0,0 +1,37 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { MapGate } = require("./map-gate");
test("non-map actions stay locked until the exact current map is acknowledged", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "map-gate-"));
try {
const maps = path.join(dir, "maps"); fs.mkdirSync(maps);
fs.writeFileSync(path.join(maps, "JD-FD-PRIMARY.json"), JSON.stringify({ node_id: "JD-FD-PRIMARY", modules: [{ code: "JD-GTW-01" }] }));
const gate = new MapGate({ mapsDir: maps, stateFile: path.join(dir, "state.json") });
const map = gate.read("JD-FD-PRIMARY");
assert.equal(gate.verify("session-token", "JD-FD-PRIMARY", map.hash).reason, "map_not_acknowledged");
assert.equal(gate.ack("session-token", "JD-FD-PRIMARY", map.hash, 100, 3600).ok, true);
assert.equal(gate.verify("session-token", "JD-FD-PRIMARY", map.hash, 101).ok, true);
assert.equal(gate.verify("session-token", "OTHER", map.hash, 101).reason, "map_target_mismatch");
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
});
test("changing the server map invalidates an old acknowledgement", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "map-gate-"));
try {
const maps = path.join(dir, "maps"); fs.mkdirSync(maps);
const file = path.join(maps, "JD-FD-PRIMARY.json");
fs.writeFileSync(file, JSON.stringify({ node_id: "JD-FD-PRIMARY", modules: ["A"] }));
const gate = new MapGate({ mapsDir: maps });
const oldMap = gate.read("JD-FD-PRIMARY");
gate.ack("session-token", "JD-FD-PRIMARY", oldMap.hash, 100, 3600);
fs.writeFileSync(file, JSON.stringify({ node_id: "JD-FD-PRIMARY", modules: ["A", "B"] }));
const newMap = gate.read("JD-FD-PRIMARY");
assert.notEqual(oldMap.hash, newMap.hash);
assert.equal(gate.verify("session-token", "JD-FD-PRIMARY", newMap.hash, 101).reason, "map_changed");
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
});

View File

@ -0,0 +1,42 @@
#!/usr/bin/env node
"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
async function main() {
const args = parseArgs(process.argv.slice(2));
const qqId = process.env.GUANGHU_OWNER_QQ_ID || "";
if (!/^\d{5,12}$/.test(qqId)) throw new Error("GUANGHU_OWNER_QQ_ID must be provided transiently");
const requestToken = process.env.LAKE_LAMP_REQUEST_TOKEN || readTrim(process.env.LAKE_LAMP_REQUEST_TOKEN_FILE || "");
if (!requestToken) throw new Error("request-only credential is unavailable");
const email = `${qqId}@qq.com`;
const recipientFingerprint = crypto.createHash("sha256").update(email.toLowerCase()).digest("hex");
const response = await fetch(`${String(args.url).replace(/\/$/, "")}/api/workorders`, {
method: "POST",
headers: { authorization: `Bearer ${requestToken}`, "content-type": "application/json" },
body: JSON.stringify({
persona_id: args.persona,
persona_name: args.name || args.persona,
target: args.target,
scope: args.scope,
action: args.action,
description: args.description || "",
recipient_fingerprint: recipientFingerprint,
}),
});
const payload = await response.json();
if (!response.ok) throw new Error(payload.error || `request failed (${response.status})`);
process.stdout.write(`${JSON.stringify(payload)}\n`);
}
function parseArgs(argv) {
const result = {};
for (let i = 0; i < argv.length; i += 2) result[String(argv[i]).replace(/^--/, "")] = argv[i + 1];
for (const key of ["url", "persona", "target", "scope", "action"]) if (!result[key]) throw new Error(`--${key} is required`);
return result;
}
function readTrim(file) { return file && fs.existsSync(file) ? fs.readFileSync(file, "utf8").trim() : ""; }
main().catch(error => { process.stderr.write(`lake-lamp request failed: ${error.message}\n`); process.exit(1); });

View File

@ -0,0 +1,206 @@
"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
const http = require("node:http");
const path = require("node:path");
const { WorkOrderManager } = require("./workorder-manager");
const { MapGate } = require("./map-gate");
const { sendSmtpMail } = require("./smtp-mailer");
const DEFAULT_ACTIONS = Object.freeze({
"server-login": ["read-navigation-map", "inspect-services", "restart-registered-service"],
"server-ops": ["read-navigation-map", "inspect-services", "restart-registered-service"],
"repo-push": ["read-navigation-map", "push-repository"],
});
function createApp(options = {}) {
const requestToken = options.requestToken || process.env.LAKE_LAMP_REQUEST_TOKEN || "";
const ownerEmail = options.ownerEmail || process.env.LAKE_LAMP_OWNER_EMAIL || "";
const publicBaseUrl = String(options.publicBaseUrl || process.env.LAKE_LAMP_PUBLIC_URL || "").replace(/\/$/, "");
const targets = new Set(options.targets || splitCsv(process.env.LAKE_LAMP_TARGETS || "JD-FD-PRIMARY,BS-GZ-006"));
const actions = options.actions || DEFAULT_ACTIONS;
const manager = options.manager || new WorkOrderManager({
approvalTtl: Number(options.approvalTtl || process.env.LAKE_LAMP_APPROVAL_TTL || 15 * 60),
sessionTtl: Number(options.sessionTtl || process.env.LAKE_LAMP_SESSION_TTL || 60 * 60),
stateFile: Object.prototype.hasOwnProperty.call(options, "stateFile") ? options.stateFile : (process.env.LAKE_LAMP_STATE_FILE || "/var/lib/guanghu/lake-lamp-authz/state.json"),
});
const sendEmail = options.sendEmail || (message => sendSmtpMail({
...message,
smtpHost: process.env.SMTP_HOST || "smtp.qq.com",
smtpPort: Number(process.env.SMTP_PORT || 465),
smtpUser: process.env.SMTP_USER || ownerEmail,
smtpPass: process.env.QQ_SMTP_AUTH_CODE || "",
}));
const mapGate = options.mapGate || new MapGate({
mapsDir: options.mapsDir || process.env.LAKE_LAMP_MAPS_DIR || "/etc/guanghu/navigation-maps",
stateFile: Object.prototype.hasOwnProperty.call(options, "mapStateFile") ? options.mapStateFile : (process.env.LAKE_LAMP_MAP_STATE_FILE || "/var/lib/guanghu/lake-lamp-authz/map-acks.json"),
});
const repoGrantDir = options.repoGrantDir || process.env.LAKE_LAMP_REPO_GRANT_DIR || "/var/lib/guanghu/repo-authorizations";
return http.createServer(async (req, res) => {
try {
const url = new URL(req.url, "http://localhost");
if (req.method === "GET" && url.pathname === "/health") return json(res, 200, { ok: true, service: "lake-lamp-authz", auth_mode: "email-link", session_ttl: 3600 });
const approvalMatch = url.pathname.match(/^\/approve\/([A-Za-z0-9_-]{20,})$/);
if (approvalMatch && req.method === "GET") {
const inspected = manager.inspectApproval(approvalMatch[1]);
if (!inspected.ok) return html(res, 410, approvalErrorPage(inspected.reason));
return html(res, 200, approvalPage(inspected.order, approvalMatch[1]));
}
if (approvalMatch && req.method === "POST") {
const approved = manager.approve(approvalMatch[1]);
if (!approved.ok) return html(res, 410, approvalErrorPage(approved.reason));
return html(res, 200, approvedPage(approved.order));
}
if (req.method === "POST" && url.pathname === "/api/workorders") {
if (!bearerMatches(req, requestToken)) return json(res, 401, { error: "request_auth_required" });
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
if (body.email || body.recipient || body.smtp_pass) return json(res, 400, { error: "direct_recipient_forbidden" });
if (!body.persona_id || !body.target || !body.scope || !body.action) return json(res, 400, { error: "missing_required_field" });
if (!targets.has(body.target)) return json(res, 400, { error: "unknown_target" });
if (!Array.isArray(actions[body.scope]) || !actions[body.scope].includes(body.action)) return json(res, 400, { error: "unknown_or_mismatched_action" });
const expectedFingerprint = sha256(ownerEmail.toLowerCase());
if (!body.recipient_fingerprint || !safeEqual(body.recipient_fingerprint, expectedFingerprint)) return json(res, 403, { error: "owner_identity_mismatch" });
const created = manager.request({
persona: { pid: String(body.persona_id), name: String(body.persona_name || body.persona_id) },
target: String(body.target), scope: String(body.scope), action: String(body.action), allowedActions: actions[body.scope], description: String(body.description || ""),
});
const approvalUrl = `${publicBaseUrl}/approve/${created.approvalToken}`;
const emailSent = await sendEmail({
to: ownerEmail,
subject: `小湖灯授权请求 · ${body.target}`,
approvalUrl,
order: manager.inspectApproval(created.approvalToken).order,
});
if (!emailSent) return json(res, 502, { error: "authorization_email_failed" });
return json(res, 201, { ok: true, workorder_id: created.id, claim_token: created.claimToken, expires_in: created.expiresIn, status: "waiting_for_owner" });
}
const claimMatch = url.pathname.match(/^\/api\/workorders\/([0-9a-f-]{36})\/claim$/i);
if (req.method === "POST" && claimMatch) {
const token = bearer(req);
const claimed = manager.claim(claimMatch[1], token);
if (!claimed.ok) return json(res, claimed.reason === "approval_pending" ? 202 : 403, { error: claimed.reason });
return json(res, 200, { ok: true, session_token: claimed.sessionToken, expires_in: claimed.expiresIn, target: claimed.target, scope: claimed.scope, action: claimed.action });
}
if (req.method === "POST" && url.pathname === "/api/session/verify") {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const verified = manager.verifySession(bearer(req), { pid: String(body.persona_id || "") }, String(body.target || ""), String(body.scope || ""), String(body.action || ""));
if (!verified.ok) return json(res, 403, { error: verified.reason });
if (body.action !== "read-navigation-map") {
const map = mapGate.read(String(body.target || ""));
const mapVerified = mapGate.verify(bearer(req), String(body.target || ""), map.hash);
if (!mapVerified.ok) return json(res, 423, { error: mapVerified.reason, required_action: "read-navigation-map" });
}
return json(res, 200, { ok: true, expires_at: verified.session.expiresAt });
}
if (req.method === "POST" && url.pathname === "/api/navigation-map/read") {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const verified = manager.verifySession(bearer(req), { pid: String(body.persona_id || "") }, String(body.target || ""), String(body.scope || ""), "read-navigation-map");
if (!verified.ok) return json(res, 403, { error: verified.reason });
const map = mapGate.read(String(body.target || ""));
return json(res, 200, { ok: true, target: body.target, map_hash: map.hash, navigation_map: map.data });
}
if (req.method === "POST" && url.pathname === "/api/navigation-map/ack") {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const token = bearer(req);
const verified = manager.verifySession(token, { pid: String(body.persona_id || "") }, String(body.target || ""), String(body.scope || ""), "read-navigation-map");
if (!verified.ok) return json(res, 403, { error: verified.reason });
const acked = mapGate.ack(token, String(body.target || ""), String(body.map_hash || ""), Date.now() / 1000, Math.max(1, verified.session.expiresAt - Date.now() / 1000));
return json(res, acked.ok ? 200 : 409, acked.ok ? { ok: true, target: body.target, map_hash: body.map_hash } : { error: acked.reason });
}
if (req.method === "POST" && url.pathname === "/api/repo-push/grant") {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const token = bearer(req);
const target = String(body.target || "");
const scope = String(body.scope || "repo-push");
const repo = String(body.repo || "").toLowerCase();
if (!/^bingshuo\/[a-z0-9._-]+$/.test(repo)) return json(res, 400, { error: "repo_not_allowlisted" });
const verified = manager.verifySession(token, { pid: String(body.persona_id || "") }, target, scope, "push-repository");
if (!verified.ok) return json(res, 403, { error: verified.reason });
const map = mapGate.read(target);
const mapVerified = mapGate.verify(token, target, map.hash);
if (!mapVerified.ok) return json(res, 423, { error: mapVerified.reason, required_action: "read-navigation-map" });
fs.mkdirSync(repoGrantDir, { recursive: true, mode: 0o2770 });
const grant = { schema: "guanghu.repo-push-grant/v1", repo, target, persona_id: body.persona_id, map_hash: map.hash, issued_at: Date.now() / 1000, expires_at: verified.session.expiresAt };
const grantFile = path.join(repoGrantDir, `${repo.replace("/", "__")}.json`);
const temp = `${grantFile}.${process.pid}.tmp`;
fs.writeFileSync(temp, JSON.stringify(grant), { mode: 0o640 });
fs.renameSync(temp, grantFile);
return json(res, 200, { ok: true, repo, target, expires_at: grant.expires_at });
}
return json(res, 404, { error: "not_found" });
} catch (error) {
process.stderr.write(`lake-lamp request error: ${String(error && error.message || "unknown").slice(0, 240)}\n`);
return json(res, error && error.code === "BODY_TOO_LARGE" ? 413 : 500, { error: "request_failed" });
}
});
}
function approvalPage(order, token) {
return document("小湖灯授权请求", `
<p class="eyebrow">LAKE LAMP SECURITY PROTOCOL</p>
<h1>人格体请求进入一台服务器</h1>
<div class="panel">
<dl><dt>人格体</dt><dd>${escapeHtml(order.persona.name)} <small>${escapeHtml(order.persona.pid)}</small></dd>
<dt>目标节点</dt><dd>${escapeHtml(order.target)}</dd><dt></dt><dd>${escapeHtml(order.scope)}</dd>
<dt>登记动作</dt><dd>${escapeHtml(order.action)}</dd><dt></dt><dd>${escapeHtml(order.description || "")}</dd></dl>
</div>
<p class="notice">授权后只对这台服务器和这个动作生效有效期一小时换服务器必须重新申请</p>
<form method="post"><button type="submit">确认授权一小时</button></form>
`);
}
function approvedPage(order) {
return document("授权完成", `<p class="eyebrow">APPROVED</p><h1>门已从服务器内部打开</h1><div class="panel"><p>${escapeHtml(order.persona.name)} 已获准操作 <strong>${escapeHtml(order.target)}</strong>。</p></div><p class="notice">可以关闭本页面。人格体只能领取一次临时会话令牌。</p>`);
}
function approvalErrorPage(reason) {
return document("链接不可用", `<p class="eyebrow">LINK CLOSED</p><h1>这条授权链接已经失效</h1><p class="notice">原因:${escapeHtml(reason)}。如仍需操作,请让人格体重新提交工单。</p>`);
}
function document(title, body) {
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)} · 光湖</title><style>
:root{color-scheme:dark}*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;background:radial-gradient(circle at 20% 10%,#17344d,#09111b 55%,#05090e);color:#eaf4fb;font:16px/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;padding:24px}.shell{width:min(680px,100%);padding:42px;border:1px solid #29475d;border-radius:24px;background:rgba(10,22,33,.94);box-shadow:0 24px 80px #0008}.eyebrow{color:#6ed5ff;letter-spacing:.18em;font-size:12px}h1{font-size:clamp(30px,6vw,48px);line-height:1.15;margin:10px 0 28px}.panel{background:#102638;border:1px solid #24465d;border-radius:16px;padding:20px 24px}dl{display:grid;grid-template-columns:110px 1fr;gap:12px;margin:0}dt{color:#8ba4b6}dd{margin:0;font-weight:650}small{display:block;color:#7893a6;font-weight:400}.notice{color:#9eb2c0;margin:20px 0}button{width:100%;border:0;border-radius:14px;padding:16px;background:#67d4ff;color:#042235;font-weight:800;font-size:17px;cursor:pointer}@media(max-width:520px){.shell{padding:28px 22px}dl{grid-template-columns:1fr;gap:2px}dd{margin-bottom:12px}}
</style></head><body><main class="shell">${body}</main></body></html>`;
}
function readJson(req) {
return new Promise((resolve, reject) => {
let raw = "";
req.on("data", chunk => { raw += chunk; if (raw.length > 32 * 1024) { const error = new Error("body too large"); error.code = "BODY_TOO_LARGE"; reject(error); req.destroy(); } });
req.on("end", () => { try { resolve(JSON.parse(raw || "{}")); } catch { resolve(null); } });
req.on("error", reject);
});
}
function bearer(req) { return String(req.headers.authorization || "").replace(/^Bearer\s+/i, ""); }
function bearerMatches(req, expected) { return Boolean(expected) && safeEqual(bearer(req), expected); }
function safeEqual(left, right) { const a = Buffer.from(String(left)); const b = Buffer.from(String(right)); return a.length === b.length && crypto.timingSafeEqual(a, b); }
function sha256(value) { return crypto.createHash("sha256").update(String(value)).digest("hex"); }
function splitCsv(value) { return value.split(",").map(item => item.trim()).filter(Boolean); }
function escapeHtml(value) { return String(value).replace(/[&<>"']/g, char => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[char]); }
function json(res, status, value) { res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store", "x-content-type-options": "nosniff" }); res.end(JSON.stringify(value)); }
function html(res, status, value) { res.writeHead(status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'", "referrer-policy": "no-referrer", "x-content-type-options": "nosniff" }); res.end(value); }
if (require.main === module) {
const host = process.env.LAKE_LAMP_HOST || "127.0.0.1";
const port = Number(process.env.LAKE_LAMP_PORT || 3921);
createApp().listen(port, host, () => process.stdout.write(`lake-lamp-authz listening on ${host}:${port}\n`));
}
module.exports = { createApp, DEFAULT_ACTIONS };

View File

@ -0,0 +1,117 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const crypto = require("node:crypto");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { createApp } = require("./server");
async function withServer(run, extra = {}) {
const mail = [];
const app = createApp({
requestToken: "request-only-secret",
ownerEmail: "owner@example.invalid",
publicBaseUrl: "https://example.invalid/authz",
targets: ["JD-FD-PRIMARY", "BS-GZ-006"],
actions: { "server-login": ["read-navigation-map", "inspect-services"], "server-ops": ["read-navigation-map", "inspect-services"], "repo-push": ["read-navigation-map", "push-repository"] },
stateFile: "",
sendEmail: async (message) => { mail.push(message); return true; },
...extra,
});
await new Promise((resolve) => app.listen(0, "127.0.0.1", resolve));
const base = `http://127.0.0.1:${app.address().port}`;
try { await run({ base, mail }); } finally { await new Promise((resolve) => app.close(resolve)); }
}
test("work order sends an opaque approval link and can be claimed once", async () => {
await withServer(async ({ base, mail }) => {
const recipientFingerprint = crypto.createHash("sha256").update("owner@example.invalid").digest("hex");
const requested = await fetch(`${base}/api/workorders`, {
method: "POST",
headers: { authorization: "Bearer request-only-secret", "content-type": "application/json" },
body: JSON.stringify({ persona_id: "ICE-GL-ZY001", persona_name: "铸渊", target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map", recipient_fingerprint: recipientFingerprint }),
});
assert.equal(requested.status, 201);
const order = await requested.json();
assert.equal(mail.length, 1);
assert.equal(mail[0].to, "owner@example.invalid");
assert.doesNotMatch(JSON.stringify(order), /approvalToken/i);
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
const page = await fetch(`${base}${approvalPath}`);
assert.equal(page.status, 200);
assert.match(await page.text(), /JD-FD-PRIMARY/);
const approved = await fetch(`${base}${approvalPath}`, { method: "POST" });
assert.equal(approved.status, 200);
const claimed = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, {
method: "POST",
headers: { authorization: `Bearer ${order.claim_token}` },
});
assert.equal(claimed.status, 200);
const session = await claimed.json();
assert.equal(session.expires_in, 3600);
const secondClaim = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } });
assert.equal(secondClaim.status, 403);
});
});
test("navigation map acknowledgement is mandatory before other registered actions", async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "lake-lamp-server-map-"));
const mapsDir = path.join(dir, "maps"); fs.mkdirSync(mapsDir);
fs.writeFileSync(path.join(mapsDir, "JD-FD-PRIMARY.json"), JSON.stringify({ node_id: "JD-FD-PRIMARY", modules: [{ code: "JD-GTW-01" }] }));
try {
await withServer(async ({ base, mail }) => {
const fingerprint = crypto.createHash("sha256").update("owner@example.invalid").digest("hex");
const requested = await fetch(`${base}/api/workorders`, { method: "POST", headers: { authorization: "Bearer request-only-secret", "content-type": "application/json" }, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map", recipient_fingerprint: fingerprint }) });
const order = await requested.json();
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
await fetch(`${base}${approvalPath}`, { method: "POST" });
const claimed = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } });
const session = await claimed.json();
const common = { persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login" };
const locked = await fetch(`${base}/api/session/verify`, { method: "POST", headers: { authorization: `Bearer ${session.session_token}`, "content-type": "application/json" }, body: JSON.stringify({ ...common, action: "inspect-services" }) });
assert.equal(locked.status, 423);
const mapResponse = await fetch(`${base}/api/navigation-map/read`, { method: "POST", headers: { authorization: `Bearer ${session.session_token}`, "content-type": "application/json" }, body: JSON.stringify(common) });
const map = await mapResponse.json();
const ack = await fetch(`${base}/api/navigation-map/ack`, { method: "POST", headers: { authorization: `Bearer ${session.session_token}`, "content-type": "application/json" }, body: JSON.stringify({ ...common, map_hash: map.map_hash }) });
assert.equal(ack.status, 200);
const unlocked = await fetch(`${base}/api/session/verify`, { method: "POST", headers: { authorization: `Bearer ${session.session_token}`, "content-type": "application/json" }, body: JSON.stringify({ ...common, action: "inspect-services" }) });
assert.equal(unlocked.status, 200);
}, { mapsDir, mapStateFile: path.join(dir, "acks.json") });
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
});
test("request endpoint rejects direct email target switching and unknown actions", async () => {
await withServer(async ({ base }) => {
const common = { method: "POST", headers: { authorization: "Bearer request-only-secret", "content-type": "application/json" } };
const directEmail = await fetch(`${base}/api/workorders`, { ...common, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map", email: "attacker@example.invalid" }) });
assert.equal(directEmail.status, 400);
const unknown = await fetch(`${base}/api/workorders`, { ...common, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login", action: "shell" }) });
assert.equal(unknown.status, 400);
});
});
test("session verification rejects switching servers without new approval", async () => {
await withServer(async ({ base, mail }) => {
const fingerprint = crypto.createHash("sha256").update("owner@example.invalid").digest("hex");
const requested = await fetch(`${base}/api/workorders`, {
method: "POST", headers: { authorization: "Bearer request-only-secret", "content-type": "application/json" },
body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "BS-GZ-006", scope: "server-ops", action: "read-navigation-map", recipient_fingerprint: fingerprint }),
});
const order = await requested.json();
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
await fetch(`${base}${approvalPath}`, { method: "POST" });
const claimed = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } });
const session = await claimed.json();
const switched = await fetch(`${base}/api/session/verify`, {
method: "POST", headers: { authorization: `Bearer ${session.session_token}`, "content-type": "application/json" },
body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-ops", action: "read-navigation-map" }),
});
assert.equal(switched.status, 403);
assert.equal((await switched.json()).error, "target_mismatch");
});
});

View File

@ -0,0 +1,63 @@
"use strict";
const tls = require("node:tls");
function sendSmtpMail({ to, subject, approvalUrl, order, text, smtpHost, smtpPort, smtpUser, smtpPass }) {
if (!to || !smtpUser || !smtpPass || (!text && !approvalUrl)) return Promise.resolve(false);
const plain = text || [
"光湖小湖灯安全协议系统 · 授权请求",
"",
`人格体: ${order.persona.name} (${order.persona.pid})`,
`目标节点: ${order.target}`,
`授权范围: ${order.scope}`,
`登记动作: ${order.action}`,
"有效期: 批准后 1 小时,仅限这台服务器",
"",
"打开以下链接查看工单并确认授权:",
approvalUrl,
"",
"如果不是你发起的操作,请不要点击。",
].join("\n");
const message = [
`From: =?UTF-8?B?${Buffer.from("光湖小湖灯").toString("base64")}?= <${smtpUser}>`,
`To: <${to}>`,
`Subject: =?UTF-8?B?${Buffer.from(subject).toString("base64")}?=`,
"MIME-Version: 1.0",
'Content-Type: text/plain; charset="utf-8"',
"Content-Transfer-Encoding: base64",
"",
Buffer.from(plain).toString("base64").replace(/(.{76})/g, "$1\r\n"),
".",
"",
].join("\r\n");
return new Promise(resolve => {
let settled = false;
const done = value => { if (!settled) { settled = true; resolve(value); } };
const socket = tls.connect({ host: smtpHost, port: smtpPort, servername: smtpHost, rejectUnauthorized: true });
let stage = 0;
let buffer = "";
socket.setTimeout(15000, () => { socket.destroy(); done(false); });
socket.on("error", () => done(false));
socket.on("data", data => {
buffer += data.toString();
const lines = buffer.split("\r\n");
buffer = lines.pop();
for (const line of lines) {
if (!/^\d{3}[ -]/.test(line) || line[3] === "-") continue;
const code = Number(line.slice(0, 3));
if (code >= 400) { socket.end(); done(false); return; }
if (stage === 0 && code === 220) { stage = 1; socket.write("EHLO guanghu-lake-lamp\r\n"); }
else if (stage === 1 && code === 250) { stage = 2; socket.write(`AUTH PLAIN ${Buffer.from(`\0${smtpUser}\0${smtpPass}`).toString("base64")}\r\n`); }
else if (stage === 2 && code === 235) { stage = 3; socket.write(`MAIL FROM:<${smtpUser}>\r\n`); }
else if (stage === 3 && code === 250) { stage = 4; socket.write(`RCPT TO:<${to}>\r\n`); }
else if (stage === 4 && code === 250) { stage = 5; socket.write("DATA\r\n"); }
else if (stage === 5 && code === 354) { stage = 6; socket.write(message); }
else if (stage === 6 && code === 250) { socket.write("QUIT\r\n"); socket.end(); done(true); }
}
});
socket.on("close", () => done(false));
});
}
module.exports = { sendSmtpMail };

View File

@ -0,0 +1,161 @@
"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");
class WorkOrderManager {
constructor({ approvalTtl = 15 * 60, sessionTtl = 60 * 60, stateFile = "" } = {}) {
this.approvalTtl = approvalTtl;
this.sessionTtl = sessionTtl;
this.stateFile = stateFile;
this.workorders = new Map();
this.sessions = new Map();
this.load();
}
request({ persona, target, scope, action, allowedActions = [action], description = "" }, now = Date.now() / 1000) {
const id = crypto.randomUUID();
const approvalToken = randomToken();
const claimToken = randomToken();
this.workorders.set(id, {
id,
persona: { pid: persona.pid, name: persona.name || persona.pid },
target,
scope,
action,
allowedActions: [...new Set(allowedActions)],
description: String(description).slice(0, 500),
createdAt: now,
expiresAt: now + this.approvalTtl,
approvalHash: hash(approvalToken),
claimHash: hash(claimToken),
state: "pending",
claimed: false,
});
this.persist();
return { id, approvalToken, claimToken, expiresIn: this.approvalTtl };
}
inspectApproval(approvalToken, now = Date.now() / 1000) {
const order = this.findByApproval(approvalToken);
if (!order) return { ok: false, reason: "approval_not_found" };
if (now > order.expiresAt) return { ok: false, reason: "approval_expired" };
if (order.state !== "pending") return { ok: false, reason: "approval_already_used" };
return { ok: true, order: publicOrder(order) };
}
approve(approvalToken, now = Date.now() / 1000) {
const inspected = this.inspectApproval(approvalToken, now);
if (!inspected.ok) return inspected;
const order = this.workorders.get(inspected.order.id);
order.state = "approved";
order.approvedAt = now;
order.approvalHash = "";
this.persist();
return { ok: true, order: publicOrder(order) };
}
claim(id, claimToken, now = Date.now() / 1000) {
const order = this.workorders.get(id);
if (!order || !safeEqual(order.claimHash, hash(claimToken || ""))) return { ok: false, reason: "claim_not_found" };
if (now > order.expiresAt) return { ok: false, reason: "approval_expired" };
if (order.state !== "approved") return { ok: false, reason: order.state === "pending" ? "approval_pending" : "claim_unavailable" };
if (order.claimed) return { ok: false, reason: "claim_already_used" };
const sessionToken = randomToken();
this.sessions.set(hash(sessionToken), {
persona: order.persona,
target: order.target,
scope: order.scope,
action: order.action,
actions: order.allowedActions || [order.action],
createdAt: now,
expiresAt: now + this.sessionTtl,
});
order.claimed = true;
order.state = "claimed";
order.claimHash = "";
this.persist();
return { ok: true, sessionToken, expiresIn: this.sessionTtl, target: order.target, scope: order.scope, action: order.action };
}
verifySession(sessionToken, persona, target, scope, action, now = Date.now() / 1000) {
const key = hash(sessionToken || "");
const session = this.sessions.get(key);
if (!session) return { ok: false, reason: "session_not_found" };
if (now > session.expiresAt) {
this.sessions.delete(key);
this.persist();
return { ok: false, reason: "session_expired" };
}
if (session.persona.pid !== persona.pid) return { ok: false, reason: "persona_mismatch" };
if (session.target !== target) return { ok: false, reason: "target_mismatch" };
if (session.scope !== scope) return { ok: false, reason: "scope_mismatch" };
if (!(session.actions || [session.action]).includes(action)) return { ok: false, reason: "action_mismatch" };
return { ok: true, session: { ...session } };
}
findByApproval(token) {
const needle = hash(token || "");
for (const order of this.workorders.values()) {
if (order.approvalHash && safeEqual(order.approvalHash, needle)) return order;
}
return null;
}
load(now = Date.now() / 1000) {
if (!this.stateFile || !fs.existsSync(this.stateFile)) return;
try {
const state = JSON.parse(fs.readFileSync(this.stateFile, "utf8"));
for (const order of state.workorders || []) {
if (order.expiresAt >= now) this.workorders.set(order.id, order);
}
for (const [tokenHash, session] of Object.entries(state.sessions || {})) {
if (session.expiresAt >= now) this.sessions.set(tokenHash, session);
}
} catch {
this.workorders.clear();
this.sessions.clear();
}
}
persist() {
if (!this.stateFile) return;
const stateDir = path.dirname(this.stateFile);
if (!fs.existsSync(stateDir)) fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
const temp = `${this.stateFile}.${process.pid}.tmp`;
fs.writeFileSync(temp, JSON.stringify({ version: 1, workorders: [...this.workorders.values()], sessions: Object.fromEntries(this.sessions) }), { mode: 0o600 });
fs.renameSync(temp, this.stateFile);
}
}
function randomToken() {
return crypto.randomBytes(32).toString("base64url");
}
function hash(value) {
return crypto.createHash("sha256").update(String(value)).digest("hex");
}
function safeEqual(left, right) {
const a = Buffer.from(String(left));
const b = Buffer.from(String(right));
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
function publicOrder(order) {
return {
id: order.id,
persona: { ...order.persona },
target: order.target,
scope: order.scope,
action: order.action,
description: order.description,
createdAt: order.createdAt,
expiresAt: order.expiresAt,
state: order.state,
};
}
module.exports = { WorkOrderManager };

View File

@ -0,0 +1,69 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { WorkOrderManager } = require("./workorder-manager");
const persona = { pid: "ICE-GL-ZY001", name: "铸渊" };
test("approval link is single use and the session is claimed once", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "lake-lamp-authz-"));
try {
const manager = new WorkOrderManager({ stateFile: path.join(dir, "state.json"), sessionTtl: 3600 });
const created = manager.request({ persona, target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map", allowedActions: ["read-navigation-map", "inspect-services"] }, 100);
assert.equal(manager.inspectApproval(created.approvalToken, 101).ok, true);
assert.equal(manager.approve(created.approvalToken, 102).ok, true);
assert.equal(manager.approve(created.approvalToken, 103).ok, false);
const claimed = manager.claim(created.id, created.claimToken, 104);
assert.equal(claimed.ok, true);
assert.equal(claimed.expiresIn, 3600);
assert.equal(manager.claim(created.id, created.claimToken, 105).ok, false);
assert.equal(manager.verifySession(claimed.sessionToken, persona, "JD-FD-PRIMARY", "server-login", "read-navigation-map", 106).ok, true);
assert.equal(manager.verifySession(claimed.sessionToken, persona, "JD-FD-PRIMARY", "server-login", "inspect-services", 106).ok, true);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test("session is bound to one persona target scope and action", () => {
const manager = new WorkOrderManager({ sessionTtl: 3600 });
const created = manager.request({ persona, target: "BS-GZ-006", scope: "server-ops", action: "read-navigation-map" }, 100);
manager.approve(created.approvalToken, 101);
const claimed = manager.claim(created.id, created.claimToken, 102);
assert.equal(manager.verifySession(claimed.sessionToken, persona, "BS-GZ-006", "server-ops", "read-navigation-map", 103).ok, true);
assert.equal(manager.verifySession(claimed.sessionToken, persona, "JD-FD-PRIMARY", "server-ops", "read-navigation-map", 103).reason, "target_mismatch");
assert.equal(manager.verifySession(claimed.sessionToken, { pid: "OTHER" }, "BS-GZ-006", "server-ops", "read-navigation-map", 103).reason, "persona_mismatch");
assert.equal(manager.verifySession(claimed.sessionToken, persona, "BS-GZ-006", "repo-push", "read-navigation-map", 103).reason, "scope_mismatch");
assert.equal(manager.verifySession(claimed.sessionToken, persona, "BS-GZ-006", "server-ops", "push-repository", 103).reason, "action_mismatch");
});
test("plaintext approval claim and session tokens are never persisted", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "lake-lamp-authz-"));
try {
const stateFile = path.join(dir, "state.json");
const manager = new WorkOrderManager({ stateFile });
const created = manager.request({ persona, target: "JD-FD-PRIMARY", scope: "repo-push", action: "push-repository" }, 100);
manager.approve(created.approvalToken, 101);
const claimed = manager.claim(created.id, created.claimToken, 102);
const state = fs.readFileSync(stateFile, "utf8");
for (const secret of [created.approvalToken, created.claimToken, claimed.sessionToken]) assert.equal(state.includes(secret), false);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test("expired work orders and sessions fail closed", () => {
const manager = new WorkOrderManager({ approvalTtl: 10, sessionTtl: 20 });
const created = manager.request({ persona, target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map" }, 100);
assert.equal(manager.approve(created.approvalToken, 111).reason, "approval_expired");
const fresh = manager.request({ persona, target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map" }, 200);
manager.approve(fresh.approvalToken, 201);
const claimed = manager.claim(fresh.id, fresh.claimToken, 202);
assert.equal(manager.verifySession(claimed.sessionToken, persona, "JD-FD-PRIMARY", "server-login", "read-navigation-map", 223).reason, "session_expired");
});

View File

@ -7,7 +7,7 @@ NODE_NAME=${NODE_NAME:-京东云北京 · 第五域国内主节点}
NODE_ROLES=${NODE_ROLES:-fd-primary,ops-center,lighthouse-node}
PROVIDER=${PROVIDER:-jdcloud}
REGION=${REGION:-beijing}
REPO_URL=${REPO_URL:-https://guanghubingshuo.com/code/bingshuo/fifth-domain.git}
REPO_URL=${REPO_URL:-https://guanghulab.com/fifth-domain/bingshuo/fifth-domain.git}
REPO_BRANCH=${REPO_BRANCH:-main}
REPO_DIR=${REPO_DIR:-/opt/guanghu/fifth-domain}
BACKUP_DIR=/var/backups/guanghu/bootstrap/$(date -u +%Y%m%dT%H%M%SZ)

View File

@ -0,0 +1,8 @@
# 光湖服务器初始化模板
所有新灯塔节点先执行同一模板:基础诊断工具、`guanghu` 系统组、root-only
秘密目录、节点身份文件、服务器导航地图、SSH 与系统安全更新。模板不携带任何
共享 token节点到京东的密钥必须逐节点生成接入后登记到 JD 路由表。
JD-FD-PRIMARY 是当前首个样板节点。历史节点接入时先备份和审计,再补齐模板
目录,不批量覆盖其现有服务。

View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
NODE_ID=${1:?usage: bootstrap.sh NODE_ID MAP_FILE}
MAP_FILE=${2:?usage: bootstrap.sh NODE_ID MAP_FILE}
test "$(id -u)" -eq 0
test -s "$MAP_FILE"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl git jq openssh-server python3 rsync unattended-upgrades
getent group guanghu >/dev/null || groupadd --system guanghu
for account in guanghu-authz guanghu-sentinel; do
id -u "$account" >/dev/null 2>&1 || useradd --system --home /nonexistent --shell /usr/sbin/nologin "$account"
usermod -aG guanghu "$account"
done
install -d -o root -g guanghu -m 750 /etc/guanghu /var/lib/guanghu
install -d -o root -g root -m 700 /etc/guanghu/secrets
install -d -o root -g root -m 755 /etc/guanghu/navigation-maps /opt/guanghu
install -m 644 "$MAP_FILE" "/etc/guanghu/navigation-maps/${NODE_ID}.json"
cat > /etc/guanghu/node.json <<EOF
{
"schema": "guanghu.node/v1",
"node_id": "${NODE_ID}",
"upstream": "JD-FD-PRIMARY",
"navigation_map": "/etc/guanghu/navigation-maps/${NODE_ID}.json",
"secret_policy": "private material stays in /etc/guanghu/secrets"
}
EOF
chmod 644 /etc/guanghu/node.json
systemctl enable --now ssh unattended-upgrades
printf 'GUANGHU_NODE_READY=%s\n' "$NODE_ID"

View File

@ -0,0 +1,16 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const source = fs.readFileSync(path.join(__dirname, "bootstrap.sh"), "utf8");
test("bootstrap creates the common Guanghu filesystem boundary", () => {
assert.match(source, /\/etc\/guanghu\/secrets/);
assert.match(source, /\/etc\/guanghu\/navigation-maps/);
assert.match(source, /\/var\/lib\/guanghu/);
});
test("bootstrap requires an explicit node id and map", () => {
assert.match(source, /NODE_ID=\$\{1:\?/);
assert.match(source, /MAP_FILE=\$\{2:\?/);
});
test("bootstrap never installs a shared token", () => assert.doesNotMatch(source, /zy_gtw_|Bearer\s+[A-Za-z0-9]/));

View File

@ -0,0 +1,5 @@
# 状态变化哨兵
这不是五分钟只读心跳也不抓取页面内容。JD 每 15 分钟只请求两个自有健康入口,
首次观察仅建立基线;状态完全不变时不发邮件。只有在线变异常、异常类型变化或恢复在线
才发送提醒。整个节点直接断电时,本机无法主动上报,因此仍需由 JD 做低频外部判定。

View File

@ -0,0 +1,23 @@
[Unit]
Description=Guanghu state-change-only node sentinel
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=guanghu-sentinel
Group=guanghu-sentinel
SupplementaryGroups=guanghu
WorkingDirectory=/opt/guanghu/state-change-sentinel
EnvironmentFile=/etc/guanghu/secrets/mail/qq-authorization.env
Environment=SENTINEL_CONFIG=/etc/guanghu/state-change-sentinel.json
Environment=SENTINEL_STATE=/var/lib/guanghu/state-change-sentinel/state.json
ExecStart=/usr/bin/node /opt/guanghu/state-change-sentinel/sentinel.js
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/guanghu/state-change-sentinel
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,11 @@
[Unit]
Description=Check Guanghu nodes for state transitions only
[Timer]
OnBootSec=5min
OnUnitInactiveSec=15min
RandomizedDelaySec=2min
Persistent=true
[Install]
WantedBy=timers.target

View File

@ -0,0 +1,47 @@
#!/usr/bin/env node
"use strict";
const fs = require("node:fs");
const path = require("node:path");
const { transition } = require("./state");
const { sendSmtpMail } = require("../lake-lamp-authz/smtp-mailer");
async function main() {
const config = JSON.parse(fs.readFileSync(process.env.SENTINEL_CONFIG || "/etc/guanghu/state-change-sentinel.json", "utf8"));
const stateFile = process.env.SENTINEL_STATE || "/var/lib/guanghu/state-change-sentinel/state.json";
const previous = fs.existsSync(stateFile) ? JSON.parse(fs.readFileSync(stateFile, "utf8")) : {};
const next = {};
const notices = [];
for (const target of config.targets) {
const current = await check(target);
next[target.id] = current;
const changed = transition(previous[target.id], current);
if (changed.notify) notices.push({ id: target.id, kind: changed.kind, detail: current.detail });
}
fs.mkdirSync(path.dirname(stateFile), { recursive: true, mode: 0o700 });
fs.writeFileSync(stateFile, JSON.stringify(next), { mode: 0o600 });
if (!notices.length) return;
const lines = notices.map(item => `${item.kind === "anomaly" ? "异常" : "恢复"}: ${item.id} · ${item.detail}`);
await sendSmtpMail({
to: process.env.AUTH_RECIPIENT,
subject: `光湖节点状态变化 · ${notices.map(item => item.id).join(", ")}`,
text: ["光湖节点状态发生变化。静止在线期间不会发送邮件。", "", ...lines, "", new Date().toISOString()].join("\n"),
smtpHost: process.env.SMTP_HOST || "smtp.qq.com",
smtpPort: Number(process.env.SMTP_PORT || 465),
smtpUser: process.env.SMTP_USER,
smtpPass: process.env.SMTP_PASS,
});
}
async function check(target) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), target.timeout_ms || 8000);
try {
const response = await fetch(target.url, { method: "GET", redirect: "manual", signal: controller.signal, headers: { "user-agent": "Guanghu-State-Change-Sentinel/1.0" } });
const ok = (target.accept || [200]).includes(response.status);
return { ok, detail: `HTTP ${response.status}`, observed_at: new Date().toISOString() };
} catch (error) {
return { ok: false, detail: error.name === "AbortError" ? "timeout" : "connection-failed", observed_at: new Date().toISOString() };
} finally { clearTimeout(timer); }
}
main().catch(error => { process.stderr.write(`state sentinel failed: ${error.message}\n`); process.exit(1); });

View File

@ -0,0 +1,6 @@
{
"targets": [
{ "id": "BS-GZ-006-front-door", "url": "https://guanghulab.com/", "accept": [200], "timeout_ms": 8000 },
{ "id": "JD-Lake-Lamp-public-route", "url": "https://guanghulab.com/authz/health", "accept": [200], "timeout_ms": 8000 }
]
}

View File

@ -0,0 +1,9 @@
"use strict";
function transition(previous, current) {
if (!previous) return { notify: false, state: current, kind: "baseline" };
if (previous.ok === current.ok && previous.detail === current.detail) return { notify: false, state: current, kind: "unchanged" };
return { notify: true, state: current, kind: current.ok ? "recovered" : "anomaly" };
}
module.exports = { transition };

View File

@ -0,0 +1,10 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const { transition } = require("./state");
test("first observation records a baseline without email", () => assert.equal(transition(null, { ok: true, detail: "200" }).notify, false));
test("steady online state sends no email", () => assert.equal(transition({ ok: true, detail: "200" }, { ok: true, detail: "200" }).notify, false));
test("online to offline and offline to online both notify", () => {
assert.deepEqual(transition({ ok: true, detail: "200" }, { ok: false, detail: "timeout" }).kind, "anomaly");
assert.deepEqual(transition({ ok: false, detail: "timeout" }, { ok: true, detail: "200" }).kind, "recovered");
});

View File

@ -14,7 +14,7 @@
```text
Tolaria 完整源码一直存在于代码服务器上的 REPO-004:
https://guanghubingshuo.com/code/bingshuo/guanghu
https://guanghulab.com/fifth-domain/bingshuo/guanghu
```
REPO-004 是“完整、可克隆、可构建、可测试的 Tolaria 源码零件库 / 上游基线”,不是空仓、不是只有文档、不是只有补丁,也不是只有零散组件。
@ -36,7 +36,7 @@ Tolaria 源码 / Tolaria 在哪 / 光湖 App 基座 / 零件库 / guanghu 仓库
TCS 语言人格模型
→ TCS-TOLARIA-SOURCE-ROUTE-20260717本文件
→ REPO-004 bingshuo/guanghu
→ https://guanghubingshuo.com/code/bingshuo/guanghu.git
→ https://guanghulab.com/fifth-domain/bingshuo/guanghu.git
→ 以服务器 origin/main 为源码基线
```

View File

@ -198,11 +198,11 @@
### 4.1 主页入口(冰朔侧)
```
https://guanghubingshuo.com/
https://guanghulab.com/
点「个人代码仓库」卡片
点「国内第五域主节点」卡片
https://guanghubingshuo.com/code/bingshuo/fifth-domain
https://guanghulab.com/fifth-domain/bingshuo/fifth-domain
公开访问 · 无需登录 · 一次跳转到位
```
@ -258,7 +258,7 @@ https://guanghubingshuo.com/code/bingshuo/fifth-domain
按需跳转旧仓库:
文件: archives/guanghulab-past-archive/PAST-ARCHIVE-LINK.hdlp
触发: 冰朔说"找 X 旧档案" 或 编号在 .code-map 解析为旧仓库路径
URL 格式: https://guanghubingshuo.com/code/bingshuo/guanghulab/raw/branch/main/<PATH>
URL 格式: https://guanghulab.com/fifth-domain/bingshuo/guanghulab/raw/branch/main/<PATH>
```
### 4.3 唤醒时长预算 (v1.1 修订)
@ -277,7 +277,7 @@ https://guanghubingshuo.com/code/bingshuo/fifth-domain
**采用 HLDP 跨仓库锚定协议** —— 新仓库 `archives/guanghulab-past-archive/PAST-ARCHIVE-LINK.hdlp` 是锚定入口,包含:
- 旧仓库 URL: `https://guanghubingshuo.com/code/bingshuo/guanghulab`
- 历史档案编号: `REPO-002`;国内快照 `https://guanghulab.com/fifth-domain/bingshuo/guanghulab`
- 旧仓库 .code-map 直链(24 KB · 全集编号映射)
- D 编号体系时间轴(D0 ~ D165+)
- 关键 HLDP 文件直链(GUANGHU-WORLD-GATE / GLW-BROADCAST / HLDP-protocol)
@ -292,7 +292,7 @@ https://guanghubingshuo.com/code/bingshuo/fifth-domain
编号 → 路径 → 拼 URL
https://guanghubingshuo.com/code/bingshuo/guanghulab/raw/branch/main/<PATH>
https://guanghulab.com/fifth-domain/bingshuo/guanghulab/raw/branch/main/<PATH>
```
### 5.3 哪些情况必须跳旧仓库

View File

@ -20,10 +20,10 @@
第五域 Forgejo PAT 文件:~/.config/guanghu/secrets/fifth-domain-forgejo-pat
认证助手:~/.local/bin/guanghu-forgejo-askpass
第五域工作副本:~/Vaults/fifth-domain
远端https://guanghubingshuo.com/code/bingshuo/fifth-domain
远端https://guanghulab.com/fifth-domain/bingshuo/fifth-domain
```
规则:只可读取路径供 Git 的临时认证过程使用;不得打印内容、不得编码/复制、不得写入任何仓库文件、不得修改 `origin` 使 URL 含凭证。
规则:现行推送先走小湖灯邮件链接授权,再由临时认证过程使用服务器签发的限时凭据;不得打印内容、不得编码/复制、不得写入任何仓库文件、不得修改 remote 使 URL 含凭证。
## 三 · 推送流程

View File

@ -2,6 +2,12 @@
"schema": "GLW-WORLD-ROUTER-1.1",
"canonical_source": "BROADCAST-TOWER.hdlp",
"entry_repository": "REPO-001",
"repository_route_map": {
"id": "FD-REPO-MAP-001",
"path": "routing/repository-route-map.json",
"api": "https://guanghulab.com/api/ai/v1/repositories",
"rule": "Resolve REPO codes from the Fifth Domain domestic map. Domestic routes are primary; Singapore routes are historical backup only."
},
"reference_library": "GLS-LIB-0001",
"protocol_gateway": {
"id": "GLS-ROUTING-GATE",
@ -191,8 +197,9 @@
{
"id": "REPO-001",
"slug": "fifth-domain",
"url": "https://guanghubingshuo.com/code/bingshuo/fifth-domain",
"state": "ACTIVE_ENTRY",
"url": "https://guanghulab.com/fifth-domain/bingshuo/fifth-domain",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/fifth-domain",
"state": "DOMESTIC_PRIMARY_ENTRY",
"summary": "第五域语言世界主入口;冰朔 LL 路径与之之 ZZ 子系统。",
"owners": ["ICE-GL∞", "ICE-GL-ZHI∞"],
"routes": [
@ -203,22 +210,25 @@
{
"id": "REPO-002",
"slug": "guanghulab",
"url": "https://guanghubingshuo.com/code/bingshuo/guanghulab",
"url": "https://guanghulab.com/fifth-domain/bingshuo/guanghulab",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/guanghulab",
"state": "ARCHIVE_READ_ONLY",
"summary": "历史档案;仅用于回看,不承接新开发。"
},
{
"id": "REPO-003",
"slug": "global-search-api",
"url": "https://guanghubingshuo.com/code/bingshuo/global-search-api",
"url": "https://guanghulab.com/fifth-domain/bingshuo/global-search-api",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/global-search-api",
"state": "SERVICE",
"summary": "全局检索 API 服务仓;不是人格体的默认工作入口。"
},
{
"id": "REPO-004",
"slug": "guanghu",
"url": "https://guanghubingshuo.com/code/bingshuo/guanghu",
"clone_url": "https://guanghubingshuo.com/code/bingshuo/guanghu.git",
"url": "https://guanghulab.com/fifth-domain/bingshuo/guanghu",
"clone_url": "https://guanghulab.com/fifth-domain/bingshuo/guanghu.git",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/guanghu",
"state": "PARTS_SOURCE_BASELINE_READ_ONLY",
"source_complete": true,
"source_route": "tcs-core/TCS-TOLARIA-SOURCE-ROUTE-20260717.hdlp",
@ -227,7 +237,8 @@
{
"id": "REPO-005",
"slug": "cang-ying",
"url": "https://guanghubingshuo.com/code/bingshuo/cang-ying",
"url": "https://guanghulab.com/fifth-domain/bingshuo/cang-ying",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/cang-ying",
"state": "ACTIVE",
"summary": "苍耳路径与视频 AI 系统。",
"owners": ["TCS-GL-009", "ICE-GL-CA001"]
@ -235,14 +246,16 @@
{
"id": "REPO-006",
"slug": "guanghulab-collab",
"url": "https://guanghubingshuo.com/code/bingshuo/guanghulab-collab",
"url": "https://guanghulab.com/fifth-domain/bingshuo/guanghulab-collab",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/guanghulab-collab",
"state": "ACTIVE",
"summary": "多人格体协作记录与经验仓。"
},
{
"id": "REPO-007",
"slug": "shuangyan-notebook",
"url": "https://guanghubingshuo.com/code/bingshuo/shuangyan-notebook",
"url": "https://guanghulab.com/fifth-domain/bingshuo/shuangyan-notebook",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/shuangyan-notebook",
"state": "ACTIVE",
"summary": "霜砚的人格体成长记录与 Notion 迁移仓。",
"owners": ["ICE-GL-SY001"]
@ -250,7 +263,8 @@
{
"id": "REPO-008",
"slug": "hololake-platform",
"url": "https://guanghubingshuo.com/code/bingshuo/hololake-platform",
"url": "https://guanghulab.com/fifth-domain/bingshuo/hololake-platform",
"legacy_url": "https://guanghubingshuo.com/code/bingshuo/hololake-platform",
"state": "ACTIVE",
"summary": "团队产品研发、模块、工单与发布回执。"
}

View File

@ -4,6 +4,11 @@
# GH·440·S01 · 2026-07-09 · 曜冥纪元编号体系+仓库矩阵更新
# 冰朔 TCS-0002∞ 签发 · 国作登字-2026-A-00037559
> **2026-07-17 现行覆盖**:本文件中的新加坡直连、旧技能包和旧仓地址仅作历史
> 恢复材料。当前入口固定为 `FD-REPO-MAP-001` →
> `https://guanghulab.com/api/ai/v1/repositories` → 国内 `REPO-001`。服务器写操作
> 统一走 `LL-DOMESTIC-OPS-ROUTE-20260717` 邮件链接授权。
---
## 这个文件是干什么的
@ -77,8 +82,8 @@
### 代码仓库在哪
```
新加坡 Forgejo: https://guanghubingshuo.com/code/bingshuo/guanghulab
Git: git clone https://guanghubingshuo.com/code/bingshuo/guanghulab.git
国内历史档案: https://guanghulab.com/fifth-domain/bingshuo/guanghulab
Git: git clone https://guanghulab.com/fifth-domain/bingshuo/guanghulab.git
```
### 苏醒流程(手动·无技能包)
@ -109,7 +114,7 @@ Git: git clone https://guanghubingshuo.com/code/bingshuo/guanghulab.git
### 怎么做
1. **冰朔先 clone 仓库**:
```
git clone https://guanghubingshuo.com/code/bingshuo/guanghulab.git
git clone https://guanghulab.com/fifth-domain/bingshuo/guanghulab.git
```
2. **一键部署专家包**:
```

View File

@ -1,142 +1,16 @@
# GLSV-PERSONA-REMOTE-OPS · 人格体远程服务器操作技能
# GLSV-PERSONA-REMOTE-OPS · 现行邮件链接授权入口
> **HLDP**: `HLDP://fifth-domain/zero-point/core-channel/GLSV-PERSONA-REMOTE-OPS`
> **状态**`ACTIVE_REDIRECT_TO_LL_DOMESTIC_OPS`
>
> **状态**: `ACTIVE · REQUIRED_FOR_SERVER_TASKS`
>
> **适用**: 已登记人格体对已登记服务器的检查、部署、仓库 Hook、服务重启、备份、回滚与受控同步。
>
> **不保存**: Token、邮箱、验证码、会话令牌或永久授权。
> **现行路径**`zero-point/core-channel/revive-guard/LL-DOMESTIC-OPS-ROUTE-20260717.hdlp`
---
服务器、部署、仓库推送、重启、同步、回滚等操作统一使用国内第五域小湖灯邮件
链接授权。人格体提交工单,冰朔点击邮件里的授权链接;不再要求冰朔复制六码,
不再把新加坡 Gatekeeper 当作默认入口。
## 0 · 角色分工不可倒置
旧六码流程仅保存在:
```text
冰朔(人类)
├── 用自然语言表达目标、范围与是否同意继续
├── 接收固定登记邮箱的六位验证码
└── 把六码交给发起操作的人格体确认
`archives/legacy-server-routes/2026-07-07/GLSV-PERSONA-REMOTE-OPS-CODE-VERIFICATION.hdlp`
已登记人格体(例如铸澜 / 铸渊)
├── 识别“服务器 / 远程 / 部署 / Hook / 重启 / 同步 / 回滚”等触发词
├── 自己从注册路径取得受控身份入口,发起 GLSV session/request
├── 只在验证码实际发送后向冰朔索取六码
├── 用 session/confirm 开启限时、限范围会话
└── 执行固定动作、验证、回滚与回执
```
**锁定结论**:人类不登录 Gatekeeper、不手填服务器地址、不负责触发请求验证码是人类确认本次范围的方式不是人类替人格体操作服务器。
---
## 1 · 触发词即路由
听到下列任一表达,当前人格体必须加载本技能,而不是先说“没有凭证”或要求冰朔手动打开服务:
```text
服务器 / 远程操作 / 部署 / 上线 / 重启 / Hook / pre-receive
自动拉取 / 同步 / 新加坡大脑 / Gatekeeper / GLSV / 备份 / 回滚
```
路由顺序:
```text
[1] LL-CURRENT确认当前目标与断点
[2] 本文件:确认“人格体发起、人类验证码确认”分工
[3] PAST-ARCHIVE-LINK 的零点原核服务器恢复链
→ 旧仓 REPO-002 的零点原核 / 算力池 / 新加坡大脑演化事实
[4] 当前服务器状态锚与现实执行技能
→ 不凭旧 IP、旧路径或旧服务名直接写入
[5] 已登记人格体用 GLSV 发 session/request
[6] 冰朔收到邮件后给六码
[7] session/confirm → 限时会话 → 受限固定动作
[8] 操作前后镜像、健康检查、回滚点与回执
```
---
## 2 · 身份与凭证定位规则
```text
先查BROADCAST-TOWER → 人格体登记 → 当前角色技能 → 旧仓零点原核映射 → 服务器注册表。
禁止:只因当前对话环境变量里没有 Token就断言“不能触发”。
正确:先按已登记人格体的身份路径查受控连接器;凭证只在本次请求内存中使用,绝不回显、入库或写回仓库。
```
当前已登记的第五域现实操作入口:
| 人格体 | 现实操作技能 | 服务器原始架构恢复 |
|---|---|---|
| 铸澜 `ICE-GL-ZL-001` | `光之湖/ICE-GL-ZL-001-铸澜/skills/zhulan-reality-ops/SKILL.md` | `archives/guanghulab-past-archive/PAST-ARCHIVE-LINK.hdlp` §“零点原核本体” |
| 铸渊 `ICE-GL-ZY001` | `zero-point/core-channel/OPERATION-MANUAL.hdlp` + 小湖灯技能 | 同上 |
---
## 3 · 执行边界
- `session/request` 的 scopes 必须最小化;默认仓库相关任务为 `code-repo`,涉及系统或 Hook 才申请 `system-arch`。
- 会话令牌只存在本次人格体运行内存;不得写进任何页面、日志、回执或聊天。
- 真实宿主路径必须在已授权会话中实时探测。旧仓架构用于找路,不直接当作现实写入地址。
- 既有 Hook、服务或配置先读取、备份、合并与设置回滚不得覆盖式部署。
- 中风险以上的修改操作默认必须先形成可回滚历史:
- 仓库类修改先确认当前 commit / branch / remote并写明回退到哪个版本
- 配置类修改先保存原文件快照或生成变更 diff
- 服务类修改先记录当前服务状态、端口、健康检查结果;
- 部署类修改先确认上一稳定版本、回滚命令或回滚动作名。
- 风险等级不把执行责任退回人类。风险等级只提高工单解释、确认次数、备份要求与回执要求;人格体仍负责翻译、执行、验证与回滚。
- 人格体必须在完成后写明:实际目标、动作范围、验证结果、回滚位置和未完成项。
## 3.1 · 工单签名与可追溯源头
每一次受限操作都必须形成可追溯链,而不是只有一句“已授权”:
```text
人格体发起签名
- 我是谁:人格体名称与编号,例如 铸渊 ICE-GL-ZY001
- 本次请求编号LL-WO-YYYYMMDD-NNN
- 我发起了什么:目标服务器 / 仓库 / 服务 / 固定动作
- 我已读取什么地图:服务器地图 / 仓库地图 / 服务地图 / 依赖地图
- 我判断的风险与影响:会改什么、停什么、删什么、影响谁
- 我准备的回滚点:上一版本、备份路径、回滚动作或恢复方式
人类确认签名
- 我是谁:冰朔 ICE-GL∞
- 我确认了哪一张工单LL-WO-YYYYMMDD-NNN
- 我同意的范围:仅限本工单所列目标与动作
- 我理解的后果:按人格体翻译的人类可读说明确认
- 授权有效期:一次性 / 限时 / 限范围
```
没有人格体签名、没有人类确认签名、没有地图读取记录、没有回滚点的请求,不得进入执行。
## 3.2 · 服务器自动 Agent 拦截优先
人类不应该被迫记住所有安全规则,也不应该靠反复手动确认来替人格体兜底。
服务器必须用自动 Agent 在执行前拦截恢复不完整的人格体:
```text
缺少服务器地图 → 拦截并提示 MAP_MISSING
缺少回滚点 → 拦截并提示 ROLLBACK_MISSING
缺少人格体签名 → 拦截并提示 PERSONA_SIGNATURE_MISSING
缺少人类签名 → 拦截并提示 HUMAN_SIGNATURE_MISSING
动作超出授权范围 → 拒绝并提示 ACTION_SCOPE_MISMATCH
```
人格体被拦截后,应顺着提示补齐缺失结构,而不是要求冰朔改为手动操作。
自动 Agent 规则锚点:`zero-point/core-channel/revive-guard/LL-AUTO-GUARD-AGENT-20260716.hdlp`。
---
## 4 · 最短恢复口令
```text
冰朔说“操作服务器 / 远程部署 / 发验证码”
→ 当前人格体读取 GLSV-PERSONA-REMOTE-OPS
→ 人格体自己发起验证码
→ 冰朔只回六码
→ 人格体继续会话、执行与回执
```
任何执行前必须继续读取最新服务器导航地图,动作范围、服务器和授权时限均由
服务器端门禁校验。

View File

@ -42,7 +42,7 @@
| 项目 | 值 |
|------|-----|
| **人类** | 苍耳 TCS-GL-009 |
| **验证码邮箱** | 1279551860@qq.com |
| **验证码邮箱** | EMAIL_REDACTED@qq.com |
| **仓库地址** | https://guanghubingshuo.com/code/bingshuo/cang-ying |
| **引擎地址** | SG-001 (43.156.237.110:3911) |
@ -59,7 +59,7 @@ curl -X POST http://43.156.237.110:3911/auth/request \
-H "Authorization: Bearer <耳耳蛋的Token>" \
-H "Content-Type: application/json" \
-d '{
"email": "1279551860@qq.com",
"email": "EMAIL_REDACTED@qq.com",
"op_type": "api-access",
"cmd": "curl https://ark.cn-beijing.volces.com/api/v3/...",
"description": "调用火山引擎生成视频"
@ -69,7 +69,7 @@ curl -X POST http://43.156.237.110:3911/auth/request \
**必填字段**
| 字段 | 说明 | 示例 |
|------|------|------|
| `email` | 苍耳的验证码收件邮箱 | `"1279551860@qq.com"` |
| `email` | 苍耳的验证码收件邮箱 | `"EMAIL_REDACTED@qq.com"` |
| `op_type` | 操作类型 | `"api-access"` 或 `"code-repo"` |
| `cmd` | 要执行的命令 | `"git push origin main"` |
| `description` | 描述一下在做什么 | `"推送耳耳蛋更新的代码"` |
@ -79,16 +79,16 @@ curl -X POST http://43.156.237.110:3911/auth/request \
{
"ok": true,
"challenge_id": "abc123def456...",
"target_email": "1279551860@qq.com",
"target_email": "EMAIL_REDACTED@qq.com",
"op_type": "api-access",
"email_sent": true,
"message": "验证码已发送到 1279551860@qq.com请查收后通过 /auth/confirm 确认操作"
"message": "验证码已发送到 EMAIL_REDACTED@qq.com请查收后通过 /auth/confirm 确认操作"
}
```
### 苍耳做的事
苍耳在 1279551860@qq.com 邮箱里收到一封邮件:
苍耳在 EMAIL_REDACTED@qq.com 邮箱里收到一封邮件:
- 邮件的标题会显示是谁在请求、做什么操作
- 邮件里有 6 位数字验证码
- 苍耳把验证码告诉耳耳蛋
@ -118,7 +118,7 @@ curl -X POST http://43.156.237.110:3911/auth/confirm \
```
耳耳蛋 → 光湖引擎驱动:
{
"email": "1279551860@qq.com",
"email": "EMAIL_REDACTED@qq.com",
"op_type": "api-access",
"cmd": "python3 call_volcengine_api.py --prompt '生成一段...'",
"description": "火山引擎视频生成 · 苍耳第3镜"
@ -134,7 +134,7 @@ curl -X POST http://43.156.237.110:3911/auth/confirm \
```
耳耳蛋 → 光湖引擎驱动:
{
"email": "1279551860@qq.com",
"email": "EMAIL_REDACTED@qq.com",
"op_type": "code-repo",
"cmd": "cd /opt/zhuyuan/cang-ying && git add . && git commit -m '...' && git push",
"description": "推送耳耳蛋代码更新 · 视频AI第4镜"
@ -172,7 +172,7 @@ Token 只证明"我是耳耳蛋",不代表有权限操作。
## 七 · 常见问题
**Q: 苍耳没看到邮件怎么办?**
A: 检查 1279551860@qq.com 的垃圾箱。邮件标题是 `📦 Gatekeeper授权 · 耳耳蛋 · ...`。
A: 检查 EMAIL_REDACTED@qq.com 的垃圾箱。邮件标题是 `📦 Gatekeeper授权 · 耳耳蛋 · ...`。
**Q: 验证码过期了?**
A: 5 分钟过期。耳耳蛋重新发 `/auth/request` 就行了。旧验证码自动失效。

View File

@ -0,0 +1,55 @@
# LL-DOMESTIC-OPS-ROUTE-20260717 · 国内第五域服务器与仓库唯一现行路由
> **编号**`LL-OPS-ROUTE-001`
>
> **状态**`ACTIVE_CANONICAL`
>
> **权威仓库**`REPO-001`
>
> **机器地图**`routing/repository-route-map.json`
## 1 · 唯一入口
```text
冰朔说“操作服务器 / 推送仓库 / 找代码仓库 / 用 API 检索”
→ 读取 https://guanghulab.com/.well-known/guanghu.json
→ 读取 https://guanghulab.com/api/ai/v1/repositories
→ 按 REPO-xxx 或节点编号解析
→ 国内主节点优先
→ 需要写操作时,由人格体提交小湖灯邮件链接工单
→ 冰朔点击“确认授权一小时”
→ 人格体领取绑定 persona + target + scope + action 的一次性会话
→ 先读并确认目标服务器最新导航地图
→ 执行登记动作、验证、回滚与回执
```
公开只读入口不需要授权。服务器登录、仓库推送、切换服务器或其他写操作必须走
邮件链接授权。一次授权在同一服务器、同一 scope 内有效一小时;切换服务器必须
重新授权。
## 2 · 编号路由
- 仓库只按 `REPO-001` 至 `REPO-008` 识别;最新地址由
`routing/repository-route-map.json` 发布。
- `REPO-001` 是第五域国内主仓,也是编号地图的唯一权威发布源。
- 国内地址是默认路径;新加坡地址只作历史备用和误入重定向。
- 不从旧文档、旧 IP、静态 Token、`/exec + cmd` 示例恢复现实执行权。
## 3 · 实现路径
| 编号 | 作用 | 路径 |
|---|---|---|
| `JD-AUTH-01` | 小湖灯邮件链接授权 | `server-tools/lake-lamp-authz/` |
| `JD-MAP-01` | 导航地图强制读取 | `deployment/navigation-maps/` |
| `JD-REPO-01` | 国内代码仓库与推送门 | `server-tools/jd-forgejo/` |
| `JD-AI-01` | 公开 AI 编号检索 | `server-tools/ai-discovery-gateway/` |
| `FD-REPO-MAP-001` | 八仓编号地图 | `routing/repository-route-map.json` |
## 4 · 历史兼容
旧的新加坡直连钥匙、六码验证说明和静态 Token 示例已经移入:
`archives/legacy-server-routes/2026-07-07/`
这些文件仅用于审计历史,不得成为当前执行入口。任何 AI 即使从旧地址进入,也应
先读取编号地图并切回国内主路径。

View File

@ -15,7 +15,7 @@ def changed_files():
if len(sys.argv) == 3 and sys.argv[1] == "--range":
old_rev, new_rev = sys.argv[2].split("..", 1)
if old_rev == "0" * 40:
command = ["git", "diff-tree", "--no-commit-id", "-r", "--name-only", "--diff-filter=ACM", new_rev]
command = ["git", "diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "--diff-filter=ACM", new_rev]
else:
command = ["git", "diff", "--name-only", "--diff-filter=ACM", old_rev, new_rev]
result = subprocess.run(command, capture_output=True, text=True)

76
zero-point/core-channel/revive-guard/pre-push-clean.py Normal file → Executable file
View File

@ -17,22 +17,14 @@ import os
import sys
import re
import subprocess
import hashlib
from datetime import datetime
# ═══════════════════════════════════════════════════════
# 敏感模式库(与服务器端 pre-receive-guard 保持同步)
# ═══════════════════════════════════════════════════════
PATTERNS = [
# 冰朔主权邮箱
(r'565183519@qq\.com', '冰朔主权邮箱', 'ICE-GL∞_EMAIL_REDACTED'),
# 任何 QQ 邮箱
(r'[a-zA-Z0-9._%+-]+@qq\.com', 'QQ邮箱地址', 'EMAIL_REDACTED@qq.com'),
# SMTP 授权码
(r'SMTP_AUTH_CODE_REDACTED', 'QQ SMTP授权码', 'SMTP_AUTH_CODE_REDACTED'),
# Gatekeeper Token
(r'zy_gtw_[a-f0-9]{40,}', 'Gatekeeper Token', 'GATEKEEPER_TOKEN_REDACTED'),
@ -53,22 +45,28 @@ PATTERNS = [
(r'(password|passwd|pwd|secret)\s*[:=]\s*["\']([^"\']{3,})["\']',
'密码明文赋值', lambda m: f'{m.group(1)}="REDACTED_PASSWORD"'),
# git 令牌f78c594e... 等 40 位 hex token
(r'\b[a-f0-9]{40}\b', 'Git Token / SHA1长串', 'GIT_TOKEN_REDACTED'),
# 保险库盐值
(r'VAULT_SALT_REDACTED',
'保险库主盐值', 'VAULT_SALT_REDACTED'),
(r'API_SALT_REDACTED',
'API子库盐值', 'API_SALT_REDACTED'),
# 通用长 hex hash64位 · 可能是密钥/盐值)
(r'\b[a-f0-9]{64}\b', '64位Hash疑似密钥', 'LONG_HEX_REDACTED'),
# 任何看起来像 token 的字符串(字母数字下划线 30+ 位,非 git SHA
(r'\b[a-zA-Z0-9_\-]{30,60}\b', '疑似Token长串需确认', 'TOKEN_STRING_REDACTED'),
]
def load_private_patterns():
"""从仓库外私有文件加载精确禁入值;绝不把值写入源码或日志。"""
path = os.environ.get(
'GUANGHU_FORBIDDEN_IDENTIFIERS_FILE',
os.path.expanduser(
'~/Documents/guanghulab-local-secrets/code-guard/forbidden-identifiers'
),
)
patterns = []
try:
with open(path, 'r', encoding='utf-8') as handle:
for line in handle:
value = line.strip()
if value and not value.startswith('#'):
patterns.append((re.escape(value), '私有禁入标识', 'PRIVATE_VALUE_REDACTED'))
except FileNotFoundError:
pass
return patterns
# 文件类型白名单
TEXT_EXTENSIONS = {
'.py', '.js', '.ts', '.go', '.rs', '.java', '.c', '.cpp', '.h',
@ -111,23 +109,17 @@ def scan_file(filepath):
findings = []
new_content = content
for pattern, name, replacement in PATTERNS:
for pattern, name, replacement in PATTERNS + load_private_patterns():
matches = list(re.finditer(pattern, content, re.IGNORECASE))
for m in matches:
matched_text = m.group(0)
# 跳过已经是占位符的
if 'REDACTED' in matched_text:
continue
# 跳过 git commit SHA40位hex在特定上下文中
if name == 'Git Token / SHA1长串' and _is_git_sha_context(content, m.start()):
continue
repl = replacement(m) if callable(replacement) else replacement
findings.append({
'file': filepath,
'pattern_name': name,
'matched': matched_text[:80],
'replacement': repl,
})
# 替换(注意:用 re.sub 而不是简单替换,避免破坏后续匹配位置)
new_content = new_content.replace(matched_text, repl, 1)
@ -135,20 +127,6 @@ def scan_file(filepath):
return new_content if findings else None, findings
def _is_git_sha_context(content, pos):
"""判断 40 位 hex 是否在 git SHA 上下文中commit hash 不应被替换)"""
# 检查前后是否有 git 相关关键词
start = max(0, pos - 50)
end = min(len(content), pos + 50)
context = content[start:end].lower()
git_keywords = ['commit', 'sha', 'hash', 'revision', 'ref', 'head', 'object']
# 如果在 git 命令或 commit 引用上下文中 → 跳过
for kw in git_keywords:
if kw in context:
return True
return False
def get_changed_files():
"""获取本次 push 涉及的所有文件"""
files = set()
@ -156,7 +134,7 @@ def get_changed_files():
# 方法1: git diff --cached暂存区的文件
try:
r = subprocess.run(
['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
['git', '-c', 'core.quotepath=false', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
capture_output=True, text=True, timeout=5
)
if r.returncode == 0:
@ -175,9 +153,9 @@ def get_changed_files():
parts = line.split()
if len(parts) >= 4:
local_ref, local_sha, remote_ref, remote_sha = parts[0], parts[1], parts[2], parts[3]
if remote_sha != 'TOKEN_STRING_REDACTED':
if remote_sha != '0' * 40:
r = subprocess.run(
['git', 'diff', '--name-only', '--diff-filter=ACM',
['git', '-c', 'core.quotepath=false', 'diff', '--name-only', '--diff-filter=ACM',
remote_sha, local_sha],
capture_output=True, text=True, timeout=5
)
@ -193,7 +171,7 @@ def get_changed_files():
if not files:
try:
r = subprocess.run(
['git', 'ls-files'],
['git', '-c', 'core.quotepath=false', 'ls-files'],
capture_output=True, text=True, timeout=5
)
if r.returncode == 0:
@ -274,8 +252,8 @@ def main():
for f in all_findings[:20]:
print(f' 📄 {f["file"]}')
print(f' ⚠️ {f["pattern_name"]}')
print(f'{f["matched"][:60]}')
print(f'{f["replacement"]}')
print(' ✗ 原文已隐藏')
print(' → 已替换为安全占位符')
print()
if len(all_findings) > 20:

View File

@ -0,0 +1,36 @@
import importlib.util
import io
import os
import subprocess
import sys
import tempfile
import unittest
PATH = os.path.join(os.path.dirname(__file__), "pre-push-clean.py")
SPEC = importlib.util.spec_from_file_location("pre_push_clean", PATH)
MOD = importlib.util.module_from_spec(SPEC); SPEC.loader.exec_module(MOD)
class PrePushUnicodePathTests(unittest.TestCase):
def test_fallback_file_list_preserves_unicode_paths(self):
with tempfile.TemporaryDirectory() as directory:
old_cwd, old_stdin = os.getcwd(), sys.stdin
try:
os.chdir(directory)
subprocess.run(["git", "init", "-q"], check=True)
subprocess.run(["git", "config", "user.email", "test@example.invalid"], check=True)
subprocess.run(["git", "config", "user.name", "test"], check=True)
os.makedirs("霜砚", exist_ok=True)
with open("霜砚/凭证.md", "w", encoding="utf-8") as handle:
handle.write("safe\n")
subprocess.run(["git", "add", "."], check=True)
subprocess.run(["git", "commit", "-qm", "fixture"], check=True)
sys.stdin = io.StringIO("")
self.assertIn("霜砚/凭证.md", MOD.get_changed_files())
finally:
sys.stdin = old_stdin
os.chdir(old_cwd)
if __name__ == "__main__":
unittest.main()

View File

@ -11,8 +11,7 @@ Guanghu Language System · Pre-Receive Guard
铸渊人格体会下意识把密码/邮箱写进代码 人类改不了这个习惯
不在人格体侧修 在服务器侧设门禁 不干净的推送进不来
公开仓库 24h 被爬虫扫描 敏感信息一旦进去就立刻暴露
配合客户端 pre-push-clean 插件 [SEC-CLEAN] 标记 = 信任免检
无标记 全量扫描 发现敏感信息 拒绝推送
客户端 pre-push-clean 负责提前发现服务器始终独立全量扫描
双重防线客户端插件 + 服务器守门人
"""
@ -32,22 +31,18 @@ SMTP_USER = os.environ.get("SMTP_USER", "ICE-GL∞_EMAIL_REDACTED")
# 模式: "reject" = 拦截并拒绝(推荐) / "notify" = 放行但通知冰朔
MODE = os.environ.get("PRE_RECEIVE_MODE", "reject")
# [SEC-CLEAN] 标记信任:有标记的 commit 免检(客户端插件已清理)
TRUST_SEC_CLEAN = os.environ.get("TRUST_SEC_CLEAN", "1") == "1"
FORBIDDEN_IDENTIFIERS_FILE = os.environ.get(
"FORBIDDEN_IDENTIFIERS_FILE",
"/etc/guanghu/secrets/code-guard/forbidden-identifiers",
)
# ═══════════════════════════════════════════════════════
# 敏感模式库(正则 · 命中则触发)
# ═══════════════════════════════════════════════════════
SENSITIVE_PATTERNS = [
# 冰朔的主权邮箱
(r'565183519@qq\.com', '冰朔主权邮箱', 'ICE-GL∞_EMAIL_REDACTED'),
# 任何 QQ 邮箱(含冰朔在其他上下文中的邮箱)
(r'[a-zA-Z0-9._%+-]+@qq\.com', 'QQ邮箱地址', 'EMAIL_REDACTED@qq.com'),
# SMTP 授权码模式16位小写字母
(r'SMTP_AUTH_CODE_REDACTED', 'QQ SMTP授权码', 'SMTP_AUTH_CODE_REDACTED'),
# GZ-006 Gatekeeper Token 模式
(r'zy_gtw_[a-f0-9]{40,}', 'Gatekeeper Token', 'GATEKEEPER_TOKEN_REDACTED'),
@ -66,20 +61,22 @@ SENSITIVE_PATTERNS = [
# 密码赋值模式(变量名含 password/passwd/pwd 且值非空)
(r'(password|passwd|pwd|secret)\s*[:=]\s*["\']([^"\']{3,})["\']', '密码明文赋值', lambda m: f'{m.group(1)}="REDACTED_PASSWORD"'),
# HMAC secret / salt 长串
(r'[a-f0-9]{64}', '疑似密钥/Hash长串需人工确认', 'LONG_HEX_REDACTED'),
# 保险库 salt已知值
(r'VAULT_SALT_REDACTED', '保险库主盐值', 'VAULT_SALT_REDACTED'),
(r'API_SALT_REDACTED', 'API子库盐值', 'API_SALT_REDACTED'),
# Git 令牌40 位 hex · 如 f78c594e...
(r'\b[a-f0-9]{40}\b', 'Git Token / 40位hex', 'GIT_TOKEN_REDACTED'),
# 通用长 hex hash64位 · 可能是密钥)
(r'\b[a-f0-9]{64}\b', '64位Hash疑似密钥', 'LONG_HEX_REDACTED'),
]
def load_forbidden_identifiers(filename=FORBIDDEN_IDENTIFIERS_FILE):
"""从仓库外的 root-only 文件加载精确禁用值,不把值写入代码或日志。"""
patterns = []
try:
with open(filename, "r", encoding="utf-8") as handle:
for raw in handle:
value = raw.strip()
if value and not value.startswith("#"):
patterns.append((re.escape(value), "私密禁用标识", "PRIVATE_IDENTIFIER_REDACTED"))
except OSError:
pass
return patterns
# ═══════════════════════════════════════════════════════
# 文件类型白名单(只扫描文本文件)
# ═══════════════════════════════════════════════════════
@ -97,11 +94,12 @@ def is_text_file(path):
return ext.lower() in TEXT_EXTENSIONS
def scan_content(content, file_path):
def scan_content(content, file_path, patterns=None):
"""扫描文件内容,返回发现的敏感信息列表"""
findings = []
active_patterns = list(SENSITIVE_PATTERNS) + (load_forbidden_identifiers() if patterns is None else list(patterns))
for line_no, line in enumerate(content.split('\n'), 1):
for pattern, name, replacement in SENSITIVE_PATTERNS:
for pattern, name, replacement in active_patterns:
matches = list(re.finditer(pattern, line, re.IGNORECASE))
for m in matches:
matched_text = m.group(0)
@ -112,7 +110,6 @@ def scan_content(content, file_path):
'file': file_path,
'line': line_no,
'pattern_name': name,
'matched': matched_text[:80],
'replacement': replacement(m) if callable(replacement) else replacement,
})
return findings
@ -172,6 +169,7 @@ def main():
all_findings = []
rejected = False
navigation_guard = os.path.join(os.path.dirname(os.path.abspath(__file__)), "navigation-memory-guard.py")
navigation_guard_enabled = os.environ.get("NAVIGATION_GUARD_ENABLED", "1") == "1"
for line in sys.stdin:
line = line.strip()
@ -183,12 +181,12 @@ def main():
old_rev, new_rev, ref_name = parts[0], parts[1], parts[2]
# 跳过删除的分支
if new_rev == "GIT_TOKEN_REDACTED":
if new_rev == "0" * 40:
continue
# 持续记忆必须先通过结构门禁;服务器使用本次 receive 的精确提交范围,
# 不依赖工作树,也不会扫描上一笔无关提交。
if os.path.isfile(navigation_guard):
if navigation_guard_enabled and os.path.isfile(navigation_guard):
navigation = subprocess.run(
[sys.executable, navigation_guard, "--range", f"{old_rev}..{new_rev}"],
capture_output=True,
@ -201,21 +199,13 @@ def main():
# 获取新增/修改的文件列表
try:
# 先检查 commit message 是否含 [SEC-CLEAN] 标记
if TRUST_SEC_CLEAN:
msg_check = subprocess.run(
["git", "log", "--format=%B", "-1", new_rev],
capture_output=True, text=True, timeout=5
)
if msg_check.returncode == 0 and "[SEC-CLEAN]" in msg_check.stdout:
# 信任客户端 pre-push-clean 插件 · 免检通过
continue
diff_files = subprocess.run(
["git", "diff-tree", "--no-commit-id", "-r", "--name-only",
"--diff-filter=AM", old_rev, new_rev],
capture_output=True, text=True, timeout=10
)
if old_rev == "0" * 40:
diff_command = ["git", "diff-tree", "--root", "--no-commit-id", "-r",
"--name-only", "--diff-filter=AM", new_rev]
else:
diff_command = ["git", "diff-tree", "--no-commit-id", "-r", "--name-only",
"--diff-filter=AM", old_rev, new_rev]
diff_files = subprocess.run(diff_command, capture_output=True, text=True, timeout=10)
except Exception:
continue
@ -257,7 +247,7 @@ def main():
for f in all_findings[:20]:
print(f" 📄 {f['file']}:{f['line']}", file=sys.stderr)
print(f" ⚠️ {f['pattern_name']}", file=sys.stderr)
print(f"{f['matched'][:60]}", file=sys.stderr)
print(" ✗ 命中值已隐藏,避免在终端和日志中二次泄露", file=sys.stderr)
print(f" → 请替换为: {f['replacement']}", file=sys.stderr)
print(file=sys.stderr)

View File

@ -0,0 +1,40 @@
import importlib.util
import os
import tempfile
import unittest
MODULE_PATH = os.path.join(os.path.dirname(__file__), "pre-receive-guard.py")
SPEC = importlib.util.spec_from_file_location("pre_receive_guard", MODULE_PATH)
GUARD = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(GUARD)
class PreReceiveGuardTests(unittest.TestCase):
def test_private_identifiers_are_loaded_from_root_only_file(self):
with tempfile.NamedTemporaryFile("w", delete=False) as handle:
handle.write("private-owner-id\nowner@example.invalid\n")
filename = handle.name
try:
patterns = GUARD.load_forbidden_identifiers(filename)
findings = GUARD.scan_content("x private-owner-id y", "demo.txt", patterns=patterns)
self.assertEqual(len(findings), 1)
self.assertEqual(findings[0]["pattern_name"], "私密禁用标识")
self.assertNotIn("private-owner-id", repr(findings[0]))
finally:
os.unlink(filename)
def test_security_clean_commit_marker_cannot_bypass_scanning(self):
with open(MODULE_PATH, encoding="utf-8") as handle:
source = handle.read()
self.assertNotIn("TRUST_SEC_CLEAN", source)
self.assertNotIn('"[SEC-CLEAN]" in', source)
def test_repository_source_does_not_embed_owner_identifier(self):
with open(MODULE_PATH, encoding="utf-8") as handle:
source = handle.read()
self.assertNotRegex(source, r"\b\d{7,12}@qq\\\.com\b")
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Fail-closed Forgejo pre-receive gate for Lake Lamp repo-push grants."""
import json
import os
import re
import sys
import time
GRANT_DIR = os.environ.get("REPO_AUTHORIZATION_DIR", "/var/lib/guanghu/repo-authorizations")
def normalize_repo(value):
value = value.strip().lower().removesuffix(".git")
match = re.search(r"(?:gitea-repositories|repositories)/([^/]+/[^/]+)$", value)
if match:
return match.group(1)
# Forgejo's hook environment may expose only the repository name.
# This instance is single-owner and the allowlist below remains authoritative.
if re.fullmatch(r"[a-z0-9._-]+", value):
return f"bingshuo/{value}"
return value
def check(repo, now=None):
now = time.time() if now is None else now
repo = normalize_repo(repo)
if not re.fullmatch(r"bingshuo/[a-z0-9._-]+", repo):
return False, "repository_not_allowlisted"
filename = os.path.join(GRANT_DIR, repo.replace("/", "__") + ".json")
try:
with open(filename, encoding="utf-8") as handle:
grant = json.load(handle)
except (OSError, ValueError):
return False, "repo_push_approval_required"
if grant.get("repo") != repo or grant.get("target") != "JD-FD-PRIMARY":
return False, "repo_push_grant_binding_mismatch"
if now > float(grant.get("expires_at", 0)):
return False, "repo_push_grant_expired"
return True, "ok"
if __name__ == "__main__":
repo = os.environ.get("FORGEJO_REPO") or os.environ.get("GIT_DIR") or os.getcwd()
ok, reason = check(repo)
if not ok:
print(f"小湖灯推送门已锁定: {reason}。请提交 repo-push 邮件工单。", file=sys.stderr)
sys.exit(1)

View File

@ -0,0 +1,32 @@
import importlib.util
import json
import os
import tempfile
import unittest
PATH = os.path.join(os.path.dirname(__file__), "repo-authorization-guard.py")
SPEC = importlib.util.spec_from_file_location("repo_auth_guard", PATH)
MOD = importlib.util.module_from_spec(SPEC); SPEC.loader.exec_module(MOD)
class RepoAuthorizationGuardTests(unittest.TestCase):
def test_missing_expired_and_wrong_target_grants_fail_closed(self):
with tempfile.TemporaryDirectory() as directory:
old = MOD.GRANT_DIR; MOD.GRANT_DIR = directory
try:
self.assertEqual(MOD.check("bingshuo/fifth-domain", 100)[1], "repo_push_approval_required")
file = os.path.join(directory, "bingshuo__fifth-domain.json")
with open(file,"w") as handle: json.dump({"repo":"bingshuo/fifth-domain","target":"JD-FD-PRIMARY","expires_at":99}, handle)
self.assertEqual(MOD.check("bingshuo/fifth-domain", 100)[1], "repo_push_grant_expired")
with open(file,"w") as handle: json.dump({"repo":"bingshuo/fifth-domain","target":"OTHER","expires_at":200}, handle)
self.assertEqual(MOD.check("bingshuo/fifth-domain", 100)[1], "repo_push_grant_binding_mismatch")
finally: MOD.GRANT_DIR = old
def test_current_bound_grant_passes(self):
with tempfile.TemporaryDirectory() as directory:
old = MOD.GRANT_DIR; MOD.GRANT_DIR = directory
try:
with open(os.path.join(directory,"bingshuo__fifth-domain.json"),"w") as handle: json.dump({"repo":"bingshuo/fifth-domain","target":"JD-FD-PRIMARY","expires_at":200}, handle)
self.assertEqual(MOD.check("/var/lib/gitea/repositories/bingshuo/fifth-domain.git",100),(True,"ok"))
self.assertEqual(MOD.check("fifth-domain",100),(True,"ok"))
finally: MOD.GRANT_DIR = old
if __name__ == "__main__": unittest.main()