# knowledge_base — 主機上替筆記本服務的配套

筆記本身、使用守則、進度紀錄都在 `~/data/vault/`（**守則 `~/data/vault/AGENTS.md`，先讀它**）。
這個資料夾只放跟這台機器綁在一起的東西：發布工具、自動化設定、搬家時用過的腳本。
**不放筆記，沒有 STATUS**：做了什麼記 vault 的 `log.md`，決定與回顧寫 vault 的 `journal/`。

## 這裡有什麼

```
AGENTS.md        指路（三行）
DESIGN.md        本檔
build_site.py    vault → 靜態網站
backup_knowledge_base.py / restore_knowledge_base.py   每週加密備份到雲端硬碟、還原（見「備份」節）
requirements.txt / .venv/   只有備份工具用（.venv 可重建）
scripts/
  gen_index.py               重建 vault/index.md（服務自動跑；手動 python3 scripts/gen_index.py）
  make_share_pdf.py          某篇筆記的分享版 PDF（清洗個資後轉 PDF）
  extract_session_images.py  從 Claude Code session 撈使用者上傳的圖到 inbox/
  todo-due.sh                列 vault/todo.md 過期、今天、三天內到期的 to-do（root agent 用）
  migration/                 搬舊筆記系統的一次性腳本，留著備查
systemd/         kb-site.{service,path,timer}、kb-site-failure.service、knowledge-base-backup.{service,timer} 的正本（link 進 ~/.config/systemd/user/）
share/<topic>/   公開分享頁的產物
inbox/           使用者丟檔案的窗口（有東西＝還沒處理；處理完封存到 sources/raw、清空）
work/            暫存與搬家時的中間產物，整個可刪
```

## 網頁版（VPN 內）

- 網址 `http://<tailnet-ip>/knowledge_base/`。
- `build_site.py` 讀 `~/data/vault` 輸出到 `~/projects/infra/caddy/sites/knowledge_base/`（INFRA.md 情境 B）。約 1 秒，冪等。
- 有 wiki／archive／登錄簿／journal／標籤／全文搜尋／守則，和「待校對」頁（status draft、frontmatter 壞掉、`[[ ]]` 斷鏈），給使用者把關用。不輸出 sources/staging。
- 首頁「最新更新」列 10 篇，排除 tags 含 `worklog`（工作日誌）的，太頻繁。>200KB 的圖產縮圖（手機在外面開太重）。

## 自動重建（systemd --user）

- `kb-site.path` 監看 vault 各層資料夾（path unit 不遞迴；**vault 新增子資料夾要補進去並 `daemon-reload`**）；`kb-site.timer` 每 15 分鐘保險；失敗推告警 topic（標題帶 knowledge_base）。
- `kb-site.service`：sleep 3 秒合併連續寫入 → `gen_index.py`（index.md 內容沒變就不寫，避免自己觸發自己）→ `build_site.py`。
- 單元檔正本在 `systemd/`，用 `systemctl --user link <絕對路徑>` 掛進去；改了要 `daemon-reload`。
- 查：`systemctl --user status kb-site.path kb-site.timer`、`journalctl --user -u kb-site.service -n 30`。

## 同步（WebDAV）

設定正本在 `~/projects/INFRA.md`：`infra/webdav/` 容器把 `~/data/vault` 掛在 `127.0.0.1:<port>`，tailscale serve 到 tailnet。
手機 Obsidian 用 Remotely Save（Remote Base Directory 填 `vault`）。帳密 `~/.config/knowledge-base-webdav/.env`。
Remotely Save 會在 vault 根目錄留 `_remotely-save-metadata-on-remote.*`，build_site 已略過。

## 分享版 PDF

`python3 scripts/make_share_pdf.py`：清洗（整節刪帳密節與遠端管理節、個人裝置名改泛稱）→ pandoc → odt → libreoffice → pdf，
成品 `vault/attachments/<主題>-報告-<updated 日期>.pdf`，舊版刪、筆記裡的連結自動改。筆記改了 PDF 不會自動重產，要手動跑。
坑：手機照片靠 EXIF 標旋轉，LibreOffice 不理會、會橫躺；照片進 attachments 前用 PIL `ImageOps.exif_transpose` 烤進像素。
成品帶日期檔名，因為同名覆蓋後手機仍會開舊快取。

## 公開分享頁（Cloudflare Pages）

某次活動筆記的分享版＋zip。`share/<topic>/pkg/`＝zip 內容、`site/`＝上傳目錄。
重做：改 pkg、重新 zip 到 site/，然後 `npx wrangler pages deploy site --project-name=<name> --branch=main --commit-dirty=true`。token 見 INFRA.md 情境 E。

## 備份（雲端硬碟，每週）

筆記本沒有 git、沒有雲端副本（手機的 WebDAV 同步只是工作副本，本機誤刪會同步過去），所以雲端備份是唯一的異地備份。
做法跟其他專案同一套（INFRA.md §5）：7z + AES-256、連檔名加密，密碼共用（也要記在人腦）；覆蓋前舊版留 `archive/`（永不自動刪）。

- 包：`vault-notes`（筆記，每週重做，幾 MB）、`vault-attachments`、`sources-<來源>`、`sources-uploads-YYYY-MM`（一月一包）、`project`（本資料夾的腳本與設計）、`share`。
  除了 vault-notes 與 project，其他沒變就略過（指紋＝路徑＋大小＋mtime）。不備份 `work/`、`inbox/`、`.venv/`、網頁版輸出。
- 包內路徑相對於家目錄，全部解到同一個空資料夾就拼回原樣。
- 手動：

```bash
cd ~/projects/knowledge_base
.venv/bin/python backup_knowledge_base.py plan          # 看要做什麼，不動東西
.venv/bin/python backup_knowledge_base.py run --auto    # 用密碼檔跑一次（同排程）
.venv/bin/python restore_knowledge_base.py --list       # 看雲端上有哪些包
.venv/bin/python restore_knowledge_base.py --dest ~/restore --only vault-notes   # 只還原筆記
```

- 還原到還在跑 WebDAV 的機器：先停 WebDAV 容器、手機別同步，搬完再開。
- 坑（第一次跑踩的）：googleapiclient 預設 socket 逾時 60 秒，大包最後一塊送完雲端要處理一陣子才回應，
  一百多 MB 那包連續四次死在 `TimeoutError`。解法是 `socket.setdefaulttimeout(600)` 再 `build()`；
  **不要自己 new `httplib2.Http` 塞進去**，會少掉 googleapiclient 對 308 續傳回應的處理、炸 `RedirectMissingLocation`。
  上傳失敗改成用同一個 request 續傳，不從頭重來。

## 對話裡的圖片

Claude Code 對話裡貼的圖不會落地成檔案，`python3 scripts/extract_session_images.py inbox/<主題>` 從 session jsonl 撈（依內容 hash 去重、會比對 sources/raw/uploads 的封存檔）。撈出的檔名看腳本輸出，別用 ls 猜。

## 其他

- pandoc 在 `~/.local/bin/pandoc`（官方靜態檔，不用 sudo）。
- 沒有 git。
- 遷移之前的 STATUS／DESIGN 在 `work/old-docs/`；當時的決定與經過已併進 vault `journal/`。
