From 41cb1cf53d89f2b7cc245ece603b5f2b4519b475 Mon Sep 17 00:00:00 2001 From: zhiqiang feng Date: Mon, 29 Dec 2025 13:52:38 +0800 Subject: [PATCH] vault backup: 2025-12-29 13:52:38 --- .obsidian/workspace.json | 23 +- .smart-env/event_logs/event_logs.ajson | 33 +- 2025-12-29.md | 401 +++++++++++++++++++++++++ 3 files changed, 446 insertions(+), 11 deletions(-) create mode 100644 2025-12-29.md diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index d396102..d8e5c22 100755 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -4,17 +4,21 @@ "type": "split", "children": [ { - "id": "9e7e27783759b32f", + "id": "535cd3aee7a6c27d", "type": "tabs", "children": [ { - "id": "2f3ad4ca52c735ee", + "id": "e7acd6d53c22af38", "type": "leaf", "state": { - "type": "empty", - "state": {}, + "type": "markdown", + "state": { + "file": "2025-12-29.md", + "mode": "source", + "source": false + }, "icon": "lucide-file", - "title": "New tab" + "title": "2025-12-29" } } ] @@ -250,8 +254,11 @@ "smart-connections:Smart Connections: Open random connection": false } }, - "active": "2f3ad4ca52c735ee", + "active": "ec72abdaaa8e91a6", "lastOpenFiles": [ + "2025-12-29.md", + "Untitled.base", + "Untitled 1.base", "100-project/Personal/VPS/hk2.chans.xyz.md", "000-inbox/clippings/2024/12/GitHub - DubhAdHome-AssistantConfig My Home Assistant configuration files.md", "100-project/Personal/VPS/https proxy.md", @@ -279,8 +286,6 @@ "100-project/Personal/AI/Kiro/in-memoria.md", "100-project/Personal/AI/Kiro", "100-project/Personal/AI/mcp.md", - "100-project/Personal/AI/DeepSeek.md", - "100-project/Personal/AI/AutoGPT.md", "100-project/Personal/Hardware/Home Assistant", "ReadItLater Inbox/assets/math.png", "ReadItLater Inbox/assets/medal.png", @@ -292,8 +297,6 @@ "Pasted image 20240909145917.png", "100-project/Personal/resume", "copilot/copilot-conversations", - "copilot/copilot-custom-prompts", - "copilot", "ReadItLater Inbox/assets/elec.png", "ReadItLater Inbox/assets/Menu-1.png", "ReadItLater Inbox/assets/bike-1.png", diff --git a/.smart-env/event_logs/event_logs.ajson b/.smart-env/event_logs/event_logs.ajson index 4b265a4..55d0932 100644 --- a/.smart-env/event_logs/event_logs.ajson +++ b/.smart-env/event_logs/event_logs.ajson @@ -15,4 +15,35 @@ "event_logs:sources:import_completed": {"key":"sources:import_completed","ct":1,"first_at":1766986882852,"last_at":1766986882852,"class_name":"EventLog"}, "event_logs:blocks:cleaned": {"key":"blocks:cleaned","ct":1,"first_at":1766986882899,"last_at":1766986882899,"class_name":"EventLog"}, "event_logs:event_log:first": {"key":"event_log:first","ct":12,"first_at":1766986874718,"last_at":1766986884103,"class_name":"EventLog"}, -"event_logs:connections:opened": {"key":"connections:opened","ct":2,"first_at":1766986884102,"last_at":1766986884117,"class_name":"EventLog"}, \ No newline at end of file +"event_logs:connections:opened": {"key":"connections:opened","ct":2,"first_at":1766986884102,"last_at":1766986884117,"class_name":"EventLog"}, +"event_logs:event_log:first": {"key":"event_log:first","ct":13,"first_at":1766986874718,"last_at":1766987075389,"class_name":"EventLog"}, +"event_logs:sources:created": {"key":"sources:created","ct":1,"first_at":1766987075389,"last_at":1766987075389,"class_name":"EventLog","event_sources":{"obsidian:vault.create":1}}, +"event_logs:event_log:first": {"key":"event_log:first","ct":14,"first_at":1766986874718,"last_at":1766987077711,"class_name":"EventLog"}, +"event_logs:sources:created": {"key":"sources:created","ct":2,"first_at":1766987075389,"last_at":1766987077702,"class_name":"EventLog","event_sources":{"obsidian:vault.create":2}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":1,"first_at":1766987077711,"last_at":1766987077711,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":1}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":2,"first_at":1766987077711,"last_at":1766987081391,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":2}}, +"event_logs:event_log:first": {"key":"event_log:first","ct":15,"first_at":1766986874718,"last_at":1766987089766,"class_name":"EventLog"}, +"event_logs:sources:deleted": {"key":"sources:deleted","ct":1,"first_at":1766987089766,"last_at":1766987089766,"class_name":"EventLog","event_sources":{"obsidian:vault.delete":1}}, +"event_logs:sources:deleted": {"key":"sources:deleted","ct":2,"first_at":1766987089766,"last_at":1766987094937,"class_name":"EventLog","event_sources":{"obsidian:vault.delete":2}}, +"event_logs:event_log:first": {"key":"event_log:first","ct":16,"first_at":1766986874718,"last_at":1766987105710,"class_name":"EventLog"}, +"event_logs:sources:created": {"key":"sources:created","ct":3,"first_at":1766987075389,"last_at":1766987105674,"class_name":"EventLog","event_sources":{"obsidian:vault.create":3}}, +"event_logs:sources:opened": {"key":"sources:opened","ct":1,"first_at":1766987105710,"last_at":1766987105710,"class_name":"EventLog","event_sources":{"active-leaf-change":1}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":3,"first_at":1766987077711,"last_at":1766987133412,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":2,"obsidian:workspace.editor-change":1}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":7,"first_at":1766987077711,"last_at":1766987135416,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":3,"obsidian:workspace.editor-change":4}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":16,"first_at":1766987077711,"last_at":1766987140295,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":4,"obsidian:workspace.editor-change":12}}, +"event_logs:sources:imported": {"key":"sources:imported","ct":449,"first_at":1766986877786,"last_at":1766987142184,"class_name":"EventLog"}, +"event_logs:sources:modified": {"key":"sources:modified","ct":24,"first_at":1766987077711,"last_at":1766987142109,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":5,"obsidian:workspace.editor-change":19}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":34,"first_at":1766987077711,"last_at":1766987145892,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":7,"obsidian:workspace.editor-change":27}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":46,"first_at":1766987077711,"last_at":1766987150337,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":9,"obsidian:workspace.editor-change":37}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":56,"first_at":1766987077711,"last_at":1766987152862,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":10,"obsidian:workspace.editor-change":46}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":57,"first_at":1766987077711,"last_at":1766987154420,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":11,"obsidian:workspace.editor-change":46}}, +"event_logs:sources:imported": {"key":"sources:imported","ct":450,"first_at":1766986877786,"last_at":1766987156523,"class_name":"EventLog"}, +"event_logs:sources:modified": {"key":"sources:modified","ct":58,"first_at":1766987077711,"last_at":1766987407748,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":11,"obsidian:workspace.editor-change":47}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":59,"first_at":1766987077711,"last_at":1766987408636,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":11,"obsidian:workspace.editor-change":48}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":60,"first_at":1766987077711,"last_at":1766987409752,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":12,"obsidian:workspace.editor-change":48}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":61,"first_at":1766987077711,"last_at":1766987414884,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":12,"obsidian:workspace.editor-change":49}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":62,"first_at":1766987077711,"last_at":1766987416887,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":13,"obsidian:workspace.editor-change":49}}, +"event_logs:sources:imported": {"key":"sources:imported","ct":451,"first_at":1766986877786,"last_at":1766987420759,"class_name":"EventLog"}, +"event_logs:sources:modified": {"key":"sources:modified","ct":95,"first_at":1766987077711,"last_at":1766987427062,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":14,"obsidian:workspace.editor-change":81}}, +"event_logs:sources:modified": {"key":"sources:modified","ct":96,"first_at":1766987077711,"last_at":1766987428427,"class_name":"EventLog","event_sources":{"obsidian:vault.modify":15,"obsidian:workspace.editor-change":81}}, +"event_logs:sources:imported": {"key":"sources:imported","ct":452,"first_at":1766986877786,"last_at":1766987437279,"class_name":"EventLog"}, \ No newline at end of file diff --git a/2025-12-29.md b/2025-12-29.md new file mode 100644 index 0000000..eb790ca --- /dev/null +++ b/2025-12-29.md @@ -0,0 +1,401 @@ + + +# Soft Serve 安装指南(Docker Compose + Traefik TCP + CNAME) + +## 1. 目标与最终形态 + +- 域名:`repo.windy.me` + +- DNS:`repo.windy.me` **CNAME → `us2.wsvc.info`** + +- 部署主机:`us2.wsvc.info` 对应的 VPS(本文称 “us2”) + +- Soft Serve 镜像:`ghcr.io/charmbracelet/soft-serve:latest` + +- 数据持久化:宿主机 `./data` → 容器 `/var/lib/soft-serve` + +- 访问方式:SSH(Soft Serve SSH 服务端口为容器内 `23231`) + +- 暴露方式(推荐):Traefik TCP entrypoint `ssh` 监听宿主机 `2222`,转发到容器 `23231` + + +--- + +## 2. 前置条件清单 + +### 2.1 DNS(CNAME) + +你已设置: + +- `repo.windy.me` CNAME → `us2.wsvc.info` + + +关键含义: + +- 用户访问 `repo.windy.me` 时,最终会解析到 **us2 的公网 IP** + +- 只要 us2 上对外开放 SSH 入口端口(示例:2222),访问就成立 + + +建议验证(任意机器): + +```bash +dig +short repo.windy.me CNAME +dig +short repo.windy.me A +``` + +--- + +### 2.2 网络与防火墙 + +在 us2 上确保对外放行你用于 Soft Serve SSH 的端口(示例 2222): + +- 入站允许:TCP 2222 + + +--- + +### 2.3 Traefik 已存在并使用外部网络 + +你当前 compose 使用: + +- external network:`vw-net` + + +确保 Traefik 容器也在同一个 `vw-net` 网络内。 + +--- + +## 3. 准备目录与配置文件 + +在 us2 上: + +```bash +mkdir -p /opt/soft-serve +cd /opt/soft-serve +mkdir -p data +``` + +最终结构: + +``` +/opt/soft-serve/ + compose.yml + .env + data/ +``` + +--- + +## 4. 准备初始化管理员公钥(必须) + +Soft Serve 首次启动会根据环境变量写入初始管理员 key。你已经验证的公钥写法如下(单行): + +`.env`: + +```env +SOFT_SERVE_INITIAL_ADMIN_KEYS=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9irsGu03p+1xrwIfzrzjGZCcExJ/XFEgkqsgfEN70j windy@windy-mbp +``` + +注意事项: + +- 必须是 **完整公钥的一整行** + +- 只在 **数据目录首次初始化(空目录)** 时生效 + + +--- + +## 5. Docker Compose(推荐:Traefik TCP 暴露 SSH) + +`compose.yml`(与你当前成功的结构一致,并保留注释): + +```yaml +services: + soft-serve: + image: ghcr.io/charmbracelet/soft-serve:latest + container_name: soft-serve + restart: unless-stopped + + environment: + SOFT_SERVE_DATA_PATH: /var/lib/soft-serve + SOFT_SERVE_INITIAL_ADMIN: windy + SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS} + + volumes: + - ./data:/var/lib/soft-serve + + # 方案B:直连端口映射(不走 Traefik) + # ports: + # - "2222:23231" + + networks: + - traefik + + labels: + - traefik.enable=true + + # SSH over TCP via Traefik (entryPoint ssh -> container port 23231) + - traefik.tcp.routers.softserve-ssh.entrypoints=ssh + - traefik.tcp.routers.softserve-ssh.rule=HostSNI(`*`) + - traefik.tcp.routers.softserve-ssh.tls=false + - traefik.tcp.services.softserve-ssh.loadbalancer.server.port=23231 + +networks: + traefik: + external: true + name: vw-net +``` + +### 关于 “SSH 不能走 Traefik 代理域名分流”的结论 + +- SSH 不是 HTTP;Traefik 在这里是 **TCP 转发** + +- 不要使用 `HostSNI(repo.windy.me)` 之类的规则来“按域名”分流 SSH(会引发 TLS/HostSNI 相关报错) + +- 最稳妥的做法就是: + + - `tls=false` + + - `HostSNI('*')` + + - 依赖端口入口(2222) + + +--- + +## 6. Traefik 静态配置要求(必须有 entrypoint) + +你必须在 Traefik 的静态配置中定义 `ssh` entrypoint,并监听对外端口(示例:2222)。 + +示例(只示意关键段): + +```yaml +entryPoints: + ssh: + address: ":2222" +``` + +如果缺失,会出现典型错误: + +- `EntryPoint doesn't exist entryPointName=ssh` + + +--- + +## 7. 首次启动与“只初始化一次”的规则 + +### 7.1 首次启动 + +在 `/opt/soft-serve`: + +```bash +docker compose up -d +docker compose ps +``` + +### 7.2 初始化只发生一次(关键规则) + +如需重新初始化(比如 `.env` 修改后不生效),必须清空数据目录: + +```bash +docker compose down +rm -rf ./data +mkdir -p ./data +docker compose up -d +``` + +--- + +## 8. 客户端连接与“user not found”修正方法 + +### 8.1 强制使用指定 key(排错与首次推荐) + +你最终验证成功的关键点是:**固定 key + IdentitiesOnly**。 + +```bash +ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me info +``` + +若成功会输出类似: + +``` +Username: admin (或 windy) +Admin: true +Public keys: ... +``` + +### 8.2 把默认用户名从 `admin` 改成 `windy` + +你已成功的改名命令(注意同样要固定 key): + +```bash +ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me set-username windy +ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me info +``` + +**解释:**“user not found” 的真实根因通常不是 Soft Serve 没用户,而是 SSH 客户端未固定 key 时选用了另一把 key,导致 Soft Serve 无法把该连接映射到已存在的用户。 + +### 8.3 永久固化:写 `~/.ssh/config` + +在本机写入: + +```sshconfig +Host repo.windy.me + HostName repo.windy.me + Port 2222 + User git + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes +``` + +之后即可: + +```bash +ssh repo.windy.me info +ssh repo.windy.me repo list +``` + +--- + +## 9. 创建仓库与 Git clone/push + +### 9.1 创建仓库 + +```bash +ssh repo.windy.me repo create test +ssh repo.windy.me repo list +``` + +### 9.2 Clone(推荐写法) + +写法 A(最清晰): + +```bash +git clone ssh://repo.windy.me:2222/test.git +``` + +写法 B(scp 风格,依赖 ssh config 的 Port): + +```bash +git clone repo.windy.me:test.git +``` + +### 9.3 Push 验证 + +```bash +cd test +echo "# test" > README.md +git add . +git commit -m "init" +git push +``` + +--- + +## 10. 常见故障排查(快速定位) + +### 10.1 连接到错误端口 + +现象:你以为是 23231,但实际对外是 2222(由 Traefik entrypoint 决定)。 + +验证(在 us2 上): + +```bash +ss -lntp | grep :2222 +``` + +应看到 Traefik 监听 2222。 + +--- + +### 10.2 `EntryPoint doesn't exist entryPointName=ssh` + +原因:Traefik 静态配置未定义 `entryPoints.ssh`。 + +修复:给 Traefik 增加: + +```yaml +entryPoints: + ssh: + address: ":2222" +``` + +并重启 Traefik。 + +--- + +### 10.3 `Error: user not found` + +高概率原因:SSH 客户端用了“另一把 key”。 + +修复(强制固定 key): + +```bash +ssh -vvv -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me info +``` + +观察日志中是否出现: + +- `Offering public key: ... id_ed25519` + +- `Server accepts key: ... id_ed25519` + + +--- + +## 11. 备份与恢复(生产建议) + +### 11.1 需要备份的内容 + +Soft Serve 核心数据都在宿主机 `./data`(映射自 `/var/lib/soft-serve`): + +- `soft-serve.db`(用户/设置) + +- `repos/`(仓库数据,如存在) + +- `ssh/`(host keys 等) + + +### 11.2 最简单备份命令 + +在 us2 上: + +```bash +cd /opt/soft-serve +tar -czf soft-serve-backup-$(date +%F).tar.gz ./data +``` + +恢复流程: + +1. `docker compose down` + +2. 解压覆盖 `./data` + +3. `docker compose up -d` + + +--- + +## 12. 推荐的“最终检查清单” + +- `repo.windy.me` CNAME 指向 `us2.wsvc.info`,并能解析到 us2 IP + +- us2 对外开放 TCP 2222 + +- Traefik 静态配置存在 `entryPoints.ssh=:2222` + +- Soft Serve 数据目录持久化:`./data:/var/lib/soft-serve` + +- 客户端 `~/.ssh/config` 固定 `IdentityFile` + `IdentitiesOnly yes` + +- `ssh repo.windy.me info` 输出 `Username: windy` 且 `Admin: true` + + +--- + +如果你希望我把这份指南进一步“落地化”,我可以再给你两个附录(可选): + +1. **一份生产级 compose**(healthcheck、日志限制、只读 filesystem、资源限制) + +2. **一份 Traefik 静态配置片段**(与你当前 Traefik 容器的真实配置方式对齐:file provider / command args / labels) \ No newline at end of file