feat(iwooos): 顯示 Wazuh 即時中繼資料閘門

This commit is contained in:
ogt
2026-06-24 23:07:29 +08:00
parent 5a5cb50f65
commit 64eef5a252
5 changed files with 340 additions and 0 deletions

View File

@@ -19003,6 +19003,59 @@
}
}
},
"wazuhLiveMetadataEnvGate": {
"eyebrow": "Wazuh 即時中繼資料環境閘門",
"title": "Wazuh 查詢要等正式路由、負責人與機密中繼資料都過關",
"subtitle": "這張卡把 Wazuh 即時中繼資料啟用前的條件拆開:正式路由讀回、伺服端環境變數負責人、機密來源中繼資料、管理節點健康、唯讀帳號範圍與啟用後讀回都要先補齊;目前即時查詢、主動回應、主機寫入與執行期閘門都是 0。",
"checkLabel": "檢核",
"stateLabel": "狀態",
"boundaryTitle": "即時中繼資料環境邊界",
"boundaryIntro": "以下鍵值固定:正式路由 200、Wazuh 已建置或 UI 可見都不能直接代表可以查 Wazuh 即時中繼資料;不得收機密明文值、不得改 K8s 機密、不得用 Nginx 或主機操作繞過釋出閘門。",
"summary": {
"routeReadback": {
"label": "路由讀回",
"detail": "正式部署後讀回尚未通過,因此仍不能啟用即時中繼資料。"
},
"owner": {
"label": "負責人回覆",
"detail": "伺服端環境變數與啟用責任尚未被審查者接受。"
},
"secretMeta": {
"label": "機密中繼資料",
"detail": "只允許機密來源中繼資料與負責人,不收密碼、權杖或雜湊值。"
},
"liveQuery": {
"label": "即時查詢",
"detail": "Wazuh API 即時查詢授權目前維持 0。"
}
},
"items": {
"releaseReadback": {
"title": "先等正式路由不再 404",
"body": "部署後必須用正式讀回指令驗證 `/api/iwooos/wazuh`,不能用部署前 404 或閘道繞路當成功。"
},
"serverEnv": {
"title": "伺服端環境變數需負責人",
"body": "IWOOOS_WAZUH_READONLY_ENABLED 與 Wazuh API 環境變數只能由正式伺服端流程注入,不能寫到前端 bundle。"
},
"secretMetadata": {
"title": "機密只收中繼資料",
"body": "只記錄注入來源、負責人、維護窗口與回滾路徑不得貼密碼、權杖、Cookie、工作階段或任何機密片段。"
},
"managerHealth": {
"title": "Wazuh 管理節點健康待參照",
"body": "需要管理節點、API 與 TLS 健康狀態的脫敏參照;不能只用 Wazuh 已安裝或儀表板可見判斷。"
},
"readonlyScope": {
"title": "唯讀帳號範圍待驗",
"body": "只允許中繼資料查詢範圍;主動回應、規則變更、解碼器變更與主機操作必須留在另一個閘門。"
},
"postEnable": {
"title": "啟用後還要讀回",
"body": "即使未來環境變數啟用,也必須再驗證不回傳原始載荷、代理端原名、內網 IP 或機密。"
}
}
},
"socSiemKaliWazuhIntegration": {
"eyebrow": "SOC / SIEM / Kali 112 整合控制",
"title": "把 Wazuh、Kali、告警鏈與主流資安機制接成同一條證據線",

View File

@@ -19003,6 +19003,59 @@
}
}
},
"wazuhLiveMetadataEnvGate": {
"eyebrow": "Wazuh 即時中繼資料環境閘門",
"title": "Wazuh 查詢要等正式路由、負責人與機密中繼資料都過關",
"subtitle": "這張卡把 Wazuh 即時中繼資料啟用前的條件拆開:正式路由讀回、伺服端環境變數負責人、機密來源中繼資料、管理節點健康、唯讀帳號範圍與啟用後讀回都要先補齊;目前即時查詢、主動回應、主機寫入與執行期閘門都是 0。",
"checkLabel": "檢核",
"stateLabel": "狀態",
"boundaryTitle": "即時中繼資料環境邊界",
"boundaryIntro": "以下鍵值固定:正式路由 200、Wazuh 已建置或 UI 可見都不能直接代表可以查 Wazuh 即時中繼資料;不得收機密明文值、不得改 K8s 機密、不得用 Nginx 或主機操作繞過釋出閘門。",
"summary": {
"routeReadback": {
"label": "路由讀回",
"detail": "正式部署後讀回尚未通過,因此仍不能啟用即時中繼資料。"
},
"owner": {
"label": "負責人回覆",
"detail": "伺服端環境變數與啟用責任尚未被審查者接受。"
},
"secretMeta": {
"label": "機密中繼資料",
"detail": "只允許機密來源中繼資料與負責人,不收密碼、權杖或雜湊值。"
},
"liveQuery": {
"label": "即時查詢",
"detail": "Wazuh API 即時查詢授權目前維持 0。"
}
},
"items": {
"releaseReadback": {
"title": "先等正式路由不再 404",
"body": "部署後必須用正式讀回指令驗證 `/api/iwooos/wazuh`,不能用部署前 404 或閘道繞路當成功。"
},
"serverEnv": {
"title": "伺服端環境變數需負責人",
"body": "IWOOOS_WAZUH_READONLY_ENABLED 與 Wazuh API 環境變數只能由正式伺服端流程注入,不能寫到前端 bundle。"
},
"secretMetadata": {
"title": "機密只收中繼資料",
"body": "只記錄注入來源、負責人、維護窗口與回滾路徑不得貼密碼、權杖、Cookie、工作階段或任何機密片段。"
},
"managerHealth": {
"title": "Wazuh 管理節點健康待參照",
"body": "需要管理節點、API 與 TLS 健康狀態的脫敏參照;不能只用 Wazuh 已安裝或儀表板可見判斷。"
},
"readonlyScope": {
"title": "唯讀帳號範圍待驗",
"body": "只允許中繼資料查詢範圍;主動回應、規則變更、解碼器變更與主機操作必須留在另一個閘門。"
},
"postEnable": {
"title": "啟用後還要讀回",
"body": "即使未來環境變數啟用,也必須再驗證不回傳原始載荷、代理端原名、內網 IP 或機密。"
}
}
},
"socSiemKaliWazuhIntegration": {
"eyebrow": "SOC / SIEM / Kali 112 整合控制",
"title": "把 Wazuh、Kali、告警鏈與主流資安機制接成同一條證據線",

View File

@@ -271,6 +271,14 @@ type WazuhIntrusionReadbackItem = {
tone: 'steady' | 'warn' | 'locked'
}
type WazuhLiveMetadataEnvGateItem = {
key: string
check: string
state: string
icon: typeof ShieldCheck
tone: 'steady' | 'warn' | 'locked'
}
type SocSiemKaliWazuhIntegrationItem = {
key: string
check: string
@@ -2182,6 +2190,50 @@ const wazuhIntrusionReadbackBoundaries = [
'not_authorization=true',
] as const
const wazuhLiveMetadataEnvGateSummary = [
{ key: 'routeReadback', value: '0', icon: Route, tone: 'locked' },
{ key: 'owner', value: '0', icon: ClipboardCheck, tone: 'locked' },
{ key: 'secretMeta', value: '0', icon: Lock, tone: 'locked' },
{ key: 'liveQuery', value: '0', icon: Radar, tone: 'locked' },
] as const
const wazuhLiveMetadataEnvGateItems: WazuhLiveMetadataEnvGateItem[] = [
{ key: 'releaseReadback', check: 'ENV-1', state: '待部署驗證', icon: Route, tone: 'locked' },
{ key: 'serverEnv', check: 'ENV-2', state: '待負責人', icon: Server, tone: 'warn' },
{ key: 'secretMetadata', check: 'ENV-3', state: '只收中繼資料', icon: Lock, tone: 'locked' },
{ key: 'managerHealth', check: 'ENV-4', state: '待健康參照', icon: Activity, tone: 'warn' },
{ key: 'readonlyScope', check: 'ENV-5', state: '待範圍參照', icon: ShieldCheck, tone: 'warn' },
{ key: 'postEnable', check: 'ENV-6', state: '待讀回驗證', icon: SearchCheck, tone: 'locked' },
] as const
const wazuhLiveMetadataEnvGateBoundaries = [
'wazuh_live_metadata_env_gate_visible=true',
'wazuh_live_metadata_env_gate_server_side_env_key_count=4',
'wazuh_live_metadata_env_gate_required_owner_field_count=15',
'wazuh_live_metadata_env_gate_reviewer_check_count=15',
'wazuh_live_metadata_env_gate_outcome_lane_count=10',
'wazuh_live_metadata_env_gate_blocked_action_count=23',
'wazuh_live_metadata_env_gate_production_route_readback_passed_count=0',
'wazuh_live_metadata_env_gate_live_metadata_owner_response_accepted_count=0',
'wazuh_live_metadata_env_gate_secret_source_metadata_accepted_count=0',
'wazuh_live_metadata_env_gate_wazuh_manager_health_ref_accepted_count=0',
'wazuh_live_metadata_env_gate_readonly_account_scope_accepted_count=0',
'wazuh_live_metadata_env_gate_post_enable_readback_passed_count=0',
'wazuh_live_metadata_env_gate_wazuh_api_live_query_authorized_count=0',
'wazuh_live_metadata_env_gate_wazuh_active_response_authorized_count=0',
'wazuh_live_metadata_env_gate_runtime_gate_count=0',
'secret_value_collection_allowed=false',
'raw_wazuh_payload_storage_allowed=false',
'k8s_secret_patch_authorized=false',
'argocd_sync_authorized=false',
'docker_restart_authorized=false',
'nginx_gateway_workaround_authorized=false',
'firewall_change_authorized=false',
'host_write_authorized=false',
'kali_active_scan_authorized=false',
'not_authorization=true',
] as const
const socSiemKaliWazuhIntegrationSummary = [
{ key: 'frameworks', value: '7', icon: ClipboardCheck, tone: 'steady' },
{ key: 'domains', value: '16', icon: Network, tone: 'steady' },
@@ -7669,6 +7721,137 @@ function IwoooSWazuhIntrusionReadbackBoard() {
)
}
function IwoooSWazuhLiveMetadataEnvGateBoard() {
const t = useTranslations('iwooos.wazuhLiveMetadataEnvGate')
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
return (
<section
style={{ marginBottom: 14, maxWidth: '100%', overflow: 'hidden' }}
data-testid="iwooos-wazuh-live-metadata-env-gate-board"
>
<div style={{ ...band, padding: 16, background: '#f8fbf7', borderColor: '#c7dec4' }}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 300px), 1fr))', gap: 14 }}>
<div style={{ minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, color: '#3f6d3c', fontSize: 12, fontWeight: 700 }}>
<Server size={17} color="#4b7d47" />
{t('eyebrow')}
</div>
<h2 style={{ fontSize: 17, margin: '8px 0 0', color: '#141413' }}>{t('title')}</h2>
<p style={{ fontSize: 12, color: '#47683f', margin: '6px 0 0', lineHeight: 1.55, ...textWrap }}>
{t('subtitle')}
</p>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(126px, 1fr))', gap: 8 }}>
{wazuhLiveMetadataEnvGateSummary.map(item => {
const Icon = item.icon
return (
<div key={item.key} style={{ border: '0.5px solid #d2e4cf', borderRadius: 8, padding: 12, background: '#fff' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ fontSize: 11, color: '#607a5c' }}>{t(`summary.${item.key}.label` as never)}</span>
<Icon size={16} color={toneColors[item.tone]} />
</div>
<div style={{ fontSize: 19, fontWeight: 700, color: toneColors[item.tone], lineHeight: 1.1, marginTop: 8, ...textWrap }}>
{item.value}
</div>
<p style={{ fontSize: 11, color: '#47683f', lineHeight: 1.45, margin: '8px 0 0', ...textWrap }}>
{t(`summary.${item.key}.detail` as never)}
</p>
</div>
)
})}
</div>
</div>
<div
style={{
marginTop: 14,
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 210px), 1fr))',
gap: 10,
}}
>
{wazuhLiveMetadataEnvGateItems.map(item => {
const Icon = item.icon
return (
<div
key={item.key}
style={{
border: '0.5px solid #d2e4cf',
borderRadius: 8,
background: item.tone === 'locked' ? '#f5f8f4' : '#fff',
padding: 13,
minHeight: 158,
display: 'grid',
alignContent: 'space-between',
minWidth: 0,
...textWrap,
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ color: toneColors[item.tone], fontSize: 12, fontWeight: 700 }}>
{t('checkLabel')} {item.check}
</span>
<Icon size={18} color={toneColors[item.tone]} />
</div>
<div style={{ minWidth: 0 }}>
<div style={{ fontSize: 15, fontWeight: 700, color: '#141413', lineHeight: 1.25 }}>
{t(`items.${item.key}.title` as never)}
</div>
<div style={{ fontSize: 12, color: toneColors[item.tone], fontWeight: 700, marginTop: 5 }}>
{t('stateLabel')}{item.state}
</div>
</div>
<p style={{ fontSize: 11, color: '#47683f', lineHeight: 1.45, margin: 0, ...textWrap }}>
{t(`items.${item.key}.body` as never)}
</p>
</div>
)
})}
</div>
<details
data-testid="iwooos-wazuh-live-metadata-env-gate-boundaries"
style={{
marginTop: 12,
border: '0.5px solid #d2e4cf',
borderRadius: 8,
background: '#fff',
padding: '8px 10px',
}}
>
<summary style={{ cursor: 'pointer', fontSize: 12, fontWeight: 700, color: '#3f6d3c' }}>
{t('boundaryTitle')}
</summary>
<p style={{ fontSize: 11, color: '#47683f', lineHeight: 1.5, margin: '8px 0', ...textWrap }}>
{t('boundaryIntro')}
</p>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 6 }}>
{wazuhLiveMetadataEnvGateBoundaries.map(item => (
<code
key={item}
style={{
border: '0.5px solid #d2e4cf',
borderRadius: 8,
padding: '6px 8px',
color: '#47683f',
fontSize: 11,
lineHeight: 1.4,
background: '#f8fbf7',
overflowWrap: 'anywhere',
}}
>
{item}
</code>
))}
</div>
</details>
</div>
</section>
)
}
function IwoooSSocSiemKaliWazuhIntegrationBoard() {
const t = useTranslations('iwooos.socSiemKaliWazuhIntegration')
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
@@ -20592,6 +20775,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
<IwoooSAgentBountySecurityOnboardingBoard />
<IwoooSRolloutRiskReadOnlyBoard />
<IwoooSWazuhIntrusionReadbackBoard />
<IwoooSWazuhLiveMetadataEnvGateBoard />
<IwoooSSocSiemKaliWazuhIntegrationBoard />
<IwoooSSecurityAssetControlLedgerBoard />
<IwoooSExternalHostIntrusionPreventionControlBoard />

View File

@@ -35,6 +35,41 @@
**邊界**:本輪沒有手動 import、沒有 Drive write、沒有 DB restore / truncate、沒有 Docker / Nginx / firewall / K8s / ArgoCD runtime 寫入,沒有 Wazuh / SOC 修改,沒有使用聊天中的密碼,也沒有讀取或保存 secret。
## 2026-06-24IwoooS Wazuh 即時中繼資料環境閘門前台驗證
**背景**Wazuh 只讀 API source-side 修補、release gate、release lane preflight、owner request / acceptance 與 live metadata env gate 已完成,但 production 尚未部署,`/api/iwooos/wazuh` 仍不可用來代表 Wazuh runtime 狀態。本輪補上 IwoooS 前台可視化卡片,讓使用者在 `/zh-TW/iwooos` 直接看到「正式路由讀回、伺服端環境變數負責人、機密中繼資料、管理節點健康、唯讀帳號範圍、啟用後讀回」全部仍為 `0`,避免把 Wazuh 已建置或 UI 可見誤判為可查 live metadata。
**Source-side 完成**
- `apps/web/src/app/[locale]/iwooos/page.tsx` 新增 `IwoooSWazuhLiveMetadataEnvGateBoard`,位置在 Wazuh 入侵 readback 與 SOC / SIEM / Kali 整合卡之間。
- `apps/web/messages/zh-TW.json``apps/web/messages/en.json` 新增同一份繁中鏡像文案;前台文案改為「負責人回覆、路由讀回、機密中繼資料、即時查詢」等產品治理語,不放工作視窗逐字稿、委派 XML、聊天內容或個人英文名稱。
- 卡片邊界固定:`wazuh_live_metadata_env_gate_visible=true``production_route_readback_passed_count=0``live_metadata_owner_response_accepted_count=0``secret_source_metadata_accepted_count=0``wazuh_api_live_query_authorized_count=0``wazuh_active_response_authorized_count=0``runtime_gate_count=0``secret_value_collection_allowed=false``host_write_authorized=false``kali_active_scan_authorized=false`
**驗證**
- `node -e "JSON.parse(...zh-TW...); JSON.parse(...en...)"`:通過。
- `cmp -s apps/web/messages/zh-TW.json apps/web/messages/en.json`:通過,維持繁中鏡像。
- `pytest apps/api/tests/test_iwooos_wazuh_api.py``4 passed`
- `python3 scripts/security/security-mirror-progress-guard.py --root .``SECURITY_MIRROR_PROGRESS_GUARD_OK`
- `python3 scripts/security/wazuh-readonly-route-boundary-guard.py --root .``route=2 public_ui_files=1 forbidden=0 runtime_gate=0`
- `python3 scripts/security/wazuh-readonly-release-gate.py --root .``source=1 push=0 deploy=0 readback=0 runtime_gate=0`
- `python3 scripts/security/wazuh-readonly-release-lane-preflight.py --root .``ready=0 acks=0/6 evidence=0/6 runtime_gate=0`
- `python3 scripts/security/wazuh-readonly-release-owner-request.py --root .``drafts=1 sent=0 accepted=0 runtime_gate=0`
- `python3 scripts/security/wazuh-readonly-release-owner-response-acceptance.py --root .``received=0 accepted=0 acks=0/6 evidence=0/6 runtime_gate=0`
- `python3 scripts/security/wazuh-readonly-live-metadata-env-gate.py --root .``route_readback=0 owner=0 secret_meta=0 live_query=0 runtime_gate=0`
- `pnpm --filter @awoooi/web typecheck`:通過。
- 首次未帶 `NEXT_PUBLIC_API_URL` 的 build 依既有前端禁令在 prerender 階段 fail重跑 `NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PRIVATE_BUILD_WORKER_COUNT=1 SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING=1 pnpm --filter @awoooi/web build` 通過,`92/92` static pages`/zh-TW/iwooos` route size `61.8 kB`、First Load JS `292 kB`
- 本機 preview `http://localhost:3137/zh-TW/iwooos?_v=wazuh-live-metadata-env-local` 桌機寬度:卡片可見、`scrollWidth=clientWidth=1434`、水平溢出 `false`、未命中工作視窗、委派、來源執行緒、外部對話與通知暱稱等內部協作詞;卡片也未命中英文流程裸字。
- 本機 preview 手機寬度 `390x844`:卡片可見、`scrollWidth=clientWidth=384`、水平溢出 `false`,同樣未命中工作視窗字樣與英文流程裸字。
**完成度**
- Wazuh live metadata env gate source / guard`100%`
- IwoooS 前台卡片 source / 本機桌機與手機驗證:`100%`
- Production deploy`0%`
- Production `/api/iwooos/wazuh` readback`0%`,部署前仍只允許 `predeploy_404_observed`
- Wazuh server-side env enable`0%`
- Wazuh live query、active response、host write、Kali active scan、SOAR action全部維持 `0 / false`
**邊界**:本輪沒有 push、沒有 deploy、沒有 Nginx / Docker / K8s / firewall / Wazuh manager / secret / runtime 寫入,沒有 active scan沒有收集或保存任何 secret value只做 source、docs、guard、本機 build 與本機瀏覽器驗證。
## 2026-06-2422:40 MOMO source absence readback 與資料 freshness blocker 定位
**背景**22:17 已完成 MOMO import-boundary production deploy 驗證與 full cold-start refresh但 cold-start 仍因 `MOMO_DAILY_FRESHNESS 7|2026-06-17` 保持 `BLOCKED=1`。本輪只做 MOMO scheduler / DB / import metadata 的 read-only 追查目標是判斷資料停更是服務、版本、DB 同步、排程或來源缺席。

View File

@@ -40,6 +40,9 @@
- `docs/security/wazuh-readonly-release-owner-request.snapshot.json`
- `docs/security/wazuh-readonly-release-owner-response-acceptance.snapshot.json`
- `docs/security/wazuh-readonly-live-metadata-env-gate.snapshot.json`
- `apps/web/src/app/[locale]/iwooos/page.tsx`
- `apps/web/messages/zh-TW.json`
- `apps/web/messages/en.json`
- `docs/LOGBOOK.md`
完成內容:
@@ -57,6 +60,7 @@
- 新增 release lane preflight snapshot 與 guard固定正式 release 前必須選擇 `formal_gitea_merge``formal_patch_apply``maintainer_local_push_with_safe_credential` 其中一條合規 lane且 owner ack / evidence 未到齊前不得 push、deploy、force push、使用明文 token workaround 或改 runtime。
- 新增 release owner request 草稿與 owner response acceptance 帳本,將 required ack flags、required evidence fields、allowed release methods、blocked actions、forbidden payloads 與 reviewer checks 機器可讀化;目前 request sent、response received / accepted、release ready、runtime gate 全部維持 `0`
- 新增 live metadata env gate固定部署後要先通過 production route readback、server-side env owner response、secret source metadata、Wazuh manager health ref、readonly account scope、post-enable readback、rollback 與 no-secret / no-raw-payload attestation目前 live query authorized 仍為 `0`
- 新增 IwoooS 前台「Wazuh 即時中繼資料環境閘門」卡片,公開顯示上述 gate 的 `0 / false` 邊界;文案全部為繁體中文治理語,不放工作視窗逐字稿、委派 XML、聊天內容或個人英文名稱。
## 已完成驗證
@@ -74,6 +78,10 @@ python3 scripts/security/security-mirror-progress-guard.py --root .
python3 scripts/ops/doc-secrets-sanity-check.py docs apps/api/src/api/v1/iwooos.py apps/web/src/app/api/iwooos/wazuh/route.ts scripts/security/wazuh-readonly-route-boundary-guard.py scripts/security/wazuh-readonly-production-readback.py scripts/security/wazuh-readonly-release-gate.py scripts/security/wazuh-readonly-release-lane-preflight.py scripts/security/wazuh-readonly-release-owner-request.py scripts/security/wazuh-readonly-release-owner-response-acceptance.py scripts/security/wazuh-readonly-live-metadata-env-gate.py
python3 -m py_compile apps/api/src/api/v1/iwooos.py scripts/security/wazuh-readonly-route-boundary-guard.py scripts/security/wazuh-readonly-production-readback.py scripts/security/wazuh-readonly-release-gate.py scripts/security/wazuh-readonly-release-lane-preflight.py scripts/security/wazuh-readonly-release-owner-request.py scripts/security/wazuh-readonly-release-owner-response-acceptance.py scripts/security/wazuh-readonly-live-metadata-env-gate.py scripts/security/security-mirror-progress-guard.py
git diff --check
node -e "JSON.parse(require('fs').readFileSync('apps/web/messages/zh-TW.json','utf8')); JSON.parse(require('fs').readFileSync('apps/web/messages/en.json','utf8')); console.log('i18n json ok')"
cmp -s apps/web/messages/zh-TW.json apps/web/messages/en.json
pnpm --filter @awoooi/web typecheck
NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PRIVATE_BUILD_WORKER_COUNT=1 SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING=1 pnpm --filter @awoooi/web build
```
驗證結果:
@@ -89,6 +97,12 @@ git diff --check
- `doc-secrets-sanity-check``DOC_SECRET_SANITY_OK scanned_files=973`
- `py_compile`:通過。
- `git diff --check`:通過。
- i18n JSON parse通過。
- `zh-TW` / `en` messages mirror通過。
- `pnpm --filter @awoooi/web typecheck`:通過。
- Web production build通過`92/92` static pages`/zh-TW/iwooos` route size `61.8 kB`、First Load JS `292 kB`build env 使用公網 `NEXT_PUBLIC_API_URL=https://awoooi.wooo.work`,未使用內網 IP。
- 本機 preview desktop`/zh-TW/iwooos` 卡片可見,水平溢出 `false`,沒有工作視窗 / 委派 / 對話字樣,卡片沒有英文流程裸字。
- 本機 preview mobile `390x844`:卡片可見,水平溢出 `false`,同樣沒有工作視窗 / 委派 / 對話字樣。
## 乾淨套用 Proof
@@ -185,6 +199,7 @@ python3 scripts/security/wazuh-readonly-production-readback.py --json
| Wazuh release lane preflight | `100%` | 已完成owner acks `0/6`、evidence `0/6`、正式 release ready `0` |
| Wazuh release owner request / acceptance | `100%` | 已完成只讀草稿與收件帳本request sent `0`、response accepted `0` |
| Wazuh live metadata env gate | `100%` | 已完成只讀 gateroute readback / owner / secret metadata / live query 仍 `0` |
| IwoooS 前台 Wazuh live metadata env gate 卡片 | `100%` | source-side 與本機桌機 / 手機驗證完成production deploy 仍 `0` |
| 乾淨套用 proof | `100%` | patch set 可落在最新 `gitea/main` 並通過同組 guard最終 hash 以 release 前 readback 為準 |
| Gitea push | `0%` | 受控 workspace HTTPS credential 缺失 |
| Production deploy / readback | `0%` | 等待 release lane |