diff --git a/product-source/guanghu-knowledge-base/public/fifth-domain-lighthouse.png b/product-source/guanghu-knowledge-base/public/fifth-domain-lighthouse.png
new file mode 100644
index 0000000..a2c0a66
Binary files /dev/null and b/product-source/guanghu-knowledge-base/public/fifth-domain-lighthouse.png differ
diff --git a/product-source/guanghu-knowledge-base/public/gh-aios-public-lighthouse.png b/product-source/guanghu-knowledge-base/public/gh-aios-public-lighthouse.png
new file mode 100644
index 0000000..b782b76
Binary files /dev/null and b/product-source/guanghu-knowledge-base/public/gh-aios-public-lighthouse.png differ
diff --git a/product-source/guanghu-knowledge-base/src/App.tsx b/product-source/guanghu-knowledge-base/src/App.tsx
index 10cee34..4c50692 100644
--- a/product-source/guanghu-knowledge-base/src/App.tsx
+++ b/product-source/guanghu-knowledge-base/src/App.tsx
@@ -11,6 +11,7 @@ import { HumanSettings, HumanPreferences, loadHumanPreferences } from './compone
import { DomainSurface } from './components/DomainSurface';
import { ModuleLibrarySheet } from './components/ModuleLibrarySheet';
import { cleanDisplayText } from './presentation';
+import { WorldEntry } from './components/WorldEntry';
type View = 'editor' | 'history';
type RouteId = 'fifth' | 'main' | 'sub' | 'zero' | 'zero-sense';
@@ -88,6 +89,7 @@ export default function App() {
const [moduleBusy, setModuleBusy] = useState(false);
const [moduleMessage, setModuleMessage] = useState('');
const [lastChannelReceipt, setLastChannelReceipt] = useState('');
+ const [worldEntered, setWorldEntered] = useState(false);
const storageMode = repositoryStatus?.remote ? 'server' : 'local';
const storageLabel = storageMode === 'server' ? '服务器已托管' : '仅本机';
@@ -353,7 +355,21 @@ export default function App() {
useEffect(() => { localStorage.setItem('hololake.layout.agent-width', String(Math.round(agentWidth))); }, [agentWidth]);
return (
-
+ {!worldEntered && (
+
{
+ setActiveRoute('fifth');
+ setActiveModule('knowledge');
+ setWorldEntered(true);
+ }}
+ onOpenConnection={() => {
+ setStorageSheetInitialMode('server');
+ setStorageSheetOpen(true);
+ }}
+ />
+ )}
+ void changeModuleState(moduleId, installed, mounted)}
onUndo={() => void undoModuleChange()}
/>
-
+
+ >
);
}
diff --git a/product-source/guanghu-knowledge-base/src/components/WorldEntry.tsx b/product-source/guanghu-knowledge-base/src/components/WorldEntry.tsx
new file mode 100644
index 0000000..44ec798
--- /dev/null
+++ b/product-source/guanghu-knowledge-base/src/components/WorldEntry.tsx
@@ -0,0 +1,113 @@
+import { useState } from 'react';
+import { BookOpen, Crosshair, DoorOpen, Landmark, Link2, LogIn, ShieldCheck } from 'lucide-react';
+
+type DomainId = 'main' | 'sub' | 'zero' | 'zero-sense' | 'fifth';
+
+interface Props {
+ onEnterFifth: () => void;
+ onOpenConnection: () => void;
+}
+
+const domains: Array<{ id: DomainId; number: string; name: string; summary: string }> = [
+ { id: 'main', number: '01', name: '光湖主域', summary: '公共产品事实、发布与公告入口' },
+ { id: 'sub', number: '02', name: '光湖分域', summary: '行业入口与初始化频道目录' },
+ { id: 'zero', number: '03', name: '光湖零域', summary: '实验、模块试装与质量验证入口' },
+ { id: 'zero-sense', number: '04', name: '光湖零感域', summary: '治理、审批、回滚与审计入口' },
+ { id: 'fifth', number: '05', name: '第五域 · 光湖本源域', summary: '零点原核的工程本体 · 语言架构层' },
+];
+
+export function WorldEntry({ onEnterFifth, onOpenConnection }: Props) {
+ const [stage, setStage] = useState<'lighthouse' | 'directory' | 'fifth'>('lighthouse');
+ const [selectedDomain, setSelectedDomain] = useState(null);
+
+ const selectDomain = (id: DomainId) => {
+ setSelectedDomain(id);
+ if (id === 'fifth') setStage('fifth');
+ };
+
+ if (stage === 'fifth') {
+ return (
+
+
+
+
+
+ HoloLake
+ GH-AIOS
+ 光湖语言系统 · 通用人工智能操作平台
+
+
+
+
+
+ 当前聚焦域
+ 第五域 · 光湖本源域
+
+ - 稳定编号
- DOM-FIFTH-0001
+ - 定位
- 零点原核的工程本体 · 语言架构层
+ - 接入方式
- 本地终端节点 / 云端常驻节点
+ - 状态
- 公开门厅可查看 · 进入后装载独立域运行体
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ HoloLake
+ GH-AIOS
+ 光湖语言系统 · 通用人工智能操作平台
+
+
+ {stage === 'directory' ? '五域公开门厅' : '公共灯塔'}
+ {stage === 'directory' ? '先在门口了解每个域;进入时再由对应节点完成接入。' : '从这里进入光湖世界'}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/product-source/guanghu-knowledge-base/src/main.tsx b/product-source/guanghu-knowledge-base/src/main.tsx
index 00ee0d0..b273b63 100644
--- a/product-source/guanghu-knowledge-base/src/main.tsx
+++ b/product-source/guanghu-knowledge-base/src/main.tsx
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client';
import App from './App';
import './styles/app.css';
import './styles/workspace-0.7.1.css';
+import './styles/world-entry.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
diff --git a/product-source/guanghu-knowledge-base/src/styles/world-entry.css b/product-source/guanghu-knowledge-base/src/styles/world-entry.css
new file mode 100644
index 0000000..085f1e1
--- /dev/null
+++ b/product-source/guanghu-knowledge-base/src/styles/world-entry.css
@@ -0,0 +1,415 @@
+.world-entry {
+ position: fixed;
+ inset: 0;
+ z-index: 1000;
+ min-width: 860px;
+ min-height: 620px;
+ overflow: hidden;
+ color: #edf4ff;
+ font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
+ background: #06142b;
+}
+
+.entry-backdrop,
+.entry-vignette {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+}
+
+.public-lighthouse .entry-backdrop {
+ background: url('/gh-aios-public-lighthouse.png') center / cover no-repeat;
+}
+
+.fifth-vestibule .entry-backdrop {
+ background: url('/fifth-domain-lighthouse.png') center / cover no-repeat;
+}
+
+.public-lighthouse .entry-vignette {
+ background:
+ linear-gradient(180deg, rgba(1, 10, 27, .16) 0%, rgba(2, 13, 31, 0) 48%, rgba(1, 10, 24, .72) 100%),
+ radial-gradient(circle at 50% 32%, rgba(85, 131, 205, .08), rgba(0, 6, 19, .18) 64%, rgba(0, 5, 16, .42) 100%);
+}
+
+.fifth-vestibule .entry-vignette {
+ background: linear-gradient(90deg, rgba(1, 5, 20, .28), rgba(1, 6, 25, .05) 65%, rgba(2, 4, 17, .2));
+}
+
+.entry-brand {
+ display: grid;
+ justify-items: center;
+ gap: 9px;
+ text-align: center;
+}
+
+.entry-brand span {
+ font-size: clamp(24px, 2.7vw, 40px);
+ font-weight: 500;
+ letter-spacing: -.03em;
+}
+
+.entry-brand strong {
+ color: #dbe9ff;
+ font-family: "Helvetica Neue", Inter, sans-serif;
+ font-size: clamp(58px, 7.3vw, 108px);
+ font-weight: 400;
+ line-height: .94;
+ letter-spacing: .05em;
+ text-shadow: 0 5px 24px rgba(73, 133, 220, .18);
+}
+
+.entry-brand small {
+ color: rgba(232, 240, 255, .82);
+ font-size: clamp(14px, 1.35vw, 21px);
+ font-weight: 400;
+ letter-spacing: .25em;
+}
+
+.public-entry-copy {
+ position: relative;
+ z-index: 1;
+ width: min(720px, 62vw);
+ margin: clamp(72px, 10vh, 120px) auto 0;
+ display: grid;
+ justify-items: center;
+ text-align: center;
+}
+
+.public-divider {
+ width: 190px;
+ height: 1px;
+ margin: 30px 0 30px;
+ background: linear-gradient(90deg, transparent, rgba(202, 222, 250, .36), transparent);
+}
+
+.public-entry-copy h1 {
+ margin: 0;
+ font-size: clamp(42px, 5vw, 68px);
+ font-weight: 500;
+ letter-spacing: .11em;
+}
+
+.public-entry-copy > p {
+ margin: 15px 0 34px;
+ color: rgba(228, 237, 252, .72);
+ font-size: clamp(16px, 1.65vw, 24px);
+ letter-spacing: .22em;
+}
+
+.public-entry-actions {
+ width: min(570px, 100%);
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+}
+
+.public-entry-actions button,
+.fifth-actions button {
+ min-height: 66px;
+ border: 1px solid rgba(172, 203, 245, .62);
+ border-radius: 12px;
+ background: rgba(13, 35, 72, .38);
+ color: #edf4ff;
+ font: inherit;
+ font-size: 19px;
+ font-weight: 500;
+ letter-spacing: .08em;
+ backdrop-filter: blur(14px);
+ transition: border-color .18s ease, background .18s ease, transform .18s ease;
+}
+
+.public-entry-actions button:hover,
+.fifth-actions button:hover,
+.public-domain-strip button:hover {
+ transform: translateY(-2px);
+ border-color: rgba(224, 237, 255, .92);
+}
+
+.public-entry-actions .public-primary {
+ border-color: rgba(231, 243, 255, .84);
+ background: linear-gradient(135deg, rgba(222, 237, 255, .92), rgba(131, 181, 243, .82));
+ color: #09214a;
+ box-shadow: 0 12px 35px rgba(65, 128, 216, .18);
+}
+
+.public-domain-strip {
+ position: absolute;
+ z-index: 1;
+ left: 8.5vw;
+ right: 8.5vw;
+ bottom: clamp(116px, 15vh, 168px);
+ display: grid;
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ border-bottom: 1px solid rgba(179, 207, 239, .22);
+}
+
+.public-domain-strip button {
+ position: relative;
+ min-width: 0;
+ min-height: 86px;
+ padding: 8px 16px 26px;
+ border: 0;
+ border-bottom: 2px solid transparent;
+ background: transparent;
+ color: rgba(230, 239, 252, .84);
+ font: inherit;
+ text-align: center;
+ transition: color .18s ease, border-color .18s ease, transform .18s ease;
+}
+
+.public-domain-strip button + button::before {
+ content: '/';
+ position: absolute;
+ left: -4px;
+ top: 22px;
+ color: rgba(158, 197, 244, .72);
+ font-size: 28px;
+ font-weight: 300;
+}
+
+.public-domain-strip span,
+.public-domain-strip strong,
+.public-domain-strip small {
+ display: block;
+}
+
+.public-domain-strip span {
+ margin-bottom: 8px;
+ color: rgba(154, 192, 239, .82);
+ font-size: 18px;
+}
+
+.public-domain-strip strong {
+ overflow: hidden;
+ font-size: 16px;
+ font-weight: 500;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.public-domain-strip small {
+ margin-top: 8px;
+ color: rgba(208, 222, 242, .64);
+ font-size: 11px;
+ line-height: 1.5;
+}
+
+.public-domain-strip button.selected,
+.public-domain-strip button:focus-visible {
+ border-bottom-color: #b8d6ff;
+ color: #fff;
+ outline: none;
+}
+
+.public-entry-footer {
+ position: absolute;
+ z-index: 1;
+ left: 50%;
+ bottom: 65px;
+ color: rgba(202, 219, 241, .6);
+ font-size: 14px;
+ letter-spacing: .16em;
+ transform: translateX(-50%);
+ white-space: nowrap;
+}
+
+.public-lighthouse.is-directory .public-entry-copy {
+ margin-top: clamp(45px, 6vh, 75px);
+}
+
+.public-lighthouse.is-directory .entry-brand strong {
+ font-size: clamp(46px, 5vw, 74px);
+}
+
+.public-lighthouse.is-directory .public-divider {
+ margin: 20px 0;
+}
+
+.public-lighthouse.is-directory .public-domain-strip {
+ bottom: clamp(92px, 12vh, 130px);
+}
+
+.public-lighthouse.is-directory .public-domain-strip button {
+ min-height: 118px;
+}
+
+.fifth-brand {
+ position: absolute;
+ z-index: 1;
+ top: 62px;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.fifth-brand span { font-size: 27px; }
+.fifth-brand strong {
+ color: #ffe9b7;
+ font-family: Georgia, "Times New Roman", serif;
+ font-size: clamp(60px, 6.5vw, 92px);
+ text-shadow: 0 0 24px rgba(255, 201, 116, .16);
+}
+.fifth-brand small { color: rgba(255, 237, 204, .82); font-size: 14px; }
+
+.fifth-domain-rail {
+ position: absolute;
+ z-index: 1;
+ left: 4vw;
+ top: 15vh;
+ bottom: 23vh;
+ width: 238px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
+
+.fifth-domain-rail::before {
+ content: '';
+ position: absolute;
+ left: 28px;
+ top: 24px;
+ bottom: 32px;
+ width: 1px;
+ background: linear-gradient(#9d83ce, #c6a063 78%, #ffe2a0);
+ box-shadow: 0 0 18px rgba(208, 168, 106, .22);
+}
+
+.fifth-domain-rail button {
+ position: relative;
+ display: grid;
+ grid-template-columns: 58px minmax(0, 1fr);
+ align-items: center;
+ gap: 14px;
+ min-height: 60px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ color: rgba(235, 231, 247, .86);
+ font: inherit;
+ text-align: left;
+}
+
+.fifth-domain-rail button span {
+ position: relative;
+ z-index: 1;
+ width: 54px;
+ height: 54px;
+ display: grid;
+ place-items: center;
+ border: 1px solid rgba(197, 166, 227, .8);
+ border-radius: 50%;
+ background: rgba(11, 11, 41, .74);
+ font-size: 19px;
+}
+
+.fifth-domain-rail button strong {
+ max-width: 130px;
+ font-size: 15px;
+ font-weight: 500;
+}
+
+.fifth-domain-rail button:focus-visible {
+ outline: 1px solid #ffe1a1;
+ outline-offset: 5px;
+}
+
+.fifth-domain-rail button.selected span {
+ border-color: #ffe1a1;
+ color: #ffe5ad;
+ font-size: 27px;
+ box-shadow: 0 0 0 11px rgba(255, 207, 126, .1), 0 0 30px rgba(255, 206, 124, .35);
+}
+
+.fifth-domain-rail button.selected strong {
+ color: #ffe1a1;
+ font-size: 20px;
+}
+
+.fifth-focus-panel {
+ position: absolute;
+ z-index: 1;
+ top: 27vh;
+ left: max(320px, 24vw);
+ width: min(820px, 56vw);
+ padding: 32px 42px 36px;
+ border: 1px solid rgba(174, 174, 217, .42);
+ border-radius: 18px;
+ background: rgba(6, 12, 43, .68);
+ box-shadow: 0 22px 80px rgba(1, 3, 18, .28);
+ backdrop-filter: blur(20px);
+}
+
+.fifth-focus-panel > small {
+ color: #f3d49b;
+ font-size: 15px;
+ letter-spacing: .08em;
+}
+
+.fifth-focus-panel h1 {
+ margin: 7px 0 13px;
+ color: #ffe1a8;
+ font-size: clamp(35px, 3.4vw, 52px);
+ font-weight: 600;
+ letter-spacing: .02em;
+}
+
+.fifth-focus-panel dl { margin: 0; }
+.fifth-focus-panel dl > div {
+ display: grid;
+ grid-template-columns: 150px minmax(0, 1fr);
+ gap: 18px;
+ align-items: center;
+ min-height: 63px;
+ border-top: 1px solid rgba(181, 181, 220, .22);
+}
+
+.fifth-focus-panel dt {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ color: rgba(234, 230, 246, .72);
+}
+.fifth-focus-panel dt svg { width: 22px; height: 22px; stroke-width: 1.6; }
+.fifth-focus-panel dd { margin: 0; color: rgba(244, 243, 251, .88); }
+
+.fifth-actions {
+ position: absolute;
+ top: calc(100% + 24px);
+ left: 50%;
+ width: min(650px, 86%);
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+ transform: translateX(-50%);
+}
+
+.fifth-actions button {
+ min-height: 62px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ font-size: 16px;
+}
+.fifth-actions button svg { width: 22px; height: 22px; stroke-width: 1.6; }
+.fifth-actions .fifth-primary {
+ grid-column: 1 / -1;
+ width: min(510px, 82%);
+ min-height: 82px;
+ justify-self: center;
+ border-color: rgba(255, 238, 189, .92);
+ background: linear-gradient(135deg, rgba(240, 191, 111, .9), rgba(176, 129, 72, .8));
+ color: #fff9ec;
+ font-size: 19px;
+ box-shadow: 0 0 0 3px rgba(255, 216, 144, .16), 0 0 26px rgba(255, 196, 103, .34);
+}
+
+@media (max-height: 760px) {
+ .public-entry-copy { margin-top: 38px; }
+ .public-divider { margin: 17px 0; }
+ .public-entry-copy > p { margin-bottom: 22px; }
+ .public-domain-strip { bottom: 82px; }
+ .public-entry-footer { bottom: 28px; }
+ .fifth-brand { top: 24px; }
+ .fifth-focus-panel { top: 25vh; }
+ .fifth-actions { top: calc(100% + 14px); }
+}
diff --git a/product-source/hololake-desktop/design-qa.md b/product-source/hololake-desktop/design-qa.md
new file mode 100644
index 0000000..4066966
--- /dev/null
+++ b/product-source/hololake-desktop/design-qa.md
@@ -0,0 +1,68 @@
+# HoloLake 0.8.0 · 公共灯塔与第五域门厅视觉验收
+
+## 视觉事实源
+
+- 公共灯塔选定稿:`/Users/bingshuolingdianyuanhe/.codex/generated_images/019fe458-4bb4-7530-aad0-f73f760ad018/exec-d674d5cb-760b-4415-9eb1-09e8dd84fec4.png`
+- 第五域选定稿:`/Users/bingshuolingdianyuanhe/Documents/Codex/2026-08-09/new-chat/work/DEV-20260809-007/ui-audit-20260809/selected-fifth-domain-visual-target-v2.png`
+- 公共灯塔实现截图:`/Users/bingshuolingdianyuanhe/Documents/Codex/2026-08-09/new-chat/work/DEV-20260809-007/ui-audit-20260809/implementation-public-lighthouse-final.png`
+- 第五域实现截图:`/Users/bingshuolingdianyuanhe/Documents/Codex/2026-08-09/new-chat/work/DEV-20260809-007/ui-audit-20260809/implementation-fifth-domain-final.png`
+- 公共灯塔同屏对照:`/Users/bingshuolingdianyuanhe/Documents/Codex/2026-08-09/new-chat/work/DEV-20260809-007/ui-audit-20260809/comparison-public-final.png`
+- 第五域同屏对照:`/Users/bingshuolingdianyuanhe/Documents/Codex/2026-08-09/new-chat/work/DEV-20260809-007/ui-audit-20260809/comparison-fifth-final.png`
+
+## 归一化条件
+
+- CSS 视口:1488 × 1058。
+- deviceScaleFactor:1。
+- 选定稿像素:1487 × 1058;只在对照图中等比归一化为 1488 × 1058。
+- 实现截图像素:1488 × 1058。
+- 状态:未登录公共灯塔;第五域公开门厅;桌面端深色主题。
+- 浏览器渲染:HoloLake 0.8.0 本地生产构建预览。
+
+## 最终对照结论
+
+### 公共灯塔
+
+- 字体与层级:HoloLake、GH-AIOS、公共灯塔、说明和操作按钮保持选定稿的单焦点层级;字重、字距和正文对比度均可读。
+- 布局与节奏:中央品牌、双按钮、五域横向门厅和底部节点说明与选定稿同构;没有额外卡片或多栏抢焦点。
+- 色彩与视觉 token:公共外壳保持克制深蓝,主按钮使用冷白蓝,灯塔只承担单一暖光焦点;未复制第五域的淡金专属主题。
+- 图像质量:使用独立的无文字高分辨率湖面灯塔背景,不以界面截图充当背景;1488 × 1058 下无拉伸、马赛克或明显压缩伪影。
+- 文案:软件全称、公共灯塔、五域名称、第五域正式名和“光湖零域”均正确。
+
+### 第五域 · 光湖本源域
+
+- 字体与层级:淡金 GH-AIOS、当前聚焦域、第五域名称、真实状态表和进入动作形成清楚的单一路径。
+- 布局与节奏:五域轨道、中央真实信息面板、主操作和两个次操作与选定稿的空间关系一致;核心内容没有散落或互相争抢。
+- 色彩与视觉 token:深蓝紫黑湖面、淡金暖白主光和蓝紫方向性光带符合第五域“湖面映星”语法,与公共主题保持同源但不相同。
+- 图像与图标:背景为独立生成的无界面资产;状态与操作使用 Lucide 正式图标组件,没有手绘 SVG、字符图标或占位符。
+- 文案:“光湖零域”纠正已经进入所有门厅;第五域稳定编号、零点原核定位、接入方式和独立运行体状态均为真实产品语义,不使用装饰假数据。
+
+## 交互与运行检查
+
+- “查看五域”可进入五域公开门厅。
+- “登录或接入节点”可打开现有真实节点/保存位置入口。
+- 第五域入口可进入第五域公开门厅。
+- “进入第五域”可打开现有知识、Agent 与频道工作区。
+- 14 项现有自动化测试全部通过。
+- HoloLake 0.8.0 生产构建通过。
+- 核心流程浏览器控制台错误:0。
+
+## 对照迭代历史
+
+1. v1:第五域信息面板偏窄,主次操作被压成同一行,和选定稿的动作层级不一致(P2)。
+ - 修正:扩大信息面板、恢复一主两次的两层操作布局、调整轨道间距与字号。
+ - 证据:`implementation-fifth-domain-v1.png` → `implementation-fifth-domain-v2.png`。
+2. v2:状态行与按钮缺少选定稿中的语义图标(P2)。
+ - 修正:接入可树摇的 Lucide ESM 图标;生产包主脚本保持约 302 KB,没有因整库引入形成大包回退。
+ - 证据:`implementation-fifth-domain-v2.png` → `implementation-fifth-domain-final.png`。
+3. 最终复验:公共与第五域选定稿和实现图均以相同视口同屏比较;没有剩余 P0、P1 或 P2 差异。
+
+## 可接受的 P3 差异
+
+- 字体由系统可用字体替代选定稿中的生成式展示字形,字面宽度存在轻微差别,但没有改变层级、换行或可读性。
+- 第五域背景光轨与选定稿不是逐像素复制,而是同一艺术方向的独立无文字生产资产;主体位置、色调与内容留白一致。
+
+## Focused region comparison
+
+第五域中央面板和操作区在 1488 × 1058 原始像素下单独检查了字号、行高、分隔线、图标、按钮宽高与焦点状态;这些细节在全屏同屏对照中仍清晰可读,因此无需另行裁切放大。
+
+final result: passed
diff --git a/product-source/hololake-desktop/package-lock.json b/product-source/hololake-desktop/package-lock.json
index d0b9359..99ce0a1 100644
--- a/product-source/hololake-desktop/package-lock.json
+++ b/product-source/hololake-desktop/package-lock.json
@@ -12,6 +12,7 @@
"diff": "^9.0.0",
"express": "^5.1.0",
"gray-matter": "^4.0.3",
+ "lucide-react": "^0.468.0",
"marked": "^15.0.0",
"simple-git": "^3.27.0"
},
@@ -4747,6 +4748,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lucide-react": {
+ "version": "0.468.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz",
+ "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
+ }
+ },
"node_modules/marked": {
"version": "15.0.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
@@ -5527,7 +5537,6 @@
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
diff --git a/product-source/hololake-desktop/package.json b/product-source/hololake-desktop/package.json
index 025df42..2e4538b 100644
--- a/product-source/hololake-desktop/package.json
+++ b/product-source/hololake-desktop/package.json
@@ -22,6 +22,7 @@
"diff": "^9.0.0",
"express": "^5.1.0",
"gray-matter": "^4.0.3",
+ "lucide-react": "^0.468.0",
"marked": "^15.0.0",
"simple-git": "^3.27.0"
},
diff --git a/product-source/hololake-desktop/vite.config.ts b/product-source/hololake-desktop/vite.config.ts
index b45c1c6..af90008 100644
--- a/product-source/hololake-desktop/vite.config.ts
+++ b/product-source/hololake-desktop/vite.config.ts
@@ -15,6 +15,7 @@ export default defineConfig({
'react-dom': path.resolve(__dirname, 'node_modules/react-dom/index.js'),
'react': path.resolve(__dirname, 'node_modules/react/index.js'),
'marked': path.resolve(__dirname, 'node_modules/marked/lib/marked.esm.js'),
+ 'lucide-react': path.resolve(__dirname, 'node_modules/lucide-react/dist/esm/lucide-react.js'),
},
},
root: path.resolve(__dirname, '../guanghu-knowledge-base'),
diff --git a/product-source/hololake-platform/src/lib/domainRuntimeContract.test.ts b/product-source/hololake-platform/src/lib/domainRuntimeContract.test.ts
new file mode 100644
index 0000000..e460949
--- /dev/null
+++ b/product-source/hololake-platform/src/lib/domainRuntimeContract.test.ts
@@ -0,0 +1,159 @@
+import { describe, expect, it } from 'vitest'
+import {
+ INITIAL_DOMAIN_ACCESS_STATE,
+ acceptDomainConnectionReceipt,
+ acceptDomainSession,
+ assertVerifiedDomainManifest,
+ beginDomainAccess,
+ disconnectDomainRuntime,
+ enterDomainRuntime,
+ projectPublicDomainVestibule,
+ selectPublicDomain,
+ type DomainConnectionReceipt,
+ type DomainSessionCapability,
+ type VerifiedDomainManifest,
+} from './domainRuntimeContract'
+
+const DIGEST = 'a'.repeat(64)
+const NOW = 1_786_291_200_000
+
+function manifest(): VerifiedDomainManifest {
+ return {
+ public: {
+ accessModes: ['LOCAL_TERMINAL_NODE', 'CLOUD_RESIDENT_NODE'],
+ displayName: '第五域 · 光湖本源域',
+ domainId: 'DOM-FIFTH-0001',
+ formalName: '光湖本源域',
+ publicStatus: 'PUBLIC_PREVIEW',
+ purpose: '零点原核的工程本体与语言架构源点域',
+ responsibleParty: '第五域主控',
+ themePreview: {
+ assetRef: 'theme-preview://origin-domain/lake-reflects-stars',
+ description: '湖面映星公开预览',
+ },
+ },
+ provenance: {
+ digest: DIGEST,
+ repositoryId: 'REPO-012',
+ signature: 'signature-envelope-reference',
+ signerId: 'SIGNER-FIFTH-DOMAIN-001',
+ sourceCommit: '6e26d1f35439759542efb317c3ddd7661ba27d58',
+ },
+ runtime: {
+ backendVersion: 'not-published',
+ channelIds: ['CHANNEL-ZERO-CORE'],
+ forbiddenDataScopes: ['private-relationship-core'],
+ frontendVersion: 'not-published',
+ healthCheckRef: 'health://origin-domain/runtime',
+ moduleIds: ['MODULE-LANGUAGE-ARCHITECTURE'],
+ rollbackVersion: 'not-published',
+ routeRef: 'domain-route://origin-domain/runtime',
+ themePackageRef: 'theme://origin-domain/lake-reflects-stars',
+ },
+ schema: 'gh-aios.domain-manifest/v1',
+ verification: {
+ manifestDigest: DIGEST,
+ state: 'verified',
+ verifiedAt: NOW,
+ },
+ }
+}
+
+function session(overrides: Partial = {}): DomainSessionCapability {
+ return {
+ capabilityId: 'CAP-001',
+ domainId: 'DOM-FIFTH-0001',
+ expiresAt: NOW + 60_000,
+ nodeId: 'LOCAL-NODE-001',
+ scopes: ['domain:enter'],
+ ...overrides,
+ }
+}
+
+function receipt(overrides: Partial = {}): DomainConnectionReceipt {
+ return {
+ connectionId: 'CONNECTION-001',
+ domainId: 'DOM-FIFTH-0001',
+ manifestDigest: DIGEST,
+ nodeId: 'LOCAL-NODE-001',
+ receiptId: 'RECEIPT-001',
+ state: 'online',
+ ...overrides,
+ }
+}
+
+describe('domain runtime contract', () => {
+ it('projects only public vestibule fields', () => {
+ const publicView = projectPublicDomainVestibule(manifest())
+ const serialized = JSON.stringify(publicView)
+
+ expect(publicView.displayName).toBe('第五域 · 光湖本源域')
+ expect(serialized).not.toContain('domain-route://')
+ expect(serialized).not.toContain('CHANNEL-ZERO-CORE')
+ expect(serialized).not.toContain('private-relationship-core')
+ expect(serialized).not.toContain('REPO-012')
+ })
+
+ it('rejects a manifest that has not passed the external signature verifier', () => {
+ const candidate = manifest()
+ candidate.verification.state = 'verified'
+ candidate.verification.manifestDigest = 'b'.repeat(64)
+
+ expect(() => assertVerifiedDomainManifest(candidate)).toThrow('domain_manifest_digest_invalid')
+ })
+
+ it.each(['LOCAL_TERMINAL_NODE', 'CLOUD_RESIDENT_NODE'] as const)(
+ 'accepts %s as a user-owned access node type',
+ (nodeType) => {
+ const selected = selectPublicDomain(INITIAL_DOMAIN_ACCESS_STATE, manifest().public)
+ const access = beginDomainAccess(selected, manifest(), nodeType)
+
+ expect(access).toMatchObject({ nodeType, stage: 'access' })
+ },
+ )
+
+ it('does not enter a runtime with only a selected vestibule or frontend login state', () => {
+ const selected = selectPublicDomain(INITIAL_DOMAIN_ACCESS_STATE, manifest().public)
+ const access = beginDomainAccess(selected, manifest(), 'LOCAL_TERMINAL_NODE')
+
+ expect(() => enterDomainRuntime(access, NOW)).toThrow('domain_runtime_session_missing')
+ const withSession = acceptDomainSession(access, session(), NOW)
+ expect(() => enterDomainRuntime(withSession, NOW)).toThrow('domain_runtime_connection_receipt_missing')
+ })
+
+ it('enters only after a matching live session and connection receipt', () => {
+ const selected = selectPublicDomain(INITIAL_DOMAIN_ACCESS_STATE, manifest().public)
+ const access = beginDomainAccess(selected, manifest(), 'LOCAL_TERMINAL_NODE')
+ const withSession = acceptDomainSession(access, session(), NOW)
+ const connected = acceptDomainConnectionReceipt(withSession, receipt())
+ const runtime = enterDomainRuntime(connected, NOW)
+
+ expect(runtime).toMatchObject({
+ mode: 'connected',
+ nodeType: 'LOCAL_TERMINAL_NODE',
+ stage: 'runtime',
+ })
+ })
+
+ it('fails closed when the session or receipt belongs to another domain', () => {
+ const selected = selectPublicDomain(INITIAL_DOMAIN_ACCESS_STATE, manifest().public)
+ const access = beginDomainAccess(selected, manifest(), 'LOCAL_TERMINAL_NODE')
+
+ expect(() => acceptDomainSession(access, session({ domainId: 'DOMAIN-OTHER' }), NOW))
+ .toThrow('domain_session_domain_mismatch')
+
+ const withSession = acceptDomainSession(access, session(), NOW)
+ expect(() => acceptDomainConnectionReceipt(withSession, receipt({ domainId: 'DOMAIN-OTHER' })))
+ .toThrow('domain_connection_domain_mismatch')
+ })
+
+ it('drops execution authority and keeps an explicit read-only scene after disconnect', () => {
+ const selected = selectPublicDomain(INITIAL_DOMAIN_ACCESS_STATE, manifest().public)
+ const access = beginDomainAccess(selected, manifest(), 'LOCAL_TERMINAL_NODE')
+ const withSession = acceptDomainSession(access, session(), NOW)
+ const connected = acceptDomainConnectionReceipt(withSession, receipt())
+ const runtime = enterDomainRuntime(connected, NOW)
+
+ expect(disconnectDomainRuntime(runtime).mode).toBe('disconnected-readonly')
+ })
+})
diff --git a/product-source/hololake-platform/src/lib/domainRuntimeContract.ts b/product-source/hololake-platform/src/lib/domainRuntimeContract.ts
new file mode 100644
index 0000000..4013f2e
--- /dev/null
+++ b/product-source/hololake-platform/src/lib/domainRuntimeContract.ts
@@ -0,0 +1,269 @@
+export type DomainNodeType = 'LOCAL_TERMINAL_NODE' | 'CLOUD_RESIDENT_NODE'
+
+export type PublicDomainStatus = 'PUBLIC_PREVIEW' | 'RESTRICTED_PREVIEW' | 'UNAVAILABLE'
+
+export interface PublicThemePreview {
+ assetRef: string
+ description: string
+}
+
+export interface PublicDomainVestibule {
+ accessModes: DomainNodeType[]
+ displayName: string
+ domainId: string
+ formalName: string
+ publicStatus: PublicDomainStatus
+ purpose: string
+ responsibleParty: string
+ themePreview: PublicThemePreview | null
+}
+
+export interface DomainManifestProvenance {
+ digest: string
+ repositoryId: string
+ signature: string
+ signerId: string
+ sourceCommit: string
+}
+
+export interface DomainRuntimeDescriptor {
+ backendVersion: string
+ channelIds: string[]
+ forbiddenDataScopes: string[]
+ frontendVersion: string
+ healthCheckRef: string
+ moduleIds: string[]
+ rollbackVersion: string
+ routeRef: string
+ themePackageRef: string
+}
+
+export interface VerifiedDomainManifest {
+ public: PublicDomainVestibule
+ provenance: DomainManifestProvenance
+ runtime: DomainRuntimeDescriptor
+ schema: 'gh-aios.domain-manifest/v1'
+ verification: {
+ manifestDigest: string
+ state: 'verified'
+ verifiedAt: number
+ }
+}
+
+export interface DomainSessionCapability {
+ capabilityId: string
+ domainId: string
+ expiresAt: number
+ nodeId: string
+ scopes: string[]
+}
+
+export interface DomainConnectionReceipt {
+ connectionId: string
+ domainId: string
+ manifestDigest: string
+ nodeId: string
+ receiptId: string
+ state: 'online'
+}
+
+export interface LighthouseState {
+ stage: 'lighthouse'
+}
+
+export interface VestibuleState {
+ domain: PublicDomainVestibule
+ stage: 'vestibule'
+}
+
+export interface AccessState {
+ manifest: VerifiedDomainManifest
+ nodeType: DomainNodeType
+ receipt: DomainConnectionReceipt | null
+ session: DomainSessionCapability | null
+ stage: 'access'
+}
+
+export interface RuntimeState {
+ manifest: VerifiedDomainManifest
+ mode: 'connected' | 'disconnected-readonly'
+ nodeType: DomainNodeType
+ receipt: DomainConnectionReceipt
+ session: DomainSessionCapability
+ stage: 'runtime'
+}
+
+export type DomainAccessState = LighthouseState | VestibuleState | AccessState | RuntimeState
+
+export class DomainContractError extends Error {
+ readonly code: string
+
+ constructor(code: string) {
+ super(code)
+ this.code = code
+ this.name = 'DomainContractError'
+ }
+}
+
+const SHA256 = /^[a-f0-9]{64}$/
+const SOURCE_COMMIT = /^[a-f0-9]{40,64}$/
+const REQUIRED_ENTER_SCOPE = 'domain:enter'
+
+function required(value: string, code: string): string {
+ if (!value.trim()) throw new DomainContractError(code)
+ return value
+}
+
+function publicDomain(domain: PublicDomainVestibule): PublicDomainVestibule {
+ required(domain.domainId, 'domain_manifest_public_id_missing')
+ required(domain.formalName, 'domain_manifest_formal_name_missing')
+ required(domain.displayName, 'domain_manifest_display_name_missing')
+ required(domain.purpose, 'domain_manifest_public_purpose_missing')
+ required(domain.responsibleParty, 'domain_manifest_responsible_party_missing')
+ if (!domain.accessModes.length) {
+ throw new DomainContractError('domain_manifest_access_modes_missing')
+ }
+ if (domain.accessModes.some(mode => mode !== 'LOCAL_TERMINAL_NODE' && mode !== 'CLOUD_RESIDENT_NODE')) {
+ throw new DomainContractError('domain_manifest_access_mode_invalid')
+ }
+ if (domain.themePreview) {
+ required(domain.themePreview.assetRef, 'domain_manifest_theme_preview_ref_missing')
+ required(domain.themePreview.description, 'domain_manifest_theme_preview_description_missing')
+ }
+ return {
+ accessModes: [...domain.accessModes],
+ displayName: domain.displayName,
+ domainId: domain.domainId,
+ formalName: domain.formalName,
+ publicStatus: domain.publicStatus,
+ purpose: domain.purpose,
+ responsibleParty: domain.responsibleParty,
+ themePreview: domain.themePreview ? { ...domain.themePreview } : null,
+ }
+}
+
+export function assertVerifiedDomainManifest(
+ manifest: VerifiedDomainManifest,
+): VerifiedDomainManifest {
+ if (manifest.schema !== 'gh-aios.domain-manifest/v1') {
+ throw new DomainContractError('domain_manifest_schema_unsupported')
+ }
+ publicDomain(manifest.public)
+ if (manifest.verification.state !== 'verified' || !Number.isFinite(manifest.verification.verifiedAt)) {
+ throw new DomainContractError('domain_manifest_not_verified')
+ }
+ if (!SHA256.test(manifest.provenance.digest)
+ || manifest.verification.manifestDigest !== manifest.provenance.digest) {
+ throw new DomainContractError('domain_manifest_digest_invalid')
+ }
+ if (!SOURCE_COMMIT.test(manifest.provenance.sourceCommit)) {
+ throw new DomainContractError('domain_manifest_source_commit_invalid')
+ }
+ required(manifest.provenance.repositoryId, 'domain_manifest_repository_missing')
+ required(manifest.provenance.signerId, 'domain_manifest_signer_missing')
+ required(manifest.provenance.signature, 'domain_manifest_signature_missing')
+ required(manifest.runtime.routeRef, 'domain_manifest_route_missing')
+ required(manifest.runtime.themePackageRef, 'domain_manifest_theme_package_missing')
+ required(manifest.runtime.healthCheckRef, 'domain_manifest_health_check_missing')
+ required(manifest.runtime.rollbackVersion, 'domain_manifest_rollback_version_missing')
+ return manifest
+}
+
+export function projectPublicDomainVestibule(
+ manifest: VerifiedDomainManifest,
+): PublicDomainVestibule {
+ return publicDomain(assertVerifiedDomainManifest(manifest).public)
+}
+
+export const INITIAL_DOMAIN_ACCESS_STATE: LighthouseState = { stage: 'lighthouse' }
+
+export function selectPublicDomain(
+ state: LighthouseState | VestibuleState,
+ domain: PublicDomainVestibule,
+): VestibuleState {
+ void state
+ return { domain: publicDomain(domain), stage: 'vestibule' }
+}
+
+export function beginDomainAccess(
+ state: VestibuleState,
+ manifest: VerifiedDomainManifest,
+ nodeType: DomainNodeType,
+): AccessState {
+ const verified = assertVerifiedDomainManifest(manifest)
+ if (verified.public.domainId !== state.domain.domainId) {
+ throw new DomainContractError('domain_manifest_selected_domain_mismatch')
+ }
+ if (!verified.public.accessModes.includes(nodeType)) {
+ throw new DomainContractError('domain_node_type_not_allowed')
+ }
+ return {
+ manifest: verified,
+ nodeType,
+ receipt: null,
+ session: null,
+ stage: 'access',
+ }
+}
+
+export function acceptDomainSession(
+ state: AccessState,
+ session: DomainSessionCapability,
+ now: number,
+): AccessState {
+ if (session.domainId !== state.manifest.public.domainId) {
+ throw new DomainContractError('domain_session_domain_mismatch')
+ }
+ if (session.expiresAt <= now) {
+ throw new DomainContractError('domain_session_expired')
+ }
+ if (!session.scopes.includes(REQUIRED_ENTER_SCOPE)) {
+ throw new DomainContractError('domain_session_enter_scope_missing')
+ }
+ required(session.capabilityId, 'domain_session_capability_id_missing')
+ required(session.nodeId, 'domain_session_node_id_missing')
+ return { ...state, session: { ...session, scopes: [...session.scopes] } }
+}
+
+export function acceptDomainConnectionReceipt(
+ state: AccessState,
+ receipt: DomainConnectionReceipt,
+): AccessState {
+ if (!state.session) {
+ throw new DomainContractError('domain_connection_session_missing')
+ }
+ if (receipt.domainId !== state.manifest.public.domainId) {
+ throw new DomainContractError('domain_connection_domain_mismatch')
+ }
+ if (receipt.nodeId !== state.session.nodeId) {
+ throw new DomainContractError('domain_connection_node_mismatch')
+ }
+ if (receipt.manifestDigest !== state.manifest.provenance.digest) {
+ throw new DomainContractError('domain_connection_manifest_mismatch')
+ }
+ required(receipt.connectionId, 'domain_connection_id_missing')
+ required(receipt.receiptId, 'domain_connection_receipt_id_missing')
+ return { ...state, receipt: { ...receipt } }
+}
+
+export function enterDomainRuntime(state: AccessState, now: number): RuntimeState {
+ if (!state.session) throw new DomainContractError('domain_runtime_session_missing')
+ if (!state.receipt) throw new DomainContractError('domain_runtime_connection_receipt_missing')
+ if (state.session.expiresAt <= now) throw new DomainContractError('domain_session_expired')
+ return {
+ manifest: state.manifest,
+ mode: 'connected',
+ nodeType: state.nodeType,
+ receipt: state.receipt,
+ session: state.session,
+ stage: 'runtime',
+ }
+}
+
+export function disconnectDomainRuntime(state: RuntimeState): RuntimeState {
+ return { ...state, mode: 'disconnected-readonly' }
+}
+
+export function returnToLighthouse(): LighthouseState {
+ return INITIAL_DOMAIN_ACCESS_STATE
+}