[{"content":"本机开发环境：Pi 网关、Pi-Switch 与 Incus 隔离容器 本文适用于需要在本机同时满足“模型统一路由”与“开发环境隔离”的场景。你会学到：本机 Arch + Incus + Pi + Pi-Switch 网关的整体拓扑、各组件职责与真实配置、以及从宿主机到容器内可复现的验证路径。\n本文基于 2026-08-31 本机实测整理，敏感值已脱敏（API Key、真实域名以占位符展示），以本机 incus list / ~/.pi/agent/settings.json / ~/.pi-switch/config.json 实测为准。\n适用场景 你在裸机上跑 pi、opencode 等 AI 编码代理，担心依赖污染宿主机，或多个项目需要独立文件系统/网络 模型 Key 分散在多个上游（OpenAI、DeepSeek、私有网关），需要在本地统一入口做模型名路由、故障转移与用量统计 需要一套“宿主机可写、容器内也可写”的共享目录，且不踩 nobody 65534 / chown: Invalid argument 的坑 整体架构 flowchart LR subgraph Host[\u0026#34;宿主机 Arch Linux 7.1.10-zen1\u0026#34;] PI[\u0026#34;pi 0.84.4\\n~/.pi/agent\u0026#34;] PS[\u0026#34;Pi-Switch 网关\\n127.0.0.1:43112\\nproviderPrefix=pi-switch\u0026#34;] INCUS[\u0026#34;Incus 7.3 / LXC 7.0.0\\ndir 存储 / nftables\u0026#34;] end subgraph Container[\u0026#34;Incus 容器 arch\\n10.10.10.85\u0026#34;] CPI[\u0026#34;pi 0.84.4\\ndefaultProvider=pi-switch\u0026#34;] end UP[\u0026#34;上游模型\\nhttps://opencode.ai/zen/go/v1\\nDeepSeek / Kimi / GLM\u0026#34;] PROJ[\u0026#34;共享目录\\n/home/shial/Project ↔ /home/arch/Project\\nshift=true\u0026#34;] CPI --\u0026gt;|hostproxy 127.0.0.1:43112| PS --\u0026gt; UP PI -.-\u0026gt;|宿主机直连| PS INCUS --- PROJ Container --- PROJ 组件 职责 本机形态 pi Agent 运行时、会话/扩展/技能管理 fnm + npm 全局安装 @earendil-works/pi-coding-agent，配置集中在 ~/.pi/agent/ Pi-Switch Provider 管理 + 模型名网关（路由/转换/故障转移） 本地 Rust 网关 0.0.0.0:43112，WebUI 127.0.0.1:43110，providerPrefix=pi-switch 写入 ~/.pi/agent/models.json Pi 网关 协议转换与统一端点 由 Pi-Switch 承担：openai-responses API、无状态按 profile/model 路由、SSE 透传、断路器 Incus 非特权容器隔离 3 容器（arch RUNNING，其余模板 STOPPED），shift=true 共享目录，hostproxy 透出网关端口到容器 数据流：容器内 pi --provider pi-switch --model oc/muse-spark-1.2-contributor → 127.0.0.1:43112（hostproxy）→ Pi-Switch 按模型名选 profile oc → https://opencode.ai/zen/go/v1/responses → 上游。\n主机基座 本机为 Arch Linux（7.1.10-zen1-1-zen），Incus 7.3 + LXC 7.0.0，storage: dir，firewall: nftables，api_extensions_count: 541。\nNode 与 pi：\nnode -v # v24.16.0（由 fnm 管理，路径 ~/.local/share/fnm） pi --version # 0.84.4 pi 通过 npm install -g --ignore-scripts @earendil-works/pi-coding-agent 安装，可执行文件在 ~/.local/share/fnm/node-versions/v24.16.0/installation/bin/pi，切 Node 版本后需在对应版本重装。\n~/.pi/agent/ 核心布局（本机实测）：\n~/.pi/agent/ ├── settings.json # 全局设置（defaultProvider、theme、packages） ├── models.json # 由 Pi-Switch 网关发布写入 ├── sessions/ # 会话 JSONL，按工作目录分树 ├── state/workspace-history ├── extensions/、skills/ # 本地扩展与技能 └── auth.json # 凭证，勿提交 宿主机 ~/.pi/agent/settings.json 关键项（脱敏）：\n{ \u0026#34;defaultProvider\u0026#34;: \u0026#34;opencode-go\u0026#34;, \u0026#34;defaultModel\u0026#34;: \u0026#34;muse-spark-1.2-contributor\u0026#34;, \u0026#34;defaultThinkingLevel\u0026#34;: \u0026#34;xhigh\u0026#34;, \u0026#34;theme\u0026#34;: \u0026#34;front-end-delight\u0026#34;, \u0026#34;packages\u0026#34;: [\u0026#34;../../Project/pi-switch\u0026#34;, \u0026#34;npm:pi-mcp-adapter\u0026#34;, \u0026#34;...\u0026#34;] } 宿主机 defaultProvider 为 opencode-go，但已通过本地包 ../../Project/pi-switch 接入 Pi-Switch；容器内 ~/.pi/agent/settings.json 则直接 defaultProvider: pi-switch。\nPi 网关（由 Pi-Switch 提供） 本机没有独立的 AxonHub 实例，Pi 网关即 Pi-Switch 的本地代理。这是本机与早期 ai/axonhub-* 文档（Docker + SQLite 8090 方案）的差异：AxonHub 仍可作为可选网关，但当前日常链路为 Pi-Switch。\nPi-Switch 本质是轻量 profile 切换器 + 模型名网关（Rust 核心 + TUI/CLI/WebUI 三端同源）：\nProvider 管理：CRUD、多上游 upstreams[]、模型暴露 expose 到 pi、Responses 透传/转换 网关：独立进程，无状态按 profile/model 路由，SSE 流式、User-Agent 伪装、OpenAI ↔ Anthropic、Responses ↔ Chat Completions 互转、故障转移与断路器 发布模型：显式 发布到 Pi 才会写入 ~/.pi/agent/models.json，非自动同步 本机 ~/.pi-switch/config.json（脱敏后节选）：\n{ \u0026#34;current\u0026#34;: \u0026#34;oc\u0026#34;, \u0026#34;profiles\u0026#34;: { \u0026#34;oc\u0026#34;: { \u0026#34;api\u0026#34;: \u0026#34;openai-responses\u0026#34;, \u0026#34;baseUrl\u0026#34;: \u0026#34;https://opencode.ai/zen/go/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;sk-***\u0026#34;, \u0026#34;exposedModels\u0026#34;: [\u0026#34;muse-spark-1.2-contributor\u0026#34;, \u0026#34;mimo-v2.5\u0026#34;, \u0026#34;hy3\u0026#34;, \u0026#34;kimi-k2.5\u0026#34;, \u0026#34;glm-5.2\u0026#34;], \u0026#34;models\u0026#34;: [\u0026#34;deepseek-v4-flash\u0026#34;, \u0026#34;mimo-v2.5\u0026#34;, \u0026#34;...共 30+ 个\u0026#34;] } }, \u0026#34;settings\u0026#34;: { \u0026#34;providerPrefix\u0026#34;: \u0026#34;pi-switch\u0026#34;, \u0026#34;gatewayApi\u0026#34;: \u0026#34;openai-responses\u0026#34;, \u0026#34;proxy\u0026#34;: { \u0026#34;host\u0026#34;: \u0026#34;0.0.0.0\u0026#34;, \u0026#34;port\u0026#34;: 43112, \u0026#34;failover\u0026#34;: [\u0026#34;oc\u0026#34;] }, \u0026#34;web\u0026#34;: { \u0026#34;host\u0026#34;: \u0026#34;127.0.0.1\u0026#34;, \u0026#34;port\u0026#34;: 43110 } } } 本机运行态：\npi-switch proxy status # Proxy daemon is running (PID 526553) # Listen: http://0.0.0.0:43112 # Failover: oc pi-switch provider list # * oc [p1] api: openai-responses baseUrl: https://opencode.ai/zen/go/v1 # models: deepseek-v4-flash, mimo-v2.5, hy3, kimi-k2.5, glm-5.2, ... pi-switch config show | head -n 20 参考：Pi-Switch GitHub 与本地 ~/Project/pi-switch/README.md、CONTEXT.md。\nPi-Switch Pi-Switch 在 pi 侧以本地包 ../../Project/pi-switch 形式安装，并通过 extensions/index.ts 注入会话归因头（x-conversation-id / x-opencode-session），用于网关侧的 requests.log 聚合与 WebUI 统计。\n配置入口：pi-switch tui（推荐）或 pi-switch webui start --daemon（浏览器 http://127.0.0.1:43110） 诊断：pi-switch doctor 检查 config.json / models.json / 结构完整性 统计：每次代理请求追加写入 ~/.pi-switch/requests.log（JSONL），WebUI 按 today/last24h/last7d/custom 聚合四维度 token（input/output/cached/reasoning）与缓存命中率 断路器：failureThreshold: 3、cooldownSeconds: 60，半开探测恢复 宿主机与容器协同：宿主机网关监听 0.0.0.0:43112，Incus 通过 hostproxy 设备将该端口透入容器，容器内 127.0.0.1:43112 即宿主机网关，无需额外网络打洞。\nIncus 隔离环境 容器清单（2026-08-31 实测，脱敏） +-----------------+---------+--------------------+------+-----------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-----------------+---------+--------------------+------+-----------+-----------+ | arch | RUNNING | 10.10.10.85 (eth0) | | CONTAINER | 0 | | debian-template | STOPPED | | | CONTAINER | 0 | | ubuntu-template | STOPPED | | | CONTAINER | 0 | +-----------------+---------+--------------------+------+-----------+-----------+ incus info：server_version: 7.3、driver: lxc | qemu 7.0.0 | 11.1.0、kernel: 7.1.10-zen1-1-zen。\n容器 arch 规格：limits.cpu: 8、limits.memory: 16GiB、security.nesting: true、security.protection.shift: true。\n非特权映射与共享目录 容器为非特权，volatile.idmap.current：\n[{\u0026#34;Isuid\u0026#34;:true,\u0026#34;Hostid\u0026#34;:165536,\u0026#34;Nsid\u0026#34;:0,\u0026#34;Maprange\u0026#34;:65536}, {\u0026#34;Isgid\u0026#34;:true,\u0026#34;Hostid\u0026#34;:165536,\u0026#34;Nsid\u0026#34;:0,\u0026#34;Maprange\u0026#34;:65536}] 容器内：\ncat /proc/self/uid_map # 0 165536 65536 含义：容器 UID 0 → 宿主机 165536，容器 1000 → 宿主机 166536，以此类推。此前排障沉淀见 Incus 非特权容器 UID/GID 映射与共享目录排障。\n本机当前采用 shift=true（Incus shiftfs）而非手动 chown 166536:175536：\ndevices: Project: path: /home/arch/Project source: /home/shial/Project shift: \u0026#34;true\u0026#34; type: disk hostproxy: bind: container connect: tcp:127.0.0.1:43112 listen: tcp:127.0.0.1:43112 type: proxy 效果：\n宿主机 id shial：uid=1000 gid=1000 groups=...175536(project_shared)，目录 drwxr-xr-x shial:shial /home/shial/Project 容器内 ls -ln /home/arch/Project 显示 1000:1000，nobody 65534 不再出现，Invalid argument 不再触发 宿主机已保留 project:x:166536:175536 与 project_shared:x:175536:shial 供回退到手动对齐方案时使用；但 shift=true 下无需 sudo chown -R 166536:175536 流水：宿主机 /home/shial/Project --shiftfs--\u0026gt; 容器 /home/arch/Project，双向读写即时可见。\n端到端工作流 以容器内发起一次 pi 请求为例（全程可复现）：\n# 1. 宿主机：确认网关 pi-switch proxy status # Listen: http://0.0.0.0:43112 # 2. 宿主机：确认 Incus incus list incus config show arch | grep -A2 hostproxy # 3. 容器内：确认影子映射 incus exec arch -- bash -c \u0026#34;cat /proc/self/uid_map; id\u0026#34; # 4. 容器内：经网关发起请求（模型名含 profile 前缀） incus exec arch -- bash -c \u0026#34;pi --provider pi-switch --model oc/muse-spark-1.2-contributor -p \u0026#39;hello via pi-switch\u0026#39;\u0026#34; # 5. 宿主机：查看聚合 tail -n 1 ~/.pi-switch/requests.log | python3 -m json.tool # 含 conversationId、model、provider、promptTokens、costTotal # 6. 宿主机：WebUI 统计 # 浏览器打开 http://127.0.0.1:43110 → Stats 页按 today 聚合 ~/.pi-switch/requests.log 示例（脱敏，单行 JSONL）：\n{\u0026#34;conversationId\u0026#34;:\u0026#34;01a05616-4078-7e11-9972-2f1af750b73b\u0026#34;,\u0026#34;model\u0026#34;:\u0026#34;muse-spark-1.2-contributor\u0026#34;,\u0026#34;provider\u0026#34;:\u0026#34;oc\u0026#34;,\u0026#34;promptTokens\u0026#34;:125856,\u0026#34;completionTokens\u0026#34;:107,\u0026#34;ok\u0026#34;:true,\u0026#34;status\u0026#34;:200} 排障与常见问题 现象 排查 nobody / 65534 容器内 cat /proc/self/uid_map，确认 0 165536 65536；若用手动方案则检查 Host UID = 165536 + Container UID 是否对齐，参考 排障记录 chown: Invalid argument 同上，宿主机 UID 未落在映射范围；或 shift=false 时未 chown -R 166536:175536 宿主机改不动容器写入的文件 id shial 是否含 project_shared，ls -ld /home/shial/Project 是否 g+rwX 且 g+s（手动方案） pi-switch proxy status 未运行 pi-switch proxy start --daemon，查 ~/.pi-switch/proxy.log 容器内 curl 127.0.0.1:43112 不通 incus config show arch 是否含 hostproxy，宿主机 `ss -tlnp 模型 401 / 429 pi-switch config show 中 baseUrl/apiKey 脱敏核对，requests.log 看 status/error，触发断路器时等待 60s 冷却 验证与自查 pi --version 输出 0.84.4，pi list 与 ~/.pi/agent/settings.json 的 packages 一致 pi-switch proxy status 显示 running 且 Listen: http://0.0.0.0:43112 incus list 中 arch RUNNING 10.10.10.85，incus exec arch -- cat /proc/self/uid_map 为 0 165536 65536 宿主机 id shial 含 project_shared，ls -ld /home/shial/Project 可写 容器内 ls -ln /home/arch/Project 非 65534，touch /home/arch/Project/.test \u0026amp;\u0026amp; ls /home/shial/Project/.test 双向可见 容器内 pi --provider pi-switch --model oc/muse-spark-1.2-contributor -p \u0026quot;hello\u0026quot; 成功返回，并在 ~/.pi-switch/requests.log 留痕 hugo --quiet --ignoreCache 构建零错误，本文在 http://localhost:1313/ops/dev-env-pi-gateway-incus/ 渲染正常，Mermaid 可见 参考 Pi 官网 与 Pi GitHub Pi-Switch GitHub（本地 ~/Project/pi-switch/README.md、CONTEXT.md） Incus 官方文档 与 Arch Wiki - Incus AxonHub GitHub（本机未启用，仅作网关选型参考） 本站：Pi 本机安装与使用、Incus 非特权容器 UID/GID 映射排障 ","permalink":"https://heihei0299.github.io/posts/ops/dev-env-pi-gateway-incus/","summary":"\u003ch1 id=\"本机开发环境pi-网关pi-switch-与-incus-隔离容器\"\u003e本机开发环境：Pi 网关、Pi-Switch 与 Incus 隔离容器\u003c/h1\u003e\n\u003cp\u003e本文适用于需要在本机同时满足“模型统一路由”与“开发环境隔离”的场景。你会学到：本机 Arch + Incus + Pi + Pi-Switch 网关的整体拓扑、各组件职责与真实配置、以及从宿主机到容器内可复现的验证路径。\u003c/p\u003e","title":"本机开发环境：Pi 网关、Pi-Switch 与 Incus 隔离容器"},{"content":"Incus 非特权容器 UID/GID 映射与共享目录权限问题排障记录 1. 环境 宿主机：\nArch Linux Kernel：7.1.6-zen1-1-zen Incus：7.3 LXC：7.0.0 宿主机用户：\nuid=1000(shial) gid=1000(shial) Incus 容器：\narch 容器内部用户：\nuid=1000(arch) gid=1000(arch) 共享目录：\n宿主机：/home/shial/Project 容器：/Project 2. 最初遇到的问题 创建 Incus 容器时出现：\nInstance creation failed Failed creating instance record: Failed initializing instance: System doesn\u0026#39;t have a functional idmap setup 检查宿主机：\ncat /etc/subuid cat /etc/subgid 结果：\nshial:100000:65536 同时确认：\nsysctl kernel.unprivileged_userns_clone 结果：\nkernel.unprivileged_userns_clone = 1 确认 User Namespace：\nzgrep CONFIG_USER_NS /proc/config.gz 结果：\nCONFIG_USER_NS=y CONFIG_USER_NS_UNPRIVILEGED=y 确认：\nwhich newuidmap which newgidmap 结果：\n/usr/bin/newuidmap /usr/bin/newgidmap 并且：\ngetcap /usr/bin/newuidmap getcap /usr/bin/newgidmap 结果：\n/usr/bin/newuidmap cap_setuid=ep /usr/bin/newgidmap cap_setgid=ep 最终通过日志发现真正的问题：\nUnable to parse system idmap err=\u0026#34;No map found for user\u0026#34; Incus 服务使用的是宿主机用户 shial，因此需要确保该用户存在正确的 UID/GID subordinate 映射。\n最终确认并修复 UID/GID 映射后，容器可以正常启动。\n3. Incus UID/GID 映射 容器启动后检查：\ncat /proc/self/uid_map cat /proc/self/gid_map 结果：\n0 165536 65536 含义：\n容器 UID 0 ↓ 宿主机 UID 165536 映射范围： 宿主机 UID 165536-231071 因此：\n容器 UID 0 → 宿主机 UID 165536 容器 UID 1000 → 宿主机 UID 166536 计算方式：\n165536 + 1000 = 166536 GID 同理。\n4. 共享目录出现 nobody 容器中查看 /Project：\nls -ln /Project 大量文件显示：\n65534 普通 ls 则显示：\nnobody 例如：\ndrwxr-xr-x nobody anytls-go-script drwxr-xr-x nobody Aur drwxr-xr-x nobody conductor 某个文件：\n-rw-r--r-- 65534 22 parseUtil.cjs 原因：\n宿主机原来的文件 owner UID，例如：\n1000 不在容器的 UID 映射范围：\n165536-231071 所以这个 UID 无法映射到容器中的正常 UID。\n容器最终只能显示：\n65534 = nobody 5. chown 出现 Invalid argument 在容器中执行某些开发工具、npm、node_modules 操作时出现：\nchown: changing ownership of \u0026#39;...\u0026#39;: Invalid argument 例如：\nchown: changing ownership of \u0026#39;Project/project-init/test/.opencode/node_modules/zod/...\u0026#39;: Invalid argument 根本原因也是 UID/GID 映射。\n容器无法把某些宿主机 UID 映射成合法的容器 UID，因此对这些文件执行 chown 时会失败。\n6. 采用 UID/GID 对齐方案 参考：\nhttps://blog.kye.dev/proxmox-zfs-mounts/\n核心思想：\n为共享目录创建一个宿主机 UID/GID，使其能够准确映射到容器中的 UID/GID。\n本环境映射起点：\n165536 容器中的目标用户：\nUID 1000 所以宿主机对应 UID：\n165536 + 1000 = 166536 选择容器中的共享组：\nGID 10000 因此宿主机对应 GID：\n165536 + 10000 = 175536 最终建立：\n宿主机 容器 UID 166536 ───────────────→ UID 1000 GID 175536 ───────────────→ GID 10000 7. 宿主机创建映射用户和组 宿主机：\nsudo groupadd -g 175536 project_shared 创建用户：\nsudo useradd \\ -u 166536 \\ -g 175536 \\ -M \\ -s /usr/bin/nologin \\ project 检查：\ngetent passwd project getent group project_shared 应该类似：\nproject:x:166536:175536::/home/project:/usr/bin/nologin project_shared:x:175536: 8. 修改共享目录 owner 共享目录：\n/home/shial/Project 宿主机执行：\nsudo chown -R 166536:175536 /home/shial/Project 这样宿主机文件：\nUID 166536 GID 175536 进入容器后会正确映射为：\nUID 1000 GID 10000 而不再显示为：\nnobody 9. 容器内创建对应用户 进入容器：\nincus exec arch -- bash 创建对应组：\ngroupadd -g 10000 project_shared 创建用户：\nuseradd \\ -u 1000 \\ -g 10000 \\ -m \\ -s /bin/bash \\ project 此时：\n容器： project UID = 1000 GID = 10000 正好对应宿主机：\nproject UID = 166536 GID = 175536 10. 共享目录配置 Incus 将宿主机：\n/home/shial/Project 挂载到容器：\n/Project 例如：\nincus config device add arch project disk \\ source=/home/shial/Project \\ path=/Project 检查：\nincus config show arch 应该存在类似：\ndevices: project: path: /Project source: /home/shial/Project type: disk 11. 测试容器写入 进入容器：\nincus exec arch -- bash 切换到映射用户：\nsu - project 检查：\nid 应该：\nuid=1000(project) gid=10000(project_shared) 测试写入：\ntouch /Project/test.txt echo hello \u0026gt; /Project/test.txt 检查：\nls -ln /Project/test.txt 应该看到：\n1000 10000 而不是：\n65534 这说明 UID/GID 映射已经正常。\n12. 让宿主机 shial 也能写 解决容器写入之后，宿主机 shial 也需要访问共享目录。\n宿主机 shial：\nUID = 1000 GID = 1000 共享目录使用：\nUID = 166536 GID = 175536 因此最简单的方法不是再次修改 owner，而是：\n让宿主机用户 shial 加入共享组 project_shared。\n宿主机执行：\nsudo usermod -aG project_shared shial 然后退出当前登录会话并重新登录。\n检查：\nid shial 应该能看到：\nproject_shared 13. 确保共享组具有写权限 检查：\nls -ld /home/shial/Project 如果 group 没有写权限：\ndrwxr-xr-x 修改：\nsudo chmod -R g+rwX /home/shial/Project 为了让新创建的目录继承 group：\nsudo find /home/shial/Project \\ -type d \\ -exec chmod g+s {} \\; 最终理想状态：\nowner: 166536 group: 175536 (project_shared) 容器 project ↓ UID 1000 / GID 10000 ↓ 可以写入 /Project 宿主机 shial ↓ UID 1000 加入 project_shared ↓ 可以写入 /home/shial/Project 14. 双向测试 宿主机 → 容器 宿主机：\necho \u0026#34;hello from host\u0026#34; \\ \u0026gt; /home/shial/Project/host-test.txt 容器：\ncat /Project/host-test.txt 应该输出：\nhello from host 容器 → 宿主机 容器：\necho \u0026#34;hello from container\u0026#34; \\ \u0026gt; /Project/container-test.txt 宿主机：\ncat /home/shial/Project/container-test.txt 应该输出：\nhello from container 15. 最终权限模型 最终采用：\n宿主机 ──────────────────────────────── shial UID 1000 │ │ 加入 project_shared ↓ project_shared GID 175536 │ │ ↓ /home/shial/Project UID 166536 GID 175536 │ │ Incus disk mount ↓ ──────────────────────────────── 容器 /Project project UID 1000 GID 10000 容器 UID 1000 ↓ 宿主机 UID 166536 容器 GID 10000 ↓ 宿主机 GID 175536 这样可以实现：\n宿主机 shial ──┐ │ ├── /Project 双向读写 │ 容器 project ──┘ 16. 关键经验 不要直接把共享目录改回 UID 1000 不要：\nsudo chown -R 1000:1000 /home/shial/Project 因为对于当前 Incus UID 映射：\n宿主机 UID 1000 并不是容器 UID 1000。\n容器看到该 UID 时，文件很可能显示为：\nnobody 不要把 nobody 当成真正的 owner 容器看到：\nnobody 通常不是文件真的属于 nobody，而是：\n宿主机 UID 无法映射到容器 UID。\n应该先检查：\ncat /proc/self/uid_map cat /proc/self/gid_map 然后根据映射计算对应的宿主机 UID/GID。\n当前环境最重要的计算公式 当前：\nContainer UID 0 ↓ Host UID 165536 所以：\nHost UID = 165536 + Container UID 例如：\nContainer UID 1000 → Host UID 166536 GID 同理：\nContainer GID 10000 → Host GID 175536 17. 最终常用命令速查 查看映射：\ncat /proc/self/uid_map cat /proc/self/gid_map 创建共享组：\nsudo groupadd -g 175536 project_shared 创建映射用户：\nsudo useradd -u 166536 -g 175536 -M -s /usr/bin/nologin project 共享目录：\nsudo chown -R 166536:175536 /home/shial/Project 让宿主机用户加入共享组：\nsudo usermod -aG project_shared shial 检查用户组：\nid shial 给 group 写权限：\nsudo chmod -R g+rwX /home/shial/Project 设置目录 SGID：\nsudo find /home/shial/Project \\ -type d \\ -exec chmod g+s {} \\; Incus 挂载：\nincus config device add arch project disk \\ source=/home/shial/Project \\ path=/Project 测试容器：\nincus exec arch -- bash su - project touch /Project/test.txt echo hello \u0026gt; /Project/test.txt 18. 总结 本次问题本质上不是 LXC/Incus 本身无法写文件，而是：\n宿主机 UID/GID ↓ 没有落在容器 UID/GID mapping 范围 ↓ 容器显示 nobody ↓ chown 无法完成 ↓ 开发工具 / npm / node_modules 报 Invalid argument 解决方法是：\n确认 Incus 的 UID/GID 映射。 根据映射计算容器用户对应的宿主机 UID/GID。 创建对应的宿主机用户和共享组。 让共享目录使用这个 UID/GID。 容器内创建对应 UID/GID 的用户。 将宿主机 shial 加入共享组。 确保共享组拥有目录写权限。 通过双向读写测试验证。 对于本机当前的映射：\nContainer UID 1000 → Host UID 166536 Container GID 10000 → Host GID 175536 因此共享目录最终采用：\n166536:175536 宿主机 shial 加入：\nproject_shared (GID 175536) 即可同时满足：\n宿主机 shial：可读写 + Incus 容器：可读写 ","permalink":"https://heihei0299.github.io/posts/arch/lxc%E5%85%B1%E4%BA%AB%E6%96%87%E4%BB%B6%E5%A4%B9%E6%97%A0%E6%B3%95%E7%BC%96%E8%BE%91%E9%97%AE%E9%A2%98/","summary":"\u003ch1 id=\"incus-非特权容器-uidgid-映射与共享目录权限问题排障记录\"\u003eIncus 非特权容器 UID/GID 映射与共享目录权限问题排障记录\u003c/h1\u003e\n\u003ch2 id=\"1-环境\"\u003e1. 环境\u003c/h2\u003e\n\u003cp\u003e宿主机：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eArch Linux\u003c/li\u003e\n\u003cli\u003eKernel：\u003ccode\u003e7.1.6-zen1-1-zen\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eIncus：\u003ccode\u003e7.3\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eLXC：\u003ccode\u003e7.0.0\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e宿主机用户：\u003c/p\u003e","title":"Incus 非特权容器 UID/GID 映射与共享目录权限问题排障记录"},{"content":"Noctalia 剪切板历史丢失修复：KeepassXC 替代 gnome-keyring 现象 Niri + Noctalia（基于 quickshell 的桌面 shell）自带剪切板管理器，但每次重启后剪切板历史全部丢失。面板打开后历史为空，仿佛从未复制过任何内容。\n诊断过程 1. 剪切板历史是加密持久化的，不是内存态 Noctalia 的剪切板历史保存在：\n~/.local/state/noctalia/clipboard/ ├── index.enc # 加密索引（477B，NOCTALIAENC1 头） └── entries/ # 加密条目 └── \u0026lt;时间戳\u0026gt;-1.enc 文件头 NOCTALIAENC1 表明使用 libsodium 加密。历史数据其实一直没丢，只是解不开。\n2. 解密需要一把 master key，存在 Secret Service 里 Noctalia 用 32 字节 master key 派生各用途密钥（crypto_kdf_derive_from_key，context 区分 clipboard / calendar）。这把 master key 通过 libsecret 存到 Secret Service（gnome-keyring），schema 为 dev.noctalia.Secret，属性：\napplication = noctalia scope = storage owner = encrypted-state name = master-key version = 1 3. 启动日志暴露真相 每次启动日志都出现：\n[WRN] [storage-key] encrypted cache/history exists, but its storage master key is missing 对应源码 lookupKey() 的 NotFound 分支：加密数据存在，但 Secret Service 里查不到 master key → 进入 MissingKey 状态 → 历史无法解密，且新条目也不落盘（持久化禁用）→ 每次重启都\u0026quot;丢\u0026quot;。\n4. 真正的根因：gnome-keyring 双实例 + 启动锁定 排查发现 master key 其实存在（用 secret-tool search 在 keyring 里找到了对应 item，md5 哈希属性完全匹配），但 noctalia 启动时读不到。原因有两层：\ngnome-keyring 双实例冲突：systemd user service（gnome-keyring-daemon.service）与 dbus activation 实例（dbus-:1.1-org.freedesktop.secrets@0.service）并存，日志出现 discover_other_daemon: 1、The Secret Service was already initialized。实际持有 org.freedesktop.secrets 的是 activation 实例。 默认密钥环启动时处于 Locked 状态：org.freedesktop.Secret.Collection.Locked = true，noctalia 带 SECRET_SEARCH_UNLOCK 解锁失败 → 搜不到 → 误报 NotFound。 gnome-keyring 被卸载/重装、keyring 文件重建等历史操作（本机 7 月起反复出现该警告）加剧了死锁：数据在、key 在、但服务状态异常导致永远读不到。\n修复方案：KeepassXC 替代 gnome-keyring 与其修复脆弱的 gnome-keyring，不如让 KeepassXC 作为 Secret Service 提供者（用户已有的密码管理器，支持 FdoSecrets 集成）。\n步骤 1：备份 master key（关键保险） 趁 keyring 解锁窗口，把 key 导出为 hex 文件：\nsecret-tool lookup application noctalia scope storage \\ owner encrypted-state name master-key version 1 \\ | xxd -p -c 100 \u0026gt; ~/.local/state/noctalia/storage.key.bak chmod 600 ~/.local/state/noctalia/storage.key.bak 即使后续方案失败，也可用 Noctalia 的 [storage] key_source = \u0026quot;file\u0026quot; 模式回退（key 文件要求 64 位小写 hex，可选换行）。\n步骤 2：KeepassXC 启用 Secret Service 集成（两个开关！） 只开全局开关不够，新版 KeepassXC 要求每个数据库单独配置暴露组：\nSettings → Secret Service Integration → Enable KeePassXC Freedesktop.org Secret Service integration Database → Database Settings → Secret Service Integration → 勾选 Expose entries under this group 并选择组（否则该数据库不创建 collection，文档原话：\u0026ldquo;Collections are deleted if the exposed group is set to none\u0026rdquo;） 验证服务接管：\nbusctl --user list | grep org.freedesktop.secrets # owner 变成 keepassxc gdbus call --session --dest org.freedesktop.secrets \\ --object-path /org/freedesktop/secrets \\ --method org.freedesktop.DBus.Properties.Get \\ org.freedesktop.Secret.Service Collections 步骤 3：卸载 gnome-keyring，释放 D-Bus 名字 org.freedesktop.secrets 是独占名字，gnome-keyring 不退出 KeepassXC 抢不到。卸载后 keyring 数据文件（~/.local/share/keyrings/）保留在磁盘上，可随时回滚。\n步骤 4：迁移 master key（踩坑记录） 用 gdbus 调 org.freedesktop.Secret.Service.CreateItem 会报 UnknownInterface——KeepassXC 的 Service 接口没有 CreateItem（只有 OpenSession/CreateCollection/SearchItems/Unlock/Lock/GetSecrets/ReadAlias/SetAlias）。\n正确入口在 Collection 接口：\ngdbus call --session \\ --dest org.freedesktop.secrets \\ --object-path /org/freedesktop/secrets/collection/\u0026lt;collection-id\u0026gt; \\ --method org.freedesktop.Secret.Collection.CreateItem \\ \u0026#34;{\u0026#39;org.freedesktop.Secret.Item.Label\u0026#39;: \u0026lt;\u0026#39;Noctalia encrypted storage key\u0026#39;\u0026gt;, \\ \u0026#39;org.freedesktop.Secret.Item.Attributes\u0026#39;: \u0026lt;{\u0026#39;application\u0026#39;: \u0026#39;noctalia\u0026#39;, \u0026#39;scope\u0026#39;: \u0026#39;storage\u0026#39;, \\ \u0026#39;owner\u0026#39;: \u0026#39;encrypted-state\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;master-key\u0026#39;, \u0026#39;version\u0026#39;: \u0026#39;1\u0026#39;, \\ \u0026#39;xdg:schema\u0026#39;: \u0026#39;dev.noctalia.Secret\u0026#39;}\u0026gt;}\u0026#34; \\ \u0026#34;(\u0026#39;\u0026#39;, [], [bytearray([\u0026lt;32字节二进制\u0026gt;])])\u0026#34; \\ \u0026#34;true\u0026#34; 要点：\n必须带 xdg:schema: dev.noctalia.Secret 属性：Noctalia 用自定义 schema 搜索，libsecret 会把 schema 作为 xdg:schema 一起匹配；用 secret-tool store（Generic schema）存进去 Noctalia 搜不到。 value 是二进制 32 字节：gdbus 解析嵌套 (oayay) 有缺陷（unknown keyword），可用 dbus-next 等库；KeepassXC 的 collection id 形如 _E5_AF_86_E7_A0_81（URL 编码的\u0026quot;密码\u0026quot;）。 密码字段存的是原始字节，不能手动在 GUI 里输 hex（会变成 64 字节 ASCII，Noctalia 校验 SecureKey::Size 会报 invalid size）。 步骤 5：重启 Noctalia 验证 kill \u0026lt;noctalia-pid\u0026gt; \u0026amp;\u0026amp; nohup noctalia \u0026amp; 验证点：\n启动日志不再出现 storage master key is missing 剪切板面板恢复历史条目 新复制的内容落盘到 entries/ 降级场景（重要） KeepassXC 数据库锁定时 FdoSecrets 不公开 collection，Noctalia 会回到 Unavailable 状态。Noctalia 内置了恢复机制：监听 org.freedesktop.secrets 名字出现 → retrySecretServiceConsumers() → 重新 lookup。所以解锁 KeepassXC 后 Noctalia 能自动恢复，无需重启。代价是保持 KeepassXC 常驻解锁。\n关键命令速查 # 谁持有 Secret Service 名字 busctl --user list | grep org.freedesktop.secrets # 查看服务/集合暴露了哪些接口 gdbus introspect --session --dest org.freedesktop.secrets --object-path /org/freedesktop/secrets gdbus introspect --session --dest org.freedesktop.secrets --object-path /org/freedesktop/secrets/collection/\u0026lt;id\u0026gt; # 读 master key（验证迁移） secret-tool lookup application noctalia scope storage owner encrypted-state name master-key version 1 # 查看 Noctalia 日志中的 storage-key 告警 grep \u0026#34;storage-key\u0026#34; ~/.cache/noctalia/noctalia.log 注意事项 停用 gnome-keyring 会影响依赖它的应用：Chrome 密码（Chrome Safe Storage）、GitHub 凭据、部分 Flatpak 应用密钥等。数据文件保留，但服务不提供，相关应用需重新登录。 KeepassXC 的 Secret Service 是只读优先实现：Service 接口没有 CreateItem（Collection 接口有），写入能力有限，读取/搜索是完整支持的。 备份的 storage.key.bak 等于历史内容的解密钥匙，务必 600 权限妥善保管。 ","permalink":"https://heihei0299.github.io/posts/arch/noctalia%E5%89%AA%E5%88%87%E6%9D%BF%E5%8E%86%E5%8F%B2%E4%BF%AE%E5%A4%8D-keepassxc%E8%BF%81%E7%A7%BB/","summary":"\u003ch1 id=\"noctalia-剪切板历史丢失修复keepassxc-替代-gnome-keyring\"\u003eNoctalia 剪切板历史丢失修复：KeepassXC 替代 gnome-keyring\u003c/h1\u003e\n\u003ch2 id=\"现象\"\u003e现象\u003c/h2\u003e\n\u003cp\u003eNiri + Noctalia（基于 quickshell 的桌面 shell）自带剪切板管理器，但\u003cstrong\u003e每次重启后剪切板历史全部丢失\u003c/strong\u003e。面板打开后历史为空，仿佛从未复制过任何内容。\u003c/p\u003e","title":"Noctalia 剪切板历史丢失修复：KeepassXC 替代 gnome-keyring"},{"content":"Pi 扩展生态与插件推荐 本文分享经高强度使用 Pi + Kimi-K3 组合后整理的一套实用插件：基础类（子代理、MCP、Web 访问）、流程类（todo、问卷、计划模式）、上下文类、界面优化类、备份同步类，以及使用小贴士。\n前言 这几天一直在高强度使用 Pi + Kimi-K3 这套组合，越用越觉得十分顺手。被 GPT 画的神必前端界面折磨了太久，这次竟感到了久违的省心……（不过工作日下午 Kimi 还是会遇到 429，希望官方可以尽快扩充算力储备，或许等开源了会好些？）\n可能有些朋友还不太了解 Pi，这里先简单介绍一下：\nPi 是一个 Agent，与 Codex、Claude Code 不同的是，它并不是开箱即用的。它的功能很少，可以说是个毛坯房，MCP、子代理、计划模式这些统统没有。\nPi 的理念是非必要的东西一律不加，与此同时，它的扩展性极强，内置官方文档，可以很方便地自己开发想要的功能，官方也有自己的插件市场。\n在我的理解里，Pi 负责提供极简的基座 Agent，由用户自定义各项功能，最后组合出自己顺手的 Agent 工具。\n回到正题，这几天一边用一边整理插件，目前列表算是比较稳定，没有什么特别想加的了。这里分享出来，权当抛砖引玉。\n基础类 @gotgenes/pi-subagents 提供可并行派发的进程内子代理功能。与后面要介绍的权限系统出自同一作者，两者有联动——子代理触发权限规则时，询问弹窗会转发到主界面，体验很丝滑。自带三个子 Agent 角色：\ngeneral-purpose：通用复杂任务 Explore：快速仓库探索（只读） Plan：架构规划（只读） 同时支持自定义新 Agent 角色，轻量实用。\npi-mcp-adapter 提供 MCP 适配，通过懒加载方式接入，按需调用 MCP 工具。如果你需要使用 MCP 生态中的各类工具（数据库操作、API 调用等），这是目前最简洁的接入方式。\npi-web-access 提供联网能力，比如网页搜索、抓取 URL 等。不过有两点需要注意：\n首次安装后，每次搜索都会打开网页展示总结，如果觉得烦人，可以让 Pi 修改 ~/.pi/web-search.json，加入以下配置关闭： { \u0026#34;workflow\u0026#34;: \u0026#34;none\u0026#34; } 该插件需要配合可用的搜索 API 使用。 流程类 @juicesharp/rpiv-todo 提供结构化、可视化的待办清单工具。与计划模式配合使用效果更佳——确认计划开始执行后，模型一般会自动创建待办清单，一气呵成。\n@juicesharp/rpiv-ask-user-question 提供问卷工具，给予 Agent 主动提问的能力。当需求不明确时，Agent 可以通过结构化的问卷向用户确认细节，避免盲目开干。\n@narumitw/pi-plan-mode 一个简洁的、Codex 风格的计划模式，且可以很方便地配置在计划模式下的工具白名单。\n自带两个必备工具：一个用于计划模式下的提问，一个用于主动结束计划模式，除此之外没有任何多余功能，恰到好处。\n使用 /plan 命令进入计划模式后，再次输入 /plan 即可出现菜单，选择相应选项即可快速配置计划模式下的工具白名单。\n⚠️ 当时安装的时候允许的工具列表里只有插件自带的两个工具，导致无法读取文件，建议第一次安装优先配置一下工具白名单。\n如果同时安装了 rpiv-todo，那么确认计划开始执行之后，模型一般会自动创建待办清单，流程衔接非常自然。\n上下文类 @cortexkit/pi-magic-context 站内很多朋友推荐过的插件，用了一下确实不错。给 Pi 补充跨会话记忆和上下文管理的能力，后台会自动压缩对话，项目级记忆持续保留，新开会话也能回忆起之前的决策和约定。还有 Dreamer 机制定期整理记忆。\n它有自己的压缩机制和记忆系统，模型可以通过工具写入和查询记忆。使用 K3 时，它会自己调用工具选择性地写入记忆。\n⚠️ 建议用官方仓库里的安装命令进行安装，它会自动把 Pi 自带的压缩关掉——两边同时开着会互相干扰。\n@mrclrchtr/supi-claude-md 提供两个命令用于维护 AGENTS.md / CLAUDE.md：\n/claude-md-improver：对整个仓库里的 AGENTS.md / CLAUDE.md 做一次质量审查和改进。 /claude-md-revision：把当前会话中学到的、与项目相关的持久经验写进 AGENTS.md / CLAUDE.md。 对于需要长期维护的项目来说非常实用，可以不断沉淀项目知识。\npi-cache-optimizer 优化缓存命中率的插件，可选安装。如果发现模型频繁读取相同文件导致 token 消耗较大，这个插件能起到一定缓解作用。\n界面优化类 pi-tool-display 提供紧凑型的工具渲染，安装后可以使用 /tool-display 进行自定义，且可以通过配置文件兼容非官方内置工具。能让 Pi 的 toolcall block 变得简洁清晰，edit diff 记录更美观，还可以根据终端宽度自动切换 split/unified 显示模式。\n⚠️ 它会覆盖注册 Pi 的内置工具，有可能会跟其他调整了 Pi 内置工具的扩展冲突。\n@narumitw/pi-statusline 非常好看的 Pi 状态栏美化插件，效果立竿见影。安装后即可在 Pi 界面底部看到美观的状态栏，显示当前状态信息，整体风格与 Pi 契合度很高。\n备份同步类 pi-webdav-sync 提供两个斜杠命令，用于快速便捷地将 Pi 的个人配置备份/还原到 WebDAV（比如坚果云），实现跨设备同步。\n对于多设备使用 Pi 的用户来说几乎是必备插件——配置一次，所有设备同步，再也不用手动复制配置文件了。\n使用小贴士 关于搜索工具 有些扩展可能会激活 Pi 内置的 grep、find、ls 这三个默认隐藏的命令，导致工具列表里同时出现多套搜索工具，这时一些模型会优先使用较慢的内置工具而非增强版。\n目前的解决办法：使用 pi --exclude-tools find,grep 排除内置的搜索工具。这个参数从 Pi v0.77.0 开始支持。\n关于工具冲突 多个扩展如果都修改了 Pi 的同一组件（如输入框、工具渲染等），可能会出现冲突。具体谁覆盖谁取决于扩展的加载顺序，极端情况下可能报错。建议安装新扩展后先简单测试一下核心功能是否正常。\n关于插件选择原则 个人理念是无论使用什么 Agent，先装一些通用的基础插件，之后再根据具体需求增加插件并根据项目选择性开启。\n尽量避免一上来就装一大堆重型插件，尤其是那种大而全的包办类型插件质量往往不高。功能复杂往往导致架构和实现复杂，维护困难。很多时候根据不同的项目需求自己写 Skills / 插件 / 工作流效果更好，没必要什么级别的项目都非要用重型框架和重型扩展。\n对于 Pi 的 Packages 也是——并不是下载量越高就越好用，得根据自己实际需求来选。\n验证与自查 pi list 能列出已安装的包，且状态为启用 各插件的斜杠命令可用：/plan 进入计划模式、/todo 创建待办、/tool-display 打开渲染配置 pi --exclude-tools find,grep 启动后，会话中模型不再调用内置 find / grep 安装新扩展后先在一个小任务上验证核心功能，确认没有工具冲突 结语 应该就这些了，其实还装了别的一些插件，但感觉不是很有必要就没有写上去。目前我使用场景主要是让 Agent 以本地文档为开发依赖，所以暂时并没有配搜索的扩展 / MCP / Skills。\n如果你也有什么觉得好用的插件，欢迎分享交流！\n参考 Pi 官网（pi.dev） Pi Coding Agent GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/ai/pi-%E6%89%A9%E5%B1%95%E7%94%9F%E6%80%81%E4%B8%8E%E6%8F%92%E4%BB%B6%E6%8E%A8%E8%8D%90/","summary":"\u003ch1 id=\"pi-扩展生态与插件推荐\"\u003ePi 扩展生态与插件推荐\u003c/h1\u003e\n\u003cp\u003e本文分享经高强度使用 Pi + Kimi-K3 组合后整理的一套实用插件：基础类（子代理、MCP、Web 访问）、流程类（todo、问卷、计划模式）、上下文类、界面优化类、备份同步类，以及使用小贴士。\u003c/p\u003e","title":"Pi 扩展生态与插件推荐"},{"content":"Pi 本机安装与使用 本文不是通用教程，而是基于本机实际环境整理的一份安装与使用实录：记录了 pi（Pi Coding Agent）在这台机器上是怎么装的、配置文件放在哪、日常怎么用，以及当前已安装的扩展与包清单。通用玩法可以看站内另外几篇 pi 文章（使用教程、快速上手指南、扩展生态等）。\n环境概览 本机当前环境：\nNode.js 由 fnm 管理，当前版本 v26.5.1 pi 通过 npm 全局安装，包名 @earendil-works/pi-coding-agent 当前版本 0.83.0 可执行文件路径：~/.local/share/fnm/node-versions/v26.5.1/installation/bin/pi pi --version # 0.83.0 因为全局包挂在 fnm 管理的 node 版本目录下，切换 node 版本后如果找不到 pi 命令，检查一下全局包是否装在了当前激活的版本里。\n安装 npm 全局安装（本机采用的方式） npm install -g --ignore-scripts @earendil-works/pi-coding-agent --ignore-scripts 会跳过依赖的生命周期脚本；pi 的正常安装不需要执行这些脚本，所以可以放心加。装完验证：\npi --version pi --help 官方安装脚本（替代方案） curl -fsSL https://pi.dev/install.sh | sh 两种方式任选其一，本机用的是 npm 方式。\n升级 pi update --self # 只升级 pi 本体 pi update --all # 同时升级 pi 和已安装的包 pi update --extensions # 只升级包 pi update --models # 刷新模型目录 配置目录解析 pi 的所有配置都集中在 ~/.pi/agent/ 下，本机实际目录结构：\n~/.pi/agent/ ├── AGENTS.md # 全局上下文文件（所有项目都会加载） ├── settings.json # 全局设置（默认模型、主题、已装包等） ├── settings-extensions.json ├── auth.json # 登录凭证（勿泄露，不要提交到仓库） ├── trust.json # 项目信任记录 ├── models.json # 模型目录缓存 ├── extensions/ # 本地扩展 │ ├── pi-guard/ │ ├── pi-permission-system/ │ └── pi-rtk-optimizer/ ├── skills/ # 本地技能 │ └── find-skills/ ├── npm/ # npm 源安装的 pi 包 ├── git/ # git 源安装的 pi 包 ├── sessions/ # 会话存档（JSONL） └── state/ # 运行时状态 关键文件说明：\n文件/目录 用途 settings.json 全局设置，/settings 或直接编辑均可 AGENTS.md 全局上下文指令，所有项目启动时都会加载（本机写入了中文回复、安全铁律、包管理规则等） auth.json 各 provider 的登录凭证，千万不要泄露或提交 trust.json 项目信任决策记录，配合 /trust 使用 extensions/ 手工放置的扩展（TypeScript 模块） skills/ 按 Agent Skills 标准组织的技能（Markdown 指令包） npm/、git/ pi install 安装的第三方包 sessions/ 会话按工作目录分类存放的 JSONL 文件 全局 vs 项目：~/.pi/agent/settings.json 是全局配置，项目根目录的 .pi/settings.json 会覆盖全局；上下文文件（AGENTS.md/CLAUDE.md）按 ~/.pi/agent → 父目录 → 当前目录的优先级全部加载并拼接。\n本机 settings.json 说明 本机 ~/.pi/agent/settings.json 里的关键项：\n{ \u0026#34;compaction\u0026#34;: { \u0026#34;enabled\u0026#34;: true }, \u0026#34;defaultModel\u0026#34;: \u0026#34;opencode-go/deepseek-v4-flash\u0026#34;, \u0026#34;defaultProvider\u0026#34;: \u0026#34;pi-switch\u0026#34;, \u0026#34;defaultThinkingLevel\u0026#34;: \u0026#34;high\u0026#34;, \u0026#34;hideThinkingBlock\u0026#34;: true, \u0026#34;theme\u0026#34;: \u0026#34;front-end-delight\u0026#34;, \u0026#34;transport\u0026#34;: \u0026#34;auto\u0026#34; } 默认模型/provider：opencode-go/deepseek-v4-flash，provider 是 pi-switch（默认模型就是本机日常在用的） 思考级别：默认 high，可在会话中随时用 Shift+Tab 切换 主题：front-end-delight（来自 pi-curated-themes 包） compaction：开启，长会话自动压缩，防止撑爆上下文 transport：auto，让 provider 自动选择 SSE 或 WebSocket 已安装的包清单（settings.json 的 packages 字段，共 22 个）：\npi-fff pi-permission-system pi-extension-settings pi-powerbar rpiv-ask-user-question rpiv-todo pi-btw pi-caffeinate pi-goal pi-plan-mode pi-subagents pi-raw-paste pi-curated-themes pi-agent-browser-native pi-autoresearch pi-cache-optimizer pi-hashline-edit-pro pi-mcp-adapter pi-rtk-optimizer pi-slopchop pi-web-access pi-workspace-history 登录与模型 登录 在交互模式中输入 /login（退出用 /logout），按提示选择 provider 并完成认证。pi 支持两类认证方式：\n订阅：Anthropic Claude Pro/Max、OpenAI ChatGPT Plus/Pro（Codex）、GitHub Copilot API key：Anthropic、OpenAI、Azure OpenAI、DeepSeek、NVIDIA NIM、Google Gemini、Google Vertex 等 凭证保存位置见上文 auth.json，注意保密。\n切换模型 操作 说明 /model 打开模型选择器 Ctrl+L 打开模型选择器 Ctrl+P / Shift+Ctrl+P 在 /scoped-models 限定的模型间前后循环 /scoped-models 配置 Ctrl+P 循环的模型范围 也可以启动时直接指定：\npi --provider anthropic --model claude-sonnet \u0026#34;帮我重构这个函数\u0026#34; pi --model openai/gpt-4o \u0026#34;你好\u0026#34; pi --model sonnet:high \u0026#34;思考级别简写\u0026#34; pi --list-models # 列出可用模型 日常使用 启动方式 pi # 交互模式（新会话） pi \u0026#34;列出 src/ 下所有 .ts 文件\u0026#34; # 带初始提示词进入 pi -c # 继续最近的会话 pi -r # 浏览并选择历史会话 pi --name \u0026#34;release 审计\u0026#34; # 给会话起显示名 pi --no-session # 临时会话，不落盘 pi -p \u0026#34;总结这个代码库\u0026#34; # 非交互模式，打印结果后退出 非交互模式还支持管道输入：\ncat README.md | pi -p \u0026#34;总结这份文档\u0026#34; 常用命令 编辑器里输入 / 触发命令：\n命令 说明 /new 新建会话 /resume 选择历史会话 /session 查看当前会话信息（ID、消息数、token、费用） /name \u0026lt;name\u0026gt; 设置会话显示名 /tree 跳转到会话树任意节点继续 /fork 从某条历史消息分叉出新会话 /clone 复制当前分支到新会话 /compact [prompt] 手动压缩上下文 /export [file] 导出会话为 HTML / JSONL /import \u0026lt;file\u0026gt; 导入并恢复会话 /share 上传为私有 GitHub gist 并生成可分享链接 /trust 保存项目信任决策（重启生效） /settings 修改思考级别、主题、消息投递、transport 等 /reload 重载快捷键、扩展、技能、提示词模板、主题、上下文文件 /hotkeys 查看全部快捷键 /changelog 版本历史 /quit 退出 常用快捷键 快捷键 动作 Ctrl+C 清空编辑器；连续按两次退出 Esc 取消/中止当前操作；连按两次打开 /tree Ctrl+L 打开模型选择器 Shift+Tab 循环切换思考级别 Ctrl+O 折叠/展开工具输出 Ctrl+T 折叠/展开思考块 Ctrl+X 复制最后一条助手消息 Ctrl+G 用外部编辑器编辑输入（externalEditor → $VISUAL/$EDITOR → nano） 编辑器特性 @：模糊搜索并引用项目文件 Tab：补全路径 Shift+Enter：多行输入 Ctrl+V：粘贴图片或文本，也可以直接把图片拖进终端 !command：执行 shell 命令并把输出发送给模型；!!command 执行但不发送 Enter：排入一条 steering 消息（当前回合工具执行完后送达） Alt+Enter：排入一条 follow-up 消息（等 agent 全部工作结束才送达） Esc：中止并把排队消息退回编辑器；Alt+Up 取回排队消息 会话管理 会话以 JSONL 文件存放在 ~/.pi/agent/sessions/，按工作目录组织，用 id + parentId 构成树状结构，支持原地分支而不新建文件。\n/tree：在会话树中原地导航，选中任意历史节点继续、切换分支；支持搜索、折叠（Ctrl+←/→、Alt+←/→）、过滤模式（Ctrl+O），Ctrl+X 复制选中消息，Shift+L 加书签 /fork：从活动分支的某条历史消息派生新会话文件 /clone：把当前分支完整复制到新会话文件 --fork \u0026lt;path|id\u0026gt;：从 CLI 直接 fork 指定会话 压缩（compaction）在上下文接近上限时自动触发（也可 /compact 手动触发），会总结旧消息、保留新消息。压缩是有损的，完整历史仍在 JSONL 里，随时可用 /tree 回溯。\n本机已装扩展与包 extensions/（手工放置的本地扩展） 扩展 作用 pi-guard 安全护栏类扩展 pi-permission-system 权限系统，控制 agent 能访问的路径和能执行的命令（本机日常生效，例如限制 bash 访问敏感路径） pi-rtk-optimizer 输出优化扩展，对 grep 等工具输出做紧凑化处理 skills/（本地技能） find-skills：查找可用技能的技能 已装包清单及简要用途 包 用途 pi-fff 工具/输出相关增强 pi-permission-system 权限系统（与本地扩展配合） pi-extension-settings 扩展设置管理 pi-powerbar 状态栏美化/信息栏 rpiv-ask-user-question 结构化提问组件 rpiv-todo 任务列表跟踪 pi-btw 杂项增强 pi-caffeinate 会话期间防止系统休眠 pi-goal 目标管理 pi-plan-mode 计划模式（规划后再执行） pi-subagents 子代理支持 pi-raw-paste 原始粘贴 pi-curated-themes 精选主题集（本机主题 front-end-delight 来自这里） pi-agent-browser-native 浏览器自动化（agent_browser 工具） pi-autoresearch 自动调研/研究 pi-cache-optimizer 提示词缓存优化 pi-hashline-edit-pro 行级编辑增强 pi-mcp-adapter MCP 适配器（桥接 MCP 服务器工具） pi-rtk-optimizer 输出优化（与本地扩展对应） pi-slopchop 输出精简 pi-web-access Web 访问能力 pi-workspace-history 工作区历史 查看/管理已装包：\npi list # 列出已装包 pi install npm:@foo/pi-tools # 安装（npm/git/URL 源） pi install git:github.com/user/repo@v1 pi remove npm:@foo/pi-tools # 卸载 pi config # 启用/禁用包里的扩展、技能、提示词、主题 安全提醒：pi 包以完整系统权限运行，扩展会执行任意代码，技能可以指使模型做任何事（包括执行程序）。安装第三方包前先审查源码。\n环境变量与常见问题 常用环境变量 变量 说明 PI_OFFLINE=1 关闭所有启动期网络操作（更新检查、包更新检查、遥测） PI_SKIP_VERSION_CHECK=1 只跳过版本更新检查（不再请求 pi.dev 查新版本） PI_TELEMETRY=0 关闭安装/更新遥测（不影响版本检查） PI_CODING_AGENT_DIR 覆盖配置目录（默认 ~/.pi/agent） PI_CACHE_RETENTION=long 延长提示词缓存时长（Anthropic 1h、OpenAI 24h） VISUAL/EDITOR Ctrl+G 外部编辑器回退 项目信任 pi 启动交互会话时，如果项目目录里有项目级设置（.pi/settings.json）、项目资源或项目 .agents/skills，会先询问是否信任。信任后才能加载项目级配置和扩展。相关行为：\n用 /trust 保存信任决策（写入 ~/.pi/agent/trust.json，重启生效） 非交互模式（-p、--mode json/rpc）不弹信任提示，受 defaultProjectTrust 控制（ask/always/never，可在全局 settings 里设置） 单次运行可用 -a（信任）/ -na（忽略）覆盖 常见问题 切换 fnm node 版本后找不到 pi：全局包装在当前 node 版本的目录下，切版本后需在对应版本重装或改回原版本 提示包被更新检查/网络请求卡住：网络受限环境用 pi --offline 或 PI_OFFLINE=1 想完全不落盘地试一下：pi --no-session 只读模式审查代码：pi --tools read,grep,find,ls -p \u0026quot;Review the code\u0026quot; 权限系统拦截命令：本机装了 pi-permission-system，未授权路径/命令会被拦截，这是预期行为；需要放行时调整权限配置或改用授权方式执行 验证与自查 pi --version 输出版本号，pi --help 正常打印帮助 pi list 列出的已装包与 settings.json 的 packages 字段一致 启动 pi 后发起一次对话，/session 能看到当前会话的 ID、消息数与 token 消耗 修改 ~/.pi/agent/settings.json 或 models.json 后，会话内 /model 或 /reload 热加载生效，无需重启 本文记录的是本机 2026-08 的实际状态，pi 迭代很快，配置目录结构、默认值等以 pi --version 和 ~/.pi/agent/ 实际内容为准。\n参考 Pi 官网（pi.dev） Pi Coding Agent GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/ai/pi-%E6%9C%AC%E6%9C%BA%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8/","summary":"\u003ch1 id=\"pi-本机安装与使用\"\u003ePi 本机安装与使用\u003c/h1\u003e\n\u003cp\u003e本文不是通用教程，而是基于\u003cstrong\u003e本机实际环境\u003c/strong\u003e整理的一份安装与使用实录：记录了 pi（Pi Coding Agent）在这台机器上是怎么装的、配置文件放在哪、日常怎么用，以及当前已安装的扩展与包清单。通用玩法可以看站内另外几篇 pi 文章（使用教程、快速上手指南、扩展生态等）。\u003c/p\u003e","title":"Pi 本机安装与使用"},{"content":"Pi 基础扩展简单推荐 本文分享一套实用的 Pi 基础扩展选择与配置经验：权限管理（pi-permission-system）、子代理（pi-subagents）、上下文漂移缓解、后悔药（undo/rewind）、查询增强与显示美化，覆盖 Coding 场景的主要需求。\n前言 昨天看到佬友写的帖子【π 关于 pi 的完善之路，package 生态的折腾之路】感觉真心不错，刚睡醒，心血来潮也来分享一下自己近期折腾 Pi 扩展的经验。\n先给误入此帖（bushi）的佬介绍下，Pi 是一个轻量实现的 harness，跟 Claude Code / Codex / OpenCode 这些工具比起来绝对是简易甚至可以称得上是毛坯房了，很多功能作者都刻意避开，这也让它的 system prompt token 开销很小。\n虽然 Pi 这类 Agent 可应用的领域不止 Coding，但我目前主要还是用于 Coding。本贴推荐的都是一些基础插件，Coding 场景还是都能用得上的，做其他任务也有一定意义。\nPi 现在是有 7 个内置工具，其中 read、write、edit、bash 这四个是默认启用的，而 grep（后端为 rg）、find（后端为 fd）、ls 这三个默认是隐藏的，可以通过 Pi 的 CLI 选项 --tools \u0026lt;list\u0026gt; 启用。\nPi 的插件系统是 Package，优点是很灵活，像 Extension、Theme、Skills 等都可以做成 Package，且一个 Package 里可以包含任意种类和数量的 Extension/Skill/Theme。\n先放个 Pi Package 的官网：pi.dev\n个人理念就是无论使用什么 Agent，先装一些通用的基础插件，之后再根据具体需求增加插件并根据项目选择性开启。尽量避免一上来就装一大堆重型插件，尤其是那种大而全的包办类型插件质量往往不高。功能复杂往往导致架构和实现复杂，维护困难，就全用 AI 改。其实很多时候根据不同的项目需求自己写 Skills/插件/工作流效果更好，没必要什么级别的项目都非要用重型框架和重型扩展。另外对于 Pi 的 Packages 也是，并不是下载量越高就越好用，得根据自己实际需求来选。\n权限管理 Pi 默认是不带任何权限管理以及沙盒的。这使得 Pi 的整体使用很丝滑（rm -rf / 也很丝滑，嗯）。所以在非容器内跑 Pi，加一个权限管理还是安心不少。\n这里推荐 gotgenes 的 pi-permission-system，比较喜欢它的扁平化配置。\n配置位于 ~/.pi/agent/extensions/pi-permission-system/config.json。\n我目前的策略是：\n拒绝访问项目和用户目录的敏感路径。 允许访问当前工作目录，访问外部目录时询问用户，但排除 .pi 自身的目录，不然读个全局 skill 疯狂弹窗。 Bash 执行敏感操作时询问——至于为什么不是拒绝，我测试过即使设置为拒绝，LLM 有时候也会疯狂想办法钻漏洞绕过，比如写 JS 脚本删。所以我选择 ask 并由用户来判断。 我正在使用的示例配置如下：\n{ \u0026#34;$schema\u0026#34;: \u0026#34;https://raw.githubusercontent.com/gotgenes/pi-packages/refs/heads/main/packages/pi-permission-system/schemas/permissions.schema.json\u0026#34;, \u0026#34;debugLog\u0026#34;: false, \u0026#34;permissionReviewLog\u0026#34;: false, \u0026#34;yoloMode\u0026#34;: false, \u0026#34;permission\u0026#34;: { \u0026#34;*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;grep\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;find\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;external_directory\u0026#34;: { \u0026#34;*\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;*\\\\users\\\\*\\\\.pi\\\\agent\\\\skills\\\\*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\users\\\\*\\\\.pi\\\\agent\\\\npm\\\\*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\users\\\\*\\\\.pi\\\\agent\\\\sessions\\\\*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\users\\\\*\\\\.pi\\\\agent\\\\repos\\\\*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\node_modules\\\\@earendil-works\\\\*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\node_modules\\\\@earendil-works\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\dev\\\\null\\\\*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\dev\\\\null*\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\dev\\\\null\\\\\u0026#34;: \u0026#34;allow\u0026#34;, \u0026#34;*\\\\dev\\\\null\u0026#34;: \u0026#34;allow\u0026#34; }, \u0026#34;path\u0026#34;: { \u0026#34;*.env\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;*.env.*\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;~/.ssh/*\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;$HOME/.ssh/*\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;*\\\\users\\\\*\\\\.pi\\\\agent\\\\auth.json\u0026#34;: \u0026#34;deny\u0026#34;, \u0026#34;*\\\\users\\\\*\\\\.pi\\\\agent\\\\models.json\u0026#34;: \u0026#34;deny\u0026#34; }, \u0026#34;bash\u0026#34;: { \u0026#34;Remove-Item *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;remove-item *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;rm *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;del *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;erase *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;rd *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;rmdir *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;ri *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;mv *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd /c del *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd /c erase *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd /c rd *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd /c rmdir *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd.exe /c del *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd.exe /c erase *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd.exe /c rd *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;cmd.exe /c rmdir *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;powershell *Remove-Item*\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;powershell.exe *Remove-Item*\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;pwsh *Remove-Item*\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;pwsh.exe *Remove-Item*\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;git clean *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;git reset --hard *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;git checkout -- *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;git restore *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;Clear-Content *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;Set-Content *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;Out-File *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;Move-Item *\u0026#34;: \u0026#34;ask\u0026#34;, \u0026#34;Rename-Item *\u0026#34;: \u0026#34;ask\u0026#34; } } } 这里有个反直觉的地方：pi-permission-system 会从 Pi 的全部已注册工具里重新生成 active tools，而 grep、find、ls 原本就在 Pi 内置工具全集里只是没激活。而扩展配置里默认权限的 fallback 是 ask，而只要设置为 ask，全部默认工具会被暴露。所以只有在配置里显式写 \u0026quot;grep\u0026quot;: \u0026quot;deny\u0026quot;、\u0026quot;find\u0026quot;: \u0026quot;deny\u0026quot; 才会把它们从 active tools 里过滤掉。嗯，有点离谱。我这里还是保留了 ls，也算是高频使用的工具了，而内置的 grep 和 find 被之后要介绍的 pi-fff 替代了。\nSubagent 子代理 这里推荐 gotgenes 的 pi-subagents，和上面的权限管理是同一个作者（虽然这两个项目好像都是这位从其他 repo fork 二开的）。这个子代理的特点就是与上面的权限管理有联动，子代理触发权限规则，询问弹窗也会转发到主界面。目测实现比较轻量，应该是对标 CC 或者 OpenCode 的子代理做的，用起来挺丝滑。自带有三个子 Agent 角色：\ngeneral-purpose：用于通用复杂任务 Explore：用于快速仓库探索（只读） Plan：用于架构规划（只读） 支持自定义新 Agent 角色。\n下载量最高的 pi-subagents 也不错，个人感觉更偏向预定 Agent 角色 + 预定工作流，有点 omo-slim 内味，也可以手动配置不同 Agent 的角色和模型。我个人一般用不太到这种复杂工作流，感觉平时偶尔用一下直接跟 AI 说自然语言也就调用子 Agent 了，反复用的就写个 Skills 规定一个带子 Agent 的工作流，然后配合上面 gotgenes 的 pi-subagents 效果也大差不差。\n上下文漂移缓解 目前在用 pi-observational-memory，比较轻量，功能也比较有限，不像 magic-context 那样支持跨 session 记忆和其他复杂特性。\n原理差不多就是后台起个子进程跑 observer 和 reflector，将精简后的对话记录和用户提过的重要要求固化到 Pi 的 session 记录里。配合更加激进地触发 Pi 内置的 compact，可以保证 LLM 一直工作在召回率较高的区间，同时不容易丢失一些重要信息。\n扩展支持手动配置触发 observer、reflector 和 compact 的 token 数。配置写在 Pi 的 settings.json 里即可。\n后悔药 Undo / Rewind 类 pi-rewind — 这个插件比较接近 CC 上 /rewind（undo）的效果，不过几个月没更新了。它是通过 Git checkpoint 实现的，强制要求项目已经初始化 Git。默认无需额外配置。\npi-workspace-history — 相比上面的我更推荐这个。它用的是 shadow git，所以不需要项目初始化 Git。它注册的 /undo 命令是撤销上一次的整个对话以及文件更改，也支持 /redo。如果想实现类似 CC 的 /rewind 效果，得用 Pi 自带的 /tree 命令，切换节点的时候就会自动触发 pi-workspace-history 的文件恢复了。\n消息撤回类 pi-wtf — 在你输入内容有 typo 或者没输完却不小心回车之后，输入 /fuck（十分甚至九分的文明）就可以强制停止当前工作并通过 Pi 的 tree 撤回，并且回填之前用户的输入内容。不过要注意的是，/fuck 只会撤回对话状态，不会更改本地文件。\n测试的时候发现的一个有意思的点是，这个插件会通过切换 tree 来达成撤回的效果，所以跟上面的 pi-workspace-history 一起装会达成一套奇妙 combo：/fuck 通过 tree 撤销当前正在进行的会话时，即使文件已经做了更改，也会同时触发 pi-workspace-history 撤销文件更改。\n查询增强 推荐 pi-fff，会向 Pi 注册两个工具 fffind 和 ffgrep，索引之后查询速度极快，还可以显示 Git 状态，算是 fd 和 rg 的上位替代了。\n有些扩展可能会激活 Pi 内置的 grep、find、ls 这三个默认隐藏的命令，导致工具列表里既有 grep 和 find，也有 fffind 和 ffgrep，这时候一些笨比模型就喜欢优先用 grep 和 find 了。\n如果不幸遇到的话，目前有一个暂时的解决办法：使用 pi --exclude-tools find,grep。--exclude-tools 这个参数也是最近版本才更新的（见 Release v0.77.0 更新日志），也有用户在 Issue #5084 提到了可以把 exclude 写到配置文件里，不过 issue 被自动关掉了。\n还需要注意的一点就是这个扩展为了增强 Pi 自带的 @ 选文件功能，覆盖了 editor（Pi 的输入框），所以会和一些同样修改了输入框部分的一些扩展冲突，具体谁覆盖谁就看扩展的加载顺序了（当然也有可能直接报错）。\n显示美化 推荐 pi-tool-display，可以让 Pi 的 toolcall block 变得比较简洁，edit diff 记录更美观（还可以根据终端宽度自动切换 split/unified）。详细配置可以在 Pi 内 /tool-display 触发一个友好的 TUI 弹窗调整配置。\n需要注意的是它会覆盖注册 Pi 的内置工具，有可能会跟其他调整了 Pi 内置工具的扩展打架。\nI use Pi btw 这个功能用的比较少，目前比较推荐 @narumitw/pi-btw，体验比较类似 CC 的 btw，支持 TUI Markdown 渲染，割裂感不会很重，甚至可以说风格与原版挺契合。不会污染主线程。要说缺点就是不能连续对话，不同的 btw 对话之间没有共享上下文。btw 里模型能看到工具，但模型会明确拒绝执行，有可能是通过提示词限制了。\n下载量最高的是 @juicesharp/rpiv-btw，特点是支持多 btw 上下文共享，并且当前 session 只要不结束就会一直保留上下文，可以通过 TUI 手动清除 btw 的上下文历史记录。缺点是不支持 Markdown 渲染。README 明确指出已经禁用了工具的，但模型还是能读到工具列表，而且调用的时候会卡住，而且好像把工具调用日志都干出来了，这点感觉不是很好。\npi-btw 是 btw 类当前下载量第二，它可以在主线程 TUI 上覆盖一个 TUI 窗口进行对话，并且支持连续对话，但似乎不支持 Markdown 渲染。能调用工具，但工具数量变少了，应该是故意限制了。不过在 Powershell 上有时候渲染还是有点问题，而且在我第一次尝试时，只是问个问题让它读个文件就给我整个 Pi 进程干崩了。\n验证与自查 安装扩展后重启 Pi，/reload 重载扩展，确认新命令（如 /undo、/plan、/btw、/tool-display）出现在命令列表中 权限系统：在项目中让 Agent 执行 rm 或读取 .env，应触发询问/拒绝（取决于配置） 子代理：给 Agent 一个复杂任务，观察它能否派发子代理并在主界面收到权限询问弹窗 查询增强：在会话里让模型使用 ffgrep / fffind，确认工具可用且速度明显快于内置 grep / find 结语 Man! What can I say, Pi out!\n就写到这吧，在 Joplin 里全文手打无 AI 帮忙整理润色还是太累了，写了一半撤销后面发现丢了点内容，但具体丢了哪些我已经没啥印象了，气笑了。之后要是还能想起来就再更新（乐）。我目前使用场景主要是让 Agent 以本地文档为开发依赖，所以暂时并没有配搜索的扩展/MCP/Skills。欢迎佬友们补充更多实用扩展，实测好用的我会更新到帖子中。\n参考 Pi Package 官网（pi.dev） pi-permission-system（gotgenes/pi-packages） ","permalink":"https://heihei0299.github.io/posts/ai/pi-%E5%9F%BA%E7%A1%80%E6%89%A9%E5%B1%95%E6%8E%A8%E8%8D%90/","summary":"\u003ch1 id=\"pi-基础扩展简单推荐\"\u003ePi 基础扩展简单推荐\u003c/h1\u003e\n\u003cp\u003e本文分享一套实用的 Pi 基础扩展选择与配置经验：权限管理（pi-permission-system）、子代理（pi-subagents）、上下文漂移缓解、后悔药（undo/rewind）、查询增强与显示美化，覆盖 Coding 场景的主要需求。\u003c/p\u003e","title":"Pi 基础扩展简单推荐"},{"content":"Pi 的完善之路：Package 生态折腾指南 本文记录一条完整的 Pi Package 折腾之路：从 Package 是什么，到子 Agent 与 MCP、上下文压缩、/goal、代码搜索、安全审查、UI 增强、图像生成、操作回退，再到踩过的坑与 Package 总览。\n前言 看了很多强调上下文重要性的文章，用 CC 接国模愈发不顺手，总感觉 CC 的上下文屎山是不是越来越大了，于是捡起之前折腾了一半的 Pi。我的想法和作者部分一致——上下文工程是第一要务，准确的控制注入模型的每一个字肯定能获得比黑盒更舒服、更有包裹感（误）。\n点进这个帖子的应该或多或少都是听说过 Pi 的，如果不了解请看作者的文章。\n总而言之 Pi 是一个极度精简的 Agent——无 MCP、无后台 Bash、无内置 To-do、无 Plan mode、无子 Agent，本体的内置提示词不超过 1000 token，仅内置 4 个工具：read、write、edit、bash。\n但是极简的框架意味着极强的可塑性，下文正式开始折腾。\n关于 Package 是什么 在 Pi 中，Package 可以是：\nExtension： 注册新命令、工具、事件钩子、UI 组件 Skill： 带 SKILL.md 的技能文档，指导模型使用特定工作流 Prompt Template： 提示词模板 Theme： 终端主题 JSON 在 Pi 中，一切都可以由 Package 引入。\n子 Agent 与 MCP pi-subagents、pi-mcp-adapter——没什么好说的，补全其他家都有的功能。不过 MCP 的 Package 我没装，我用到的 MCP 基本都可以做成 Package。\n关于上下文压缩 我主要纠结在三个包里：context-mode、DCP、pi-observational-memory。\ncontext-mode 作为 Pi Package 安装量排行榜第一，我自然第一个尝试了，但是效果并不好。用 Mimo 容易出现理解偏差，它把工具输出拦截到沙箱，模型只能看到摘要？要看原文得自己判断是否展开，但模型经常判断错，导致关键信息丢失（猜的流程）。我是觉得没必要省这种上下文，所以最后卸了。\nDCP（Dynamic Context Pruning） 就是比较常见的总结形式的工具了，没什么好说的，我现在就在用，具体可以看看项目文档。\npi-observational-memory 可以单独配置模型进行总结，主要用于长会话防偏移，也是总结类的，但是设计比 DCP 复杂一点，还在体验中。\n关于 /goal Codex 的 /goal 很不错，目标完成前不会停止。Pi 的相关 Package 我主要使用 pi-until-done，如果追求 Codex 的感觉可以装 pi-codex-goal。\n代码搜索与检索 pi-ace-tool — 自己写的 ace-tool 插件，用过 MCP 的应该知道有多好用。 @ff-labs/pi-fff — Rust/SIMD 加速的模糊 find 和 grep，替代原生 find/grep，速度极快，体验不错。 pi-fast-context — vibe 了一个 fast-context 的插件，用着不错速度挺快的。 搜索与抓取 pi-search — 基于站内的 grok-search-mcp 自己写的，加了 context7 和反检测 fetcher 之类的实用工具，基本搜索相关装这一个就够了。不过我也犯了塞太多的毛病，不想用可以看看 pi-web-access 和 pi-smart-fetch。\n安全与审查 @juicesharp/rpiv-advisor — 请求强模型给第二意见/审查建议，关键决策前多一层校验 pi-simplify — 审查近期代码改动的清晰度、维护性和一致性 @narumitw/pi-plan-mode — /plan 只读规划模式，禁止 edit/write/危险 bash，输出 proposed_plan 确认后才恢复写权限 UI 与交互 pi-nano-context — 紧凑上下文占用条，显示 system/user/assistant/tool/free 各占多少，替代 powerline 的轻量选择 pi-tool-display — OpenCode 风格工具输出折叠和 diff 渲染，减少 TUI 被大段工具输出刷屏 pi-markdown-preview — Markdown/LaTeX 预览 @juicesharp/rpiv-ask-user-question — 结构化提问 UI 思考与辅助 @feniix/pi-sequential-thinking — 同名 MCP 一个作用 pi-btw — Claude Code 同款 /btw，并行旁路问题，不污染主对话 图像生成 pi-image-gen — 自己写的 Image2 图像生成/编辑工具，支持文生图、图生图等，优化前端设计场景。我主要用在写前端前的风格、素材等生成。\n操作回退 pi-rewind — 依赖 Git 的存档点回退工具，很不错，原生级的回退体验。\n踩过的坑 pi-powerline-footer 太重 Powerline 会接管编辑器布局和鼠标滚动，改变了 Pi 原生简洁的 TUI 体验。推荐 pi-nano-context，只做上下文占用显示，干净轻量。\nPackage 总览 Package 链接 安装命令 pi-subagents npm pi install npm:pi-subagents pi-mcp-adapter npm pi install npm:pi-mcp-adapter pi-markdown-preview npm pi install npm:pi-markdown-preview @juicesharp/rpiv-ask-user-question npm pi install npm:@juicesharp/rpiv-ask-user-question @victor-software-house/pi-curated-themes npm pi install npm:@victor-software-house/pi-curated-themes pi-ace-tool GitHub pi install git:github.com/justhil/pi-ace-tool pi-rewind npm pi install npm:pi-rewind pi-image-gen GitHub pi install git:github.com/justhil/pi-image-gen pi-search GitHub pi install git:github.com/justhil/pi-search pi-btw npm pi install npm:pi-btw pi-simplify npm pi install npm:pi-simplify pi-dynamic-context-pruning (DCP) GitHub pi install git:github.com/complexthings/pi-dynamic-context-pruning @ff-labs/pi-fff npm pi install npm:@ff-labs/pi-fff pi-until-done npm pi install npm:pi-until-done pi-codex-goal npm pi install npm:pi-codex-goal pi-nano-context npm pi install npm:pi-nano-context pi-tool-display npm pi install npm:pi-tool-display @narumitw/pi-plan-mode npm pi install npm:@narumitw/pi-plan-mode pi-observational-memory npm pi install npm:pi-observational-memory 验证与自查 pi list 确认已安装包及版本，pi config 可管理各包内的扩展、技能、提示词与主题 按上表安装后，各包的斜杠命令（如 /plan、/btw、/undo）应可正常调用 上下文压缩类：长会话中观察 token 占用曲线，确认压缩按预期触发 装完新包后先跑一个小任务，确认无工具冲突、TUI 渲染正常 写在最后 Pi 的拓展感觉接口给的很好，但是很多写拓展的都想把自己想到的全塞进去，反而违背 Pi 的创作本意。想找个好用的、独立的、依赖少又完善的拓展真的难。反而用 AI 自己写适合自己工作流的拓展更简单——如果已经在 CC、Codex 有完善的工作流了，可以自己尝试用 AI 写插件迁移。\n参考 Pi 官网（pi.dev） Pi Coding Agent GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/ai/pi-package-%E7%94%9F%E6%80%81%E6%8A%98%E8%85%BE%E6%8C%87%E5%8D%97/","summary":"\u003ch1 id=\"pi-的完善之路package-生态折腾指南\"\u003ePi 的完善之路：Package 生态折腾指南\u003c/h1\u003e\n\u003cp\u003e本文记录一条完整的 Pi Package 折腾之路：从 Package 是什么，到子 Agent 与 MCP、上下文压缩、/goal、代码搜索、安全审查、UI 增强、图像生成、操作回退，再到踩过的坑与 Package 总览。\u003c/p\u003e","title":"Pi 的完善之路：Package 生态折腾指南"},{"content":"Pi Coding Agent 使用教程 本文是 Pi Coding Agent 的完整上手教程：从设计理念、安装，到模型配置、TUI 指南与自定义扩展，覆盖日常使用所需的全部操作。\n前言 按照作者的话来说，Pi Coding Agent 是一个有主见且极简的 Coding Agent，这段时间爆火的 OpenClaw 就是基于此工具包构建的。上手了一段时间后，我敢说这就是目前最好用的 AI Agent CLI。\n很多项目爆火后都会迎来各种低质量的 Issues 和 AI 生成的 PR，而 Pi 这个名字非常有意思，会与数学信息产生关联，是作者有意取的，就是为了降低知名度。\n设计理念 翻译自 README\nPi 奉行近乎激进的可扩展性，因此无需、也不愿替你规定工作流。许多在别的工具中\u0026quot;内建\u0026quot;的能力，在这里都可通过 Extensions、Skills，或安装第三方 Pi Packages 来实现。这样既能让核心保持精简，又能让你按自己的工作方式塑造 Pi。\n不做 MCP。 你可以构建带有 README 的 CLI 工具（见 Skills），也可以编写 Extension，为 Pi 增加 MCP 支持。为何如此？ 不设 Sub-agents。 实现路径有很多：可借助 tmux 启动多个 Pi 实例，或用 Extensions 自行搭建，亦可安装按你思路实现的软件包。 不弹 Permission popups。 你可以在容器中运行，或通过 Extensions 构建与自身环境及安全要求相匹配的确认流程。 不设 Plan mode。 计划可直接写入文件，或借助 Extensions 自行实现，或安装相应软件包。 不内置 To-dos。 它们容易让模型困惑。请使用 TODO.md，或用 Extensions 自定义。 不提供后台 Bash。 请使用 tmux：全程可观测，交互更直接。 详情见作者博文，这位大佬的文章写的非常棒！\n快速上手 使用过程中有困难可以 Ask DeepWiki。\n安装 npm install -g @mariozechner/pi-coding-agent 接着可通过 pi 命令来启动，或为你的终端配置快捷键。\nWindows 注意：使用 Windows 的佬友还需要一个 Bash Shell。检查顺序：\n~/.pi/agent/settings.json 中的自定义路径 Git Bash (C:\\Program Files\\Git\\bin\\bash.exe) PATH 中的 bash.exe（如 Cygwin, MSYS2, WSL） 对于大多数佬友，Git for Windows 足够了。\n自定义 Shell 路径（settings.json）：\n{ \u0026#34;shellPath\u0026#34;: \u0026#34;C:\\\\cygwin64\\\\bin\\\\bash.exe\u0026#34; } Termux（Android） 见原文，因为要控制篇幅的原因便不再赘述。\n模型配置 配置好后可以通过 /model（或 Ctrl+L）选择模型。\nPi 有着优质的模型配置方式，但 OpenClaw 选择在此之上再造一层史，这让我非常不理解。\n订阅 对于具有以下订阅之一的佬友：\nClaude Pro/Max ChatGPT Plus/Pro (Codex) GitHub Copilot Google Gemini CLI Google Antigravity 可通过 /login 进行登入，使用 /logout 登出。认证 Token 会被储存在 ~/.pi/agent/auth.json。\nAPI 密钥 详见原文。\n可通过环境变量设置：\nexport ANTHROPIC_API_KEY=sk-ant-... pi 或写入 ~/.pi/agent/auth.json：\n{ \u0026#34;anthropic\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;api_key\u0026#34;, \u0026#34;key\u0026#34;: \u0026#34;sk-ant-...\u0026#34; }, \u0026#34;openai\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;api_key\u0026#34;, \u0026#34;key\u0026#34;: \u0026#34;sk-...\u0026#34; }, \u0026#34;google\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;api_key\u0026#34;, \u0026#34;key\u0026#34;: \u0026#34;...\u0026#34; }, \u0026#34;opencode\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;api_key\u0026#34;, \u0026#34;key\u0026#34;: \u0026#34;...\u0026#34; } } 供应商与环境变量对照表：\n供应商 环境变量 auth.json 键 Anthropic ANTHROPIC_API_KEY anthropic Azure OpenAI Responses AZURE_OPENAI_API_KEY azure-openai-responses OpenAI OPENAI_API_KEY openai Google Gemini GEMINI_API_KEY google Mistral MISTRAL_API_KEY mistral Groq GROQ_API_KEY groq Cerebras CEREBRAS_API_KEY cerebras xAI XAI_API_KEY xai OpenRouter OPENROUTER_API_KEY openrouter Vercel AI Gateway AI_GATEWAY_API_KEY vercel-ai-gateway ZAI ZAI_API_KEY zai OpenCode Zen OPENCODE_API_KEY opencode Hugging Face HF_TOKEN huggingface Kimi For Coding KIMI_API_KEY kimi-coding MiniMax MINIMAX_API_KEY minimax MiniMax (中国) MINIMAX_CN_API_KEY minimax-cn 默认情况下 auth.json 是携带 0600 权限创建的（仅用户可读/写），Auth 文件凭证优先于环境变量。\n第三方提供商 详见原文。\n创建 ~/.pi/agent/models.json 完整示例：\n{ \u0026#34;providers\u0026#34;: { \u0026#34;CloseAI\u0026#34;: { \u0026#34;baseUrl\u0026#34;: \u0026#34;https://api.closeai.ai/v1\u0026#34;, \u0026#34;api\u0026#34;: \u0026#34;openai-responses\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;sk-a1b1c4d5e14f5\u0026#34;, \u0026#34;models\u0026#34;: [ { \u0026#34;id\u0026#34;: \u0026#34;gpt-5.4\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;GPT-5.4\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;contextWindow\u0026#34;: 1000000, \u0026#34;maxTokens\u0026#34;: 128000, \u0026#34;cost\u0026#34;: { \u0026#34;input\u0026#34;: 0, \u0026#34;output\u0026#34;: 0, \u0026#34;cacheRead\u0026#34;: 0, \u0026#34;cacheWrite\u0026#34;: 0 } } ] } } } API 类型说明：\nAPI 描述 openai-completions OpenAI Chat Completions（最兼容） openai-responses OpenAI Responses API anthropic-messages Anthropic Messages API google-generative-ai Google Generative AI 在 providers 层级设置的 api，作为该 provider 下所有 models 的默认值。 在 models 层级中，单个模型可以通过自己的 api 字段覆盖这个默认值。 如遇报错 Error: 403 Your request was blocked.，说明请求被 CF 阻断。自定义请求头加上 UA 即可： \u0026#34;headers\u0026#34;: { \u0026#34;User-Agent\u0026#34;: \u0026#34;MyCustomClient/1.0\u0026#34; } 以下是御三家模型配置（要套到上面的完整配置中）：\n{ \u0026#34;models\u0026#34;: [ { \u0026#34;id\u0026#34;: \u0026#34;gpt-5.4\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;GPT 5.4\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;contextWindow\u0026#34;: 1000000, \u0026#34;maxTokens\u0026#34;: 128000 }, { \u0026#34;id\u0026#34;: \u0026#34;claude-opus-4-6\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;Claude Opus 4.6\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;contextWindow\u0026#34;: 200000, \u0026#34;maxTokens\u0026#34;: 128000 }, { \u0026#34;id\u0026#34;: \u0026#34;gemini-3.1-pro-preview\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;Gemini 3.1 Pro Preview\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;contextWindow\u0026#34;: 1048576, \u0026#34;maxTokens\u0026#34;: 65536 } ] } 每家站点的花费各不相同，根据情况修改。\n每次在 Pi 中键入 /model 时，文件都会重新加载，因此在会话期间编辑 models.json 无需重启。\n指南 详见 README。\n感受一下 TUI，OpenClaw 用的也是这个。可以编写主题和扩展进行爆改，甚至让 Pi 像 Claude Code。\n编辑器 （指的是输入框）\n功能 用法 文件引用 输入 @ 可模糊搜索项目文件 路径补全 按 Tab 自动补全路径 多行输入 Shift+Enter（Windows Terminal 下也可用 Ctrl+Enter） 图片 Ctrl+V 粘贴（Windows 下可用 Alt+V），或直接拖到终端 Bash 命令 !command 执行并把输出发给模型，!!command 执行但不发送输出 删除单词、撤销等使用标准编辑快捷键。详见此处。\n命令 在编辑器里输入 / 可触发命令。扩展可注册自定义命令，技能可用 /skill:name 调用，提示词模板可通过 /templatename 展开。\n命令 说明 /login, /logout OAuth 登录/退出 /model 切换模型 /scoped-models 启用/禁用 Ctrl+P 轮换可选模型 /settings 设置思考等级、主题、消息投递、传输方式 /resume 从历史会话中恢复 /new 新建会话 /name \u0026lt;name\u0026gt; 设置会话显示名称 /session 显示会话信息（路径、Token、费用） /tree 跳转到会话任意节点并从那继续 /fork 从当前分支创建新会话 /compact [prompt] 手动压缩上下文，可自定义压缩提示 /copy 复制助手上一条回复到剪贴板 /export [file] 导出会话为 HTML 文件 /share 上传为私有 GitHub Gist，并生成可分享 HTML 链接 /reload 重载扩展、技能、提示词、上下文文件（主题会自动热更新） /hotkeys 显示全部快捷键 /changelog 显示版本更新记录 /quit, /exit 退出 pi 消息队列 智能体工作时，你也可以继续发消息：\nEnter： 排入一条引导消息，会在当前工具执行完后立即送达（并中断后续未执行工具） Alt+Enter： 排入一条跟进消息，只会在代理完成全部工作后送达 Escape： 中止当前过程，并把已排队消息恢复到编辑器 Alt+Up： 把队列中的消息取回到编辑器 可在 /settings 配置投递方式：steeringMode 和 followUpMode 可设为 \u0026quot;one-at-a-time\u0026quot;（默认，收到回复后再发下一条）或 \u0026quot;all\u0026quot;（一次性发送队列全部消息）。transport 用于选择支持多传输的提供方通道偏好（\u0026quot;sse\u0026quot;、\u0026quot;websocket\u0026quot; 或 \u0026quot;auto\u0026quot;）。\n会话 会话以 JSONL 树结构保存。每条记录都有 id 和 parentId，所以可以在同一个文件里直接分支，不必新建文件。\n管理 会话会自动保存到 ~/.pi/agent/sessions/，并按工作目录（cwd）分组。\npi -c：继续最近一次会话 pi -r：浏览并选择历史会话 pi --no-session：临时模式（不保存会话） pi --session \u0026lt;path\u0026gt;：使用指定会话文件或会话 ID 分支 /tree：在当前会话文件内浏览会话树。你可以选中任意历史节点，从那继续，并在不同分支间切换。所有历史都保留在会话文件中。输入关键词可搜索，←/→ 翻页。过滤模式（Ctrl+O）：default → no-tools → user-only → labeled-only → all。按 l 可给条目标记书签。 /fork：从当前分支创建一个新的会话文件。系统会打开选择器，复制到所选节点为止的历史，并把该节点消息放入编辑器，方便你继续修改。 设置 使用 /settings 修改常用选项，或直接编辑 JSON 文件：\n位置 范围 ~/.pi/agent/settings.json 全局 .pi/settings.json 项目 详见此处。\n项目上下文 Pi 在启动时会从以下位置加载 AGENTS.md（或 CLAUDE.md）：\n~/.pi/agent/AGENTS.md（全局） 父目录（从当前工作目录向上查找） 当前目录 用于项目说明、约束和常用命令封装。所有匹配的 md 文件将被拼接在一起。\n系统提示 用 .pi/SYSTEM.md（项目）或 ~/.pi/agent/SYSTEM.md（全局）替换系统提示词，或通过 APPEND_SYSTEM.md 追加在系统提示词末尾。\n自定义 这部分的内容都可以封装为 Pi Package。这里整理了公开的 Pi 包：Packages - pi.dev\n提示词模板 将提示词封装为 Markdown 文件，输入 /文件名 展开。\n\u0026lt;!-- ~/.pi/agent/prompts/review.md --\u0026gt; Review this code for bugs, security issues, and performance problems. Focus on: {{focus}} 放置在 ~/.pi/agent/prompts/（全局）、.pi/prompts/（项目）或封装为 Pi Package 分享给别人。\n技能 按需加载的技能包，遵循 Agent Skills 标准。可通过输入 /skill:name 调用，也可让 Agent 自动加载。\n\u0026lt;!-- ~/.pi/agent/skills/my-skill/SKILL.md --\u0026gt; # My Skill Use this skill when the user asks about X. ## Steps 1. Do this 2. Then that 安装路径：\n全局： ~/.pi/agent/skills/、~/.agents/skills/ 项目： .pi/skills/、.agents/skills/（从当前工作目录向上逐级查找父目录） 或封装为 Pi Package。\nPi 作者维护的技能包，包含浏览器控制、Brave 搜索等技能，Pi 和其它支持 Skill 的项目都能直接使用。\n扩展 放入 ~/.pi/agent/extensions/（全局）、.pi/extensions/（项目）或封装为 Pi Package 分享给别人。\n参见文档和例子。\n主题 内置暗色与明亮，修改主题配置后可热重载。\n放入 ~/.pi/agent/themes/（全局）、.pi/themes/（项目）或封装为 Pi Package 分享给别人。\n详见此处。\n通过扩展与主题系统可以极大增强我们的使用体验！直接对模型说出需求即可，因为 Pi 的系统提示词中包含了 Pi 的文档路径。\nPi Telegram Bot 这是我用 Pi 编写出来的项目，可以在 TG 上与 Pi Agent 沟通。\n且值得一提的是，我没有蠢到像 OpenClaw 一样手写 MD 到 HTML 的转换（难绷），而是直接使用已有包进行转换和标签清洗。\n但目前暂时没有记忆系统，因为找不到合适的项目，希望佬友们能出谋划策。不过你可以把重要信息写入工作目录（cwd）下的 AGENTS.md，它会载入上下文。\n具体信息 README 写的很清晰就不再重复发一遍。使用过程中有困难可以 Ask DeepWiki。\nhttps://github.com/Ziphyrien/Pi-Telegram\n验证与自查 pi --version 正常输出版本号 输入 pi 启动后，/model（或 Ctrl+L）能打开模型选择器并列出已配置模型 发起一次对话能收到模型回复；/session 能显示当前会话的路径、Token 与费用 自定义了 models.json 后，在会话中 /model 一次即可热加载，无需重启 参考 Pi 官网（pi.dev） Pi Coding Agent GitHub 仓库 Pi Telegram Bot ","permalink":"https://heihei0299.github.io/posts/ai/pi-%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"pi-coding-agent-使用教程\"\u003ePi Coding Agent 使用教程\u003c/h1\u003e\n\u003cp\u003e本文是 Pi Coding Agent 的完整上手教程：从设计理念、安装，到模型配置、TUI 指南与自定义扩展，覆盖日常使用所需的全部操作。\u003c/p\u003e\n\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e按照作者的话来说，Pi Coding Agent 是一个有主见且极简的 Coding Agent，这段时间爆火的 OpenClaw 就是基于此工具包构建的。上手了一段时间后，我敢说这就是目前最好用的 AI Agent CLI。\u003c/p\u003e","title":"Pi Coding Agent 使用教程"},{"content":"Pi Coding Agent 快速上手指南与扩展推荐 本文教你快速上手 Pi Coding Agent：npm 安装、models.json 手动配置、公益站重试配置、22 个精选扩展推荐，以及 Windows Terminal 的已知问题。\n介绍 Pi 是一个类似 Claude Code / Codex / OpenCode 的 Coding Agent，但更精简更轻量。\n由于是快速上手教程，就不过多介绍了，详情可以看方生无归佬的贴子。\n安装 推荐用 npm 安装：\nnpm install -g --ignore-scripts @earendil-works/pi-coding-agent 手动配置 编辑 ~/.pi/agent/models.json，参考下面的配置，把你的 baseUrl、apiKey、models 信息添加进去。\n一般来说大部分 AI 服务都支持 openai chat 格式，所以 api 可以填 openai-completions。 如果有其他格式支持也可以填 openai-responses、anthropic-messages 等。\n{ \u0026#34;providers\u0026#34;: { \u0026#34;my-provider-1\u0026#34;: { \u0026#34;baseUrl\u0026#34;: \u0026#34;https://my-provider-1.com/v1\u0026#34;, \u0026#34;api\u0026#34;: \u0026#34;openai-responses\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;sk-******\u0026#34;, \u0026#34;headers\u0026#34;: { \u0026#34;User-Agent\u0026#34;: \u0026#34;claude-cli/2.1.217\u0026#34; }, \u0026#34;compat\u0026#34;: { \u0026#34;sendSessionAffinityHeaders\u0026#34;: true }, \u0026#34;models\u0026#34;: [ { \u0026#34;id\u0026#34;: \u0026#34;grok-4.5\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;contextWindow\u0026#34;: 500000, \u0026#34;maxTokens\u0026#34;: 128000 }, { \u0026#34;id\u0026#34;: \u0026#34;glm-5.2\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;], \u0026#34;contextWindow\u0026#34;: 1000000, \u0026#34;maxTokens\u0026#34;: 128000 } ] }, \u0026#34;my-provider-2\u0026#34;: { \u0026#34;baseUrl\u0026#34;: \u0026#34;https://my-provider-2.com/v1\u0026#34;, \u0026#34;api\u0026#34;: \u0026#34;openai-completions\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;sk-******\u0026#34;, \u0026#34;headers\u0026#34;: { \u0026#34;User-Agent\u0026#34;: \u0026#34;claude-cli/2.1.217\u0026#34; }, \u0026#34;compat\u0026#34;: { \u0026#34;sendSessionAffinityHeaders\u0026#34;: true }, \u0026#34;models\u0026#34;: [ { \u0026#34;id\u0026#34;: \u0026#34;minimax-m3\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;contextWindow\u0026#34;: 500000, \u0026#34;maxTokens\u0026#34;: 128000 }, { \u0026#34;id\u0026#34;: \u0026#34;deepseek-v4-pro\u0026#34;, \u0026#34;reasoning\u0026#34;: true, \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;], \u0026#34;contextWindow\u0026#34;: 1000000, \u0026#34;maxTokens\u0026#34;: 128000 } ] } } } 重试配置 配完 models.json 就已经可以输入 pi 开蹬了，但如果用的公益站，可能会遇到一些问题。\n公益站通常有 RPM 限制，而 Pi 默认的重试机制是 2 秒、4 秒、8 秒，重试 3 次后就会报错，导致任务中断。 公益站的 RPM 限制是以分钟为单位的，也就是说默认配置下触发了必定会报错。\n编辑 ~/.pi/agent/settings.json，把 retry 配置加进去，可以解决这个问题。\n{ \u0026#34;retry\u0026#34;: { \u0026#34;enabled\u0026#34;: true, \u0026#34;maxRetries\u0026#34;: 5, \u0026#34;baseDelayMs\u0026#34;: 15000 } } 扩展推荐 原生状态的 Pi 已经足以应对大部分的任务，如果想用得更顺手，可以安装一些 package。\n以下推荐 22 个精选 package，可按需安装。\n一键安装全部推荐扩展 pi install npm:@juanibiapina/pi-extension-settings npm:@juanibiapina/pi-powerbar npm:pi-hashline-edit-pro npm:pi-slopchop npm:@narumitw/pi-goal npm:@narumitw/pi-plan-mode npm:@narumitw/pi-subagents npm:@juicesharp/rpiv-ask-user-question npm:@juicesharp/rpiv-todo npm:@narumitw/pi-btw npm:pi-mcp-adapter npm:@ff-labs/pi-fff npm:pi-rtk-optimizer npm:pi-cache-optimizer npm:@narumitw/pi-lsp npm:pi-agent-browser-native npm:pi-add-dir npm:pi-workspace-history npm:@narumitw/pi-caffeinate npm:@tmustier/pi-raw-paste npm:@victor-software-house/pi-curated-themes npm:pi-autoresearch 扩展清单 pi-extension-settings 为扩展提供一个统一的配置命令 /extension-settings，目前只有 pi-powerbar 用到了。由于是扩展管理器，需要第一个安装，或者手动编辑 ~/.pi/agent/settings.json 将其挪到 packages 数组首位。\npi-powerbar 添加一个简洁的底部信息栏。\npi-hashline-edit-pro 把内置的 read 和 edit 替换成哈希版，或许可以改善读写准确性。\npi-slopchop 添加 /slopchop 和 /diff 命令，方便代码审查。\npi-goal 添加 /goal 命令。\npi-plan-mode 添加 /plan 命令。\npi-subagents 添加 subagent 工具，支持主 Agent 自主决定。\npi-autoresearch 添加 /autoresearch 命令，功能是针对指定目标自我迭代。\nrpiv-ask-user-question 添加多功能的 ask-user-question 工具。\nrpiv-todo 添加 todo 列表。\npi-btw 添加 /btw 命令。\npi-mcp-adapter 添加按需发现的 MCP 服务器适配器。\npi-fff 用 FFF 替换内置的 find 和 grep 工具。\npi-rtk-optimizer 自动调用 rtk 来压缩工具调用的输出，降低 Token 消耗。\npi-cache-optimizer 缓存优化器，在提示要修改 model 配置时可以调用 /cache-optimizer fix 自动修改。\npi-lsp 添加 lsp 配置。\npi-agent-browser-native 添加 agent_browser 工具，可以执行打开页面、截图、点击、填表等操作。\npi-add-dir 添加 /add-dir 命令。\npi-workspace-history 添加 /undo 命令。\npi-caffeinate 处理任务时阻止电脑休眠，适合开个 /goal 模式挂一晚上。\npi-raw-paste 添加 /paste 命令，直接贴入大段原始文本。\npi-curated-themes 添加一些主题，可以通过 /settings 命令切换。\n关于 oh-my-pi oh-my-pi (omp) 是一个 Pi 的 fork 版本，添加了很多开箱即用的功能，但也有过于臃肿之嫌。如有兴趣，也可以试试。\nbun install -g @oh-my-pi/pi-coding-agent 吐槽环节 如果是使用 Pi + Windows Terminal 的用户，可能会遇到窗口滚动条忽然跳到顶部的 bug。 Pi 社区说是 Windows Terminal 的渲染问题，不会做特殊处理，要等 Windows Terminal 修复。 以微软的效率，估计要几个月后了。 ╮（╯＿╰）╭\n验证与自查 pi --version 正常输出版本号 输入 pi 进入交互界面，/model 能列出 models.json 中配置的模型并成功切换 配置了 retry 后，遇到公益站限流时任务不会在 3 次重试后立即中断（观察日志确认按新配置重试） 安装了扩展后，用 /reload 重载，确认对应命令（如 /goal、/plan、/undo、/btw）可用 参考 Pi 官网（pi.dev） Pi Coding Agent GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/ai/pi-%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B%E6%8C%87%E5%8D%97%E4%B8%8E%E6%89%A9%E5%B1%95%E6%8E%A8%E8%8D%90/","summary":"\u003ch1 id=\"pi-coding-agent-快速上手指南与扩展推荐\"\u003ePi Coding Agent 快速上手指南与扩展推荐\u003c/h1\u003e\n\u003cp\u003e本文教你快速上手 Pi Coding Agent：npm 安装、models.json 手动配置、公益站重试配置、22 个精选扩展推荐，以及 Windows Terminal 的已知问题。\u003c/p\u003e","title":"Pi Coding Agent 快速上手指南与扩展推荐"},{"content":"DeepSeek 长上下文对话的幻觉与使用规范 本文适用于长期使用 DeepSeek 辅助开发的场景。你会学到：DeepSeek 在什么情况下容易产生幻觉、如何识别上下文超限的信号，以及降低幻觉的交流与工作流规范。\n存在的问题 以下是实际使用中观察到的三个问题，按影响程度排列：\n上下文超过 200k 后幻觉加重：一个窗口上下文一旦超过 200k，DeepSeek 不再遵守 agent.md 约定，也不会按照 trills 要求的步骤开发代码。 上下文混乱：内容过多导致 AI 注意力下降、幻觉加重，交流成本随之增加。 deepseek-v4-flash 擅自写代码：在讨论对话结束后，经常不等确认就开始写代码，不遵守 treills 的开发规范。 阈值参考：DeepSeek 系列模型的上下文窗口有限，长对话会挤占可用窗口并稀释注意力。建议在对话接近窗口上限前主动开启新会话，把上下文压缩成背景说明再继续。详见 DeepSeek 官方文档。\n自我反思与使用规范 不能许愿式编程：不能指望 AI 会严格按照约定的规范执行，规范必须落在可检查的交付物上（如 checklist、验收标准），而不是口头约定。 提高信息密度：与 AI 交流时描述不清楚、信息密度低，会导致严重的理解偏差。正确做法是把想法准确、结构化地传递给 AI，并假设 AI 会犯错——深入研究使用的技能组，观察 AI 的行为，出错后及时更正。 验证与自查 每次长对话开发会话结束后，对照检查：\n会话总字符数是否接近模型上下文上限（200k 量级）？若是，下次拆分为新会话并附上压缩后的背景。 检查 AI 是否在对话未结束时擅自写代码或跳步执行——出现即打断并回到规范流程。 核对最终产出是否满足 agent.md / treills 的交付物清单（如 prd、design、implement），不满足视为幻觉输出。 参考 DeepSeek 官方文档 DeepSeek 开放平台 ","permalink":"https://heihei0299.github.io/posts/ai-issues/deepseek%E4%BD%BF%E7%94%A8%E6%B1%87%E6%80%BB/","summary":"\u003ch1 id=\"deepseek-长上下文对话的幻觉与使用规范\"\u003eDeepSeek 长上下文对话的幻觉与使用规范\u003c/h1\u003e\n\u003cp\u003e本文适用于长期使用 DeepSeek 辅助开发的场景。你会学到：DeepSeek 在什么情况下容易产生幻觉、如何识别上下文超限的信号，以及降低幻觉的交流与工作流规范。\u003c/p\u003e","title":"DeepSeek 长上下文对话的幻觉与使用规范"},{"content":"treills 交付物清单与实现流程 本文适用于使用 treills 工作流驱动 AI 编码的场景。你会学到：每个开发任务必须产出的三份交付物，以及从评审到执行的标准流程。\n交付物清单 每个任务在进入编码前，必须先产出以下三份文档：\n文件 内容 prd.md 需求与验收标准 design.md 技术设计 implement.md 执行计划 三份文档按「做什么 → 怎么做 → 按什么顺序做」逐层细化。prd.md 未通过评审前不写 design.md，避免在错误的需求上做设计。\n实现流程 标准流程固定为四步，顺序不可跳过：\nreview 通过 → task.py start → dispatch review：评审 prd.md / design.md，确认需求与设计。 task.py start：通过任务脚本启动任务，锁定交付物。 dispatch：按 implement.md 的执行计划派发编码。 该流程与 OpenAI Agent 技能（skill）组织方式一致：把规范、清单写进技能文件，让 AI 按文件执行而非凭记忆发挥。参考 OpenAI Agents SDK 文档。\n验证与自查 编码开始前检查三份交付物是否齐全，缺失则退回 review 阶段。 implement.md 中的每步完成后，对照 prd.md 的验收标准逐条勾选。 任务收尾时确认执行顺序为 review → task.py start → dispatch，无跳步。 参考 OpenAI Agents SDK 文档 OpenAI 平台文档 ","permalink":"https://heihei0299.github.io/posts/ai-issues/%E5%B8%B8%E7%94%A8%E6%8C%87%E4%BB%A4%E6%B1%87%E6%80%BB/","summary":"\u003ch1 id=\"treills-交付物清单与实现流程\"\u003etreills 交付物清单与实现流程\u003c/h1\u003e\n\u003cp\u003e本文适用于使用 treills 工作流驱动 AI 编码的场景。你会学到：每个开发任务必须产出的三份交付物，以及从评审到执行的标准流程。\u003c/p\u003e\n\u003ch2 id=\"交付物清单\"\u003e交付物清单\u003c/h2\u003e\n\u003cp\u003e每个任务在进入编码前，必须先产出以下三份文档：\u003c/p\u003e","title":"treills 交付物清单与实现流程"},{"content":"Mihomo 流量路径分析报告 基于本机（Arch Linux, Mihomo Clash.Meta 内核）实际配置，分析三类流量在 nftables + ip rule + TUN 三层拦截机制下的完整路径。\n1. 环境概况 1.1 系统信息 项目 值 OS Arch Linux 内核 Linux 6.x Mihomo 版本 Clash.Meta 内核 运行命令 /opt/mihomo/bin/mihomo -d /opt/mihomo/etc PID 1057 运行方式 systemd service（enabled） 配置文件 /opt/mihomo/etc/config.yaml 1.2 Mihomo 监听端口 端口 类型 绑定地址 用途 10810 mixed (HTTP+SOCKS) 127.0.0.1 通用代理入站 11000 socks5 127.0.0.1 socks5-in-1 11001 mixed 127.0.0.1 direct 入站（强制 DIRECT） 9090 HTTP 127.0.0.1 external-controller (API) 46569 TCP 0.0.0.0 透明代理（nftables redirect 目标） 1.3 Docker 容器 容器 网络 IP 端口映射 axonhub axonhub_default (bridge) 172.18.0.2 127.0.0.1:8090 → 8090/tcp Docker 网络设备：\nbr-700ced5fc520（axonhub_default 桥接）：172.18.0.1/16 docker0（默认桥接）：172.17.0.1/16（当前 linkdown） 1.4 物理网络 接口 IP 用途 wlp0s20f3 192.168.1.5/24 Wi-Fi 物理网卡 Meta 198.18.0.1/30 Mihomo TUN 虚拟网卡（fake-ip 范围） 2. 核心配置 2.1 Mihomo TUN 配置 tun: enable: true stack: mixed dns-hijack: - \u0026#34;any:53\u0026#34; - \u0026#34;tcp://any:53\u0026#34; auto-route: true strict-route: true auto-redirect: true auto-detect-interface: true dns: enable: true enhanced-mode: fake-ip fake-ip-filter: - geosite:cn - geosite:category-ads-all ```text ### 2.2 TUN 模式详解 `auto-route: true` + `strict-route: true` 使 Mihomo 在启动时自动添加： - **ip rule**（策略路由规则） - **nftables**（`table inet mihomo`，prerouting + output 链） 两层拦截覆盖不同的协议和路径。 --- ## 3. 拦截机制详解 ### 3.1 nftables 规则集 ```nft table inet mihomo { set inet4_local_address_set { type ipv4_addr flags interval elements = { 127.0.0.0/8, 198.18.0.0/30 } } chain output { type nat hook output priority mangle; policy accept; oifname \u0026#34;Meta\u0026#34; meta nfproto ipv4 meta l4proto tcp counter redirect to :46569 return } chain prerouting { type nat hook prerouting priority dstnat + 1; policy accept; # 从 TUN 回来的包跳过（防循环） iifname \u0026#34;Meta\u0026#34; counter return # 本地 fake-ip 源发出的 DNS 查询 → DNAT 到 TUN 设备 ip saddr @inet4_local_address_set meta l4proto { tcp, udp } th dport 53 counter dnat ip to 198.18.0.2 # 目标为本地地址的包跳过 ip daddr @inet4_local_address_set counter return # MPTCP 选项丢弃 tcp option mptcp exists counter drop # IPv6 拒绝 meta nfproto ipv6 counter reject with icmpv6 no-route # ★ 核心规则：所有 IPv4 TCP → redirect 到透明代理端口 :46569 meta nfproto ipv4 meta l4proto tcp counter redirect to :46569 return } } ```text ### 3.2 Docker 相关 nftables 表 ```nft # 由 iptables-nft 管理，非 Mihomo 创建 table ip nat { chain PREROUTING { type nat hook prerouting priority dstnat; ... jump DOCKER } chain OUTPUT { type nat hook output priority dstnat; ... jump DOCKER } chain POSTROUTING { type nat hook postrouting priority srcnat; policy accept; ip saddr 172.17.0.0/16 oifname != \u0026#34;docker0\u0026#34; MASQUERADE ip saddr 172.18.0.0/16 oifname != \u0026#34;br-700ced5fc520\u0026#34; MASQUERADE } } table ip filter { chain FORWARD { type filter hook forward priority filter; policy drop; jump DOCKER-USER jump DOCKER-FORWARD } chain DOCKER-FORWARD { jump DOCKER-CT jump DOCKER-INTERNAL jump DOCKER-BRIDGE iifname \u0026#34;br-700ced5fc520\u0026#34; accept iifname \u0026#34;docker0\u0026#34; accept } } table ip raw { chain PREROUTING { type filter hook prerouting priority raw; policy accept; ip daddr 172.18.0.2 iifname != \u0026#34;br-700ced5fc520\u0026#34; drop ip daddr 127.0.0.1 iifname != \u0026#34;lo\u0026#34; tcp dport 8090 drop } } ```text ### 3.3 ip rule（策略路由） PRI 规则 → 路由表 ──────────────────────────────────────────────────────────────── 0 from all lookup local → local 表 9000 from all to 198.18.0.0/30 lookup 2022 → 表 2022（fake-IP 回包） 9001 not from all dport 53 lookup main suppress 0 → main（DNS 绕过） 9001 from all iif Meta goto 9010 → 跳过（TUN 回包防循环） 9002 ★ not from all iif lo lookup 2022 → 表 2022（非 lo → TUN） 9002 from 0.0.0.0 iif lo lookup 2022 → 表 2022（lo → TUN） 9002 from 198.18.0.0/30 iif lo lookup 2022 → 表 2022（fake-IP → TUN） 32766 from all lookup main → main 表 32767 from all lookup default → default 表\n### 3.4 路由表 2022 default via 198.18.0.2 dev Meta\n### 3.5 路由表 main（摘要） default via 192.168.1.1 dev wlp0s20f3 proto dhcp metric 20600 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 172.18.0.0/16 dev br-700ced5fc520 proto kernel scope link src 172.18.0.1 192.168.1.0/24 dev wlp0s20f3 proto kernel scope link src 192.168.1.5 metric 600 198.18.0.0/30 dev Meta proto kernel scope link src 198.18.0.1\n--- ## 4. 流量路径分析 ### 4.1 本机程序发出的 TCP 流量 **场景：** `firefox` 等本地进程访问 `https://www.google.com:443`（IP: 142.250.80.68） 本机 socket（src=192.168.1.5, dst=142.250.80.68:443） │ ▼ 路由决策（ip rule 逐条检查） ├── rule 0（local）: 目标不是本地地址 → 不匹配 ├── rule 9000（fake-IP）: 目标不在 198.18.0.0/30 → 不匹配 ├── rule 9001（DNS, port 53）: 不匹配 ├── rule 9001（iif Meta）: iif ≠ Meta → 不匹配 ├── rule 9002（not iif lo）: 本机出站，iif=lo → 不匹配 └── rule 9002（from 0.0.0.0 iif lo）: ★ 匹配！ → 查路由表 2022 → default via 198.18.0.2 dev Meta → 此时 oifname = \u0026ldquo;Meta\u0026rdquo; │ ▼ nftables OUTPUT hook（type nat hook output priority mangle） │ ├── 其他规则不匹配 └── ★ oifname \u0026ldquo;Meta\u0026rdquo; meta nfproto ipv4 meta l4proto tcp → redirect to :46569 → 目标改为 127.0.0.1:46569，源维持 192.168.1.5 │ ▼ 路由决策（第二次） → 目标 127.0.0.1 → 本地投递 → 经 INPUT → Mihomo socket (:46569) │ ▼ Mihomo 核心引擎 → [TCP] 192.168.1.5:xxxxx \u0026ndash;\u0026gt; 142.250.80.68:443 match GeoSite(geolocation-!cn) using SELECT[🇺🇸US-DMIT] → 经代理节点（VLESS XTLS）发出\n**关键点：** - 路由决策决定了 oif = Meta，但包并未实际到达 TUN 设备 - OUTPUT hook 在路由决策之后执行，利用了已知的 `oifname` - redirect 到 `:46569` 后重新路由为本地投递 --- ### 4.2 本机程序发出的 ICMP/UDP 流量 **场景：** `ping 1.1.1.1`（ICMP echo） 本机 socket（src=192.168.1.5, dst=1.1.1.1, ICMP） │ ▼ 路由决策 ├── rule 9002（from 0.0.0.0 iif lo） → ★ 匹配 └── table 2022 → default via 198.18.0.2 dev Meta │ ▼ nftables OUTPUT hook └── oifname \u0026ldquo;Meta\u0026rdquo; \u0026hellip; tcp → 不匹配（ICMP ≠ tcp） → 放行 │ ▼ 到达 TUN 设备 Meta │ ▼ Mihomo TUN 栈读取原始 ICMP 包 │ ├── ICMP 不在代理核心引擎处理范围 │ （VLESS/XTLS 隧道无法承载 ICMP） │ └── TUN 栈执行 NAT： src = 192.168.1.5 → 198.18.0.1（TUN 接口地址） → 写回 TUN 设备 │ ▼ ip rule（再次，此时 iif=Meta） ├── rule 9001（iif Meta goto 9010） → ★ 匹配 └── 跳转到 9010（nop） │ ▼ 查找 main 路由表 └── default via 192.168.1.1 dev wlp0s20f3 │ ▼ 物理网卡发出（src=198.18.0.1 → 主机再次 SNAT 为 192.168.1.5）\n**关键点：** - ICMP 和 UDP 不触发 nftables OUTPUT 的 `tcp redirect`，实际到达 TUN 设备 - TUN 栈直接 NAT 转发，不经过核心引擎的规则匹配 - 日志中表现为 `write ICMPv4 echo request` / `read ICMPv4 echo reply`，无 `[TCP]` 条目 --- ### 4.3 Docker 容器发出的 TCP 流量 **场景：** `docker exec axonhub wget http://1.1.1.1` 容器 axonhub（172.18.0.2:xxxxx → 1.1.1.1:80, TCP SYN） │ ▼ veth pair → br-700ced5fc520 │ iif = br-700ced5fc520 ▼ ip raw PREROUTING（priority raw） └── 172.18.0.2 是容器地址，检查通过 │ ▼ nftables prerouting（priority dstnat + 1） │ ├── iifname \u0026ldquo;Meta\u0026rdquo; return ❌ iif=br-700ced5fc520 ├── dport 53 dnat ❌ 非 DNS ├── daddr local set return ❌ 1.1.1.1 不在本地 ├── tcp mptcp drop ❌ 无 MPTCP ├── ipv6 reject ❌ IPv4 └── ★ meta nfproto ipv4 meta l4proto tcp → redirect to :46569 → 目标改为 127.0.0.1:46569 → 源保持 172.18.0.2（容器原始 IP） │ ▼ 路由决策（因目标变为 127.0.0.1 → 本地投递） └── 经 INPUT → Mihomo socket (:46569) │ ▼ Mihomo 核心引擎 ├── 从 socket 读取包，识别 src=172.18.0.2 │ ├── 容器 DNS（前置步骤）： │ 容器 → 127.0.0.11（Docker 内置 DNS） │ → 转发到 192.168.1.1:53（宿主机 DNS） │ → 包经 br-700ced5fc520 → prerouting │ → iif=br-700ced5fc520, src=172.18.0.2 │ → 不匹配 dport 53 DNAT 规则（源不在 local set 内） │ → tcp redirect 匹配 → DNS (TCP 53) 也被 redirect │ → Mihomo dns-hijack 劫持 → fake-ip 响应 │ └── 日志输出： [TCP] 172.18.0.2:56690 \u0026ndash;\u0026gt; 1.1.1.1:443 match Match using SELECT[🇺🇸US-DMIT] │ ▼ 经代理节点发出 └── 实际物理网卡上看到的是： 192.168.1.5:xxxxx → 154.17.224.88:50025（代理节点）\n**关键点：** - **容器源 IP（172.18.0.2）完整保留**，未经 SNAT - 原因：`redirect` 在 `prerouting`（路由决策前）发生，将目标改为 `127.0.0.1` - 重新路由判定为本地投递，不经过 `POSTROUTING`（Docker SNAT 在 POSTROUTING） - `ip rule 9002` 也**不参与**，因为包在 nftables 阶段已被截获 **实测验证：** ```bash # nftables prerouting 计数器 # 容器 wget 前：counter packets 29 bytes 1740 # 容器 wget 后：counter packets 32 bytes 1920 ← 增量 3（SYN+请求+ACK） # Mihomo 日志 [TCP] 172.18.0.2:56690 --\u0026gt; 1.1.1.1:443 match Match using SELECT[🇺🇸US-DMIT] ↑ 容器原始 IP，未 SNAT ```text --- ### 4.4 Docker 容器发出的 ICMP/UDP 流量 **场景：** `docker exec axonhub ping 1.1.1.1` 容器 axonhub（172.18.0.2 → 1.1.1.1, ICMP echo） │ ▼ br-700ced5fc520（iif = br-700ced5fc520） │ ▼ nftables prerouting └── meta l4proto tcp redirect → 不匹配（ICMP ≠ tcp） → 放行 │ ▼ ip rule ├── rule 0（local）: 1.1.1.1 不在本地 → 不匹配 ├── rule 9000（fake-IP）: 不匹配 ├── rule 9001（port 53）: 不匹配 ├── rule 9001（iif Meta）: iif=br-700ced5fc520 → 不匹配 └── ★ rule 9002（not iif lo）: iif=br-700ced5fc520 ≠ lo → 匹配 → table 2022 → default via 198.18.0.2 dev Meta │ ▼ TUN 设备 Meta │ ▼ Mihomo TUN 栈 └── ICMP 不进核心引擎 → TUN 栈 NAT：src = 172.18.0.2 → 198.18.0.1 → 写回 TUN 设备 │ ▼ ip rule（iif=Meta） └── 9001（iif Meta goto 9010）→ 跳过 │ ▼ main 路由表 └── default via 192.168.1.1 dev wlp0s20f3 │ ▼ 物理网卡发出（src=198.18.0.1 → SNAT 为 192.168.1.5）\n**实测验证：** ```bash # 容器 ping 1.1.1.1 → 192ms（经代理转发的典型延迟） # 容器 ping 192.168.1.1 → 1.2ms（直连延迟） # Mihomo debug 日志 write ICMPv4 echo request from 198.18.0.1 to 1.1.1.1 id 19022 seq 166 read ICMPv4 echo reply from 1.1.1.1 to 198.18.0.1 id 19022 seq 166 # 注意 src 是 198.18.0.1（TUN 接口），不是 172.18.0.2（容器源 IP） # 且无 [TCP] 规则匹配记录 ```text **TCP vs ICMP 路径差异总结：** | 维度 | 容器 TCP | 容器 ICMP/UDP | |------|---------|--------------| | 拦截点 | nftables prerouting redirect :46569 | ip rule 9002 → TUN | | 源 IP | 172.18.0.2（原始，未变） | 198.18.0.1（TUN 栈 NAT） | | 经过核心引擎 | ✅ 是（规则匹配、代理选择） | ❌ 否（TUN 栈直接转发） | | 规则引擎日志 | `[TCP] 172.18.0.2:xxx → ... match ...` | 无 `[TCP]` 日志 | --- ### 4.5 外部入站流量 **场景：** 外部机器 `203.0.113.5` 访问本机 `192.168.1.5` #### 4.5.1 外部 TCP 入站（理论分析） 外部 → wlp0s20f3（iif=wlp0s20f3） │ ▼ nftables prerouting └── ★ meta l4proto tcp redirect to :46569 → 匹配！ → 目标改为 127.0.0.1:46569 → 源保持 203.0.113.5 │ ▼ 路由决策：本地投递 → INPUT → Mihomo socket │ ▼ Mihomo 核心引擎 └── 将此 TCP 连接视为透明代理请求处理\n**问题：** 所有外部入站 TCP（如 SSH、HTTP 服务等）会被强制吸入 Mihomo 透明代理端口，可能导致： - 外部 SSH 到本机无法正常建立 - 需在 nftables 中添加豁免规则 **验证盲区：** 本机 SSH `192.168.1.5` 实际走 OUTPUT hook（`oif=lo`），不经过 PREROUTING，无法验证。需另一台物理机或虚拟机从外部发起连接。 #### 4.5.2 外部 ICMP/UDP 入站（经实验验证） 外部 → wlp0s20f3（iif=wlp0s20f3） │ ▼ nftables prerouting └── tcp redirect → 不匹配（ICMP/UDP ≠ tcp）→ 放行 │ ▼ ip rule ├── ★ rule 0（from all lookup local） │ → 目标 192.168.1.5 在本地（local 表有对应路由） │ → 最高优先级匹配 → 本地投递 │ ├── rule 9000/9001/9002 → 永远不会检查到 │ （因为 rule 0 已匹配） │ ▼ 正常交付给本地对应 UDP socket / ICMP handler\n**实测验证：** ```bash # 从本机 ping 自己的外部 IP ping 192.168.1.5 # 在 Meta 设备上 tcpdump 监听 sudo tcpdump -ni Meta icmp # → 没有抓到包 ```text 得出结论：外部入站 ICMP/UDP **不被吸入 TUN**，因为 `ip rule 0`（`lookup local`）优先级最高，目的地为本机的包直接被 local 表捕获，正常交付。 \u0026gt; **注意：** 此实验使用本机 ping 自己的外部 IP。严格来说数据包在本机内部经由路由/lo 处理，与真正物理网卡上外部流量仍有差异。但基于 rule 0 的优先级分析，结论应是可靠的。 --- ## 5. 关键机制总结表 | 协议 | 流量来源 | 拦截点 | 规则/链 | 是否进核心引擎 | 源 IP 变化 | |------|---------|--------|---------|--------------|-----------| | TCP | 本机 | OUTPUT hook | `oif=Meta tcp redirect :46569` | ✅ | 保持（192.168.1.5） | | TCP | 容器 | PREROUTING hook | `meta l4proto tcp redirect :46569` | ✅ | 保持（172.18.0.2） | | TCP | 外部入站 | PREROUTING hook | `meta l4proto tcp redirect :46569` | ✅（有风险） | 保持（外部 IP） | | ICMP/UDP | 本机 | ip rule 9002 | `not iif lo → table 2022 → Meta` | ❌ | 改为 198.18.0.1 | | ICMP/UDP | 容器 | ip rule 9002 | `not iif lo → table 2022 → Meta` | ❌ | 改为 198.18.0.1 | | ICMP/UDP | 外部入站 | ip rule 0（local） | `from all lookup local` | ❌ | 不变（正常交付） | ### 拦截优先级链 ip rule 0（local） → 最高优先级，目的为本机的包被拦截 ↓ nftables prerouting（redirect） → TCP 在此处被截获改写目标 ↓ ip rule 9000/9001/9002 → ICMP/UDP 在此处被转向 TUN ↓ nftables output（redirect） → 本机 TCP 在路由决策后 oif=Meta 时被截\n--- ## 6. TUN 栈与核心引擎的区分 Mihomo 内部有两个不同的处理层： | 层 | 处理对象 | 行为 | |----|---------|------| | **TUN 栈** | 从 TUN 设备读取的原始 IP 包 | 对 TCP 送核心引擎；对 ICMP/UDP 做 NAT 后直接转发 | | **核心引擎** | 从 socket 收到的流（代理端口 / 透明代理端口） | 规则匹配 → 代理选择 → 协议封装 → 经代理节点发出 | 日志中可清晰区分： - `[TCP]` 开头 → 核心引擎处理 - `[TUN]`、`write/read ICMPv4` 开头 → TUN 栈处理 --- ## 7. 盲点与待验证项 ### 7.1 外部 TCP 入站测试 **待验证：** 从另一台物理机/虚拟机 SSH 到本机 `192.168.1.5:22`，观察是否被 `prerouting redirect :46569` 拦截破坏。 **修复方案（如需）：** ```bash nft add rule inet mihomo prerouting ip daddr 192.168.1.5 tcp dport {22, 9090} return ```text ### 7.2 可能的 `bypass` 规则 当前 Mihomo 规则中有 `GEOIP,lan,DIRECT,no-resolve`，但这条规则在**核心引擎**内生效，不阻止 TCP 被 redirect 截获。入站 TCP 在 nftables 阶段就被拦截，核心引擎的规则无法阻止这一过程。 ### 7.3 宿主机/容器出站延迟差异 容器 TCP 和本机 TCP 都经核心引擎代理，但路径不同： - 本机：OUTPUT redirect → socket - 容器：PREROUTING redirect → socket 两者理论上延迟一致，可通过实际测试验证是否存在差异。 --- ## 验证与自查 1. `ip route get 1.1.1.1` 与 `ip route get 1.1.1.1 from 172.18.0.2 iif br-700ced5fc520` 分别模拟本机/容器流量，均落入表 2022（dev Meta） 2. `sudo nft list chain inet mihomo prerouting | grep \u0026#34;redirect to :46569\u0026#34;` 观察计数器：容器一次 wget 后增量约为 3（SYN+请求+ACK） 3. 容器 `ping 1.1.1.1` 延迟约 192ms（经代理转发），`ping 192.168.1.1` 约 1.2ms（直连），对比确认转发路径 4. Mihomo debug 日志可区分两层处理：`[TCP]` 开头为核心引擎，`write/read ICMPv4` 开头为 TUN 栈 NAT 转发 ## 附录 ### A. 常用诊断命令 ```bash # 查看 nftables 规则集 sudo nft list ruleset # 查看策略路由规则 ip rule show # 查看所有路由表 ip route show table all # 查看特定路由表 ip route show table 2022 ip route show table main # 模拟路由决策（查看包会走哪个路由表） ip route get 1.1.1.1 from 172.18.0.2 iif br-700ced5fc520 # 查看 nftables 计数器 sudo nft list chain inet mihomo prerouting | grep \u0026#34;redirect to :46569\u0026#34; # 查看 Mihomo debug 日志 sudo journalctl -u mihomo --since \u0026#34;1 min ago\u0026#34; # 抓包 sudo tcpdump -ni Meta icmp sudo tcpdump -ni lo port 46569 sudo tcpdump -ni wlp0s20f3 \u0026#39;tcp[tcpflags] \u0026amp; (tcp-syn) != 0 and tcp[tcpflags] \u0026amp; (tcp-ack) == 0\u0026#39; # conntrack 连接跟踪（需 root） sudo conntrack -E -p tcp --dport 46569 ```text ### B. 相关文件路径 | 文件 | 路径 | |------|------| | Mihomo 配置文件 | `/opt/mihomo/etc/config.yaml` | | Mihomo 二进制 | `/opt/mihomo/bin/mihomo` | | Systemd unit | `/etc/systemd/system/mihomo.service` | | 旧配置备份 | `/etc/mihomo/config.yaml` | ### C. 对话中关键日志输出 容器 TCP → 核心引擎 [TCP] 172.18.0.2:56690 \u0026ndash;\u0026gt; 1.1.1.1:443 match Match using SELECT[🇺🇸US-DMIT] [TCP] 172.18.0.2:46184 \u0026ndash;\u0026gt; 1.1.1.1:80 match Match using SELECT[🇺🇸US-DMIT]\n容器 ICMP → TUN 栈 NAT 转发 write ICMPv4 echo request from 198.18.0.1 to 1.1.1.1 id 19022 seq 166 read ICMPv4 echo reply from 1.1.1.1 to 198.18.0.1 id 19022 seq 166\n本机 TCP → 核心引擎 + 代理节点 192.168.1.5:57870 \u0026gt; 154.17.224.88:50025: Flags [S] # 代理节点出站\nnftables prerouting redirect 计数器增量 29 → 32（一次容器 wget 请求） ## 参考 - [Mihomo 官方文档](https://wiki.metacubex.one/) - [Mihomo GitHub 仓库](https://github.com/MetaCubeX/mihomo) - [nftables 官方 wiki](https://wiki.nftables.org/) - [Arch Wiki: nftables](https://wiki.archlinux.org/title/Nftables) - [ip-rule 手册页](https://man7.org/linux/man-pages/man8/ip-rule.8.html) ","permalink":"https://heihei0299.github.io/posts/mihomo-any/traffic-analysis/","summary":"\u003ch1 id=\"mihomo-流量路径分析报告\"\u003eMihomo 流量路径分析报告\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e基于本机（Arch Linux, Mihomo Clash.Meta 内核）实际配置，分析三类流量在\nnftables + ip rule + TUN 三层拦截机制下的完整路径。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"1-环境概况\"\u003e1. 环境概况\u003c/h2\u003e\n\u003ch3 id=\"11-系统信息\"\u003e1.1 系统信息\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e项目\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e值\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eOS\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eArch Linux\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e内核\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eLinux 6.x\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eMihomo 版本\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eClash.Meta 内核\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e运行命令\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e/opt/mihomo/bin/mihomo -d /opt/mihomo/etc\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003ePID\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e1057\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e运行方式\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003esystemd service（enabled）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e配置文件\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e/opt/mihomo/etc/config.yaml\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"12-mihomo-监听端口\"\u003e1.2 Mihomo 监听端口\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e端口\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e类型\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e绑定地址\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e用途\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e10810\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003emixed (HTTP+SOCKS)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e127.0.0.1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e通用代理入站\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e11000\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003esocks5\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e127.0.0.1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003esocks5-in-1\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e11001\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003emixed\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e127.0.0.1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003edirect 入站（强制 DIRECT）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e9090\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHTTP\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e127.0.0.1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eexternal-controller (API)\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e46569\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eTCP\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0.0.0.0\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e透明代理（nftables redirect 目标）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"13-docker-容器\"\u003e1.3 Docker 容器\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e容器\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e网络\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eIP\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e端口映射\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eaxonhub\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eaxonhub_default (bridge)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e172.18.0.2\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e127.0.0.1:8090 → 8090/tcp\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eDocker 网络设备：\u003c/p\u003e","title":"Mihomo 流量路径分析报告"},{"content":"路由表、ip rule 与 nftables：Linux 三层流量劫持机制解析 以本机 Mihomo（Clash.Meta）TUN 模式 + Docker 容器网络为实例，逐层拆解\n本文适合同时想理解概念和看真实案例的读者。\n1. 路由表（Routing Table） 1.1 什么是路由表 路由表是内核中用于决定数据包下一跳的规则集合。最简单的形式：\n目标网络 → 从哪个接口出去 → 经过哪个网关 传统 Linux 只有一张路由表（main），所有路由决策都查它。但现代 Linux 支持多路由表，允许根据不同条件选择不同路由表。\n1.2 标准路由表 Linux 预定义了三张路由表：\n表名 ID 用途 local 255 本地地址和广播地址，由内核自动维护 main 254 主路由表，ip route 不加 table 参数时操作的就是它 default 253 默认路由表（通常为空） 自定义表可以使用 1~252 之间的任意 ID。本机 Mihomo 创建了一张 2022 表。\n1.3 查看所有路由表 ip route show table all ```text 本机输出（仅 IPv4，已过滤 IPv6）： ─── 表 2022（Mihomo 创建）─── default via 198.18.0.2 dev Meta table 2022\n─── 表 main（ID 254）─── default via 192.168.1.1 dev wlp0s20f3 proto dhcp src 192.168.1.5 metric 20600 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 172.18.0.0/16 dev br-700ced5fc520 proto kernel scope link src 172.18.0.1 192.168.1.0/24 dev wlp0s20f3 proto kernel scope link src 192.168.1.5 metric 600 198.18.0.0/30 dev Meta proto kernel scope link src 198.18.0.1\n─── 表 local（ID 255）─── local 127.0.0.0/8 dev lo table local proto kernel scope host src 127.0.0.1 local 127.0.0.1 dev lo \u0026hellip; local 172.17.0.1 dev docker0 \u0026hellip; local 172.18.0.1 dev br-700ced5fc520 \u0026hellip; local 192.168.1.5 dev wlp0s20f3 \u0026hellip; local 198.18.0.1 dev Meta \u0026hellip; broadcast \u0026hellip;（各种广播地址）\n### 1.4 拆解 main 表 ```bash default via 192.168.1.1 dev wlp0s20f3 proto dhcp src 192.168.1.5 metric 20600 ```text - `default` — 目标 0.0.0.0/0（所有未匹配更具体路由的流量） - `via 192.168.1.1` — 下一跳网关（路由器） - `dev wlp0s20f3` — 从 Wi-Fi 网卡发出 - `proto dhcp` — 由 DHCP 自动添加 - `src 192.168.1.5` — 使用本机 IP 作为源地址 - `metric 20600` — 路由优先级（数值越小优先级越高） 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 172.18.0.0/16 dev br-700ced5fc520 proto kernel scope link src 172.18.0.1\n这两条是 Docker 创建的桥接网络路由： - `172.17.0.0/16` → `docker0`（当前未连接，显示 `linkdown`） - `172.18.0.0/16` → `br-700ced5fc520`（Docker Compose 创建的 axonhub 网络） - `scope link` — 直连网络，不经过网关 192.168.1.0/24 dev wlp0s20f3 proto kernel scope link src 192.168.1.5 metric 600\n物理局域网路由：到 `192.168.1.0/24` 的流量从 `wlp0s20f3` 直连发出。 198.18.0.0/30 dev Meta proto kernel scope link src 198.18.0.1\nMihomo TUN 设备 `Meta` 的直连路由。`198.18.0.0/30` 只有两个可用 IP：`198.18.0.1`（本机 TUN 接口）和 `198.18.0.2`（TUN 对端，Mihomo 进程侧）。 ### 1.5 拆解表 2022 ```bash default via 198.18.0.2 dev Meta table 2022 ```text 这张表只有**一条默认路由**：所有匹配这条表的流量都指向 `Meta` TUN 设备，下一跳是 `198.18.0.2`（Mihomo 进程端）。 关键问题是：**什么流量会进入这张表？** 这取决于 `ip rule`——下一章的主题。 --- ## 2. 策略路由（ip rule） ### 2.1 什么是策略路由 传统路由只用**目标 IP** 查路由表。策略路由允许在查表之前附加条件： if（数据包满足条件 X）→ 查路由表 A else → 查路由表 B\n条件可以是：源 IP、入站接口、目标端口、防火墙标记等。 ### 2.2 查看规则 ```bash ip rule show ```text 本机输出： 0: from all lookup local 9000: from all to 198.18.0.0/30 lookup 2022 9001: not from all dport 53 lookup main suppress_prefixlength 0 9001: from all iif Meta goto 9010 9002: not from all iif lo lookup 2022 9002: from 0.0.0.0 iif lo lookup 2022 9002: from 198.18.0.0/30 iif lo lookup 2022 9010: from all nop 32766: from all lookup main 32767: from all lookup default\n\u0026gt; **重要：** 规则按优先级从小到大依次检查，第一个匹配的生效。 ### 2.3 逐条解析 #### 规则 `0: from all lookup local` - **含义：** 所有流量先查 `local` 表 - **作用：** 目标为本机 IP 的包在这里被匹配并本地投递 - **效果：** 防止本机地址被后续规则劫持。ICMP ping 本机外部 IP 时，rule 0 直接匹配，不会落入 TUN #### 规则 `9000: from all to 198.18.0.0/30 lookup 2022` - **含义：** 目标地址是 `198.18.0.0/30`（fake-IP 范围）→ 查表 2022 → 走 TUN - **作用：** 当 Mihomo 的 fake-IP DNS 返回 198.18.x.x 地址后，对这个地址的访问被路由到 TUN，Mihomo 才能从 TUN 读取到真实请求 - **为什么只有 `/30`：** Mihomo 只用了 `198.18.0.1` 和 `198.18.0.2` 两个地址 #### 规则 `9001: not from all dport 53 lookup main suppress_prefixlength 0` **这是第一个需要仔细理解的难点。** - **解析语法：** `not` 否定 `from all dport 53` 这个整体条件 - **实际含义：** 如果数据包**不是**（来自任意源且目标端口为 53）→ 查 main 表，但抑制前缀长度为 0 的条目 - **更直白的理解：** DNS 查询（目标端口 53）**不匹配**此规则，会继续检查后续规则；**非 DNS** 流量匹配此规则，被送到 main 表且屏蔽默认路由 **等一下，这似乎不合理——非 DNS 流量去 main 表且抑制默认路由，那不就路由失败了吗？** 实际上这正是 Mihomo 的意图： \u0026gt; 这条规则起到一个**旁路标记**的作用：非 DNS 流量在被送到 main 表后因默认路由被抑制而路由失败，于是内核继续检查下一优先级规则（9002），最终落入 TUN。 #### 规则 `9001: from all iif Meta goto 9010` - **含义：** 从 `Meta` 接口（TUN 设备）进入的包跳转到优先级 9010 - **作用：** 防止路由循环 **`goto` 与 `jump` 的区别：** - `jump` 会记住当前位置，子链执行完后返回继续检查下一条规则 - `goto` 直接跳转到指定优先级，不会返回 - 这里用 `goto` 是因为跳转到 `9010: from all nop`（空操作），然后规则会落到 `32766`（main 表）正常路由。这确保 TUN 发回的包不再被 9002 吸入 TUN 形成死循环 #### 规则 `9002: not from all iif lo lookup 2022` **核心难点。这条规则的语义是关键。** - **语法：** `not` 否定 `from all iif lo` - **实际含义：** 如果数据包**不是**（来自任意源且入站接口为 lo）→ 查表 2022 → 走 TUN - **也就是：** **所有不入 lo 的流量 → TUN** - **为什么不是 `from all iif lo` 而是 `not iif lo`：** 因为需要覆盖两种情况： - 本机发出的流量（`iif = lo`）→ 需要其他规则处理 - 非本机发出的流量（`iif = 网卡/桥接`）→ 入 TUN **这条规则是 Docker 容器流量被拦截的关键：** 容器流量从 `br-700ced5fc520` 进入内核，`iif` 不是 `lo`，因此匹配此规则 → 路由到 TUN。 #### 规则 `9002: from 0.0.0.0 iif lo lookup 2022` - **含义：** 从 lo 接口进入的任意源 IP 流量 → 查表 2022 → 走 TUN - **作用：** 覆盖本机进程发出的流量（它们都从 lo 发出） - **`from 0.0.0.0`：** 匹配所有源地址，等同于 `from all` #### 规则 `9002: from 198.18.0.0/30 iif lo lookup 2022` - **含义：** 源地址在 fake-IP 范围内且从 lo 进入 → 查表 2022 - **注意：** 这实际上是冗余规则——上一条 `from 0.0.0.0 iif lo` 已经覆盖了所有 lo 流量。这可能是 Mihomo 显式添加以保证确定性行为 #### 规则 `9010: from all nop` - **含义：** 空操作，什么都不做 - **作用：** 作为 `goto 9010` 的跳转目标。后续规则继续从 32766 开始检查 #### 规则 `32766: from all lookup main` - **含义：** 所有流量查 main 表（兜底） - **这实际上是 `ip rule` 的默认规则，Mihomo 并没有添加它** #### 规则 `32767: from all lookup default` - **含义：** 所有流量查 default 表（通常为空，兜底的兜底） - **也是系统默认规则** ### 2.4 难点总结 | 难点 | 关键理解 | |------|---------| | `not from all iif lo` 的语义 | 匹配所有**入口非 lo** 的流量，这是 Docker 流量被劫持的原因 | | `suppress_prefixlength 0` | 抑制默认路由（前缀长度 0）。非 DNS 流量被发到 main 表后因无默认路由而失败，继续走下一优先级规则 | | `goto 9010` vs `jump` | `goto` 不返回，用于 TUN 回包防循环 | | 表 2022 只有一条默认路由 | 足够了——所有匹配 9000/9002 规则的流量只需要一个去处：TUN 设备 | | `rule 0` 优先级最高 | 所有目标为本机的包在 rule 0 就被 local 表截获，不会被后续规则干扰 | --- ## 3. nftables 规则集 ### 3.1 什么是 nftables nftables 是 Linux 内核的包过滤框架，取代了早期的 iptables/ip6tables/arptables/ebtables。 核心概念： | 概念 | 说明 | |------|------| | **table** | 规则集容器，指定协议族（inet/ip/ip6/arp/bridge） | | **chain** | 规则链表，挂载到内核的 hook 点 | | **hook** | 内核网络栈中的固定拦截点（prerouting/input/forward/output/postrouting） | | **rule** | 由匹配条件（match）+ 动作（verdict/statement）组成 | | **set** | 高效匹配集合，可用于批量 IP/端口匹配 | ### 3.2 数据包在内核 hook 点的流程 [PREROUTING] → [路由决策] → [FORWARD] → [POSTROUTING] ↓ [INPUT] → 本地进程 ↑ 本地进程 → [OUTPUT] → [路由决策] → [POSTROUTING]\nnftables 可以挂载到这些 hook 点，在每个阶段对数据包进行检查、修改或丢弃。 本机 Mihomo 的 `table inet mihomo` 使用了两个 hook： - `prerouting`：数据包到达网卡后、路由决策之前 - `output`：本机进程发出的数据包、路由决策之后（因为需要知道 `oifname`） ### 3.3 配置查看 ```bash sudo nft list ruleset ```text 输出内容按 table 组织。我们只看 Mihomo 创建的 `table inet mihomo`（`inet` 表示同时处理 IPv4 和 IPv6）： table inet mihomo {\n# ─── 集合 ─── set inet4_local_address_set { type ipv4_addr flags interval elements = { 127.0.0.0/8, 198.18.0.0/30 } } **这是 Mihomo 定义的本地地址集合。** 包含： - `127.0.0.0/8`（标准 loopback 地址段） - `198.18.0.0/30`（Mihomo TUN 接口和 fake-IP 地址段） 后续规则用 `@inet4_local_address_set` 引用此集合，快速判断数据包是否与本地地址相关。 # ─── output 链 ─── chain output { type nat hook output priority mangle; policy accept; **类型、hook 和优先级说明：** - `type nat` — 此链用于网络地址转换 - `hook output` — 挂载在 OUTPUT 钩子（本机进程发出的包） - `priority mangle` — 优先级为 `mangle`（-150），在路由决策之后执行。这意味着此时内核**已经决定了这个包从哪个接口发出** oifname \u0026quot;Meta\u0026quot; meta nfproto ipv4 meta l4proto tcp counter redirect to :46569 return **逐字段拆解：** | 条件/动作 | 含义 | |-----------|------| | `oifname \u0026#34;Meta\u0026#34;` | 出站接口是 Meta（TUN 设备） | | `meta nfproto ipv4` | 网络协议是 IPv4 | | `meta l4proto tcp` | 传输层协议是 TCP | | `counter` | 统计匹配的数据包数量和字节数 | | `redirect to :46569` | 将数据包重定向到本机 46569 端口 | | `return` | 重定向后不再检查本链后续规则 | **作用：** 本机进程发出的 TCP 包，在路由决策中被决定走 TUN 设备（`oif=Meta`），被此规则捕获并 REDIRECT 到 `:46569`（Mihomo 透明代理端口）。这样包不会实际进入 TUN 设备，而是被转入 Mihomo 的代理引擎处理。 # ─── prerouting 链 ─── chain prerouting { type nat hook prerouting priority dstnat + 1; policy accept; - `type nat` — NAT 类型链 - `hook prerouting` — 挂载在 PREROUTING 钩子（所有入站包，路由决策前） - `priority dstnat + 1` — 优先级是 `dstnat`（100）加 1，即 101。这意味着它在标准 DNAT 处理之后执行 - 重要：**此时还没有路由决策**，`iif`（入站接口）是确定的，但 `oif` 还不知道 # ── 规则 1：防循环 ── iifname \u0026quot;Meta\u0026quot; counter return - **条件：** 入站接口是 `Meta`（TUN 设备） - **动作：** 计数后 `return`（跳过本链后续规则） - **作用：** 从 TUN 发回的包不再被 PREROUTING 规则处理，防止循环 # ── 规则 2：DNS 劫持 ── ip saddr @inet4_local_address_set meta l4proto { tcp, udp } th dport 53 counter dnat ip to 198.18.0.2 - **条件：** 源 IP 属于本地地址集合（127.0.0.0/8 或 198.18.0.0/30），且是 TCP/UDP 协议，目标端口 53 - **动作：** DNAT 到 `198.18.0.2`（Mihomo TUN 对端地址） - **作用：** 本机发出的 DNS 查询被 DNAT 到 TUN 设备对端，Mihomo 从 TUN 读取后用自己的 DNS 处理 - **注意：** 这个条件限制了只有**本机**的 DNS 才会被劫持（源 IP 在 local set 中）。Docker 容器的 DNS 查询源 IP 是 `172.18.0.2`，**不匹配**此规则 # ── 规则 3：本地地址放行 ── ip daddr @inet4_local_address_set counter return - **条件：** 目标 IP 是本地地址（127.0.0.0/8 或 198.18.0.0/30） - **动作：** 计数后跳过 - **作用：** 目标是本机/ fake-IP 范围的包不进入后续处理（不会被 redirect 到 :46569） # ── 规则 4：MPTCP 丢弃 ── tcp option mptcp exists counter drop - **条件：** TCP 头部包含 MPTCP（Multipath TCP）选项 - **动作：** 丢弃 - **作用：** 阻止 MPTCP 连接，避免 Mihomo 透明代理无法处理 # ── 规则 5：IPv6 拒绝 ── meta nfproto ipv6 counter reject with icmpv6 no-route - **条件：** IPv6 协议 - **动作：** 拒绝，并发送 ICMPv6 \u0026#34;no route to destination\u0026#34; - **作用：** 当前 Mihomo 配置没有完整处理 IPv6 透明代理，直接拒绝 IPv6 流量避免意外行为 # ── 规则 6：TCP 重定向（核心） ── meta nfproto ipv4 meta l4proto tcp counter redirect to :46569 return **本机 Mihomo 流量劫持的核心规则。** - **条件：** IPv4 + TCP - **动作：** REDIRECT 到本机 `:46569`，然后跳过后续规则 - **覆盖范围：** 所有未被前几条规则放行的 IPv4 TCP 包 这包括： - Docker 容器发出的 TCP 流量（源 IP 172.18.0.x，入站接口 br-700ced5fc520） - 外部进入本机的 TCP 流量（源 IP 外部，入站接口 wlp0s20f3） - **不包括本机发出的 TCP**（本机发出的 TCP 走的是 OUTPUT 链，不是 PREROUTING） **效果：** 容器 TCP、外部入站 TCP 全部被 REDIRECT 到 `:46569`，Mihomo 在此端口接收后进入代理核心引擎处理。 ### 3.4 Docker 管理的其他 nftables 表 `sudo nft list ruleset` 还会输出其他表，但它们由 Docker 维护，非 Mihomo 创建： Warning: table ip nat is managed by iptables-nft, do not touch! table ip nat { \u0026hellip; } # Docker 端口映射（DNAT）、容器出站 SNAT（MASQUERADE）\nWarning: table ip filter is managed by iptables-nft, do not touch! table ip filter { \u0026hellip; } # Docker 网络隔离规则（FORWARD 链控制容器间通信）\nWarning: table ip6 nat is managed by iptables-nft, do not touch! table ip6 nat { \u0026hellip; } # IPv6 版本（当前 Docker 相关规则为空）\ntable ip6 filter { \u0026hellip; } # IPv6 Docker filter（当前策略 accept）\ntable ip raw { \u0026hellip; } # raw 表 PREROUTING：阻止外部直接访问 Docker 映射端口\n**最关键的一条 Docker 规则——容器出站 SNAT：** ```nft table ip nat { chain POSTROUTING { ip saddr 172.18.0.0/16 oifname != \u0026#34;br-700ced5fc520\u0026#34; counter masquerade } } ```text - **条件：** 源 IP 为 docker 容器网段，且**出站接口不是桥接网卡**（即流量要去外部网络） - **动作：** MASQUERADE（动态 SNAT），将源 IP 改为宿主机 IP - **但是这条规则在 TCP 路径中不会被触发：** 因为容器 TCP 在 PREROUTING 阶段就被 `redirect to :46569` 截获（目标改为 `127.0.0.1`），路由决策判定为本地投递，不经过 POSTROUTING **验证：** Mihomo 日志中容器 TCP 连接显示 `[TCP] 172.18.0.2:xxxxx --\u0026gt; ...`，源 IP 是容器原始 IP，未被 SNAT。 --- ## 4. 三者协同：一条数据包的完整旅程 ### 4.1 容器 TCP → 外部 容器 axonhub (172.18.0.2:xxxxx → 1.1.1.1:80)\n↓ 桥接网卡 br-700ced5fc520 │ iif = br-700ced5fc520 ▼ ┌─────────────────────────────────────────────────────────────┐ │ nftables prerouting (priority dstnat + 1) │ │ │ │ 规则 1: iifname \u0026ldquo;Meta\u0026rdquo; → 不匹配 │ │ 规则 2: local src + dport 53 → 不匹配 │ │ 规则 3: ip daddr local 范围 → 不匹配 │ │ 规则 4: tcp mptcp → 不匹配 │ │ 规则 5: ipv6 → 不匹配 │ │ ★ 规则 6: meta nfproto ipv4 tcp → ★ REDIRECT :46569│ └─────────────────────────────────────────────────────────────┘ │ 目标改为 127.0.0.1:46569 │ 源保持 172.18.0.2（容器原始 IP） ▼ ┌─────────────────────────────────────────────────────────────┐ │ ip rule（策略路由） │ │ │ │ rule 0: lookup local → 127.0.0.1 是本地 → 匹配 │ │ （后续规则不再检查） │ │ → 路由决策：本地投递 │ │ → 经 INPUT → Mihomo socket (:46569) │ └─────────────────────────────────────────────────────────────┘ │ ▼ Mihomo 核心引擎 └─ [TCP] 172.18.0.2:56690 \u0026ndash;\u0026gt; 1.1.1.1:443 match Match using SELECT[🇺🇸US-DMIT] └─ 经代理节点 VLESS XTLS 发出\n**路径总结：** PREROUTING redirect → rule 0（local 表）→ INPUT → Mihomo socket → 代理引擎 ### 4.2 容器 ICMP → 外部 容器 axonhub (172.18.0.2 → 1.1.1.1, ICMP echo)\n↓ br-700ced5fc520 │ iif = br-700ced5fc520 ▼ ┌─────────────────────────────────────────────────────────────┐ │ nftables prerouting │ │ ★ 规则 6: meta nfproto ipv4 tcp → 不匹配（ICMP ≠ TCP）│ │ → 所有规则不匹配，放行 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ip rule │ │ │ │ rule 0: lookup local → 1.1.1.1 不本地 │ │ rule 9000: to 198.18.0.0/30 → 不匹配 │ │ rule 9001: dport 53 → 不匹配 │ │ rule 9001: iif Meta → iif=br → 不匹配 │ │ ★ rule 9002: not from all iif lo → iif≠lo → 匹配 │ │ → 查表 2022 → default via 198.18.0.2 dev Meta │ └─────────────────────────────────────────────────────────────┘ │ ▼ TUN 设备 Meta │ ▼ Mihomo TUN 栈 └─ ICMP 不进核心引擎（VLESS/XTLS 无法承载 ICMP） └─ TUN 栈 NAT：源 IP 172.18.0.2 → 198.18.0.1 └─ 写回 TUN 设备 │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ip rule（TUN 回包，此时 iif=Meta） │ │ ★ rule 9001: iif Meta goto 9010 → 跳过 │ │ → 查 main 表 → default via 192.168.1.1 dev wlp0s20f3 │ └─────────────────────────────────────────────────────────────┘ │ ▼ 物理网卡发出（经 SNAT 后源 IP 变为 192.168.1.5）\n**路径总结：** PREROUTING 放行 → ip rule 9002 → TUN → TUN 栈 NAT 直发（不进核心引擎） ### 4.3 本机 TCP → 外部 firefox (192.168.1.5 → 142.250.80.68:443)\n↓ socket ▼ ┌─────────────────────────────────────────────────────────────┐ │ ip rule │ │ ★ rule 9002: from 0.0.0.0 iif lo → 匹配 │ │ → 查表 2022 → default via 198.18.0.2 dev Meta │ │ → 此时 oifname = \u0026ldquo;Meta\u0026rdquo; │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ nftables output (priority mangle, 路由决策后执行) │ │ │ │ ★ oifname \u0026ldquo;Meta\u0026rdquo; tcp → REDIRECT to :46569 │ └─────────────────────────────────────────────────────────────┘ │ 目标改为 127.0.0.1:46569 ▼ 路由决策（第二次） └─ 本地投递 → INPUT → Mihomo socket └─ 进入代理核心引擎\n**路径总结：** ip rule 9002 → 路由决策（oif=Meta）→ OUTPUT redirect → 重新路由本地投递 --- ## 总结 ### 三层拦截机制的分工 | 层 | 工具 | hook/规则 | 拦截对象 | 时机 | |----|------|-----------|---------|------| | **1** | nftables prerouting | `tcp redirect :46569` | 外部入站 TCP、容器 TCP | 路由决策前 | | **2** | nftables output | `oif=Meta tcp redirect :46569` | 本机发出 TCP | 路由决策后 | | **3** | ip rule 9002 + 表 2022 | `not iif lo → TUN` | 容器 ICMP/UDP、本机 ICMP/UDP | 路由决策（nftables 放行后） | ### TCP 与 ICMP/UDP 的路径分歧 | 维度 | TCP | ICMP/UDP | |------|-----|----------| | 本机发出 | 路由(→oif=Meta) → OUTPUT redirect :46569 → 核心引擎 | 路由(→TUN) → TUN 栈 NAT → 直发 | | 容器发出 | PREROUTING redirect :46569 → 核心引擎（源 IP 保持 172.18.0.2） | PREROUTING 放行 → ip rule → TUN → TUN 栈 NAT | | 外部入站 | PREROUTING redirect :46569 → 核心引擎（需额外测试验证影响） | PREROUTING 放行 → ip rule 0 → 正常交付 | ### 文章对应命令速查 ```bash # 路由表 ip route show table all # 查看所有路由表 ip route show table main # 查看 main 表 ip route show table 2022 # 查看自定义表 ip route show table local # 查看 local 表 # 策略路由 ip rule show # 查看所有规则 ip rule add priority 100 from 10.0.0.0/8 lookup main # 添加规则示例 ip rule del priority 9000 # 删除规则示例 # nftables sudo nft list ruleset # 查看完整规则集 sudo nft list table inet mihomo # 查看特定表 sudo nft list chain inet mihomo prerouting # 查看特定链 sudo nft add rule inet mihomo prerouting ip daddr 192.168.1.5 tcp dport 22 return # 添加豁免规则示例 # 模拟路由决策 ip route get 1.1.1.1 # 本机到 1.1.1.1 走哪个路由表 ip route get 1.1.1.1 from 172.18.0.2 iif br-700ced5fc520 # 模拟容器路由 # 抓包诊断 sudo tcpdump -ni Meta icmp # 监控 TUN 设备上的 ICMP sudo tcpdump -ni lo port 46569 # 监控被 redirect 到透明代理端口的流量 # 日志 sudo journalctl -u mihomo --since \u0026#34;1 min ago\u0026#34; # Mihomo 日志 验证与自查 ip route get 1.1.1.1 显示本机流量走表 2022（dev Meta）；ip route get 1.1.1.1 from 172.18.0.2 iif br-700ced5fc520 可模拟容器流量同样落入 TUN sudo nft list table inet mihomo 中核心规则的 counter 计数随访问流量增长 sudo tcpdump -ni lo port 46569 能看到被 REDIRECT 到透明代理端口的 TCP 流量 容器内 curl -v https://1.1.1.1 正常返回，Mihomo 日志出现 [TCP] 172.18.0.2:xxxxx --\u0026gt; ...（源 IP 未被 SNAT） 参考 ip-rule 手册页 nftables 官方 wiki Mihomo 官方文档 Mihomo GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/mihomo-any/route-iprule-nftables-article/","summary":"\u003ch1 id=\"路由表ip-rule-与-nftableslinux-三层流量劫持机制解析\"\u003e路由表、ip rule 与 nftables：Linux 三层流量劫持机制解析\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e以本机 Mihomo（Clash.Meta）TUN 模式 + Docker 容器网络为实例，逐层拆解\u003c/p\u003e\n\u003cp\u003e本文适合同时想理解概念和看真实案例的读者。\u003c/p\u003e","title":"路由表、ip rule 与 nftables：Linux 三层流量劫持机制解析"},{"content":"AxonHub 集成 OpenCode 指南 本文教你配置 OpenCode 通过 AxonHub 连接模型：作为 Anthropic 端点的无缝替代方案，结合 AxonHub 模型配置（Model Profiles）实现统一端点、请求追踪与模型路由。\n本文基于 AxonHub 官方文档 - OpenCode 集成指南。\nAxonHub 可以作为 Anthropic 端点的无缝替代方案，让 OpenCode 通过您自己的基础设施进行连接。本文说明如何配置 OpenCode 以及如何将其与 AxonHub 模型配置（Model Profiles）结合使用。\n1. 概述 核心能力 协议转换：AxonHub 执行 AI 协议/格式转换，配置多个上游渠道（供应商），为 OpenCode 暴露统一的 Anthropic 兼容接口 请求追踪：将同一会话的 OpenCode 请求聚合到一个 Trace 中 模型路由：通过模型配置（Model Profiles）实现灵活的请求分发 前提条件 可从开发机器访问的 AxonHub 实例（参见部署教程） 具有项目访问权限的有效 AxonHub API 密钥 已安装 OpenCode CLI 可选：在 AxonHub 控制台中配置的一个或多个模型配置 2. 配置 OpenCode 2.1 创建配置文件 编辑 ~/.config/opencode/opencode.json：\n{ \u0026#34;$schema\u0026#34;: \u0026#34;https://opencode.ai/config.json\u0026#34;, \u0026#34;plugin\u0026#34;: [ \u0026#34;opencode-axonhub-tracing\u0026#34; ], \u0026#34;provider\u0026#34;: { \u0026#34;axonhub\u0026#34;: { \u0026#34;npm\u0026#34;: \u0026#34;@ai-sdk/anthropic\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;AxonHub\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;http://127.0.0.1:8090/anthropic/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;你的AxonHub_API_Key\u0026#34; }, \u0026#34;models\u0026#34;: { \u0026#34;claude-sonnet-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;AxonHub - Claude Sonnet 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } } } } } } 2.2 配置参数说明 参数 描述 示例 npm 提供商对应的 npm 包 @ai-sdk/anthropic name 提供商显示名称 AxonHub baseURL AxonHub Anthropic API 端点 http://127.0.0.1:8090/anthropic/v1 apiKey AxonHub API 密钥 替换为实际密钥 baseURL 必须指向 Anthropic 兼容端点 /anthropic/v1，而非通用 /v1。\n2.3 添加多个模型 在同一个提供商中配置多个模型：\n{ \u0026#34;provider\u0026#34;: { \u0026#34;axonhub\u0026#34;: { \u0026#34;npm\u0026#34;: \u0026#34;@ai-sdk/anthropic\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;AxonHub\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;http://127.0.0.1:8090/anthropic/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;your-axonhub-api-key\u0026#34; }, \u0026#34;models\u0026#34;: { \u0026#34;claude-sonnet-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;AxonHub - Claude Sonnet 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } }, \u0026#34;claude-haiku-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;AxonHub - Claude Haiku 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } }, \u0026#34;claude-opus-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;AxonHub - Claude Opus 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } } } } } } 2.4 使用远程 AxonHub 实例 如果 AxonHub 部署在远程服务器，更新 baseURL：\n{ \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;https://your-axonhub-domain.com/anthropic/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;your-axonhub-api-key\u0026#34; } } 3. 追踪插件 opencode-axonhub-tracing 插件为每个 LLM 请求注入追踪头部，实现在 AxonHub 中的请求聚合和追踪。\n3.1 启用插件 在 opencode.json 的 plugin 数组中添加：\n{ \u0026#34;plugin\u0026#34;: [\u0026#34;opencode-axonhub-tracing\u0026#34;] } OpenCode 会在需要时自动安装该插件。\n3.2 默认 Headers Header 来源 描述 AH-Thread-Id OpenCode sessionID 将同一会话的请求分组 AH-Trace-Id OpenCode message.id 每条消息的唯一标识符 3.3 自定义 Header 配置（可选） 通过环境变量覆盖默认 header key：\nexport OPENCODE_AXONHUB_TRACING_THREAD_HEADER=\u0026#34;X-Thread-Id\u0026#34; export OPENCODE_AXONHUB_TRACING_TRACE_HEADER=\u0026#34;X-Trace-Id\u0026#34; 环境变量 默认值 描述 OPENCODE_AXONHUB_TRACING_THREAD_HEADER AH-Thread-Id 自定义线程 header key OPENCODE_AXONHUB_TRACING_TRACE_HEADER AH-Trace-Id 自定义追踪 header key 空字符串会自动回退到默认 key。\n4. 模型配置（Model Profiles） AxonHub 模型配置可以将传入的模型名称重映射为特定提供商的等效名称。\n4.1 配置方式 在 AxonHub 控制台中创建一个配置 添加映射规则（精确名称或正则表达式） 将该配置分配给 API 密钥 切换活跃的配置以更改 OpenCode 行为，无需改动工具设置 4.2 使用场景 成本优化：\n请求模型 映射到 效果 claude-sonnet-4-5 deepseek-chat 降低调用成本 claude-haiku-4-5 gpt-4o-mini 简单任务走更便宜的模型 性能优化：\n请求模型 映射到 效果 claude-opus-4-5 claude-sonnet-4-5 获得更快的响应 claude-sonnet-4-5 gpt-4o 更好的可用性 高级推理：\n请求模型 映射到 效果 claude-sonnet-4-5 deepseek-reasoner 复杂推理任务 claude-opus-4-5 o1-preview 数学问题 5. 多提供商配置 可以将多个 AxonHub 实例配置为不同的提供商：\n{ \u0026#34;provider\u0026#34;: { \u0026#34;axonhub-prod\u0026#34;: { \u0026#34;npm\u0026#34;: \u0026#34;@ai-sdk/anthropic\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;AxonHub Production\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;https://prod.axonhub.com/anthropic/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;prod-api-key\u0026#34; }, \u0026#34;models\u0026#34;: { \u0026#34;claude-sonnet-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;Production - Claude Sonnet 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } } } }, \u0026#34;axonhub-dev\u0026#34;: { \u0026#34;npm\u0026#34;: \u0026#34;@ai-sdk/anthropic\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;AxonHub Development\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;http://localhost:8090/anthropic/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;dev-api-key\u0026#34; }, \u0026#34;models\u0026#34;: { \u0026#34;claude-sonnet-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;Development - Claude Sonnet 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } } } } } } 同时支持 OpenAI 兼容端点：\n{ \u0026#34;provider\u0026#34;: { \u0026#34;axonhub-openai\u0026#34;: { \u0026#34;npm\u0026#34;: \u0026#34;@ai-sdk/openai\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;AxonHub OpenAI\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;http://127.0.0.1:8090/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;your-axonhub-api-key\u0026#34; }, \u0026#34;models\u0026#34;: { \u0026#34;gpt-4\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;AxonHub - GPT-4\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } } } } } } 6. 故障排除 OpenCode 无法连接 症状：连接错误、超时\n排查步骤：\n验证 baseURL 指向正确的 AxonHub Anthropic 端点（/anthropic/v1） 检查 AxonHub 是否运行：curl http://localhost:8090/health 检查防火墙是否允许出站连接 对于自签名证书的 HTTPS 端点，配置信任设置 身份验证错误 症状：401 Unauthorized、403 Forbidden\n排查步骤：\n验证 API 密钥是否正确 在 AxonHub 控制台中检查 API 密钥是否过期 确保 API 密钥具有所请求项目和模型的访问权限 意外的模型响应 症状：错误的模型响应、意外行为\n排查步骤：\n在 AxonHub 控制台中查看活跃的配置映射 检查渠道配置和模型关联 验证请求的模型名称是否与配置匹配 如有必要，禁用或调整配置规则 配置文件未加载 症状：OpenCode 使用默认设置，忽略配置文件\n排查步骤：\n验证配置文件位置：~/.config/opencode/opencode.json 检查 JSON 语法是否有效 确保文件权限允许读取 更改配置后重启 OpenCode 7. 已知限制 CC Switch 无法注入 OpenCode Go 官方 URL 当 CC Switch 的 Provider Base URL 设置为 https://api.opencode.ai/v1（OpenCode Go 官方地址）时，CC Switch 无法将该 Provider 注入到 OpenCode 的配置文件中，修改不生效。\n解决方案：\n通过 AxonHub 反代 OpenCode Go（即本文配置方式），CC Switch 指向 localhost:8090，无此问题 或手动编辑 ~/.config/opencode/opencode.json 直接写入官方 URL 验证与自查 curl http://localhost:8090/health 确认 AxonHub 运行中 OpenCode 中 opencode 启动后能列出 axonhub provider 下的模型并成功发起对话 在 AxonHub 控制台的追踪页面能看到按 AH-Thread-Id 聚合的会话请求 修改模型配置映射后，OpenCode 中重新请求即按新路由生效，无需改动工具设置 参考 AxonHub 官方文档 - OpenCode 集成指南 AxonHub GitHub 仓库 OpenCode 官网 ","permalink":"https://heihei0299.github.io/posts/ai/axonhub-opencode-integration/","summary":"\u003ch1 id=\"axonhub-集成-opencode-指南\"\u003eAxonHub 集成 OpenCode 指南\u003c/h1\u003e\n\u003cp\u003e本文教你配置 OpenCode 通过 AxonHub 连接模型：作为 Anthropic 端点的无缝替代方案，结合 AxonHub 模型配置（Model Profiles）实现统一端点、请求追踪与模型路由。\u003c/p\u003e","title":"AxonHub 集成 OpenCode 指南"},{"content":"大家好，我是雷小帅。\n最近群里有几位老哥私我，有没有好的 git 入门资料，想学一下。\n偶然看到这个很棒的教程推荐给大家，大家可以看一下，另外还有一个不错的 git 入门网站，也推荐给大家，可以搭配使用。\nhttps://learngitbranching.js.org/?locale=zh_CN\nGit 保姆级教程：实用主义与深入探索 本文适用于 Git 零基础入门、想在项目中快速上手版本管理的场景。你会学到：日常开发 90% 场景够用的实用命令（clone/config/branch/add/commit/push/pull/log/tag/.gitignore），以及深入探索部分的高级概念与操作（暂存区、分支、reset/rebase、stash、submodule 等）。\nGit 是一种分布式版本控制系统，它可以不受网络连接的限制，加上其它众多优点，目前已经成为程序开发人员做项目版本管理时的首选，非开发人员也可以用 Git 来做自己的文档版本管理工具。\n大概是大二的时候开始接触和使用Git，从一开始的零接触到现在的重度依赖，真是感叹 Git 的强大。\nGit 的api很多，但其实平时项目中90%的需求都只需要用到几个基本的功能即可，所以本文将从 实用主义 和 深入探索 2个方面去谈谈如何在项目中使用 Git，一般来说，看完 实用主义 这一节就可以开始在项目中动手用。\n说明：本文的操作都是基于 Mac 系统\n实用主义 准备阶段 进入 Git官网 下载合适你的安装包，安装好 Git 后，打开命令行工具，进入工作文件夹（为了便于理解我们在系统桌面上演示），创建一个新的demo文件夹。\n进入 Github网站 注册一个账号并登录，进入 我的博客，点击 Clone or download，再点击 Use HTTPS ，复制项目地址 https://github.com/gafish/gafish.github.com.git 备用。\n再回到命令行工具，一切就绪，接下来进入本文的重点。\n常用操作 所谓实用主义，就是掌握了以下知识就可以玩转 Git，轻松应对90%以上的需求。以下是实用主义型的Git命令列表，先大致看一下\ngit clone git config git branch git checkout git status git add git commit git push git pull git log git tag 接下来，将通过对 我的博客 仓库进行实例操作，讲解如何使用 Git 拉取代码到提交代码的整个流程。\ngit clone 从git服务器拉取代码\ngit clone https://github.com/gafish/gafish.github.com.git 代码下载完成后在当前文件夹中会有一个 gafish.github.com 的目录，通过 cd gafish.github.com 命令进入目录。\ngit config 配置开发者用户名和邮箱\ngit config user.name gafish git config user.email gafish@qqqq.com 每次代码提交的时候都会生成一条提交记录，其中会包含当前配置的用户名和邮箱。\ngit branch 创建、重命名、查看、删除项目分支，通过 Git 做项目开发时，一般都是在开发分支中进行，开发完成后合并分支到主干。\ngit branch daily/0.0.0 创建一个名为 daily/0.0.0 的日常开发分支，分支名只要不包括特殊字符即可。\ngit branch -m daily/0.0.0 daily/0.0.1 如果觉得之前的分支名不合适，可以为新建的分支重命名，重命名分支名为 daily/0.0.1\ngit branch 通过不带参数的branch命令可以查看当前项目分支列表\ngit branch -d daily/0.0.1 如果分支已经完成使命则可以通过 -d 参数将分支删除，这里为了继续下一步操作，暂不执行删除操作\ngit checkout 切换分支\ngit checkout daily/0.0.1 切换到 daily/0.0.1 分支，后续的操作将在这个分支上进行\ngit status 查看文件变动状态\n通过任何你喜欢的编辑器对项目中的 README.md 文件做一些改动，保存。\ngit status 通过 git status 命令可以看到文件当前状态 Changes not staged for commit:（改动文件未提交到暂存区）\nOn branch daily/0.0.1 Changes not staged for commit: (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) modified: README.md no changes added to commit (use \u0026#34;git add\u0026#34; and/or \u0026#34;git commit -a\u0026#34;) git add 添加文件变动到暂存区\ngit add README.md 通过指定文件名 README.md 可以将该文件添加到暂存区，如果想添加所有文件可用 git add . 命令，这时候可通过 git status 看到文件当前状态 Changes to be committed: （文件已提交到暂存区）\nOn branch daily/0.0.1 Changes to be committed: (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) modified: README.md git commit 提交文件变动到版本库\ngit commit -m \u0026#39;这里写提交原因\u0026#39; 通过 -m 参数可直接在命令行里输入提交描述文本\ngit push 将本地的代码改动推送到服务器\ngit push origin daily/0.0.1 origin 指代的是当前的git服务器地址，这行命令的意思是把 daily/0.0.1 分支推送到服务器，当看到命令行返回如下字符表示推送成功了。\nCounting objects: 3, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 267 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), completed with 1 local objects. To https://github.com/gafish/gafish.github.com.git * [new branch] daily/0.0.1 -\u0026gt; daily/0.0.1 现在我们回到Github网站的项目首页，点击 Branch:master 下拉按钮，就会看到刚才推送的 daily/00.1 分支了\ngit pull 将服务器上的最新代码拉取到本地\ngit pull origin daily/0.0.1 如果其它项目成员对项目做了改动并推送到服务器，我们需要将最新的改动更新到本地，这里我们来模拟一下这种情况。\n进入Github网站的项目首页，再进入 daily/0.0.1 分支，在线对 README.md 文件做一些修改并保存，然后在命令中执行以上命令，它将把刚才在线修改的部分拉取到本地，用编辑器打开 README.md ，你会发现文件已经跟线上的内容同步了。\n如果线上代码做了变动，而你本地的代码也有变动，拉取的代码就有可能会跟你本地的改动冲突，一般情况下 Git 会自动处理这种冲突合并，但如果改动的是同一行，那就需要手动来合并代码，编辑文件，保存最新的改动，再通过 git add .和 git commit -m 'xxx' 来提交合并。\ngit log 查看版本提交记录\ngit log 通过以上命令，我们可以查看整个项目的版本提交记录，它里面包含了提交人、日期、提交原因等信息，得到的结果如下：\ncommit c334730f8dba5096c54c8ac04fdc2b31ede7107a Author: gafish \u0026lt;gafish@qqqq.com\u0026gt; Date: Wed Jan 11 09:44:13 2017 +0800 Update README.md commit ba6e3d21fcb1c87a718d2a73cdd11261eb672b2a Author: gafish \u0026lt;gafish@qqqq.com\u0026gt; Date: Wed Jan 11 09:31:33 2017 +0800 test ..... 提交记录可能会非常多，按 J 键往下翻，按 K 键往上翻，按 Q 键退出查看\ngit tag 为项目标记里程碑\ngit tag publish/0.0.1 git push origin publish/0.0.1 当我们完成某个功能需求准备发布上线时，应该将此次完整的项目代码做个标记，并将这个标记好的版本发布到线上，这里我们以 publish/0.0.1 为标记名并发布，当看到命令行返回如下内容则表示发布成功了\nTotal 0 (delta 0), reused 0 (delta 0) To https://github.com/gafish/gafish.github.com.git * [new tag] publish/0.0.1 -\u0026gt; publish/0.0.1 .gitignore 设置哪些内容不需要推送到服务器，这是一个配置文件\ntouch .gitignore .gitignore 不是 Git 命令，而在项目中的一个文件，通过设置 .gitignore 的内容告诉 Git 哪些文件应该被忽略不需要推送到服务器，通过以上命令可以创建一个 .gitignore 文件，并在编辑器中打开文件，每一行代表一个要忽略的文件或目录，如：\ndemo.html build/ 以上内容的意思是 Git 将忽略 demo.html 文件 和 build/ 目录，这些内容不会被推送到服务器上\n小结 通过掌握以上这些基本命令就可以在项目中开始用起来了，如果追求实用，那关于 Git 的学习就可以到此结束了，偶尔遇到的问题也基本上通过 Google 也能找到答案，如果想深入探索 Git 的高阶功能，那就继续往下看 深入探索 部分。\n深入探索 基本概念 工作区（Working Directory） 就是你在电脑里能看到的目录，比如上文中的 gafish.github.com 文件夹就是一个工作区\n本地版本库（Local Repository） 工作区有一个隐藏目录 .git，这个不算工作区，而是 Git 的版本库。\n暂存区（stage） 本地版本库里存了很多东西，其中最重要的就是称为 stage（或者叫index）的暂存区，还有 Git 为我们自动创建的第一个分支 master，以及指向 master 的一个指针叫 HEAD。\n远程版本库（Remote Repository） 一般指的是 Git 服务器上所对应的仓库，本文的示例所在的github仓库就是一个远程版本库\n以上概念之间的关系 工作区、暂存区、本地版本库、远程版本库之间几个常用的 Git 操作流程如下图所示：\n分支（Branch） 分支是为了将修改记录的整个流程分开存储，让分开的分支不受其它分支的影响，所以在同一个数据库里可以同时进行多个不同的修改\n主分支（Master） 前面提到过 master 是 Git 为我们自动创建的第一个分支，也叫主分支，其它分支开发完成后都要合并到 master\n标签（Tag） 标签是用于标记特定的点或提交的历史，通常会用来标记发布版本的名称或版本号（如：publish/0.0.1），虽然标签看起来有点像分支，但打上标签的提交是固定的，不能随意的改动，参见上图中的1.0 / 2.0 / 3.0\nHEAD HEAD 指向的就是当前分支的最新提交\n以上概念了解的差不多，那就可以继续往下看，下面将以具体的操作类型来讲解 Git 的高阶用法\n操作文件 git add 添加文件到暂存区\ngit add -i 通过此命令将打开交互式子命令系统，你将看到如下子命令\n***Commands*** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help 通过输入序列号或首字母可以选择相应的功能，具体的功能解释如下：\nstatus：功能上和 git add -i 相似，没什么鸟用 update：详见下方 git add -u revert：把已经添加到暂存区的文件从暂存区剔除，其操作方式和 update类似 add untracked：可以把新增的文件添加到暂存区，其操作方式和 update 类似 patch：详见下方 git add -p diff：比较暂存区文件和本地版本库的差异，其操作方式和 update 类似 quit：退出 git add -i 命令系统 help：查看帮助信息 git add -p 直接进入交互命令中最有用的 patch 模式\n这是交互命令中最有用的模式，其操作方式和 update 类似，选择后 Git 会显示这些文件的当前内容与本地版本库中的差异，然后您可以自己决定是否添加这些修改到暂存区，在命令行 Stage deletion [y,n,q,a,d,/,?]? 后输入 y,n,q,a,d,/,? 其中一项选择操作方式，具体功能解释如下：\ny：接受修改 n：忽略修改 q：退出当前命令 a：添加修改 d：放弃修改 /：通过正则表达式匹配修改内容 ?：查看帮助信息 git add -u 直接进入交互命令中的 update 模式\n它会先列出工作区 修改 或 删除 的文件列表，新增 的文件不会被显示，在命令行 Update\u0026gt;\u0026gt; 后输入相应的列表序列号表示选中该项，回车继续选择，如果已选好，直接回车回到命令主界面\ngit add --ignore-removal . 添加工作区 修改 或 新增 的文件列表， 删除 的文件不会被添加\ngit commit 把暂存区的文件提交到本地版本库\ngit commit -m \u0026#39;第一行提交原因\u0026#39; -m \u0026#39;第二行提交原因\u0026#39; 不打开编辑器，直接在命令行中输入多行提交原因\ngit commit -am \u0026#39;提交原因\u0026#39; 将工作区 修改 或 删除 的文件提交到本地版本库， 新增 的文件不会被提交\ngit commit --amend -m \u0026#39;提交原因\u0026#39; 修改最新一条提交记录的提交原因\ngit commit -C HEAD 将当前文件改动提交到 HEAD 或当前分支的历史ID\ngit mv 移动或重命名文件、目录\ngit mv a.md b.md -f 将 a.md 重命名为 b.md ，同时添加变动到暂存区，加 -f 参数可以强制重命名，相比用 mv a.md b.md 命令省去了 git add 操作\ngit rm 从工作区和暂存区移除文件\ngit rm b.md 从工作区和暂存区移除文件 b.md ，同时添加变动到暂存区，相比用 rm b.md 命令省去了 git add 操作\ngit rm src/ -r 允许从工作区和暂存区移除目录\ngit status git status -s 以简短方式查看工作区和暂存区文件状态，示例如下：\nM demo.html ?? test.html git status --ignored 查看工作区和暂存区文件状态，包括被忽略的文件\n操作分支 git branch 查看、创建、删除分支\ngit branch -a 查看本地版本库和远程版本库上的分支列表\ngit branch -r 查看远程版本库上的分支列表，加上 -d 参数可以删除远程版本库上的分支\ngit branch -D 分支未提交到本地版本库前强制删除分支\ngit branch -vv 查看带有最后提交id、最近提交原因等信息的本地版本库分支列表\ngit merge 将其它分支合并到当前分支\ngit merge --squash 将待合并分支上的 commit 合并成一个新的 commit 放入当前分支，适用于待合并分支的提交记录不需要保留的情况\ngit merge --no-ff 默认情况下，Git 执行\u0026quot;快进式合并\u0026quot;（fast-farward merge），会直接将 Master分支指向 Develop 分支，使用 --no-ff 参数后，会执行正常合并，在 Master分支上生成一个新节点，保证版本演进更清晰。\ngit merge --no-edit 在没有冲突的情况下合并，不想手动编辑提交原因，而是用 Git 自动生成的类似 Merge branch 'test' 的文字直接提交\ngit checkout 切换分支\ngit checkout -b daily/0.0.1 创建 daily/0.0.1 分支，同时切换到这个新创建的分支\ngit checkout HEAD demo.html 从本地版本库的 HEAD（也可以是提交ID、分支名、Tag名） 历史中检出 demo.html 覆盖当前工作区的文件，如果省略 HEAD 则是从暂存区检出\ngit checkout --orphan new_branch 这个命令会创建一个全新的，完全没有历史记录的新分支，但当前源分支上所有的最新文件都还在，真是强迫症患者的福音，但这个新分支必须做一次 git commit操作后才会真正成为一个新分支。\ngit checkout -p other_branch 这个命令主要用来比较两个分支间的差异内容，并提供交互式的界面来选择进一步的操作，这个命令不仅可以比较两个分支间的差异，还可以比较单个文件的差异。\ngit stash 在 Git 的栈中保存当前修改或删除的工作进度，当你在一个分支里做某项功能开发时，接到通知把昨天已经测试完没问题的代码发布到线上，但这时你已经在这个分支里加入了其它未提交的代码，这个时候就可以把这些未提交的代码存到栈里。\ngit stash 将未提交的文件保存到Git栈中\ngit stash list 查看栈中保存的列表\ngit stash show stash@{0} 显示栈中其中一条记录\ngit stash drop stash@{0} 移除栈中其中一条记录\ngit stash pop 从Git栈中检出最新保存的一条记录，并将它从栈中移除\ngit stash apply stash@{0} 从Git栈中检出其中一条记录，但不从栈中移除\ngit stash branch new_banch 把当前栈中最近一次记录检出并创建一个新分支\ngit stash clear 清空栈里的所有记录\ngit stash create 为当前修改或删除的文件创建一个自定义的栈并返回一个ID，此时并未真正存储到栈里\ngit stash store xxxxxx 将 create 方法里返回的ID放到 store 后面，此时在栈里真正创建了一个记录，但当前修改或删除的文件并未从工作区移除\n$ git stash create 09eb9a97ad632d0825be1ece361936d1d0bdb5c7 $ git stash store 09eb9a97ad632d0825be1ece361936d1d0bdb5c7 $ git stash list stash@{0}: Created via \u0026#34;git stash store\u0026#34;. 操作历史 git log 显示提交历史记录\ngit log -p 显示带提交差异对比的历史记录\ngit log demo.html 显示 demo.html 文件的历史记录\ngit log --since=\u0026#34;2 weeks ago\u0026#34; 显示2周前开始到现在的历史记录，其它时间可以类推\ngit log --before=\u0026#34;2 weeks ago\u0026#34; 显示截止到2周前的历史记录，其它时间可以类推\ngit log -10 显示最近10条历史记录\ngit log f5f630a..HEAD 显示从提交ID f5f630a 到 HEAD 之间的记录，HEAD 可以为空或其它提交ID\ngit log --pretty=oneline 在一行中输出简短的历史记录\ngit log --pretty=format:\u0026#34;%h\u0026#34; 格式化输出历史记录\nGit 用各种 placeholder 来决定各种显示内容，我挑几个常用的显示如下：\n%H: commit hash %h: 缩短的commit hash %T: tree hash %t: 缩短的 tree hash %P: parent hashes %p: 缩短的 parent hashes %an: 作者名字 %aN: mailmap的作者名 %ae: 作者邮箱 %ad: 日期 (\u0026ndash;date= 制定的格式) %ar: 日期, 相对格式(1 day ago) %cn: 提交者名字 %ce: 提交者 email %cd: 提交日期 (\u0026ndash;date= 制定的格式) %cr: 提交日期, 相对格式(1 day ago) %d: ref名称 %s: commit信息标题 %b: commit信息内容 %n: 换行 git cherry-pick 合并分支的一条或几条提交记录到当前分支末梢\ngit cherry-pick 170a305 合并提交ID 170a305 到当前分支末梢\ngit reset 将当前的分支重设（reset）到指定的 \u0026lt;commit\u0026gt; 或者 HEAD\ngit reset --mixed \u0026lt;commit\u0026gt; --mixed 是不带参数时的默认参数，它退回到某个版本，保留文件内容，回退提交历史\ngit reset --soft \u0026lt;commit\u0026gt; 暂存区和工作区中的内容不作任何改变，仅仅把 HEAD 指向 \u0026lt;commit\u0026gt;\ngit reset --hard \u0026lt;commit\u0026gt; 自从 \u0026lt;commit\u0026gt; 以来在工作区中的任何改变都被丢弃，并把 HEAD 指向 \u0026lt;commit\u0026gt;\ngit rebase 重新定义分支的版本库状态\ngit rebase branch_name 合并分支，这跟 merge 很像，但还是有本质区别\n合并过程中可能需要先解决冲突，然后执行 git rebase --continue\ngit rebase -i HEAD~~ 打开文本编辑器，将看到从 HEAD 到 HEAD~~ 的提交如下\npick 9a54fd4 添加commit的说明 pick 0d4a808 添加pull的说明 # Rebase 326fc9f..0d4a808 onto d286baa # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like \u0026#34;squash\u0026#34;, but discard this commit\u0026#39;s log message # x, exec = run command (the rest of the line) using shell # 将第一行的 pick 改成 Commands 中所列出来的命令，然后保存并退出，所对应的修改将会生效。如果移动提交记录的顺序，将改变历史记录中的排序。\ngit revert 撤销某次操作，此次操作之前和之后的 commit 和 history 都会保留，并且把这次撤销作为一次最新的提交\ngit revert HEAD 撤销前一次提交操作\ngit revert HEAD --no-edit 撤销前一次提交操作，并以默认的 Revert \u0026quot;xxx\u0026quot; 为提交原因\ngit revert -n HEAD 需要撤销多次操作的时候加 -n 参数，这样不会每次撤销操作都提交，而是等所有撤销都完成后一起提交\ngit diff 查看工作区、暂存区、本地版本库之间的文件差异\ngit diff --stat 通过 --stat 参数可以查看变更统计数据\ntest.md | 1 - 1 file changed, 1 deletion(-) git reflog reflog 可以查看所有分支的所有操作记录（包括commit和reset的操作、已经被删除的commit记录，跟 git log 的区别在于它不能查看已经删除了的commit记录）\n远程版本库连接 如果在GitHub项目初始化之前，文件已经存在于本地目录中，那可以在本地初始化本地版本库，再将本地版本库跟远程版本库连接起来\ngit init 在本地目录内部会生成.git文件夹\ngit remote git remote -v 不带参数，列出已经存在的远程分支，加上 -v 列出详细信息，在每一个名字后面列出其远程url\ngit remote add origin https://github.com/gafish/gafish.github.com.git 添加一个新的远程仓库，指定一个名字，以便引用后面带的URL\ngit fetch 将远程版本库的更新取回到本地版本库\ngit fetch origin daily/0.0.1 默认情况下，git fetch 取回所有分支的更新。如果只想取回特定分支的更新，可以指定分支名。\n问题排查 git blame 查看文件每行代码块的历史信息\ngit blame -L 1,10 demo.html 截取 demo.html 文件1-10行历史信息\ngit bisect 二分查找历史记录，排查BUG\ngit bisect start 开始二分查找\ngit bisect bad 标记当前二分提交ID为有问题的点\ngit bisect good 标记当前二分提交ID为没问题的点\ngit bisect reset 查到有问题的提交ID后回到原分支\n更多操作 git submodule 通过 Git 子模块可以跟踪外部版本库，它允许在某一版本库中再存储另一版本库，并且能够保持2个版本库完全独立\ngit submodule add https://github.com/gafish/demo.git demo 将 demo 仓库添加为子模块\ngit submodule update demo 更新子模块 demo\ngit gc 运行Git的垃圾回收功能，清理冗余的历史快照\ngit archive 将加了tag的某个版本打包提取\ngit archive -v --format=zip v0.1 \u0026gt; v0.1.zip --format 表示打包的格式，如 zip，-v 表示对应的tag名，后面跟的是tag名，如 v0.1。\n\u0026ndash; End \u0026ndash;\n验证与自查 git status 能准确反映工作区/暂存区状态，git log --oneline 能看到自己的提交记录 在开发分支上完成一次完整流程：git add → git commit → git push origin \u0026lt;分支\u0026gt; 误操作后能用 git reflog 找到丢失的提交，用 git reset --hard \u0026lt;commit\u0026gt; 恢复 用 git stash 保存未完成工作，切分支处理紧急任务后 git stash pop 恢复 参考 原文：保姆级Git教程，10000字详解 Learn Git Branching（可视化 Git 练习） Pro Git 官方书籍（中文版） Git 官方文档 ","permalink":"https://heihei0299.github.io/posts/blog/git-tutorial/","summary":"\u003cp\u003e大家好，我是雷小帅。\u003c/p\u003e\n\u003cp\u003e最近群里有几位老哥私我，有没有好的 git 入门资料，想学一下。\u003c/p\u003e\n\u003cp\u003e偶然看到这个很棒的教程推荐给大家，大家可以看一下，另外还有一个不错的 git 入门网站，也推荐给大家，可以搭配使用。\u003c/p\u003e","title":"Git 保姆级教程：实用主义与深入探索"},{"content":"OpenCode 环境配置 从购买 OpenCode Go 订阅到启动一个新项目，中间有 4 个环节需要打通：\nAnt 指纹浏览器 → 管理多个 OpenCode Go 订阅，提取 API Key ↓ AxonHub → 反代多个 Key，统一 API 端点 ↓ CC Switch → 管理 AxonHub 配置，切换 Provider，管理会话 ↓ Project-Initialization → init-project.sh → 启动新项目 本文逐一介绍每个环节的工具选择和配置步骤。\n1. Ant 指纹浏览器管理 OpenCode Go 订阅 1.1 项目简介 black-ant/Ant-Browser 是一款开源指纹浏览器，可以在一台机器上创建多个互相隔离的浏览器实例。每个实例有独立的指纹、Cookie、本地存储和代理配置，非常适合管理多个 OpenCode Go 订阅账号。\n1.2 安装 Linux 版提供 .deb 和 tar.gz 两种分发方式，从 Releases 下载。\n方法一：tar.gz 解压运行（推荐，免 debtap）\nwget https://github.com/black-ant/Ant-Browser/releases/download/v1.4.0/ant-browser-1.4.0-linux-amd64.tar.gz tar -xzf ant-browser-1.4.0-linux-amd64.tar.gz cd ant-browser-1.4.0-linux-amd64 ./ant-chrome 方法二：deb 包转换安装\nwget https://github.com/black-ant/Ant-Browser/releases/download/v1.4.0/ant-browser_1.4.0_amd64.deb debtap -q ant-browser_1.4.0_amd64.deb sudo pacman -U ant-browser-1.4.0-1-x86_64.pkg.tar.zst tar.gz 免安装、免 debtap，适合临时使用。deb 转换后可用系统菜单启动。debtap 安装：yay -S debtap \u0026amp;\u0026amp; sudo debtap -u，详见软件相关。\nAnt Browser 基于 Wails 构建，如启动报错，需安装 webkit2gtk-4.1：sudo pacman -S webkit2gtk-4.1。\n1.3 配置浏览实例 启动 Ant Browser（运行 ./ant-chrome 或从系统菜单启动） 新建浏览器实例，每个实例对应一个 OpenCode Go 订阅账号 可选：为不同实例绑定不同代理 IP（设置 → 代理池 → 添加代理） 1.4 提取订阅信息 每个实例内分别操作：\n登录 opencode.ai 进入 Dashboard 查看订阅计划和剩余额度 进入 Settings → API Keys → Create new key，生成 API Key 或提取 Auth Cookie（供额度看板使用）：F12 → Application → Cookies → 复制 auth 值 用表格记录对应关系：\n浏览器实例 订阅账号 API Key 实例 1 account1@gmail.com sk-opencode-xxx1 实例 2 account2@gmail.com sk-opencode-xxx2 Auth Cookie 有时效，过期后重新登录提取即可。API Key 长期有效。\n2. AxonHub 反代 OpenCode Go 2.1 项目简介 looplj/axonhub 是一个 All-in-One AI 网关，可以将多个上游 API（如 OpenCode Go）统一到一个端点，同时提供多 Key 轮换、故障转移、请求追踪和用量统计功能。\n使用 AxonHub 反代 OpenCode Go 后，所有 AI 客户端只需要配置一个地址，无需关心背后有多少个订阅账号。\nOpenCode 与 AxonHub 的详细集成配置（含 opencode.json 格式、追踪插件、模型路由）见《AxonHub 集成 OpenCode 指南》。\n2.2 Docker 部署 mkdir -p ~/axonhub \u0026amp;\u0026amp; cd ~/axonhub docker-compose.yml：\nservices: axonhub: image: looplj/axonhub:latest container_name: axonhub environment: AXONHUB_DB_DIALECT: sqlite3 AXONHUB_DB_DSN: \u0026#34;file:/data/axonhub.db?cache=shared\u0026amp;_fk=1\u0026amp;_pragma=journal_mode(WAL)\u0026#34; ports: - \u0026#34;8090:8090\u0026#34; volumes: - ./data:/data - ./config.yml:/app/config.yml:ro restart: unless-stopped config.yml：\nserver: host: \u0026#34;0.0.0.0\u0026#34; port: 8090 llm_request_timeout: \u0026#34;600s\u0026#34; cors: enabled: true allowed_origins: [\u0026#34;*\u0026#34;] db: dialect: \u0026#34;sqlite3\u0026#34; dsn: \u0026#34;file:/data/axonhub.db?cache=shared\u0026amp;_fk=1\u0026amp;_pragma=journal_mode(WAL)\u0026#34; log: level: \u0026#34;info\u0026#34; 启动：\ndocker compose up -d 访问 http://localhost:8090，首次进入初始化向导，设置管理员账号和密码。\n2.3 配置渠道（Channel） 渠道是 AxonHub 与上游提供商之间的连接。登录 http://localhost:8090，依次点击「渠道」→「添加渠道」。\n以 OpenCode Go 为例，填写以下信息：\n字段 值 渠道名称 opencode-go API 格式 OpenAI Chat Completions Base URL https://opencode.ai/zen/go/v1/chat/completions API Key 步骤 1.4 中提取的 OpenCode Go API Key 支持的模型 按订阅计划填写，多个以逗号分隔 默认测试模型 选一个常用模型用于可用性检测 支持的模型示例（取决于订阅计划，模型 ID 格式为 opencode-go/\u0026lt;model-id\u0026gt;）：\nopencode-go/deepseek-v4-pro, opencode-go/kimi-k2.7-code, opencode-go/qwen3.7-max 当前完整模型列表见 OpenCode Go 官方文档。\n添加后回到渠道列表，点击状态列的「启用」开关。启用时 AxonHub 会自动测速，绿色的延迟值表示渠道可用。\n多 Key 负载均衡：在 API Key 字段中填入多个 Key（每行一个），AxonHub 自动轮换。同一个会话保持同一 Key，单个 Key 失败自动切到下一个：\nsk-opencode-xxx1 sk-opencode-xxx2 2.4 配置模型（Model） 渠道启用了只表示与上游的连接已打通，还需要把模型注册到 AxonHub 中，下游客户端才能调用。\n依次点击「模型」→「添加模型」：\n开发者：选择对应厂商（如 OpenCode Go 的模型选 OpenAI） 模型 ID：填入模型名称，如 claude-sonnet-4 名称：自动带入，可自定义显示名 其他参数保持默认，点击「保存」 保存后设置模型关联——告诉 AxonHub 当客户端请求这个模型时，走哪个渠道的哪个模型：\n在模型列表中找到刚添加的模型，点击「关联规则」→「关联」 优先级：越低越优先（默认即可） 类型：选择「去岛内精准匹配模型」 渠道：选择前面创建的 opencode-go 模型：选择该渠道下的对应模型名（通常与模型 ID 一致） 点击「保存」 如果客户端请求的模型名与 OpenCode Go 上的实际名称不同，可以在关联时做映射。例如客户端请求 claude-3.5-sonnet，关联到渠道中的 claude-sonnet-4。\n2.5 创建 API Key 模型配置好后，需要创建一个 API Key 给下游客户端使用。\n左侧菜单找到「API 密钥」→「创建」 填入密钥名称（如 my-opencode-key） 类型选择「用户」 点击「创建」，生成密钥后复制保存 密钥仅创建时显示一次，关闭后不可再查看，务必立即保存。\n2.6 客户端接入 拿到 API Key 后，所有 AI 客户端只需指向 AxonHub 地址 http://localhost:8090。\nClaude Code：\nexport ANTHROPIC_BASE_URL=\u0026#34;http://localhost:8090/v1\u0026#34; export ANTHROPIC_API_KEY=\u0026#34;你的AxonHub_API_Key\u0026#34; 写入 ~/.claude/config.json 持久化：\n{ \u0026#34;proxy\u0026#34;: { \u0026#34;baseUrl\u0026#34;: \u0026#34;http://localhost:8090/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;你的AxonHub_API_Key\u0026#34; } } Codex CLI：\nexport OPENAI_BASE_URL=\u0026#34;http://localhost:8090/v1\u0026#34; export OPENAI_API_KEY=\u0026#34;你的AxonHub_API_Key\u0026#34; OpenCode：\n编辑 ~/.config/opencode/opencode.json（详见《AxonHub 集成 OpenCode 指南》）：\n{ \u0026#34;provider\u0026#34;: { \u0026#34;axonhub\u0026#34;: { \u0026#34;npm\u0026#34;: \u0026#34;@ai-sdk/anthropic\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;AxonHub\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;baseURL\u0026#34;: \u0026#34;http://127.0.0.1:8090/anthropic/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;你的AxonHub_API_Key\u0026#34; }, \u0026#34;models\u0026#34;: { \u0026#34;claude-sonnet-4-5\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;AxonHub - Claude Sonnet 4.5\u0026#34;, \u0026#34;modalities\u0026#34;: { \u0026#34;input\u0026#34;: [\u0026#34;text\u0026#34;, \u0026#34;image\u0026#34;], \u0026#34;output\u0026#34;: [\u0026#34;text\u0026#34;] } } } } } } Cline / Continue 等 VS Code 插件：\nProvider 选 OpenAI Compatible，Base URL 填 http://localhost:8090/v1，API Key 填你的 AxonHub 密钥。\n验证连接：\ncurl http://localhost:8090/v1/chat/completions \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -H \u0026#34;Authorization: Bearer 你的AxonHub_API_Key\u0026#34; \\ -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;claude-sonnet-4\u0026#34;, \u0026#34;messages\u0026#34;: [{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;Hello\u0026#34;}] }\u0026#39; 返回正常的 JSON 响应说明配置成功。\n3. CC Switch 统一管理工具链 3.1 项目简介 farion1231/cc-switch（官网 ccswitch.io）是一个跨平台桌面工具，用一个界面管理 Claude Code、Claude Desktop、Codex、Gemini CLI、Grok Build、OpenCode、OpenClaw、Hermes 八款 AI 工具的配置、Provider 切换、MCP 服务器和会话历史。\nArch Linux 的 AUR 包由 heihei0299/cc-switch-aur 维护。\n3.2 安装 AUR 安装（推荐）：\nyay -S cc-switch 或自行编译：\ngit clone https://github.com/heihei0299/cc-switch-aur.git cd cc-switch-aur makepkg -si 编译依赖 rust、nodejs、pnpm、webkit2gtk-4.1 等，首次编译耗时较长。\nAppImage（官方分发方式）：\n从 Releases 下载 .AppImage，赋予执行权限后运行：\nchmod +x CC-Switch-*.AppImage ./CC-Switch-*.AppImage 3.3 添加 Provider CC Switch 内置 50+ Provider 预设（包括 AWS Bedrock、NVIDIA NIM 及常见社区转发服务），也可以手动添加。\n打开 CC Switch → Provider 管理页：\nAxonHub 反代地址（首选）\n类型: OpenAI 兼容 URL: http://localhost:8090/v1 API Key: AxonHub 后台生成的 Key OpenCode Go 直连（备用）\n类型: 根据模型选择。大部分模型使用 OpenAI Chat Completions（@ai-sdk/openai-compatible），MiniMax M3/M2.7 和 Qwen 系列使用 Anthropic Messages（@ai-sdk/anthropic） URL: https://opencode.ai/zen/go/v1/chat/completions API Key: 步骤 1.4 中的 OpenCode Go API Key 模型 ID 格式: opencode-go/\u0026lt;model-id\u0026gt;（如 opencode-go/kimi-k3） 其他 Provider（DeepSeek、OpenAI、Kimi 等）可按需添加。\n如果多个工具共用同一个 Provider，可以使用「通用 Provider」功能，一次配置同步到 Claude Code、Codex 和 Gemini CLI。\n3.4 切换配置 Provider 列表中点击「启用」按钮，当前激活的亮起绿点 切换后 CC Switch 自动改写各工具的配置文件，重启对应终端即可生效 Claude Code 支持热切换：切换后无需重启终端，立即生效 列表可拖拽排序，支持导入导出。首次启动时 CC Switch 会自动导入已有 CLI 工具的配置。\n系统托盘快速切换：\n无需打开主界面，右键系统托盘图标 → 点击 Provider 名称即可即时切换。\n3.5 已知限制 CC Switch 无法将 Base URL 为 https://opencode.ai/zen/go/v1/ 的 Provider 写入 OpenCode 配置文件。\n当 Provider 的 Base URL 设为 OpenCode Go 官方地址时，CC Switch 的注入机制不生效，修改不写入 ~/.config/opencode/opencode.json。\n解决方案：\n通过 AxonHub 反代 OpenCode Go，CC Switch 指向 http://localhost:8090/（参见第 2 章） 在 OpenCode TUI 中运行 /connect 命令按向导添加 Go 订阅 或手动编辑 ~/.config/opencode/opencode.json 直接写入 4. 初始化脚本启动项目 4.1 项目简介 heihei0299/Project-Initialization 是一个项目初始化模板仓库。它提供了一键脚本，让每个新项目拥有相同的工具链、配置规范和 Git 初始状态。\n4.2 安装脚本 git clone https://github.com/heihei0299/Project-Initialization.git ~/project-init cp ~/project-init/init-project.sh ~/bin/ 确保 ~/bin 在 $PATH 中。\n4.3 使用 mkdir my-ai-project \u0026amp;\u0026amp; cd my-ai-project init-project.sh 4.4 交互流程 脚本提供两个交互选择：\n请选择要初始化的目标工具： [1] OpenCode [2] Claude [3] 两者都选 请选择技能组框架： [1] Matt Pocock Skills [2] Trellis 选择后自动执行以下步骤：\n步骤 操作 说明 Step 1 git init 初始化 Git 仓库 Step 2 写入 .gitignore 覆盖全场景的忽略规则 Step 3 写入 opencode.json / .claude/settings.json MCP 服务器配置 Step 4 安装技能组 Matt\u0026rsquo;s Skills 或 Trellis Step 5 注入命令别名 grw / gm / implement 等 Step 6 写入 AGENTS.md / CLAUDE.md CodeGraph 指令文档 Step 7 CodeGraph 索引（可选） 检测代码库时询问 所有步骤幂等，文件已存在则跳过，可以重复运行。\n4.5 常用别名 安装完成后可用的命令别名：\n别名 实际命令 用途 grw opencode task 启动 OpenCode 任务 gm git commit --amend 修正上次 Git 提交 implement 由技能组提供 实现功能 tp / tc / cw 由技能组提供 技能组快捷命令 4.6 自定义模板 模板文件在 templates/ 目录下：\ntemplates/ ├── gitignore → 新项目的 .gitignore ├── opencode.json → MCP 服务器配置 ├── claude-settings.json → Claude MCP 配置 ├── AGENTS.md → AI 指令文档 └── CLAUDE.md → 已弃用，从 AGENTS.md 拷贝 修改对应文件即可自定义所有新项目的初始化内容，无需改动脚本本身。\n5. 完整流水线 到此四个环节全部打通：\nAnt Browser → 多个 OpenCode Go 账号 → API Key ↓ AxonHub → 统一 API 网关 → 多 Key 负载均衡 ↓ CC Switch → 配置管理 → Provider 切换 → 会话管理 ↓ init-project.sh → 新项目启动 → 开始编码 从「买订阅」到「写代码」，每个环节都有了明确的工具和操作流程。\n验证与自查 Ant Browser：每个实例能独立登录 opencode.ai 并生成 API Key AxonHub：curl http://localhost:8090/v1/chat/completions 返回正常 JSON 响应；渠道列表显示绿色延迟值 CC Switch：切换 Provider 后各工具配置文件被改写，Claude Code 无需重启即生效 init-project.sh 运行后新项目具备 .gitignore、opencode.json / .claude/settings.json、AGENTS.md 与命令别名，重复运行不报错 参考 Ant-Browser（black-ant） AxonHub（looplj） CC Switch（farion1231） Project-Initialization（heihei0299） OpenCode Go 官方文档 ","permalink":"https://heihei0299.github.io/posts/ai/opencode%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/","summary":"\u003ch1 id=\"opencode-环境配置\"\u003eOpenCode 环境配置\u003c/h1\u003e\n\u003cp\u003e从购买 OpenCode Go 订阅到启动一个新项目，中间有 4 个环节需要打通：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eAnt 指纹浏览器 → 管理多个 OpenCode Go 订阅，提取 API Key\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       ↓\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eAxonHub → 反代多个 Key，统一 API 端点\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       ↓\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCC Switch → 管理 AxonHub 配置，切换 Provider，管理会话\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       ↓\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eProject-Initialization → init-project.sh → 启动新项目\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e本文逐一介绍每个环节的工具选择和配置步骤。\u003c/p\u003e","title":"OpenCode 环境配置"},{"content":"Arch Linux 系统卡死排查与修复记录 适用场景：Arch Linux 桌面突然完全冻结（鼠标键盘无响应、合成器画面卡死），只能强制关机。你会学到用 journalctl 定位异常启动会话、从时间线找出元凶（I/O 饱和、systemd 服务频繁重启、容器无内存上限）并逐一修复的完整排查思路。\n问题现象 系统突然完全冻结，鼠标键盘无响应，Niri 合成器画面卡死，只能长按电源键强制关机。重启后需要找出原因。\n排查工具 journalctl — systemd 日志，查看各启动会话 dmesg — 内核日志 systemctl — 服务状态管理 docker stats — 容器资源监控 日志分析 通过 journalctl --list-boots 查看所有启动历史，定位到卡死时的会话（boot -1）：\nIDX BOOT ID FIRST ENTRY LAST ENTRY -1 15803db... Sun 2026-07-19 13:15:37 CST Sun 2026-07-19 13:49:15 CST 0 d02c25f... Sun 2026-07-19 13:50:51 CST Sun 2026-07-19 14:32:15 CST 中间仅间隔 1.5 分钟，说明 boot -1 非正常关机。\n卡死会话时间线 时间 事件 13:15:37 系统启动，用户登录 13:17 Telegram 启动 13:18 批量打开 kitty 终端 13:15~13:40 Docker 容器运行：5.9G 内存峰值，25 分钟内写入 7.9GB 磁盘、读取 2.3GB 13:32 ant-chrome 自动化浏览器启动 13:42 Firefox：3.9G 内存峰值，10 分钟 CPU 13:49:15 日志中断（长按电源键强制关机） 根因分析 元凶一：Docker 容器写爆磁盘（主因） docker-6b48aff...scope: Consumed 52.193s CPU time over 24min 58.983s wall clock time, 5.9G memory peak, 2.3G read from disk, 7.9G written to disk. 一个 Docker 容器在 25 分钟内写入 7.9GB、读取 2.3GB 数据，峰值内存 5.9GB。持续的 I/O 负载压满 NVMe 带宽，导致 Niri 合成器无法获取帧缓冲。\n元凶二：axonhub.service 每 5 秒失败重启 $ systemctl --user status axonhub.service Active: activating (auto-restart) Process: ExecStart=/home/shial/.local/bin/axonhub (code=exited, status=203/EXEC) 查看服务文件：\n[Service] ExecStart=%h/.local/bin/axonhub Restart=on-failure RestartSec=5 二进制文件 /home/shial/.local/bin/axonhub 根本不存在，但 systemd 每 5 秒 fork 一次失败进程，重启计数器飙到 379+ 次。每次重启都触发 systemd 调度 + journald 写日志，形成无意义的资源消耗叠加。\n而真正的 axonhub 其实在 Docker 中正常运行，这个 systemd 服务是旧的残留配置。\n元凶三：Docker 容器无内存上限 $ docker inspect axonhub --format \u0026#39;{{.HostConfig.Memory}}\u0026#39; 0 # 0 = 无限制 三个容器均可使用全部 30GB 内存，Docker + Firefox 合计约 10GB，在 I/O 饱和时进一步加剧系统压力。\n触发链 Docker 容器 I/O 激增 (7.9GB/25min) ↓ NVMe 磁盘带宽饱和 ↓ Niri 合成器 DRM page flip 阻塞 ↓ 桌面完全冻结 → 只能长按电源键强制关机 修复方案 修复一：停用无用的 axonhub.service systemctl --user stop axonhub.service systemctl --user disable axonhub.service 删除残留的 systemd 用户服务，消除每 5 秒的 fork 开销。\n修复二：Docker 容器添加内存上限 根据容器实际使用量设置合理上限：\ndocker update --memory 512m --memory-swap 1g axonhub docker update --memory 256m --memory-swap 512m octopus docker update --memory 256m --memory-swap 512m openlist 验证结果 $ systemctl --user status axonhub.service ○ axonhub.service - AxonHub AI Gateway Active: inactive (dead) $ docker stats --no-stream CONTAINER ID NAME MEM USAGE / LIMIT 25b88354a29b axonhub 61.72MiB / 512MiB f499698fce21 octopus 31.6MiB / 256MiB 76899497df6b openlist 41.97MiB / 256MiB 关于 zram $ zramctl NAME ALGORITHM DISKSIZE DATA COMPR TOTAL /dev/zram0 zstd 15.4G 4K 64B 20K 当前 zram 使用量为零（15.4G 空闲），并非卡死原因。卡死是磁盘 I/O 饱和，而非内存不足触发 swap 压缩。zram 的中文名是 zram，不是 zwap。\n总结 类别 问题 修复 🔴 主因 Docker 容器 25 分钟写 7.9GB 磁盘 已加内存上限，避免 I/O 暴增 🟡 辅因 axonhub.service 每 5 秒失败重启 已停用 🟢 潜在 容器无内存上限 已设置 256M~512M 限制 排查的关键是善用 journalctl -b 查看不同启动会话的日志，从时间线中定位异常进程。这次卡死的本质是 I/O 饱和导致合成器阻塞，修复后系统响应恢复正常。\n验证与自查 journalctl --list-boots 能列出各启动会话，异常关机（非正常结束）的会话一目了然 systemctl --user status axonhub.service 状态为 inactive (dead)，不再每 5 秒重启 docker stats --no-stream 中每个容器内存使用低于所设 limit（axonhub \u0026lt; 512MiB 等） 卡死复现时先用 zramctl 确认 zram 使用率，排除内存不足，再查 journalctl -b -1 的 I/O 时间线 参考 ArchWiki - systemd/Journal docker stats 官方文档 ArchWiki - zram ","permalink":"https://heihei0299.github.io/posts/linux/archlinux-%E7%B3%BB%E7%BB%9F%E5%8D%A1%E6%AD%BB%E6%8E%92%E6%9F%A5%E4%B8%8E%E4%BF%AE%E5%A4%8D%E8%AE%B0%E5%BD%95/","summary":"\u003ch1 id=\"arch-linux-系统卡死排查与修复记录\"\u003eArch Linux 系统卡死排查与修复记录\u003c/h1\u003e\n\u003cp\u003e适用场景：Arch Linux 桌面突然完全冻结（鼠标键盘无响应、合成器画面卡死），只能强制关机。你会学到用 \u003ccode\u003ejournalctl\u003c/code\u003e 定位异常启动会话、从时间线找出元凶（I/O 饱和、systemd 服务频繁重启、容器无内存上限）并逐一修复的完整排查思路。\u003c/p\u003e","title":"Arch Linux 系统卡死排查与修复记录"},{"content":"CachyOS LiveCD 安装 Arch Linux 准备工作 下载 CachyOS ISO 从 CachyOS 官网下载 ISO 镜像，放入 Ventoy U 盘。\n配置 Live 环境 配置国内镜像源 sudo bash -c \u0026#39;echo \u0026#34;Server = https://mirrors.ustc.edu.cn/cachyos/repo/\\$arch/\\$repo\u0026#34; \u0026gt; /etc/pacman.d/cachyos-mirrorlist \u0026amp;\u0026amp; echo \u0026#34;Server = https://mirrors.ustc.edu.cn/cachyos/repo/\\$arch_v3/\\$repo\u0026#34; \u0026gt; /etc/pacman.d/cachyos-v3-mirrorlist \u0026amp;\u0026amp; echo \u0026#34;Server = https://mirrors.ustc.edu.cn/cachyos/repo/\\$arch_v4/\\$repo\u0026#34; \u0026gt; /etc/pacman.d/cachyos-v4-mirrorlist \u0026amp;\u0026amp; echo \u0026#34;Server = https://mirrors.ustc.edu.cn/archlinux/\\$repo/os/\\$arch\u0026#34; \u0026gt; /etc/pacman.d/mirrorlist\u0026#39; 再次执行以下命令，防止安装过程中镜像源被替换：\nsudo sed -i \u0026#39;s/^/#/\u0026#39; /etc/calamares/scripts/update-mirrorlist 分区与挂载 使用 GParted 分区 启动 GParted（CachyOS 自带），按以下方案分区：\n挂载点 大小 文件系统 标记 /efi 2G FAT32 boot, esp / 300G ext4 /home 400G ext4 挂载分区 使用下面的命令查看，硬盘分区情况。\nsudo fdisk -l 将根分区挂载到 /mnt\n创建 EFI 和 home 挂载点\nmkdir -p /mnt/efi /mnt/home 挂载 EFI 分区和 home 分区\nmount /dev/efi_partition /mnt/efi mount /dev/home_partition /mnt/home 安装系统 安装基础包 pacstrap -K /mnt base linux linux-firmware btrfs-progs networkmanager vim sudo amd-ucode -K 初始化密钥；base 基本包；linux 是内核，可以更换；linux-firmware 是固件；btrfs-progs 是 Btrfs 文件系统的管理工具；networkmanager 是联网用的；vim 是文本编辑器；sudo 和权限管理有关；amd-ucode 是微码，Intel 用户安装 intel-ucode。\npacstrap 是把软件安装到指定的根目录下。\n如果你使用的是 marvell 的无线网卡，这里要额外安装 linux-firmware-marvell，否则进系统找不到网卡。\nVim 是以键盘操作为核心理念的文本编辑器。\ni — 进入编辑模式 esc — 回到普通模式 u — 撤销 / — 搜索模式，n 下一个，Shift+n 上一个 :w — 写入 :q — 退出 :wq — 保存并退出 ! — 强制执行 不习惯的话可以安装 nano，记住 Ctrl+F 搜索、Ctrl+S 保存和 Ctrl+X 退出即可。也可以选择 neovim，Vim 的加强版，后面所有 vim 命令都改成 nvim。\n可选：硬盘 Swap 参考链接：\n电源管理/挂起与睡眠 - Arch Linux 中文维基 | Swap - ArchWiki | Swap - Manjaro\nSwap 大小参考：\n内存(GB) 不需要休眠(GB) 需要休眠(GB) 不建议超过(GB) 1 1 2 2 2 2 3 4 4 4 6 8 8 3 11 16 16 4 20 32 32 6 38 64 64 8 72 128 128 11 139 256 256 16 272 512 创建 Swap 文件（64g 按实际需求调整）\nbtrfs filesystem mkswapfile --size 64g --uuid clear /mnt/swap/swapfile 启用 Swap\nswapon /mnt/swap/swapfile 生成 fstab 系统根据 fstab 自动挂载分区。\ngenfstab -U /mnt \u0026gt; /mnt/etc/fstab genfstab — 生成文件系统表；-U 用 UUID 指定分区；\u0026gt; 输出覆盖写入，\u0026gt;\u0026gt; 追加写入。\n更换根目录（Chroot） 进入新安装的系统。\narch-chroot /mnt 此时根目录从 Live 环境变成了 /mnt，提示符会变化。\n系统配置 时区与时间 timedatectl set-timezone Asia/Shanghai 同步硬件时间：\nhwclock --systohc 也可以手动创建链接：\nln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ln — link；-s 软链接；-f 强制执行。\n本地化设置 编辑配置文件\nvim /etc/locale.gen 去掉 en_US.UTF-8 UTF-8 和 zh_CN.UTF-8 UTF-8 前面的 # 注释。\n生成本地化配置\nlocale-gen 设置系统语言\nvim /etc/locale.conf 写入：\nLANG=en_US.UTF-8 不要写 zh_CN.UTF-8，会导致 TTY 中文字符变成豆腐块。桌面装好后在用户空间再改语言。\n主机名 vim /etc/hostname 写入自己喜欢的主机名，比如 archlinux。\nRoot 密码 passwd 输入过程不显示，直接输入回车即可。\n引导与启动项 安装 GRUB 安装必要的软件包\npacman -S grub efibootmgr os-prober exfat-utils efibootmgr 管理 UEFI 启动项；os-prober 和 exfat-utils 搜索 Win11（不配置双系统可跳过）。\n安装 GRUB\ngrub-install --target=x86_64-efi --efi-directory=/efi --boot-directory=/efi --bootloader-id=ARCH 参数 说明 --target 指定架构 --efi-directory 指定 ESP 位置 --boot-directory GRUB 安装目录 --bootloader-id 启动项名字，可自定义 移动设备或主板只支持默认 EFI 路径时加 --removable。\n编辑 GRUB 配置源文件\nvim /etc/default/grub 启动项记忆 — GRUB_DEFAULT=0 改为 =saved，取消 GRUB_SAVEDEFAULT=true 的注释 显示开机日志 — 去掉 quiet，设置 loglevel=5 禁用 watchdog — 添加 nowatchdog 和 modprobe.blacklist=sp5100_tco（Intel CPU 换成 iTCO_wdt） 搜索其他系统 — 取消 GRUB_DISABLE_OS_PROBER=false 的注释 在 GRUB 默认位置创建链接\nln -sf /efi/grub /boot/grub 生成 GRUB 配置文件\ngrub-mkconfig -o /boot/grub/grub.cfg ZRAM zswap - ArchWiki | zram - ArchWiki\nZRAM 将内存的部分空间用作交换空间。如果没有配置 Swap，请一定配置 ZRAM。\n安装 zram-generator\npacman -S zram-generator 编辑配置文件\nvim /etc/systemd/zram-generator.conf [zram0] zram-size = ram compression-algorithm = zstd 禁用 zswap\nzswap 与 ZRAM 功能重复，禁用之。\nvim /etc/default/grub 在 GRUB_CMDLINE_LINUX_DEFAULT 中添加 zswap.enabled=0。\n重新生成 GRUB 配置文件\ngrub-mkconfig -o /boot/grub/grub.cfg 启用网络服务 systemctl enable NetworkManager NetworkManager 默认使用 wpa_supplicant，可更换为更现代的 iwd。\n部分设备更换后可能无法正常联网。\npacman -S iwd mkdir -p /etc/NetworkManager/conf.d vim /etc/NetworkManager/conf.d/iwd.conf 写入：\n[device] wifi.backend=iwd 完成安装 退出并重启 exit reboot 此时自动取消所有挂载。\n拔掉系统 U 盘 选择 UEFI 启动项 通常默认就是刚安装的 Arch。如果未出现启动项，参考 ArchWiki GRUB。\n登录并联网 登录 root 账户（密码刚才已设置）。\n验证网络\nip a ping bilibili.com 连接 WiFi\nnmtui 选择 Activate a connection 选择自己的 WiFi 连接 Esc 退出，Ctrl+L 清屏 庆祝一下 pacman -S fastfetch lolcat cmatrix fastfetch fastfetch | lolcat cmatrix cmatrix | lolcat cmatrix -r | 管道符，将左边程序的输出输入到右边程序。\n","permalink":"https://heihei0299.github.io/posts/arch/cachyos-livecd%E5%AE%89%E8%A3%85arch/","summary":"\u003ch1 id=\"cachyos-livecd-安装-arch-linux\"\u003eCachyOS LiveCD 安装 Arch Linux\u003c/h1\u003e\n\u003ch2 id=\"准备工作\"\u003e准备工作\u003c/h2\u003e\n\u003ch3 id=\"下载-cachyos-iso\"\u003e下载 CachyOS ISO\u003c/h3\u003e\n\u003cp\u003e从 CachyOS 官网下载 ISO 镜像，放入 Ventoy U 盘。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"配置-live-环境\"\u003e配置 Live 环境\u003c/h2\u003e\n\u003ch3 id=\"配置国内镜像源\"\u003e配置国内镜像源\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo bash -c \u003cspan class=\"s1\"\u003e\u0026#39;echo \u0026#34;Server = https://mirrors.ustc.edu.cn/cachyos/repo/\\$arch/\\$repo\u0026#34; \u0026gt; /etc/pacman.d/cachyos-mirrorlist \u0026amp;\u0026amp; echo \u0026#34;Server = https://mirrors.ustc.edu.cn/cachyos/repo/\\$arch_v3/\\$repo\u0026#34; \u0026gt; /etc/pacman.d/cachyos-v3-mirrorlist \u0026amp;\u0026amp; echo \u0026#34;Server = https://mirrors.ustc.edu.cn/cachyos/repo/\\$arch_v4/\\$repo\u0026#34; \u0026gt; /etc/pacman.d/cachyos-v4-mirrorlist \u0026amp;\u0026amp; echo \u0026#34;Server = https://mirrors.ustc.edu.cn/archlinux/\\$repo/os/\\$arch\u0026#34; \u0026gt; /etc/pacman.d/mirrorlist\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e再次执行以下命令，防止安装过程中镜像源被替换：\u003c/p\u003e","title":"CachyOS LiveCD 安装 Arch Linux"},{"content":"安装 Niri 与 Noctalia 本文教你从零搭建 Niri（滚动平铺 Wayland 合成器）+ Noctalia（桌面 shell）环境：字体、dotfiles 同步、核心软件、多显示器配置、fcitx5 中文输入法。全部命令在 Arch Linux 下验证。\n安装字体 sudo pacman -S \\ ttf-jetbrains-mono-nerd \\ noto-fonts \\ noto-fonts-cjk \\ noto-fonts-emoji \\ ttf-nerd-fonts-symbols-mono ttf-jetbrains-mono-nerd — 终端等宽字体（含 Nerd Fonts 图标） noto-fonts / noto-fonts-cjk / noto-fonts-emoji — 外文、中日韩、emoji 字体 同步 dotfiles 先安装 GNU Stow（dotfiles 符号链接管理工具）：\nsudo pacman -S stow 克隆 dotfiles 仓库并应用：\ngit clone https://github.com/heihei0299/dotfiles.git ~/dotfiles cd ~/dotfiles rm README.md stow * stow * 会把仓库里每个包目录分别符号链接到 $HOME 对应位置。rm README.md 是因为 README 不在任何包目录里，stow 会报错。\n安装 Niri、Noctalia 与核心软件 sudo pacman -Syu niri xwayland-satellite xdg-desktop-portal-gnome \\ xdg-desktop-portal-gtk matugen cava qt6-multimedia-ffmpeg nautilus sudo pacman -S noctalia-git noctalia-greeter-git kitty nvim niri 是滚动平铺 Wayland 合成器；noctalia-git（AUR）是基于 quickshell 的桌面 shell；kitty 终端、nvim 编辑器。\nZsh 依赖软件 根据 ~/.zshrc，zsh 配置依赖以下包。按需选择安装方式——zinit 可从 GitHub Releases 自动安装部分工具（无需 pacman），但系统级安装更稳定：\n# 核心依赖（zsh + zinit 运行所需） sudo pacman -S --needed zsh git curl unzip # 系统命令（别名/函数引用，必须系统安装） sudo pacman -S --needed \\ zoxide kitty neovim trash-cli tldr unar \\ bpytop procps-ng gnome-text-editor # 可选：也可通过 zinit 自动安装（二选一即可） sudo pacman -S --needed \\ fzf eza bat ripgrep fd fastfetch duf zsh — Shell 本体 git — 克隆 zinit 插件管理器（.zshrc 自动安装 zinit） curl / unzip — zinit 下载和解压工具 zoxide — 智能目录跳转（zoxide init zsh --cmd cd） kitty — 终端模拟器（别名 k） neovim — 编辑器（别名 nv） trash-cli — 安全删除（别名 rm） tldr — 简版 man 手册（别名 man） unar — 解压工具（别名 x） bpytop — 资源监视器（别名 top） procps-ng — 提供 ps / watch 命令 gnome-text-editor — GUI 编辑器（别名 gedit） fzf — 模糊搜索（fzf-tab、Ctrl+R 依赖） eza — ls 替代（别名 ls/ll/la/tree） bat — cat 替代（别名 cat） ripgrep — 递归内容搜索 fd — find 替代 fastfetch — 系统信息（别名 ff） duf — 磁盘使用率（别名 df） 显示器配置 参考 niri wiki: Configuration: Outputs。\n获取显示器信息 可选：用 wdisplays 临时调整显示设置：\nsudo pacman -S wdisplays 运行命令获取显示器信息：\nniri msg outputs Output \u0026#34;BOE NE156QHM-NY1 Unknown\u0026#34; (eDP-1) Current mode: 2560x1440 @ 165.000 Hz (preferred) ... Output \u0026#34;Shenzhen KTC Technology Group H27T22C 0x00000001\u0026#34; (DP-2) Current mode: 2560x1440 @ 180.000 Hz ... 记住 eDP-1、DP-2 这类名称，在 Available modes 里找到需要的模式，格式为 分辨率@刷新率。\n编写 output 配置 在 niri 配置文件中搜索 output，在示例配置下新增：\noutput \u0026#34;eDP-1\u0026#34;{ //分辨率和刷新率 mode \u0026#34;2560x1440@165\u0026#34; //缩放倍率 scale 1.33 //位置，x=0 y=0 代表最左上角 position x=0 y=0 //启动时聚焦此显示器 focus-at-startup //取消下面这行的注释设置可变刷新率 //variable-refresh-rate //取消下面这行的注释可以设置旋转，参数有：90, 180, 270, flipped（水平翻转）, flipped-90（水平翻转后旋转）, flipped-180 and flipped-270 //transform \u0026#34;90\u0026#34; } 多显示器布局计算 每个显示器都要一个 output{}，用名称指定生效的显示器。\n位置关系需要计算：把最左上角的显示器设为 position x=0 y=0，以此为原点推算其他显示器。\n例如 eDP-1 在最左上角（2560x1440，1.33 缩放），横向像素为 2560/1.33 ≈ 1925。把 DP-2 放在它右边：\noutput \u0026#34;DP-2\u0026#34;{ mode \u0026#34;2560x1440@180\u0026#34; scale 1 position x=1925 y=0 } 想放下面改 y 轴；设置旋转时横竖值也要对应旋转。\n安装中文输入法 fcitx5 安装基础框架 sudo pacman -S fcitx5-im fcitx5-im 包含 fcitx5 的基本包。\n安装输入方案 可以自己选择输入方案。全拼推荐 RIME + 雾凇拼音：\n中文输入合集 fcitx5-chinese-addons：包含所有常用方案（拼音、五笔、双拼等），安装简单但输入效果一般，不推荐。\nsudo pacman -S fcitx5-chinese-addons RIME 中州韵引擎 + 雾凇拼音（推荐全拼用户）：两大主流方案中，雾凇全拼分词效果好，万象适合双拼。\nsudo pacman -S fcitx5-rime rime-ice-git fcitx5-rime 是输入法引擎；rime-ice-git 雾凇输入方案，需要从 AUR 或 archlinuxcn 安装。 其他方案：rime-wanxiang-pinyin 万象拼音、rime-wanxiang-flypy 万象小鹤双拼（archlinuxcn 源）、fcitx5-mozc 日语、rime-wubi 五笔。\n添加 RIME 到输入法列表 打开 fcitx5-configtool，把 rime（中州韵） 添加到输入法列表。重启输入法后，Ctrl+空格 切换到 RIME 会自动初始化。默认方案是繁体的 明月拼音，按 F4 可打开设置菜单临时调整为简体。\n配置默认方案为雾凇拼音 编辑 RIME 用户配置（rime-ice 的 schema id 为 rime_ice）：\nmkdir -p ~/.config/fcitx5/rime vim ~/.config/fcitx5/rime/default.custom.yaml 写入：\npatch: schema_list: - schema: rime_ice 重新部署（右键托盘 RIME 图标 → 重新部署，或重启输入法）后，默认方案即为雾凇拼音。参考 rime-ice 仓库。\n验证输入法 # 确认 fcitx5 进程运行 pgrep -a fcitx5 # 任意输入框按 Ctrl+空格 切换输入法 # 输入 \u0026#34;shurufa\u0026#34; 应出现 \u0026#34;输入法\u0026#34; 候选 验证与自查 niri msg outputs 输出的分辨率/缩放与配置文件一致 两个显示器位置关系正确（鼠标可跨屏移动） Ctrl+空格 可切换中英文输入，候选词正常 fastfetch 正常显示系统信息（dotfiles 生效） 参考 niri wiki: Outputs fcitx5 官方文档 rime-ice 雾凇拼音 ","permalink":"https://heihei0299.github.io/posts/arch/%E6%A1%8C%E9%9D%A2%E5%AE%89%E8%A3%85%E4%B8%8E%E7%BE%8E%E5%8C%96/","summary":"\u003ch1 id=\"安装-niri-与-noctalia\"\u003e安装 Niri 与 Noctalia\u003c/h1\u003e\n\u003cp\u003e本文教你从零搭建 Niri（滚动平铺 Wayland 合成器）+ Noctalia（桌面 shell）环境：字体、dotfiles 同步、核心软件、多显示器配置、fcitx5 中文输入法。全部命令在 Arch Linux 下验证。\u003c/p\u003e","title":"安装 Niri 与 Noctalia"},{"content":"安装桌面环境前的准备 如果你想省事，在这个节点已经可以使用我的 Arch 一键配置脚本迅速获得可以日用的桌面环境。\n设置全局默认文本编辑器 通过 EDITOR 环境变量设置默认编辑器。如果不设置的话有些程序会默认调用 vi 编辑器。Arch 默认是没有安装 vi 的，会报错。\nsudo vim /etc/environment EDITOR=vim # 如果你使用neovim的话填入nvim，nano填入nano 由于是全局变量，需要 exit 注销后重新登录才能生效。\nexit 创建普通用户 很多软件会拒绝在 root 权限下运行，所以普通用户是必须的。\n新建用户\nuseradd -mG wheel 你的用户名 -m 代表创建用户的时候创建 home 目录。\n-G 代表设置组。\n设置密码\npasswd 你的用户名 可选：取消多次输错密码后的锁定时间\n这步牺牲安全性以提高日用体验和软件兼容\nvim /etc/security/faillock.conf 把 deny = 3 改成 deny = 0\n编辑权限\nvisudo 此时编辑的文件本体是 /etc/sudoers，visudo是在有安全机制的情况下进行编辑。\n搜索 wheel，取消注释。\n%wheel ALL=(ALL:ALL) ALL 可选：配置 pacman 免密\n这步牺牲安全性获得更流畅的安装软件体验\nvisudo -f /etc/sudoers.d/10-pacman-nopasswd %wheel ALL=(ALL:ALL) NOPASSWD: /usr/bin/pacman 如果你要给其他命令配置免密可以以逗号隔开 /usr/bin/pacman, /usr/bin/systemctl\n保存后修改权限\nchmod 440 /etc/sudoers.d/10-pacman-nopasswd 退出 root 使用普通用户登录\nexit 接下来需要管理员权限运行的命令要加上 sudo。\n配置国内镜像源 在使用arch linux进行安装更新时，国内网络环境特别缓慢，甚至无法完成。此时建议更新arch linux的mirrorlists，添加国内的镜像源，这样可以大大加快更新速度\n备份mirrorlist配置:\ncp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.backup 编辑 /etc/pacman.d/mirrorlist 添加如下国内源(也可以只使用国内源):\nsudo vim /etc/pacman.d/mirrorlist Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch Server = http://mirrors.163.com/archlinux/$repo/os/$arch Server = http://mirrors.aliyun.com/archlinux/$repo/os/$arch 安装密钥并更新\nsudo pacman -S archlinux-keying sudo pacman -Suy 开启32位源 运行 Windows 软件通常需要各种 32 位的依赖，想玩游戏的话 Steam 客户端也在 32 位源里，所以建议开启 32 位源。\n编辑 pacman 配置文件\nsudo vim /etc/pacman.conf 去掉 [multilib] 两行的注释。\n[multilib] Include = /etc/pacman.d/mirrorlist 同步数据库\nsudo pacman -Syu archlinuxcn源 archlinuxcn 源是由 archlinuxcn 维护的软件仓库，可以丰富我们安装软件的手段。\n编辑 pacman 配置文件添加 archlinuxcn 源\nsudo vim /etc/pacman.conf 文件底部写入\n[archlinuxcn] Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch Server = https://mirrors.hit.edu.cn/archlinuxcn/$arch Server = https://repo.huaweicloud.com/archlinuxcn/$arch 不用全写，一般用 ustc（中科大）和 tuna（清华）的镜像即可。如果你在海外的话可以直接使用官方源：\nServer = https://repo.archlinuxcn.org/$arch 同步数据库并安装 archlinuxcn 密钥\nsudo pacman -Sy archlinuxcn-keyring sudo pacman -Syu AUR助手 ArchWiki AUR Helpers\nAUR 是 Arch 最强大的软件仓库。这个仓库里通常只存放软件的PKGBUILD，可以将其理解为一个自动化安装脚本。通常的流程是从 AUR 上拉取PKGBUILD，然后makepkg -si打包软件，再用pacman -U安装，但这太过繁琐。通过社区做好的 AUR 助手可以更方便地从 AUR 安装软件。archlinuxcn 上有编译好的版本，可以直接从 archlinuxcn 安装。\nsudo pacman -S --needed base-devel yay paru base-devel 是编译软件时必须的；yay 和 paru 都是常用的助手，任选其一，也可以都装。\n字体 通常安装以下字体包：\nsudo pacman -S noto-fonts noto-fonts-cjk noto-fonts-emoji ttf-jetbrains-mono-nerd noto-fonts 包含大部分外文字体。\nnoto-fonts-cjk 最常用的中日韩字体，但是因为同时包含中日韩，所以不正确设置系统字体的话会出现中文以日文的字体显示之类问题。有关 fontconfig 字体设置 的内容可以看附录-字体设置。\nnoto-fonts-emoji emoji 表情。\nttf-jetbrains-mono-nerd 最常用的等宽字体，用于终端字体显示。nerd 代表包含了字符字体。\n音视频固件和服务 让音频设备和屏幕分享正常工作。\n可选：安装音视频固件\nsudo pacman -S --needed sof-firmware alsa-ucm-conf alsa-firmware sof-firmware 为现代音视频设备提供固件，通常装这个就可以了。\nalsa-ucm-conf 提供必要的配置文件。\nalsa-firmware 为不常见或者较旧的设备提供固件。\n安装音视频服务\nsudo pacman -S --needed pipewire wireplumber pipewire-pulse pipewire-alsa pipewire-jack pipewire 是由 Red Hat 主导开发的现代音视频服务。\nwireplumber 会智能管理 pipewire。\npipewire-pulse、pipewire-alsa、pipewire-jack 分别为 PulseAudio、ALSA、JACK 提供兼容。\n启用服务\nsystemctl --user enable --now pipewire pipewire-pulse wireplumber 性能模式切换 power-profiles-daemon 是各个桌面环境通用的性能模式切换服务，有三个档位，performance 性能、balance 平衡、powersave 省电。\n安装\nsudo pacman -S power-profiles-daemon 启动服务\nsudo systemctl enable --now power-profiles-daemon 这个已经足够了，不建议折腾 tlp 或者 auto-cpufreq，功耗上不会有明显区别。\n蓝牙 安装\nsudo pacman -S --needed bluez 启动服务\nsudo systemctl enable --now bluetooth Flatpak软件 Flatpak 是全发行版通用的打包方式。依赖和插件比较多的软件 Flatpak 版本通常更好用，比如 OBS 和 Easyeffects。如果 AUR 和仓库的软件都不太正常，也可以尝试 Flatpak 版本。\n安装 flatpak\nsudo pacman -S flatpak 可选：更换国内源\n上交大\nsudo flatpak remote-modify flathub --url=https://mirror.sjtu.edu.cn/flathub 中科大\nsudo flatpak remote-modify flathub --url=https://mirrors.ustc.edu.cn/flathub 可选：休眠到硬盘 ArchWiki Power management/Suspend and hibernate\n如果需要休眠到硬盘功能，且之前设置了硬盘 swap 的话。\n查看 /etc/mkinitcpio.conf 这个文件的 HOOKS 部分：\ngrep ^HOOKS /etc/mkinitcpio.conf grep ^HOOKS 筛选以 HOOKS 开头的行。\n如果是 HOOKS(base systemd ...) 的话无须手动配置。\n如果是 HOOKS(base udev ...) 的话：\n添加 hook\nsudo vim /etc/mkinitcpio.conf 在 HOOKS() 内添加 resume，注意需要添加在 udev 的后面。\n重新生成 initramfs\nsudo mkinitcpio -P 重启电脑\nreboot 使用命令进行休眠\nsystemctl hibernate 自动安装驱动 利用 chwd 可以自动安装驱动。\nyay -S chwd-arch-git chwd -a 其他命令\nchwd --list 可以列出可用的配置，每一个配置代表了一系列驱动包和后续修改。\nchwd -r 配置名称 删除已经安装的配置。\n更多选项可以用 chwd -h 查看。\n手动安装驱动 安装显卡驱动 NVIDIA\n在 CodeNames · freedesktop.org 这个页面搜索自己的显卡，看看对应的 family 和架构是什么。然后在 NVIDIA - ArchWiki 这个页面查找对应的包名。如果找不到你的显卡名字，可以看和你的显卡相同架构的显卡对应的 family 是什么。例如 MX205，表上没有，但通过它是 Pasca 架构这个信息可以知道它属于 NV160 family。\nNV160 family往后的显卡（差不多 16 系往后）用 nvidia-open，7 系到 10 系的 N 卡从 AUR 安装 nvidia-580xx-dkms。\n不同的内核对应的包的后缀不同，像 linux-zen 这样的自定义内核要装 -dkms 后缀。不过，考虑到日后更新的稳定和便利，推荐统一使用 -dkms 版本的驱动包，它会自动在更换和更新内核时重新编译驱动。除了驱动包，还需要 nvidia-utils 工具集，580xx 版本驱动对应的工具集包是 AUR 上的nvidia-580xx-utils，其他显卡按照 ArchWiki 的表格以此类推。\nnvidia-open 是内核模块开源的驱动，不是完全的开源驱动。nvidia-utils 通常作为驱动包的依赖自动被安装，但是某些自行打包的发行版可能没有正确处理依赖关系导致需要自己安装。\n检查头文件\nDKMS 编译内核模块需要内核的头文件。\nsudo pacman -S --needed linux-headers linux 替换为自己的内核，比如 zen 内核的头文件包名是 linux-zen-headers。\n安装驱动\nsudo pacman -S nvidia-open-dkms nvidia-utils lib32-nvidia-utils AMD\nAMDGPU - ArchWiki\nA 卡通常不需要自己安装驱动，已经由 linux-firmware 和 mesa 提供。可以安装一下 Vulkan 驱动。\nsudo pacman -S --needed mesa lib32-mesa xf86-video-amdgpu vulkan-radeon lib32-vulkan-radeon Intel\nIntel graphics - ArchWiki\nsudo pacman -S --needed mesa lib32-mesa vulkan-intel lib32-vulkan-intel 硬件编解码 archwiki_硬件视频加速\nNVIDIA\n由 nvidia-utils 和 libva-nvidia-driver 提供。\nsudo pacman -S libva-nvidia-driver AMD\n自带。\nIntel\nBroadwell 往后的 Intel 显卡装 intel-media-driver，旧的装 libva-intel-driver。\n可选：OpenCL 驱动 参考：archwiki General-purpose computing on graphics processing units\n先安装统一调用\nsudo pacman -S ocl-icd 然后安装运行时\nNVIDIA\n装opencl-nvidia lib32-opencl-nvidia 这两个包。580xx 和 470xx 的用户安装对应的版本，例如：opencl-nvidia-580xx lib32-opencl-nvidia-580xx。\nAMD\n新卡装rocm-opencl-runtime。早于 Vega 架构的卡需要额外配置 ROC_ENABLE_PRE_VEGA=1 环境变量。\n不兼容的话可以使用 mesa\nsudo pacman -S opencl-mesa lib32-opencl-mesa 安装后需要配置环境变量RUSTICL_ENABLE=radeonsi\nIntel\nGen12 以后的安装intel-compute-runtime，旧的安装intel-compute-runtime-legacy\nCPU\n装intel-opencl-runtime；不兼容的话装基于 LLVM 的 pocl。\n重启激活显卡驱动 reboot 可选：验证硬件编解码\nsudo pacman -S libva-utils 使用 libva-utils 提供的 vainfo 进行验证。\nvainfo 多显卡用户可以使用 LIBVA_DRIVER_NAME 环境变量指定要使用的显卡：\nLIBVA_DRIVER_NAME=nvidia vainfo ","permalink":"https://heihei0299.github.io/posts/arch/%E5%AE%89%E8%A3%85%E6%A1%8C%E9%9D%A2%E7%8E%AF%E5%A2%83%E5%89%8D%E7%9A%84%E5%87%86%E5%A4%87/","summary":"\u003ch1 id=\"安装桌面环境前的准备\"\u003e安装桌面环境前的准备\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e如果你想省事，在这个节点已经可以使用\u003ca href=\"%E4%B8%80%E9%94%AE%E9%85%8D%E7%BD%AE%E6%A1%8C%E9%9D%A2%E7%8E%AF%E5%A2%83.md\"\u003e我的 Arch 一键配置脚本\u003c/a\u003e迅速获得可以日用的桌面环境。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"设置全局默认文本编辑器\"\u003e设置全局默认文本编辑器\u003c/h2\u003e\n\u003cp\u003e通过 \u003ccode\u003eEDITOR\u003c/code\u003e 环境变量设置默认编辑器。如果不设置的话有些程序会默认调用 \u003ccode\u003evi\u003c/code\u003e 编辑器。Arch 默认是没有安装 \u003ccode\u003evi\u003c/code\u003e 的，会报错。\u003c/p\u003e","title":"安装桌面环境前的准备"},{"content":"软件相关 本文介绍 Arch Linux 的四种软件安装方式（pacman / AUR / Flatpak / AppImage）、推荐软件清单，以及 AppImage 与星火应用商店的使用方法。\n如何安装软件 安装软件主要分四种方式：\npacman 从官方仓库和 archlinuxcn 安装软件。\nyay 或者 paru 从 AUR（Arch Linux 用户仓库）安装软件。\nAUR 很方便，但要注意辨识它是否安全。千万不要随意安装来源不明、不再维护、无人使用的 AUR 包。安装AUR包前必须审查PKGBUILD，看不懂可以让 AI 帮忙审查。\n可以从 AUR 网站上获取包的信息。如果你想要方便的图形界面，可以安装 pacseek，或者使用这个脚本\nflatpak 从 flathub 或指定的 flathub 源下载。\n如果要使用 GUI 商店的话推荐 Bazzar。\n应用官网下载 AppImage 包直接运行。\nAppImage 和 flatpak 类似，也是所有发行版通用的打包方式，可以理解为免安装便携版。使用方法看本文的如何使用 AppImage。\n建议优先级：官方 pacman \u0026raquo; 经过验证的 flatpak \u0026gt; 用户仓库（AUR、CN 源等） \u0026gt; 官网下载\n如果你在安装软件的时候遇到了网络问题，下一节介绍了如何方便快捷地在 Linux 上进行网络代理：网络代理\n我使用的软件 以下是我会安装的软件，以及我认为好用的一些软件，你可以按照你的需求来。按安装方式分组：\npacman（官方仓库） sudo pacman -S --needed mission-center gparted dosfstools exfat-utils f2fs-tools firefox gdu wl-clipboard mission-center — 系统监控 gparted — 图形化分区工具 dosfstools / exfat-utils / f2fs-tools — 文件系统工具集 firefox — 浏览器 gdu — 终端磁盘空间分析 Yazi 及依赖 sudo pacman -S --needed \\ yazi ffmpeg 7zip jq poppler resvg imagemagick chafa \\ mediainfo duckdb ouch gvfs glib2 pandoc-cli pass yazi — 终端文件管理器 ffmpeg — 音视频缩略图/预览 7zip — 归档处理 jq — JSON 处理 poppler — PDF 预览 resvg — SVG 渲染预览 imagemagick — 图片处理 chafa — ASCII 图像预览（终端无图片协议时的 fallback） mediainfo — 媒体文件元信息预览 duckdb — CSV/JSON/Parquet/Excel 数据库文件预览 pandoc-cli — 提供 pandoc 命令，docx-preview 插件预览 .docx ouch — 压缩/解压插件后端 gvfs — GVFS 挂载设备（MTP 等） glib2 — 提供 gio 命令，gvfs 插件依赖 pass — gvfs 插件密码库模式（可选） Zsh 工具链 sudo pacman -S --needed \\ zsh git curl unzip wget \\ fzf eza bat ripgrep fd fastfetch duf \\ zoxide zsh — Z shell 本体 git — 克隆 zinit 插件管理器 curl / wget — 下载工具 unzip — 解压 zinit 下载的二进制 fzf — 模糊搜索（目录跳转、历史搜索） eza — ls 替代（彩色输出、图标） bat — cat 替代（语法高亮） ripgrep — 递归内容搜索 fd — find 替代（快速文件查找） fastfetch — 系统信息 duf — df 替代（磁盘使用率） zoxide — 智能目录跳转 以下工具可选通过 zinit 自动安装，也可用 pacman 安装系统版本：\nsudo pacman -S --needed \\ neovim trash-cli tldr unar bpytop procps-ng neovim — 编辑器 trash-cli — 回收站删除 tldr — 简版 man 手册 unar — 解压工具 bpytop — 资源监视器 procps-ng — 提供 ps / watch 命令 Flatpak flatpak install \\ io.github.flattool.Warehouse \\ com.github.tchx84.Flatseal \\ it.mijorus.gearlever \\ io.github.kolunmi.Bazaar Warehouse — 管理 flatpak 的源、软件、属性、用户数据 Flatseal — 管理 flatpak 应用的权限和环境变量 gearlever — 管理appimage Bazaar — flatpak 应用市场 如何使用AppImage 需要安装 fuse3 和 fuse2，通常已经安装好了。文件下载下来之后右键属性设置可执行权限之后即可运行，或者使用命令：\nchmod +x ~/path/to/files.appimage 感兴趣的可以使用这条命令把 AppImage 解压出来看看里面都有什么：\n/path/to/files.appimage --appimage-extract 会出现一个 squashfs-root 文件夹，里面就是解压出来的文件。\n把AppImage集成到系统 gear lever\nflathub 下载：\nflatpak install flathub it.mijorus.gearlever 或者 AUR 下载：\nyay -S gearlever 由于 AppImage 的安装涉及到解压和打包，所以用 gearlever 打开较大的 AppImage 会有一点慢，稍等一会就会出现启动或者集成到系统的选项了。卸载也是用这个软件。\nappimagehub 这个网址有很多有趣的 AppImage 应用，有兴趣的可以搜索玩玩看。\n星火应用商店 Spark Store\n因为特殊国情，星火商店安装的软件可能比 AUR 的更好用。得益于开发者的努力，现在只需要一条简单的命令就能装上星火商店啦：\nyay -S spark-store 通过星火社区的 APM 包管理器安装 deb 包 安装运行时\nsudo apm install amber-pm-bookworm bookworm是 Debian12 的版本号，新版本会变化。\n转换 Deb 包为 APM 包\namber-pm-convert --base amber-pm-bookworm /path/to/app.deb 安装\nsudo apm install /path/to/app-apm.deb 安装完成的软件会出现在应用菜单，还可以在星火应用商店的已安装软件列表中看到。\ndebtap：Deb 包转 Arch 包 debtap - AUR\ndebtap 可以将 Debian/Ubuntu 的 .deb 包转换为 Arch Linux 的 .pkg.tar.zst 包，然后用 pacman -U 安装。适合那些没有提供 Arch 版本但只有 deb 包的软件。\n从 AUR 安装 debtap\nyay -S debtap 更新 debtap 数据库\nsudo debtap -u 首次运行会下载并转换大量元数据，耗时较长，耐心等待即可。\n转换 deb 包\ndebtap /path/to/package.deb 转换过程中会询问包名、许可证等信息，一路回车使用默认值即可。也可以加 -q 静默模式跳过提问：\ndebtap -q /path/to/package.deb 安装转换后的包\nsudo pacman -U /path/to/package.pkg.tar.zst 生成的包默认与 deb 文件在同一目录，文件名以 -x86_64.pkg.tar.zst 结尾。\n","permalink":"https://heihei0299.github.io/posts/arch/%E8%BD%AF%E4%BB%B6%E7%9B%B8%E5%85%B3/","summary":"\u003ch1 id=\"软件相关\"\u003e软件相关\u003c/h1\u003e\n\u003cp\u003e本文介绍 Arch Linux 的四种软件安装方式（pacman / AUR / Flatpak / AppImage）、推荐软件清单，以及 AppImage 与星火应用商店的使用方法。\u003c/p\u003e\n\u003ch2 id=\"如何安装软件\"\u003e如何安装软件\u003c/h2\u003e\n\u003cp\u003e安装软件主要分四种方式：\u003c/p\u003e","title":"软件相关"},{"content":"AxonHub Docker Compose 部署教程 本文教你用 Docker Compose + SQLite 单容器方案部署 AxonHub：一个 AI 网关，把国内模型的 Chat Completions 接口转成新版 Codex CLI 需要的 Responses API 格式。\nAxonHub 是干什么的 新版 Codex CLI 用的是 OpenAI Responses API，不是老版的 Chat Completions。但国内厂商——DeepSeek、Kimi、GLM、火山引擎——目前基本只提供 Chat Completions 接口。直接填这些 endpoint 进 Codex，它会报格式不兼容的错误。\nAxonHub 的核心价值就在这里：它把国内模型的 Completions API 自动转换成 Responses API 格式。Codex 以为自己连的是 OpenAI，实际上 AxonHub 在背后把请求拆给 DeepSeek 或 Kimi，再把返回结果包成 Responses 的格式塞回去。一行代码不用改，就能让 Codex 跑在国内模型上。\n除此之外，它还能做渠道路由（多个 Key 自动切换）、故障检测（某个厂商挂了自动切到另一个）、用量统计（看每个模型烧了多少钱）。项目地址：https://github.com/looplj/axonhub，Go 语言开发，单容器就能跑。\n安装：SQLite 单容器方案 官方默认 docker compose 带了 PostgreSQL，个人用太重。这里用 SQLite 方案，只有一个容器，内存 50~100MB，WSL 里跑起来毫无压力。\n准备目录 mkdir -p ~/axonhub \u0026amp;\u0026amp; cd ~/axonhub 写 docker-compose.yml services: axonhub: image: looplj/axonhub:latest container_name: axonhub environment: AXONHUB_DB_DIALECT: sqlite3 AXONHUB_DB_DSN: \u0026#34;file:/data/axonhub.db?cache=shared\u0026amp;_fk=1\u0026amp;_pragma=journal_mode(WAL)\u0026#34; ports: - \u0026#34;8090:8090\u0026#34; volumes: - ./data:/data - ./config.yml:/app/config.yml:ro restart: unless-stopped healthcheck: test: [\u0026#34;CMD\u0026#34;, \u0026#34;wget\u0026#34;, \u0026#34;--no-verbose\u0026#34;, \u0026#34;--tries=1\u0026#34;, \u0026#34;--spider\u0026#34;, \u0026#34;http://localhost:8090/health\u0026#34;] interval: 30s timeout: 10s retries: 3 start_period: 40s 写 config.yml server: host: \u0026#34;0.0.0.0\u0026#34; port: 8090 name: \u0026#34;AxonHub\u0026#34; request_timeout: \u0026#34;30s\u0026#34; llm_request_timeout: \u0026#34;600s\u0026#34; debug: false cors: enabled: true allowed_origins: - \u0026#34;http://localhost:3000\u0026#34; - \u0026#34;http://localhost:5173\u0026#34; - \u0026#34;http://localhost:8090\u0026#34; allowed_methods: [\u0026#34;GET\u0026#34;, \u0026#34;POST\u0026#34;, \u0026#34;DELETE\u0026#34;, \u0026#34;PATCH\u0026#34;, \u0026#34;PUT\u0026#34;, \u0026#34;OPTIONS\u0026#34;, \u0026#34;HEAD\u0026#34;] allowed_headers: [\u0026#34;Content-Type\u0026#34;, \u0026#34;Authorization\u0026#34;, \u0026#34;X-API-Key\u0026#34;, \u0026#34;AH-Thread-Id\u0026#34;, \u0026#34;AH-Trace-Id\u0026#34;] allow_credentials: true db: dialect: \u0026#34;sqlite3\u0026#34; dsn: \u0026#34;file:/data/axonhub.db?cache=shared\u0026amp;_fk=1\u0026amp;_pragma=journal_mode(WAL)\u0026#34; max_open_conns: 10 max_idle_conns: 5 cache: mode: \u0026#34;memory\u0026#34; memory: expiration: \u0026#34;5s\u0026#34; cleanup_interval: \u0026#34;10m\u0026#34; log: level: \u0026#34;info\u0026#34; encoding: \u0026#34;console\u0026#34; output: \u0026#34;stdio\u0026#34; 启动 mkdir -p data chmod 777 data docker compose up -d 等 healthcheck 通过，访问 http://localhost:8090 ，第一次会跳初始化向导，设管理员账号密码。按提示设置之后登录即可。\n登录之后能看到仪表盘，就说明已经安装好了。\n验证与自查 docker compose ps 中 axonhub 容器状态为 healthy（等 healthcheck 通过） curl http://localhost:8090/health 返回健康状态 浏览器访问 http://localhost:8090 出现初始化向导，能创建管理员账号并登录看到仪表盘 日志无报错：docker compose logs axonhub 无 ERROR 级别输出 参考 AxonHub GitHub 仓库 AxonHub 官方文档（zh） ","permalink":"https://heihei0299.github.io/posts/ai/axonhub-docker-compose-deploy/","summary":"\u003ch1 id=\"axonhub-docker-compose-部署教程\"\u003eAxonHub Docker Compose 部署教程\u003c/h1\u003e\n\u003cp\u003e本文教你用 Docker Compose + SQLite 单容器方案部署 AxonHub：一个 AI 网关，把国内模型的 Chat Completions 接口转成新版 Codex CLI 需要的 Responses API 格式。\u003c/p\u003e\n\u003ch2 id=\"axonhub-是干什么的\"\u003eAxonHub 是干什么的\u003c/h2\u003e\n\u003cp\u003e新版 Codex CLI 用的是 OpenAI Responses API，不是老版的 Chat Completions。但国内厂商——DeepSeek、Kimi、GLM、火山引擎——目前基本只提供 Chat Completions 接口。直接填这些 endpoint 进 Codex，它会报格式不兼容的错误。\u003c/p\u003e","title":"AxonHub Docker Compose 部署教程"},{"content":"OpenCode Go 额度查询与反向代理部署教程 本文基于两个开源项目，解决 OpenCode Go 日常使用中的两个需求：用 opencode-go-dashboard 搭建多账号额度看板（Cloudflare 全栈），用 AxonHub 统一反向代理 OpenCode Go API。\n背景 OpenCode Go 是一个 AI 编程平台，提供对多种大模型的 API 访问。在日常使用中常常需要：\n查询额度：跟踪 Rolling / Weekly / Monthly 用量，避免超出限制 反向代理：统一管理 API 端点、密钥和模型路由，方便与各类 AI 客户端（Claude Code、Cline 等）集成 本文基于两个开源项目实现上述需求：\nopencode-go-dashboard — Cloudflare 全栈额度查询面板 AxonHub — All-in-one AI 网关，用于反代 OpenCode Go 一、部署额度查询面板 项目简介 opencode-go-dashboard 是一个基于 Cloudflare Workers + D1 + React 的自部署面板，功能包括：\n多账号额度集中展示（Rolling / Weekly / Monthly 百分比用量） 用量接近上限时高亮预警 一键刷新单账号或全部账号 查看历史使用记录（按模型、提供方、Token 数量） Auth Cookie 仅存储在服务端 D1，不返回浏览器 前置条件 环境 要求 Node.js 20+ Cloudflare 账号 注册 dash.cloudflare.com Wrangler CLI v4+（npm install -g wrangler） 步骤 1：克隆与安装 git clone https://github.com/Ruinique/opencode-go-dashboard.git cd opencode-go-dashboard npm install 步骤 2：创建 D1 数据库 登录 Cloudflare 并创建 D1 数据库：\nnpx wrangler login npx wrangler d1 create opencode-go-dashboard 命令会输出如下内容：\n✅ Created database \u0026#39;opencode-go-dashboard\u0026#39; at \u0026lt;region\u0026gt; database_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 将输出的 database_id 填入 wrangler.jsonc：\n{ \u0026#34;d1_databases\u0026#34;: [ { \u0026#34;binding\u0026#34;: \u0026#34;DB\u0026#34;, \u0026#34;database_name\u0026#34;: \u0026#34;opencode-go-dashboard\u0026#34;, \u0026#34;database_id\u0026#34;: \u0026#34;xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\u0026#34; // 替换这里的占位符 } ] } 步骤 3：配置管理密码 本地开发环境：\ncp .dev.vars.example .dev.vars 编辑 .dev.vars：\nADMIN_PASSWORD=你的强密码 生产环境部署前还需设置 Secrets：\nnpx wrangler secret put ADMIN_PASSWORD # 输入你的强密码 步骤 4：数据库迁移 # 本地预览前 npm run db:migrate:local # 生产部署前 npm run db:migrate:remote 步骤 5：本地预览（可选） npm run preview 默认在 http://localhost:8787 启动，同时运行 Worker API 和前端。\n仅开发前端 UI（不经过 Worker）：\nnpm run dev 步骤 6：部署到 Cloudflare npm run deploy 部署成功后，Wrangler 会输出类似 https://opencode-go-dashboard.\u0026lt;your-subdomain\u0026gt;.workers.dev 的地址。\n绑定自定义域名（可选） 在 wrangler.jsonc 中取消注释 routes 配置：\n\u0026#34;routes\u0026#34;: [ { \u0026#34;pattern\u0026#34;: \u0026#34;dashboard.your-domain.com\u0026#34;, \u0026#34;custom_domain\u0026#34;: true } ] 域名需已在 Cloudflare 账号中，然后重新执行 npm run deploy。\n使用说明 登录：打开部署后的地址，输入设置的管理密码\n添加账号：点击「添加账号」，填写：\n显示名称：便于识别的别名 Workspace ID：格式为 wrk_xxx，可在 OpenCode 工作区 URL 中找到 Auth Cookie：从浏览器开发者工具中复制 auth Cookie 值 如何获取 Auth Cookie：\n在浏览器中登录 opencode.ai 按 F12 打开开发者工具 → Application（应用程序）→ Cookies 找到 auth Cookie，复制其值（以 Fe26. 开头） 刷新额度：点击「刷新」更新单个账号，或「全部刷新」更新所有账号\n查看历史：点击账号可查看详细的历史用量记录（按模型、提供方分类）\n编辑/删除：Cookie 过期后，编辑对应账号并粘贴新的 Cookie\n面板展示内容 面板会展示每个账号的以下信息：\n指标 含义 Rolling Usage 滚动周期内的用量百分比 Weekly Usage 本周用量百分比 Monthly Usage 本月用量百分比 Plan 当前订阅计划 颜色预警 用量 ≥80% 黄色、≥95% 红色高亮 二、反代 OpenCode Go（AxonHub） 为什么需要反向代理 统一 API 端点：多个 AI 客户端只需配一个 AxonHub 地址 模型路由：可通过模型映射切换不同上游模型 密钥管理：在 AxonHub 中集中管理，客户端无需持有上游密钥 负载均衡：自动故障转移和密钥轮换 成本追踪：每次请求的 Token 消耗和费用明细 AxonHub 简介 AxonHub 是 All-in-one AI 网关，支持用 OpenAI SDK 调用 Anthropic、Gemini 等模型，也支持自定义 OpenAI 兼容的提供商。\n核心特性：\n特性 说明 Any SDK → Any Model 同一个 SDK 调用任意模型 请求追踪 完整的请求链路可观测性 智能负载均衡 \u0026lt;100ms 自动故障转移 成本追踪 每次请求费用明细 模型映射 灵活的重命名和路由规则 快速本地部署 # 下载并解压（macOS ARM64 示例） curl -sSL https://github.com/looplj/axonhub/releases/latest/download/axonhub_darwin_arm64.tar.gz | tar xz cd axonhub_* # 直接运行（默认 SQLite） ./axonhub 访问 http://localhost:8090，首次运行按引导初始化系统（创建管理员账号）。\n其他系统架构的下载链接请查看 GitHub Releases。\nDocker 部署 git clone https://github.com/looplj/axonhub.git cd axonhub # 使用 PostgreSQL 或其他支持的数据库 export AXONHUB_DB_DIALECT=\u0026#34;sqlite\u0026#34; docker-compose up -d 配置 OpenCode Go 通道 方案 A：直接添加为 OpenAI 兼容通道 OpenCode Go 提供 OpenAI 兼容的 API，因此可以作为 OpenAI 类型通道添加。\n登录 AxonHub 后台 → 通道管理 → 新建通道 填写以下信息： 字段 值 名称 OpenCode Go 类型 openai Base URL https://api.opencode.ai/v1 API Key 你的 OpenCode Go API Key 支持的模型 根据你的订阅计划填写（如 claude-sonnet-4、gpt-4o 等） 点击测试连接验证配置 通过后启用通道 方案 B：通过模型关联实现智能路由 如果你想同时使用多个提供商（如同时使用 OpenCode Go 和直连 OpenAI），可以设置模型关联：\n在 AxonHub 中创建 OpenCode Go 和 OpenAI 两个通道 进入模型管理 → 模型关联 将同一模型（如 claude-sonnet-4）关联到 OpenCode Go 通道（高优先级）和 OpenAI 通道（低优先级） AxonHub 会优先走 OpenCode Go，失败时自动回退到 OpenAI 方案 C：通过模型映射适配客户端 许多客户端使用固定的模型名（如 claude-3-5-sonnet），而 OpenCode Go 上名称可能不同。在通道的模型映射中配置：\n客户端请求的模型 实际发送到上游的模型 claude-3-5-sonnet claude-sonnet-4-20250514 gpt-4o gpt-5.4 deepseek-chat deepseek-v3 使用 AxonHub 调用 OpenCode Go 配置完成后，所有客户端只需指向 AxonHub 的地址 http://localhost:8090/v1，即可透明地调用 OpenCode Go 上的模型。\nPython 示例（OpenAI SDK）：\nfrom openai import OpenAI client = OpenAI( base_url=\u0026#34;http://localhost:8090/v1\u0026#34;, # AxonHub 地址 api_key=\u0026#34;your-axonhub-api-key\u0026#34; # AxonHub API Key ) # 调用模型（AxonHub 自动路由到 OpenCode Go） response = client.chat.completions.create( model=\u0026#34;claude-sonnet-4\u0026#34;, messages=[{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;Hello!\u0026#34;}] ) print(response.choices[0].message.content) Claude Code 配置：\n# 设置环境变量，让 Claude Code 走 AxonHub 代理 export ANTHROPIC_BASE_URL=\u0026#34;http://localhost:8090/v1\u0026#34; export ANTHROPIC_API_KEY=\u0026#34;your-axonhub-api-key\u0026#34; 或者创建 ~/.claude/config.json：\n{ \u0026#34;proxy\u0026#34;: { \u0026#34;baseUrl\u0026#34;: \u0026#34;http://localhost:8090/v1\u0026#34;, \u0026#34;apiKey\u0026#34;: \u0026#34;your-axonhub-api-key\u0026#34; } } Cline / Continue 等 VSCode 插件配置：\n在插件的 API 配置中填写：\nProvider: OpenAI Compatible Base URL: http://localhost:8090/v1 API Key: your-axonhub-api-key 三、进阶配置 AxonHub 服务器部署 生产环境建议使用 PostgreSQL 或 TiDB：\n# config.yml server: port: 8090 debug: false db: dialect: \u0026#34;postgresql\u0026#34; dsn: \u0026#34;postgres://user:pass@host:5432/axonhub\u0026#34; log: level: \u0026#34;info\u0026#34; 多 API Key 负载均衡 在 AxonHub 通道中配置多个 OpenCode Go API Key，系统自动轮换：\nsk-opencode-key-1 sk-opencode-key-2 sk-opencode-key-3 同一个 Trace ID 始终使用相同的 Key（会话一致性） 不同请求随机选择 Key 单个 Key 失败自动切换到下一个 使用 AxonHub 的请求追踪 在请求中透传追踪 ID，可在 AxonHub 后台查看完整的请求链路：\ncurl http://localhost:8090/v1/chat/completions \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -H \u0026#34;Authorization: Bearer your-axonhub-api-key\u0026#34; \\ -H \u0026#34;AH-Trace-Id: my-trace-id\u0026#34; \\ -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;claude-sonnet-4\u0026#34;, \u0026#34;messages\u0026#34;: [{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;Hello\u0026#34;}] }\u0026#39; 四、整合架构 ┌─────────────────────────┐ │ opencode-go-dashboard │ │ (Cloudflare Workers) │ │ │ │ 多个 OpenCode Go 账号 │ │ 额度集中展示 │ └──────────┬──────────────┘ │ 通过 Auth Cookie 查询 ▼ ┌──────────────┐ ┌──────────────────┐ ┌──────────────┐ │ AI 客户端 │───▶│ AxonHub │───▶│ OpenCode Go │ │ (Claude Code │ │ (AI 网关) │ │ (API 提供商) │ │ Cline │ │ 本地或服务器部署 │ │ │ │ OpenRouter │ │ 统一端点/模型路由 │ │ 多模型访问 │ │ 等) │ │ 负载均衡/日志 │ │ │ └──────────────┘ └──────────────────┘ └──────────────┘ 额度看板：通过 opencode-go-dashboard 随时查看剩余额度 API 代理：通过 AxonHub 统一管理所有客户端的 API 调用 两者独立：额度查询和 API 代理互不依赖，可单独部署 常见问题 Q1：Auth Cookie 过期了怎么办？ Cookie 有效期取决于 OpenCode 的登录会话时长。过期后编辑对应账号，粘贴新的 Cookie 即可，无需重新部署。\nQ2：Opencode-go-dashboard 显示「无法解析额度数据」 OpenCode 的页面结构可能变更，更新项目到最新版本，或者提 GitHub Issue。\nQ3：AxonHub 连接测试失败 确认 API Key 没有多余空格 确认 Base URL 可以访问（https://api.opencode.ai/v1） 检查 OpenCode Go 账号是否有足够的余额 Q4：客户端报 \u0026ldquo;Model not found\u0026rdquo; 确认通道的「支持的模型」列表中已添加该模型 检查模型映射配置是否正确 确认通道已启用 验证与自查 额度看板：npm run deploy 后访问面板地址，输入管理密码能登录并看到账号额度 AxonHub：curl http://localhost:8090/health 返回健康状态 通道连通性：在 AxonHub 后台点击「测试连接」，返回成功且显示延迟 端到端：用 curl http://localhost:8090/v1/chat/completions 发一次请求，能收到模型返回的 JSON 参考链接 opencode-go-dashboard AxonHub AxonHub Channel 配置文档 AxonHub 部署文档 Cloudflare D1 文档 ","permalink":"https://heihei0299.github.io/posts/ai/opencode-go-quota-and-proxy/","summary":"\u003ch1 id=\"opencode-go-额度查询与反向代理部署教程\"\u003eOpenCode Go 额度查询与反向代理部署教程\u003c/h1\u003e\n\u003cp\u003e本文基于两个开源项目，解决 OpenCode Go 日常使用中的两个需求：用 opencode-go-dashboard 搭建多账号额度看板（Cloudflare 全栈），用 AxonHub 统一反向代理 OpenCode Go API。\u003c/p\u003e","title":"OpenCode Go 额度查询与反向代理部署教程"},{"content":"个人 Zsh 配置 适用场景：想要一套启动快、输入无延迟、补全体验现代的高性能 zsh 环境。你会学到 zinit 的 ice 延迟加载写法、插件选型对比（zsh-bench 实测），以及完整的 .zshrc / .zsh_aliases 参考配置。\n由于系统、环境等差异可能过大，这里只是记录个人的 zsh 配置，仅供参考。\n.zshrc # My zsh settings with zinit # ------------------- 核心配置 ------------------- # 性能优化选项 skip_global_compinit=1 DISABLE_MAGIC_FUNCTIONS=true ZSH_DISABLE_COMPFIX=true # 补全系统设置 COMPLETION_WAITING_DOTS=\u0026#34;true\u0026#34; ZSH_AUTOSUGGEST_MANUAL_REBIND=1 ZSH_AUTOSUGGEST_USE_ASYNC=1 ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20 # ------------------- Powerlevel10k 即时提示 ------------------- if [[ -r \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; ]]; then source \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; fi # ------------------- zinit 插件管理器 ------------------- source \u0026#34;/usr/share/zinit/zinit.zsh\u0026#34; autoload -Uz _zinit (( ${+_comps} )) \u0026amp;\u0026amp; _comps[zinit]=_zinit # zinit annexes zinit ice wait\u0026#34;1\u0026#34; lucid as\u0026#34;null\u0026#34; for \\ zdharma-continuum/zinit-annex-as-monitor \\ zdharma-continuum/zinit-annex-bin-gem-node \\ zdharma-continuum/zinit-annex-patch-dl \\ zdharma-continuum/zinit-annex-rust # ------------------- 主题 ------------------- zinit ice depth=1 zinit light romkatv/powerlevel10k # ------------------- 核心插件 ------------------- # fzf zinit ice from\u0026#34;gh-r\u0026#34; as\u0026#34;command\u0026#34; zinit light junegunn/fzf # 补全增强 \u0026amp; 补全初始化 zinit ice wait\u0026#34;0a\u0026#34; lucid atload\u0026#34;zicompinit; zicdreplay\u0026#34; blockf zinit light zsh-users/zsh-completions # fzf-tab zinit ice wait\u0026#34;0b\u0026#34; lucid zinit light Aloxaf/fzf-tab # 自动建议 zinit ice wait\u0026#34;0c\u0026#34; lucid \\ atload\u0026#39; _zsh_autosuggest_start bindkey \u0026#34;\\`\u0026#34; autosuggest-accept \u0026#39; zinit light zsh-users/zsh-autosuggestions # 历史命令 zinit ice wait\u0026#34;0d\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*x86_64-unknown-linux-gnu.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; \\ mv\u0026#34;atuin*/atuin -\u0026gt; atuin\u0026#34; \\ atload\u0026#39; eval \u0026#34;$(atuin init zsh)\u0026#34; bindkey \u0026#34;^R\u0026#34; _atuin_search_widget \u0026#39; zinit light atuinsh/atuin # 语法高亮 zinit ice wait\u0026#34;0e\u0026#34; lucid atinit\u0026#34;zpcompinit;zpcdreplay\u0026#34; zinit light zdharma-continuum/fast-syntax-highlighting # ------------------- CLI 工具 ------------------- # 核心工具（无延迟加载） zinit ice wait\u0026#34;0\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*x86_64-unknown-linux-musl.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; \\ mv\u0026#34;eza* -\u0026gt; eza\u0026#34; zinit light eza-community/eza zinit ice wait\u0026#34;0\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*x86_64-unknown-linux-gnu.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; \\ mv\u0026#34;bat*/bat -\u0026gt; bat\u0026#34; zinit light sharkdp/bat # 延迟加载工具 zinit ice wait\u0026#34;1\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*x86_64-unknown-linux-musl.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; \\ mv\u0026#34;ripgrep*/rg -\u0026gt; rg\u0026#34; zinit light BurntSushi/ripgrep zinit ice wait\u0026#34;1\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*x86_64-unknown-linux-gnu.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; \\ mv\u0026#34;fd*/fd -\u0026gt; fd\u0026#34; zinit light sharkdp/fd zinit ice wait\u0026#34;1\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*Linux_x86_64.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; zinit light jesseduffield/lazydocker zinit ice wait\u0026#34;1\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*linux-amd64.tar.gz\u0026#34; \\ extract\u0026#34;\u0026#34; \\ mv\u0026#34;fastfetch*/usr/bin/fastfetch -\u0026gt; fastfetch\u0026#34; \\ atclone\u0026#34;chmod +x fastfetch\u0026#34; \\ atpull\u0026#34;%atclone\u0026#34; zinit light fastfetch-cli/fastfetch zinit ice wait\u0026#34;1\u0026#34; lucid from\u0026#34;gh-r\u0026#34; as\u0026#34;program\u0026#34; \\ bpick\u0026#34;*linux_x86_64.tar.gz\u0026#34; \\ extract=\u0026#34;\u0026#34; zinit light muesli/duf zinit ice wait\u0026#34;1\u0026#34; lucid as\u0026#34;program\u0026#34; pick\u0026#34;prettyping\u0026#34; zinit load denilsonsa/prettyping # bat-extras zinit ice wait\u0026#34;1\u0026#34; lucid as\u0026#34;program\u0026#34; \\ pick\u0026#34;src/batgrep.sh\u0026#34; pick\u0026#34;src/batdiff.sh\u0026#34; \\ atload\u0026#39;alias batgrep=\u0026#34;batgrep.sh\u0026#34;; alias batdiff=\u0026#34;batdiff.sh\u0026#34;\u0026#39; zinit light eth-p/bat-extras # ------------------- 补全配置 ------------------- # 基础补全设置 zstyle \u0026#39;:completion:*\u0026#39; completer _expand _complete _ignored zstyle \u0026#39;:completion:*\u0026#39; matcher-list \u0026#39;m:{a-z}={A-Z}\u0026#39; zstyle \u0026#39;:completion:*\u0026#39; list-colors \u0026#34;${(s.:.)LS_COLORS}\u0026#34; zstyle \u0026#39;:completion:*\u0026#39; menu no # fzf-tab 基础设置 zstyle \u0026#39;:fzf-tab:*\u0026#39; use-fzf-default-opts yes # fzf-tab 预览设置 zstyle \u0026#39;:fzf-tab:complete:cd:*\u0026#39; fzf-preview \u0026#39;eza --icons -1 --color=always $realpath\u0026#39; zstyle \u0026#39;:fzf-tab:complete:cd:*\u0026#39; popup-pad 30 0 zstyle \u0026#39;:fzf-tab:complete:__zoxide_z:*\u0026#39; fzf-preview \u0026#39;eza --icons -1 --color=always $realpath\u0026#39; zstyle \u0026#39;:fzf-tab:complete:z:*\u0026#39; fzf-preview \u0026#39;eza --icons -1 --color=always $realpath\u0026#39; # 进程补全预览 zstyle \u0026#39;:fzf-tab:complete:kill:argument-rest\u0026#39; fzf-preview \u0026#39;ps --pid=$word -o cmd --no-headers -w -w\u0026#39; zstyle \u0026#39;:fzf-tab:complete:kill:argument-rest\u0026#39; fzf-flags \u0026#39;--preview-window=down:3:wrap\u0026#39; zstyle \u0026#39;:fzf-tab:complete:kill:*\u0026#39; popup-pad 0 3 # fzf-tab 快捷键 zstyle \u0026#39;:fzf-tab:*\u0026#39; fzf-bindings \u0026#39;`:accept\u0026#39; zstyle \u0026#39;:fzf-tab:*\u0026#39; switch-group \u0026#39;\u0026lt;\u0026#39; \u0026#39;\u0026gt;\u0026#39; # ------------------- 自动建议配置 ------------------- ZSH_AUTOSUGGEST_STRATEGY=(history completion) ZSH_AUTOSUGGEST_COMPLETION_IGNORE=\u0026#39;( |man |pikaur -S )*\u0026#39; ZSH_AUTOSUGGEST_HISTORY_IGNORE=\u0026#39;?(#c50,)\u0026#39; # ------------------- fzf 配置 ------------------- export FZF_DEFAULT_OPTS=\u0026#34; --ansi --layout=reverse --info=inline --height=50% --multi --cycle --preview-window=right:50% --preview-window=cycle --prompt=\u0026#39;λ -\u0026gt; \u0026#39; --pointer=\u0026#39;▷\u0026#39; --marker=\u0026#39;✓\u0026#39; --color=bg+:236,gutter:-1,fg:-1,bg:-1,hl:-1,hl+:-1,prompt:-1,pointer:105,marker:-1,spinner:-1 \u0026#34; export FZF_DEFAULT_COMMAND=\u0026#39;fd --type f --hidden --follow --exclude .git\u0026#39; # ------------------- Conda 配置 ------------------- export CONDA_AUTO_ACTIVATE_BASE=false conda() { unfunction conda eval \u0026#34;$(/opt/miniforge/bin/conda shell.zsh hook)\u0026#34; conda $@ } [[ \u0026#34;${CONDA_AUTO_ACTIVATE_BASE:-true}\u0026#34; == \u0026#34;true\u0026#34; ]] \u0026amp;\u0026amp; { eval \u0026#34;$(/opt/miniforge/bin/conda shell.zsh hook)\u0026#34; conda activate base } # ------------------- 其他工具配置 ------------------- # zoxide eval \u0026#34;$(zoxide init zsh --cmd cd)\u0026#34; # 加载自定义别名 source $HOME/.zsh_aliases # Powerlevel10k 配置 [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh .zsh_aliases alias l=\u0026#34;eza --icons --long --header\u0026#34; alias ls=\u0026#34;eza --icons --grid\u0026#34; alias ll=\u0026#34;eza --icons --long --header\u0026#34; alias la=\u0026#34;eza --icons --long --header --all\u0026#34; alias lg=\u0026#34;eza --icons --long --header --all --git\u0026#34; alias tree=\u0026#34;eza --icons --tree -L1\u0026#34; alias cat=\u0026#34;bat\u0026#34; alias man=\u0026#34;tldr\u0026#34; alias mkdir=\u0026#39;mkdir -p\u0026#39; alias df=\u0026#34;duf -style unicode -hide-mp \u0026#39;/run/credentials/*\u0026#39;\u0026#34; alias top=\u0026#34;bpytop\u0026#34; alias gedit=\u0026#34;gnome-text-editor\u0026#34; alias nvidia-smi=\u0026#34;watch -n 3 -c nvidia-smi\u0026#34; alias x=\u0026#34;unar\u0026#34; alias ff=\u0026#34;fastfetch\u0026#34; # 快速显示fastfetch function tmp() { cd \u0026#34;$(mktemp -d /tmp/temp_XXX)\u0026#34; #创建临时目录 } 基本信息 System: Arch Linux x86_64 Terminal: kitty Shell: zsh Theme: powerlevel10k Plugins Manerger: zinit 原有配置痛点 如果历史命令中有中文，按 up 键，会出现历史命令错位和终止终端的问题。 有些时候，命令输入延迟很高，已经输入完一段命令，但是终端是慢慢一个字一个字跳的。 性能还是不够高。 方案对比和选择 插件管理器或插件框架 序号 项目 说明 优势 劣势 1 Oh My Zsh 最流行的 Zsh 框架 - 300+预置插件和 150+主题 - 配置简单直观 - 社区庞大 - 启动速度慢 - 插件数量增加会显著降低性能 2 Zinit 现代化插件管理器 - Turbo 模式启动速度提升 50-80% - 支持选择性禁用/启用功能 - 完整兼容 OMZ 和 Prezto 插件 - 语法相对复杂 - 不适合新手 3 Zim 轻量级框架 - 启动速度快 - 模块化设计 - 合理的默认配置 - 完整支持 OMZ 插件 - 内置功能相对较少 4 Sheldon 可配置的插件管理器 - 配置灵活 - 支持延迟加载 - 支持多种 shell - 社区相对较小 5 zsh4humans 优化的 Zsh 环境 - 启动速度快 - 默认配置合理 - 性能优化出色 - 定制性相对较低 6 Antigen 传统插件管理器 - 类似包管理器的使用方式 - 自动更新插件 - 维护不够活跃 - 性能一般 7 zgen 轻量级管理器 - 生成静态加载文件 - 启动相对较快 - 功能相对简单 - 更新较少 8 zplug 全功能插件管理器 - 功能丰富 - 并行安装插件 - 已基本停止维护 - 性能一般 在这个里面，根据轻量化、性能好、社区维护活跃等因素，我最终选择了 Zinit 作为插件管理器。\n插件 语法高亮：fast-syntax-highlighting 命令补全：zsh-completions 和 fzf-tab 智能建议：zsh-autosuggestions 目录跳转：zoxide 序号 项目 说明 优势 劣势 1 zoxide Rust 编写的现代化目录跳转工具 - 性能最快，启动速度快 - 使用简单直观 - 支持所有主流 shell - 需要额外安装 - 不支持相对路径跳转 2 autojump Python 编写的经典跳转工具 - 功能丰富完整 - 高级特性多 - 学习曲线较陡 - 性能相对较慢 3 z Zsh 内置的目录跳转插件 - 无需额外安装 - 与 Zsh 深度集成 - 功能相对基础 - 仅支持 Zsh 4 z.lua Lua 实现的跳转工具 - 跨平台支持好 - 配置灵活 - 性能比 zoxide 慢 - 需要 Lua 环境 5 zsh-z Zsh 专用的 z 实现 - 轻量级 - 安装简单 - 仅支持 Zsh - 功能较少 6 ZLOcation Shell 原生实现的跳转工具 - 无外部依赖 - 性能稳定 - 功能相对简单 - 社区较小 历史命令：atuin 序号 项目 说明 优势 劣势 1 atuin 基于 SQLite 的现代化 shell 历史记录工具 - 支持多设备同步 - 支持端到端加密 - 可按目录/主机等过滤搜索 - 记录命令执行时间和退出码 - 支持自托管同步服务器 - 配置简单，安装便捷 - 首次打开仪表板较慢 - 在 mosh 下可能出现屏幕显示问题 - 同步机制不够直观 - 配置无法跨设备同步 2 zsh-histdb 基于 SQLite 的 Zsh 历史记录插件 - 记录命令工作目录 - 记录命令执行主机 - 支持多数据库合并 - 与 zsh-autosuggestions 集成良好 - 支持按目录搜索历史 - 不使用传统文本文件存储 - 项目维护不够活跃 - 仅支持 Zsh - 配置相对复杂 主题美化：powerlevel10k 第三方命令行工具管理 由于该配置会同步 Docker深度学习环境、远程服务器使用，系统包管理器不同（有 ubuntu、archlinux、debian 等），故第三方命令行工具使用 zinit 管理，而非使用包管理器管理。则新建一系统时安装 zsh 和 zinit，再同步配置即可，其余的交给 zinit 管理。\nTODO: 之后用 chezimo 管理配置文件并同步到各个系统。\n测试工具 这里使用 zsh-bench 基准工具来测试 zsh 交互性能，再进行对比。\n测量指标\nfirst_prompt_lag_ms (首次提示符延迟)：打开新终端时，显示第一个命令提示符所需的时间 良好: \u0026lt; 50ms 较差: \u0026gt; 100ms first_command_lag_ms (首次命令延迟)：输入第一个命令到执行完成的时间 良好: \u0026lt; 200ms 较差: \u0026gt; 500ms command_lag_ms (命令延迟)：执行后续命令的平均响应时间 良好: \u0026lt; 10ms 较差: \u0026gt; 50ms input_lag_ms (输入延迟)：按键到字符显示在屏幕上的时间 良好: \u0026lt; 30ms 较差: \u0026gt; 50ms exit_time_ms (退出时间)：输入 exit 或按 Ctrl+D 到终端完全关闭的时间 良好: \u0026lt; 100ms 较差: \u0026gt; 200ms 测试命令如下：\n./zsh-bench -i 30 -l yes -g yes 测试结果 ==\u0026gt; benchmarking login shell of user xiadengma ... creates_tty=0 has_compsys=0 has_syntax_highlighting=0 has_autosuggestions=0 has_git_prompt=1 first_prompt_lag_ms=18.829 first_command_lag_ms=78.340 command_lag_ms=4.930 input_lag_ms=3.903 exit_time_ms=45.429 可以看到当前 zsh 配置在性能上表现良好，响应速度快。\n验证与自查 新开终端 first_prompt_lag_ms 应 \u0026lt; 50ms（用 ./zsh-bench -i 30 -l yes -g yes 复测） 输入历史命令中的中文后按 up 键，不再错位或卡死终端 快速输入一段命令，字符不再逐个蹦出（input_lag_ms 无感） 按 Ctrl+R 打开 atuin 历史搜索，cd 命令走 zoxide 跳转正常 参考 zsh-bench zinit 插件管理器 powerlevel10k 主题 atuin 历史记录工具 zoxide 目录跳转工具 fzf-tab 补全插件 fast-syntax-highlighting ","permalink":"https://heihei0299.github.io/posts/linux/%E4%B8%AA%E4%BA%BAzsh%E9%85%8D%E7%BD%AE/","summary":"\u003ch1 id=\"个人-zsh-配置\"\u003e个人 Zsh 配置\u003c/h1\u003e\n\u003cp\u003e适用场景：想要一套启动快、输入无延迟、补全体验现代的高性能 zsh 环境。你会学到 zinit 的 ice 延迟加载写法、插件选型对比（zsh-bench 实测），以及完整的 \u003ccode\u003e.zshrc\u003c/code\u003e / \u003ccode\u003e.zsh_aliases\u003c/code\u003e 参考配置。\u003c/p\u003e","title":"个人 Zsh 配置"},{"content":"在 Intel 设备上运行本地 LLM：llama.cpp + OpenVINO Docker 实战 无需 NVIDIA 显卡，用已有的 Intel CPU/GPU 即可运行大语言模型。\n本文适合没有 NVIDIA 显卡的 Intel 用户（含集成显卡）。你会学到：llama.cpp + OpenVINO 的 Docker 镜像如何开箱即用地跑 LLM，CPU/GPU/API 服务器三种启动方式，以及 GPU 驱动栈、上下文溢出等常见坑的排查方法。\n背景 本地运行 LLM 的好处很明显：数据隐私、零网络依赖、无限次调用不花钱。但主流方案多依赖 NVIDIA CUDA，Intel 用户（尤其是集成显卡用户）往往被排除在外。\nOpenVINO 是 Intel 的 AI 推理加速工具包，能让 LLM 推理充分发挥 Intel 硬件的潜力。配合 llama.cpp（C++ 实现的高性能推理引擎），Intel 设备上也能获得不错的推理体验。\n但该组合的编译配置较为繁琐，涉及 OpenVINO SDK 安装、CMake 参数调整以及 GPU 驱动栈配置。于是便有了这个项目：llama-openvino-docker，将整个过程封装为 Docker 多阶段构建，开箱即用。\n技术架构 Docker 多阶段构建 Build Stage (Ubuntu 24.04) ├── 安装编译工具链 ├── 下载 OpenVINO 2026.2 归档 ├── 编译 llama.cpp（DGGML_OPENVINO=ON） └── 导出 .so 库和二进制 ↓ Base Runtime Stage ├── 最小 Ubuntu 24.04 运行时 ├── Intel GPU 驱动（IGC + Compute Runtime + Level Zero） └── OpenVINO 运行时库 ↓ Target Stages ├── light → 仅 llama-cli（默认） ├── full → 全部二进制 + Python 工具 └── server → 仅 llama-server + health check 设计思路：\nbuild 阶段搭建完整的编译环境，用完即弃 base 阶段只保留运行时所需的最小依赖，镜像仅 ~300MB 多目标输出，按需选择：CLI 推理、API 服务、全工具链 GPU 驱动栈 要让 OpenVINO GPU 插件正常工作，关键在于完整的 GPU 驱动栈：\nllama-cli / llama-server ↓ OpenVINO GPU Plugin (libopenvino_intel_gpu_plugin.so) ↓ Level Zero API + OpenCL API ↓ Intel Compute Runtime (libze_intel_gpu.so) ↓ Intel Graphics Compiler (IGC) ↓ Intel GPU 硬件 最初踩了个坑：只在 Docker 中安装了 intel-opencl-icd，以为这样就够了。结果 OpenVINO GPU 插件一直报 device GPU is not available, fallback to CPU。后来才发现还需 Level Zero GPU 驱动 (libze-intel-gpu1) 和 Intel Graphics Compiler。参照官方 llama.cpp OpenVINO Dockerfile，从 Intel GitHub Releases 下载精确版本的驱动 deb 包才解决问题。\n快速上手 拉取镜像 项目配置了 GitHub Actions 自动构建，推送到 GitHub Container Registry：\ndocker pull ghcr.io/heihei0299/llama-openvino-docker:light 下载模型 mkdir -p ~/models wget https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/\\ Llama-3.2-1B-Instruct-Q4_K_M.gguf -O ~/models/Llama-3.2-1B-Instruct-Q4_K_M.gguf CPU 模式 开箱即用，无需额外配置：\ndocker run --rm -it -v ~/models:/models \\ ghcr.io/heihei0299/llama-openvino-docker:light \\ --no-warmup -c 1024 -m /models/Llama-3.2-1B-Instruct-Q4_K_M.gguf GPU 模式 需要透传 Intel GPU 设备到容器：\ndocker run --rm -it -v ~/models:/models \\ --device=/dev/dri \\ --group-add=$(stat -c \u0026#34;%g\u0026#34; /dev/dri/render* | head -n 1) \\ -u $(id -u):$(id -g) \\ --env=GGML_OPENVINO_DEVICE=GPU \\ --env=GGML_OPENVINO_STATEFUL_EXECUTION=1 \\ ghcr.io/heihei0299/llama-openvino-docker:light \\ --no-warmup -c 1024 -m /models/Llama-3.2-1B-Instruct-Q4_K_M.gguf 关键参数说明：\n--device=/dev/dri — 将宿主机的 Intel GPU 设备节点透传至容器 --group-add=$(stat -c \u0026quot;%g\u0026quot; /dev/dri/render*) — 赋予容器访问 GPU 的权限组 GGML_OPENVINO_DEVICE=GPU — 指定使用 GPU 后端 GGML_OPENVINO_STATEFUL_EXECUTION=1 — 启用状态化 KV 缓存，减少重复计算 API 服务器 启动 OpenAI 兼容的 API：\ndocker run --rm -it -p 8080:8080 -v ~/models:/models \\ ghcr.io/heihei0299/llama-openvino-docker:server \\ --no-warmup -c 8192 -m /models/model.gguf --host 0.0.0.0 # 测试 curl http://localhost:8080/v1/chat/completions \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;messages\u0026#34;:[{\u0026#34;role\u0026#34;:\u0026#34;user\u0026#34;,\u0026#34;content\u0026#34;:\u0026#34;Hello!\u0026#34;}],\u0026#34;max_tokens\u0026#34;:50}\u0026#39; 踩坑记录 1. GPU 驱动不全导致回退 CPU 症状：日志中输出 device GPU is not available, fallback to CPU\n排查过程：\n确认 /dev/dri/ 设备已透传 — ✅ 确认 intel-opencl-icd 已安装 — ✅ 检查 libze_intel_gpu.so — ❌ 不存在 解决方案：从 Intel GitHub Releases 下载并安装 libze-intel-gpu1、intel-igc-core、intel-igc-opencl 等 6 个 deb 包。\n2. Context size exceeded 症状：服务器日志大量出现 Context size has been exceeded，所有请求均失败\n原因：llama-server 默认 -np -1（auto）会根据 CPU 核数创建多个 slot（如 4 核 → 4 slots）。每个 slot 的可用上下文为 -c / -np。若 -c 1024 配合 4 slots → 每 slot 仅 256 tokens，请求稍大即溢出。\n解决：设置 -c 8192（4 slots 下每 slot 2048 tokens）或限制 -np 2。\n3. 本地安装 vs Docker 起初项目同时提供了一键安装脚本 install-arch.sh（支持 Arch Linux 本地编译）。但维护两条路线工作量翻倍，且 Docker 化的优势更为明显：\n可重复性：同一套环境在所有机器上一致 隔离性：GPU 驱动版本不会与宿主机冲突 分发便利：GitHub Actions 自动构建，用户直接 docker pull 最终决定砍掉本地安装路线，专注 Docker。\n4. GitHub Actions 分支名问题 第一次配置 CI 时，工作流中写的是 branches: [main]，但本地仓库的默认分支名为 master。push 了半天 Actions 就是不触发，排查才发现分支名不匹配。重命名分支后才恢复正常。\nGitHub Actions CI/CD 项目配置了完整的 CI 流水线：\non: push: branches: [main] tags: [\u0026#34;v*\u0026#34;] pull_request: branches: [main] 三个目标并行构建（base / light / server），利用 GitHub Actions cache 加速。构建成功后自动推送到 ghcr.io，生成标签：\n:light / :server / :base — 按目标区分 :latest → 指向 :light :v* — 语义化版本标签 性能参考 使用 llama-bench 在 Intel i7-13700H 上的测试结果：\n设备 模型 Prompt 速度 生成速度 CPU (OpenVINO) Llama-3.2-1B Q4_K_M ~45 t/s ~25 t/s GPU Iris Xe Llama-3.2-1B Q4_K_M ~1450 t/s ~27 t/s GPU (Flash Attn) Llama-3.2-1B Q4_K_M ~1500 t/s ~30 t/s GPU 的 prompt 处理速度（1450 t/s）远快于 CPU（45 t/s），但生成速度差异不大。这是因为小模型的生成阶段是存储带宽瓶颈，而非算力瓶颈。\n验证与自查 CPU 模式运行镜像，能正常输出生成文本 GPU 模式运行，日志中无 fallback to CPU，且 prompt 速度明显高于 CPU（可用 llama-bench 对比） curl http://localhost:8080/v1/chat/completions 返回正常 JSON 响应 docker run 指定 -c 8192 后，大请求不再报 Context size has been exceeded 参考 llama-openvino-docker 项目 llama.cpp 官方仓库 OpenVINO 官方文档 Hugging Face: Llama-3.2-1B-Instruct-GGUF ","permalink":"https://heihei0299.github.io/posts/ops/blog/","summary":"\u003ch1 id=\"在-intel-设备上运行本地-llmllamacpp--openvino-docker-实战\"\u003e在 Intel 设备上运行本地 LLM：llama.cpp + OpenVINO Docker 实战\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e无需 NVIDIA 显卡，用已有的 Intel CPU/GPU 即可运行大语言模型。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e本文适合没有 NVIDIA 显卡的 Intel 用户（含集成显卡）。你会学到：llama.cpp + OpenVINO 的 Docker 镜像如何开箱即用地跑 LLM，CPU/GPU/API 服务器三种启动方式，以及 GPU 驱动栈、上下文溢出等常见坑的排查方法。\u003c/p\u003e","title":"在 Intel 设备上运行本地 LLM：llama.cpp + OpenVINO Docker 实战"},{"content":"Hugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例) 前言 之前四五年的时间里也算写过五六百篇博客。以前的 Hexo 顶不住博客数量的压力了，所以，切到 Hugo 试一下。\n涉及工具 除了搭建博客和介绍如何使用这个博客框架这样一个流程本身，还包含了对 PaperMod 这个主题的一些细节的定制。\n本文涉及到的工具或者网站：\nhugo papermod github postimage giscus neovim/vscode git flaticon 为什么 这里主要解释一下为什么要选择这样一个组合。\n选择 hugo 纯粹是因为快。 选择 PaperMod 这个主题是因为它的 star 数量挺高的，不过，比起 hexo 的主题生态还是差得太远了，文档也相当简陋。不过，没办法，这已经算是最好的一档了。 博客选择托管在 github pages 上面是因为稳定，而且没有限制，基本上没有内容审查。我之前也使用 hexo 配合 github 搭建过静态博客，之前写过大概几百篇博客，从来没有遇到过内容审查，当然，也从来没有想过写一些很敏感的东西。 图床选择的是 postiamge，这个是免费的，类似的还有 imgur，但是 imgur 的图片加载速度在一些地方实在是堪忧甚至经常加载不出来，只好换上另一个有很多年头的网站了，正因为年纪大，所以给人的感觉是稳定。 评论使用 giscus 是因为快，因为也是使用 github 的服务，利用的是 github discussion，所以，比 discus 之类的小厂要快。稳定性倒是差不多。以及，github 的用户是比较多的，所以会更方便大家评论交流，这一点很重要。 neovim/vscode 是用来编辑配置用的，编辑工具/IDE这一点见仁见智。 git 不用多说了。 flaticon 是用来挑选网站的 favicon 的。 总结一下，就是几个词儿：免费、快、稳。\n安装 首先，安装 hugo，在 Windows 中，推荐使用 scoop 来安装预编译的二进制版本，\nscoop install hugo-extended 安装完之后，执行命令看一下版本信息，\nhugo version 看到类似下面的输出，就说明安装成功了，\n使用 创建博客 然后，我们就使用 hugo 在本地创建一个站点，也就是一个博客，\n按：这里可以参考 hugo 官网的指导。\nhugo new site SonnyCalcr 然后，\ncd SonnyCalcr tree . /f 可以看到默认创建的一些文件和目录，\n然后，我们先将此目录初始化成 git 仓库，\ngit init git add . git commit -m \u0026#34;first commit\u0026#34; 添加 PaperMod 主题 git submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod 可以看到，这个命令执行完之后新增的内容，其实就是往 thems 目录下添加了一个主题，而 .gitmodules 则是记录了添加的这个主题的模块的信息，\n然后，我们可以再看 PaperMod 这个主题里面都有些什么文件，从下面的命令的输出可以看到，基本上就是一些 html、css 和 js 文件，毕竟是主题嘛，\n❯ tree .\\themes\\PaperMod\\ /f Folder PATH listing for volume Windows Volume serial number is B867-6B94 C:\\HDISK\\HUGO\\SONNYCALCR\\THEMES\\PAPERMOD │ go.mod │ LICENSE │ README.md │ theme.toml │ ├───.github │ │ PULL_REQUEST_TEMPLATE.md │ │ │ ├───ISSUE_TEMPLATE │ │ bug_report.md │ │ config.yml │ │ proposal.md │ │ │ └───workflows │ gh-pages.yml │ ├───assets │ ├───css │ │ ├───common │ │ │ 404.css │ │ │ archive.css │ │ │ footer.css │ │ │ header.css │ │ │ main.css │ │ │ post-entry.css │ │ │ post-single.css │ │ │ profile-mode.css │ │ │ search.css │ │ │ terms.css │ │ │ │ │ ├───core │ │ │ license.css │ │ │ reset.css │ │ │ theme-vars.css │ │ │ zmedia.css │ │ │ │ │ ├───extended │ │ │ blank.css │ │ │ │ │ └───includes │ │ chroma-mod.css │ │ chroma-styles.css │ │ scroll-bar.css │ │ │ └───js │ fastsearch.js │ fuse.basic.min.js │ license.js │ ├───i18n │ ar.yaml │ be.yaml │ bg.yaml │ bn.yaml │ ca.yaml │ ckb.yaml │ cs.yaml │ da.yaml │ de.yaml │ el.yaml │ en.yaml │ eo.yaml │ es.yaml │ fa.yaml │ fr.yaml │ he.yaml │ hi.yaml │ hr.yaml │ hu.yaml │ id.yaml │ it.yaml │ ja.yaml │ ko.yaml │ ku.yaml │ mn.yaml │ ms.yaml │ nl.yaml │ no.yaml │ oc.yaml │ pa.yaml │ pl.yaml │ pt.yaml │ ro.yaml │ ru.yaml │ sk.yaml │ sv.yaml │ sw.yaml │ th.yaml │ tr.yaml │ uk.yaml │ uz.yaml │ vi.yaml │ zh-tw.yaml │ zh.yaml │ ├───images │ screenshot.png │ tn.png │ └───layouts │ 404.html │ robots.txt │ ├───partials │ │ anchored_headings.html │ │ author.html │ │ breadcrumbs.html │ │ comments.html │ │ cover.html │ │ edit_post.html │ │ extend_footer.html │ │ extend_head.html │ │ footer.html │ │ head.html │ │ header.html │ │ home_info.html │ │ index_profile.html │ │ post_canonical.html │ │ post_meta.html │ │ post_nav_links.html │ │ share_icons.html │ │ social_icons.html │ │ svg.html │ │ toc.html │ │ translation_list.html │ │ │ └───templates │ │ opengraph.html │ │ schema_json.html │ │ twitter_cards.html │ │ │ └───_funcs │ get-page-images.html │ ├───shortcodes │ collapse.html │ figure.html │ inTextImg.html │ ltr.html │ rawhtml.html │ rtl.html │ └───_default │ archives.html │ baseof.html │ index.json │ list.html │ rss.xml │ search.html │ single.html │ terms.html │ └───_markup render-image.html 然后，可以添加一下 .gitignore 文件，我这里就直接照抄 PaperMod 的作者部署的那个网站的文件了，\n# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test /public .DS_Store .hugo_build.lock resources/_gen/ 其实这里主要就是把 public 目录给排除掉，这个会在网页部署的时候自动生成。\n接下来就是正式的主题配置了。\n配置好之后相较于默认效果的一些改进 支持 giscus 进行评论，且主题支持明暗切换。 支持 mathjax，但是如果数学块公式中有超过三个花括号，那么，需要将整个数学公式包括外围的 $$ 符号都用 div 标签包裹起来。 代码字体自定义为 Jetbrains Mono。代码的亮色主题为 tokyo-night-light，暗色主题为 github-dark。 移动端隐藏返回顶部的按钮。 一些自定义 css 的效果，如字体大小、链接颜色、目录上的悬浮的鼠标图标等等。 主要就是上面几点，因为文档写得不详细，所以配置上面的内容花费了一些时间和工夫。\n一些基本信息的配置 首先，把博客根目录下的 hugo.toml 文件改成 hugo.yaml，因为 PaperMod 给出的配置文件就是 yaml 格式的，所以，这里改一下文件格式，就省去了我们再去将 yaml 的配置内容转为 toml 的麻烦，\nRename-Item .\\hugo.toml hugo.yaml 然后，配置一下基本信息，基本上每一个选项我都打上了注释，\nbaseURL: \u0026#34;https://sonnycalcr.github.io/\u0026#34; # 主站的 URL title: SonnyCalcr\u0026#39;s Blog # 站点标题 copyright: \u0026#34;[©2024 SonnyCalcr\u0026#39;s Blog](https://sonnycalcr.github.io/)\u0026#34; # 网站的版权声明，通常显示在页脚 theme: PaperMod # 主题 languageCode: zh-cn # 语言 enableInlineShortcodes: true # shortcode，类似于模板变量，可以在写 markdown 的时候便捷地插入，官方文档中有一个视频讲的很通俗 hasCJKLanguage: true # 是否有 CJK 的字符 enableRobotsTXT: true # 允许生成 robots.txt buildDrafts: false # 构建时是否包括草稿 buildFuture: false # 构建未来发布的内容 buildExpired: false # 构建过期的内容 enableEmoji: true # 允许 emoji pygmentsUseClasses: true defaultContentLanguage: zh # 顶部首先展示的语言界面 defaultContentLanguageInSubdir: false # 是否要在地址栏加上默认的语言代码 配置导航栏 languages: zh: languageName: \u0026#34;中文\u0026#34; # 展示的语言名 weight: 1 # 权重 taxonomies: # 分类系统 category: categories tag: tags # https://gohugo.io/content-management/menus/#define-in-site-configuration menus: main: - name: 首页 pageRef: / weight: 4 # 控制在页面上展示的前后顺序 - name: 归档 pageRef: archives/ weight: 5 - name: 分类 pageRef: categories/ weight: 10 - name: 标签 pageRef: tags/ weight: 10 - name: 搜索 pageRef: search/ weight: 20 - name: 关于 pageRef: about/ weight: 21 配置归档 在 content 目录下新建 archives.md 文件，内容如下，\n--- title: \u0026#34;归档\u0026#34; layout: \u0026#34;archives\u0026#34; url: \u0026#34;/archives/\u0026#34; summary: archives --- 配置分类和标签 在 hugo 中，这俩是一样的。上面配置好了 taxonomies 之后，我们在博客的 front matter 中加上相关信息即可，就拿本篇博客举例，\ntitle = \u0026#39;Hugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例)\u0026#39; date = 2024-08-04T03:25:53+08:00 categories = [\u0026#34;通用技术\u0026#34;] tags = [\u0026#34;博客搭建\u0026#34;, \u0026#34;Bilibili\u0026#34;] 然后执行一下 hugo server 就可以在浏览器中预览一下效果了。\n配置搜索 要在 output 中加上 JSON，\n# https://github.com/adityatelange/hugo-PaperMod/wiki/Features#search-page outputs: home: - HTML # 生成的静态页面 - RSS # 这个其实无所谓 - JSON # necessary for search, 这里的配置修改好之后，一定要重新生成一下 然后，在 content 目录下新建一个 search.md 文件，\n--- title: \u0026#34;搜索\u0026#34; # in any language you want layout: \u0026#34;search\u0026#34; # necessary for search summary: \u0026#34;search\u0026#34; placeholder: \u0026#34;搜索\u0026#34; --- 然后是搜索的一些个性化设置，\nparams: # 搜索 fuseOpts: isCaseSensitive: false # 是否大小写敏感 shouldSort: true # 是否排序 location: 0 distance: 1000 threshold: 0.4 minMatchCharLength: 0 # limit: 10 # refer: https://www.fusejs.io/api/methods.html#search keys: [\u0026#34;title\u0026#34;, \u0026#34;permalink\u0026#34;, \u0026#34;summary\u0026#34;, \u0026#34;content\u0026#34;] includeMatches: true 这样以来，搜索就可以正常工作了，\n配置关于页面 新建两个文件，一个是 layouts/_default 目录下下的 about.html，\n{{- define \u0026#34;main\u0026#34; }} \u0026lt;header class=\u0026#34;page-header\u0026#34;\u0026gt; \u0026lt;h1\u0026gt;{{ .Title }}\u0026lt;/h1\u0026gt; {{- if .Description }} \u0026lt;div class=\u0026#34;post-description\u0026#34;\u0026gt; {{ .Description }} \u0026lt;/div\u0026gt; {{- end }} \u0026lt;/header\u0026gt; \u0026lt;section\u0026gt; \u0026lt;br\u0026gt; {{ .Content }} \u0026lt;/section\u0026gt; {{- end }}{{/* end main */}} 另一个是 content 目录下的 about.md,\n--- title: \u0026#34;关于\u0026#34; layout: \u0026#34;about\u0026#34; url: \u0026#34;/about/\u0026#34; summary: about --- 这里就可以写一些关于的相关信息了。 配置评论 这里的评论使用了 giscus 插件。\n先在 layouts/partials 下新建一个 comments.html 文件，\n\u0026lt;div id=\u0026#34;tw-comment\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; // 默认是暗色，根目录下的配置中的主题默认也是暗色 const getStoredTheme = () =\u0026gt; localStorage.getItem(\u0026#34;pref-theme\u0026#34;) === \u0026#34;light\u0026#34; ? \u0026#34;{{ .Site.Params.giscus.lightTheme }}\u0026#34; : \u0026#34;{{ .Site.Params.giscus.darkTheme }}\u0026#34;; const setGiscusTheme = () =\u0026gt; { const sendMessage = (message) =\u0026gt; { const iframe = document.querySelector(\u0026#39;iframe.giscus-frame\u0026#39;); if (iframe) { iframe.contentWindow.postMessage({giscus: message}, \u0026#39;https://giscus.app\u0026#39;); } } sendMessage({setConfig: {theme: getStoredTheme()}}) } document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, () =\u0026gt; { const giscusAttributes = { \u0026#34;src\u0026#34;: \u0026#34;https://giscus.app/client.js\u0026#34;, \u0026#34;data-repo\u0026#34;: \u0026#34;{{ .Site.Params.giscus.repo }}\u0026#34;, \u0026#34;data-repo-id\u0026#34;: \u0026#34;{{ .Site.Params.giscus.repoId }}\u0026#34;, \u0026#34;data-category\u0026#34;: \u0026#34;{{ .Site.Params.giscus.category }}\u0026#34;, \u0026#34;data-category-id\u0026#34;: \u0026#34;{{ .Site.Params.giscus.categoryId }}\u0026#34;, \u0026#34;data-mapping\u0026#34;: \u0026#34;{{ .Site.Params.giscus.mapping }}\u0026#34;, \u0026#34;data-strict\u0026#34;: \u0026#34;{{ .Site.Params.giscus.strict }}\u0026#34;, \u0026#34;data-reactions-enabled\u0026#34;: \u0026#34;{{ .Site.Params.giscus.reactionsEnabled }}\u0026#34;, \u0026#34;data-emit-metadata\u0026#34;: \u0026#34;{{ .Site.Params.giscus.emitMetadata }}\u0026#34;, \u0026#34;data-input-position\u0026#34;: \u0026#34;{{ .Site.Params.giscus.inputPosition }}\u0026#34;, \u0026#34;data-theme\u0026#34;: getStoredTheme(), \u0026#34;data-lang\u0026#34;: \u0026#34;{{ .Site.Params.giscus.lang }}\u0026#34;, \u0026#34;data-loading\u0026#34;: \u0026#34;lazy\u0026#34;, \u0026#34;crossorigin\u0026#34;: \u0026#34;anonymous\u0026#34;, }; // 动态创建 giscus script const giscusScript = document.createElement(\u0026#34;script\u0026#34;); Object.entries(giscusAttributes).forEach( ([key, value]) =\u0026gt; giscusScript.setAttribute(key, value)); document.querySelector(\u0026#34;#tw-comment\u0026#34;).appendChild(giscusScript); // 页面主题变更后，变更 giscus 主题 const themeSwitcher = document.querySelector(\u0026#34;#theme-toggle\u0026#34;); if (themeSwitcher) { themeSwitcher.addEventListener(\u0026#34;click\u0026#34;, setGiscusTheme); } const themeFloatSwitcher = document.querySelector(\u0026#34;#theme-toggle-float\u0026#34;); if (themeFloatSwitcher) { themeFloatSwitcher.addEventListener(\u0026#34;click\u0026#34;, setGiscusTheme); } }); \u0026lt;/script\u0026gt; 然后，根据 giscus 官网的指导，最后生成一份代码，\n然后，把相应的字段提取到配置中，\nparams: # 评论的设置 giscus: repo: \u0026#34;sonnycalcr/sonnycalcr.github.io\u0026#34; repoId: \u0026#34;xxxxxx\u0026#34; category: \u0026#34;Announcements\u0026#34; categoryId: \u0026#34;xxxxx\u0026#34; mapping: \u0026#34;pathname\u0026#34; strict: \u0026#34;0\u0026#34; reactionsEnabled: \u0026#34;1\u0026#34; emitMetadata: \u0026#34;0\u0026#34; inputPosition: \u0026#34;bottom\u0026#34; lightTheme: \u0026#34;light\u0026#34; darkTheme: \u0026#34;dark\u0026#34; lang: \u0026#34;zh-CN\u0026#34; crossorigin: \u0026#34;anonymous\u0026#34; 这样就可以正常使用了。\n配置数学公式 这里使用的是 mathjax。\n我们需要添加两个文件，一个是 layouts/partials 下的 mathjax.html 文件，如下，\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; async src=\u0026#34;https://cdn.bootcss.com/mathjax/2.7.3/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt; MathJax.Hub.Config({ tex2jax: { inlineMath: [[\u0026#39;$\u0026#39;,\u0026#39;$\u0026#39;], [\u0026#39;\\\\(\u0026#39;,\u0026#39;\\\\)\u0026#39;]], displayMath: [[\u0026#39;$$\u0026#39;,\u0026#39;$$\u0026#39;], [\u0026#39;\\[\\[\u0026#39;,\u0026#39;\\]\\]\u0026#39;]], processEscapes: true, processEnvironments: true, skipTags: [\u0026#39;script\u0026#39;, \u0026#39;noscript\u0026#39;, \u0026#39;style\u0026#39;, \u0026#39;textarea\u0026#39;, \u0026#39;pre\u0026#39;], TeX: { equationNumbers: { autoNumber: \u0026#34;AMS\u0026#34; }, extensions: [\u0026#34;AMSmath.js\u0026#34;, \u0026#34;AMSsymbols.js\u0026#34;] } } }); MathJax.Hub.Queue(function() { // Fix \u0026lt;code\u0026gt; tags after MathJax finishes running. This is a // hack to overcome a shortcoming of Markdown. Discussion at // https://github.com/mojombo/jekyll/issues/199 var all = MathJax.Hub.getAllJax(), i; for(i = 0; i \u0026lt; all.length; i += 1) { all[i].SourceElement().parentNode.className += \u0026#39; has-jax\u0026#39;; } }); \u0026lt;/script\u0026gt; \u0026lt;style\u0026gt; code.has-jax { font: inherit; font-size: 100%; background: inherit; border: inherit; color: #515151; } \u0026lt;/style\u0026gt; 另一个是 layouts/partials 下的 extend_head.html 文件，\n{{- /* Head custom content area start */ -}} {{- /* Insert any custom code (web-analytics, resources, etc.) - it will appear in the \u0026lt;head\u0026gt;\u0026lt;/head\u0026gt; section of every page. */ -}} {{- /* Can be overwritten by partial with the same name in the global layouts. */ -}} {{ partial \u0026#34;mathjax.html\u0026#34; . }} {{- /* Head custom content area end */ -}} 到这里，数学公式就可以正常使用了，我们来写一点数学公式试一下，\n行内数学公式：$a^2 + b^2 = c^2$。 块公式， $$ a^2 + b^2 = c^2 $$ \u0026lt;div\u0026gt; $$ \\boldsymbol{x}_{i+1}+\\boldsymbol{x}_{i+2}=\\boldsymbol{x}_{i+3} $$ \u0026lt;/div\u0026gt; 渲染出来的效果如下，\n行内数学公式：$a^2 + b^2 = c^2$。\n块公式，\n$$ a^2 + b^2 = c^2 $$\n$$ \\boldsymbol{x}_{i+1}+\\boldsymbol{x}_{i+2}=\\boldsymbol{x}_{i+3} $$\n上面的第二个公式之所以要用 div 包裹起来，是因为这里的数学公式如果有超过了三对花括号，那么，其解析和转义就会出问题，这个和 hugo 有关目前折中的方案就是上面这种在外面套一层 div。\n给代码换个字体 先到谷歌字体 中找一款开源字体，我这里选用的是 Jetbrains Mono，然后复制其信息到 layouts/partials/extend_head.html 中，\n\u0026lt;link rel=\u0026#34;preconnect\u0026#34; href=\u0026#34;https://fonts.googleapis.com\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;preconnect\u0026#34; href=\u0026#34;https://fonts.gstatic.com\u0026#34; crossorigin\u0026gt; \u0026lt;link href=\u0026#34;https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800\u0026amp;display=swap\u0026#34; rel=\u0026#34;stylesheet\u0026#34;\u0026gt; 然后，新建一个 assets/css/extended/blank.css 文件，添加样式如下，\n.post-content pre, code { font-family: \u0026#34;JetBrains Mono\u0026#34;, monospace; font-size: 1rem; line-height: 1.2; } 这样就可以生效了，如果发现不生效，可以重新执行一下 hugo server 试试。\n代码明暗样式切换 我这里使用的不是 highlightjs，而是 hugo 推荐的 chroma，这样的话，我们先建立一个 assets/css/extended/chroma-styles-overrides.css 文件，\n然后，执行一下命令生成你想要的样式，\nhugo gen chromastyles --style=tokyonight-day \u0026gt; syntax.css 然后，把 syntax.css 中的内容复制到 chroma-styles-overrides.html 文件中，如果是暗色主题，那么，生成的样式则要包裹在 .dark {} 里面，我这里生成了两个样式，白天的样式是 tokyonight-day，黑暗的样式是 github-dark，同时，要记得将生成的样式中有些空缺的部分给补上默认的颜色，我这里白天的颜色补的是黑色，夜晚的颜色补的是白色，不然代码的样式会出问题，我这里完整的样式如下，\n/* Background */ .bg { color:#3760bf;background-color:#e1e2e7; } /* PreWrapper */ .chroma { color:#3760bf;background-color:#e1e2e7; } /* Other */ .chroma .x { color: #000 } /* Error */ .chroma .err { color:#c64343 } /* CodeLine */ .chroma .cl { color: #000 } /* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit } /* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; } /* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; } /* LineHighlight */ .chroma .hl { background-color:#a1a6c5 } /* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6172b0 } /* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6172b0 } /* Line */ .chroma .line { display:flex; } /* Keyword */ .chroma .k { color:#9854f1 } /* KeywordConstant */ .chroma .kc { color:#8c6c3e } /* KeywordDeclaration */ .chroma .kd { color:#9d7cd8 } /* KeywordNamespace */ .chroma .kn { color:#007197 } /* KeywordPseudo */ .chroma .kp { color:#9854f1 } /* KeywordReserved */ .chroma .kr { color:#9854f1 } /* KeywordType */ .chroma .kt { color:#0db9d7 } /* Name */ .chroma .n { color: #000 } /* NameAttribute */ .chroma .na { color:#2e7de9 } /* NameBuiltin */ .chroma .nb { color:#587539 } /* NameBuiltinPseudo */ .chroma .bp { color:#587539 } /* NameClass */ .chroma .nc { color:#b15c00 } /* NameConstant */ .chroma .no { color:#b15c00 } /* NameDecorator */ .chroma .nd { color:#2e7de9;font-weight:bold } /* NameEntity */ .chroma .ni { color:#007197 } /* NameException */ .chroma .ne { color:#8c6c3e } /* NameFunction */ .chroma .nf { color:#2e7de9 } /* NameFunctionMagic */ .chroma .fm { color:#2e7de9 } /* NameLabel */ .chroma .nl { color:#587539 } /* NameNamespace */ .chroma .nn { color:#8c6c3e } /* NameOther */ .chroma .nx { color: #000 } /* NameProperty */ .chroma .py { color:#8c6c3e } /* NameTag */ .chroma .nt { color:#9854f1 } /* NameVariable */ .chroma .nv { color: #000 } /* NameVariableClass */ .chroma .vc { color: #000 } /* NameVariableGlobal */ .chroma .vg { color: #000 } /* NameVariableInstance */ .chroma .vi { color: #000 } /* NameVariableMagic */ .chroma .vm { color: #000 } /* Literal */ .chroma .l { color: #000 } /* LiteralDate */ .chroma .ld { color: #000 } /* LiteralString */ .chroma .s { color:#587539 } /* LiteralStringAffix */ .chroma .sa { color:#9d7cd8 } /* LiteralStringBacktick */ .chroma .sb { color:#587539 } /* LiteralStringChar */ .chroma .sc { color:#587539 } /* LiteralStringDelimiter */ .chroma .dl { color:#2e7de9 } /* LiteralStringDoc */ .chroma .sd { color:#a1a6c5 } /* LiteralStringDouble */ .chroma .s2 { color:#587539 } /* LiteralStringEscape */ .chroma .se { color:#2e7de9 } /* LiteralStringHeredoc */ .chroma .sh { color:#a1a6c5 } /* LiteralStringInterpol */ .chroma .si { color:#587539 } /* LiteralStringOther */ .chroma .sx { color:#587539 } /* LiteralStringRegex */ .chroma .sr { color:#007197 } /* LiteralStringSingle */ .chroma .s1 { color:#587539 } /* LiteralStringSymbol */ .chroma .ss { color:#587539 } /* LiteralNumber */ .chroma .m { color:#8c6c3e } /* LiteralNumberBin */ .chroma .mb { color:#8c6c3e } /* LiteralNumberFloat */ .chroma .mf { color:#8c6c3e } /* LiteralNumberHex */ .chroma .mh { color:#8c6c3e } /* LiteralNumberInteger */ .chroma .mi { color:#8c6c3e } /* LiteralNumberIntegerLong */ .chroma .il { color:#8c6c3e } /* LiteralNumberOct */ .chroma .mo { color:#8c6c3e } /* Operator */ .chroma .o { color:#587539;font-weight:bold } /* OperatorWord */ .chroma .ow { color:#587539;font-weight:bold } /* Punctuation */ .chroma .p { color: #000 } /* Comment */ .chroma .c { color:#a1a6c5;font-style:italic } /* CommentHashbang */ .chroma .ch { color:#a1a6c5;font-style:italic } /* CommentMultiline */ .chroma .cm { color:#a1a6c5;font-style:italic } /* CommentSingle */ .chroma .c1 { color:#a1a6c5;font-style:italic } /* CommentSpecial */ .chroma .cs { color:#a1a6c5;font-style:italic } /* CommentPreproc */ .chroma .cp { color:#a1a6c5;font-style:italic } /* CommentPreprocFile */ .chroma .cpf { color:#a1a6c5;font-weight:bold;font-style:italic } /* Generic */ .chroma .g { color: #000 } /* GenericDeleted */ .chroma .gd { color:#c64343;background-color:#e9e9ed } /* GenericEmph */ .chroma .ge { font-style:italic } /* GenericError */ .chroma .gr { color:#c64343 } /* GenericHeading */ .chroma .gh { color:#8c6c3e;font-weight:bold } /* GenericInserted */ .chroma .gi { color:#587539;background-color:#e9e9ed } /* GenericOutput */ .chroma .go { color: #000 } /* GenericPrompt */ .chroma .gp { color: #000 } /* GenericStrong */ .chroma .gs { font-weight:bold } /* GenericSubheading */ .chroma .gu { color:#8c6c3e;font-weight:bold } /* GenericTraceback */ .chroma .gt { color:#c64343 } /* GenericUnderline */ .chroma .gl { text-decoration:underline } /* TextWhitespace */ .chroma .w { color: #000 } .dark { /* Background */ .bg { color:#e6edf3;background-color:#0d1117; } /* PreWrapper */ .chroma { color:#e6edf3;background-color:#0d1117; } /* Other */ .chroma .x { color: #fff } /* Error */ .chroma .err { color:#f85149 } /* CodeLine */ .chroma .cl { color: #fff } /* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit } /* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; } /* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; } /* LineHighlight */ .chroma .hl { background-color:#6e7681 } /* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679 } /* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681 } /* Line */ .chroma .line { display:flex; } /* Keyword */ .chroma .k { color:#ff7b72 } /* KeywordConstant */ .chroma .kc { color:#79c0ff } /* KeywordDeclaration */ .chroma .kd { color:#ff7b72 } /* KeywordNamespace */ .chroma .kn { color:#ff7b72 } /* KeywordPseudo */ .chroma .kp { color:#79c0ff } /* KeywordReserved */ .chroma .kr { color:#ff7b72 } /* KeywordType */ .chroma .kt { color:#ff7b72 } /* Name */ .chroma .n { color: #fff } /* NameAttribute */ .chroma .na { color: #fff } /* NameBuiltin */ .chroma .nb { color: #fff } /* NameBuiltinPseudo */ .chroma .bp { color: #fff } /* NameClass */ .chroma .nc { color:#f0883e;font-weight:bold } /* NameConstant */ .chroma .no { color:#79c0ff;font-weight:bold } /* NameDecorator */ .chroma .nd { color:#d2a8ff;font-weight:bold } /* NameEntity */ .chroma .ni { color:#ffa657 } /* NameException */ .chroma .ne { color:#f0883e;font-weight:bold } /* NameFunction */ .chroma .nf { color:#d2a8ff;font-weight:bold } /* NameFunctionMagic */ .chroma .fm { color: #fff } /* NameLabel */ .chroma .nl { color:#79c0ff;font-weight:bold } /* NameNamespace */ .chroma .nn { color:#ff7b72 } /* NameOther */ .chroma .nx { color: #fff } /* NameProperty */ .chroma .py { color:#79c0ff } /* NameTag */ .chroma .nt { color:#7ee787 } /* NameVariable */ .chroma .nv { color:#79c0ff } /* NameVariableClass */ .chroma .vc { color: #fff } /* NameVariableGlobal */ .chroma .vg { color: #fff } /* NameVariableInstance */ .chroma .vi { color: #fff } /* NameVariableMagic */ .chroma .vm { color: #fff } /* Literal */ .chroma .l { color:#a5d6ff } /* LiteralDate */ .chroma .ld { color:#79c0ff } /* LiteralString */ .chroma .s { color:#a5d6ff } /* LiteralStringAffix */ .chroma .sa { color:#79c0ff } /* LiteralStringBacktick */ .chroma .sb { color:#a5d6ff } /* LiteralStringChar */ .chroma .sc { color:#a5d6ff } /* LiteralStringDelimiter */ .chroma .dl { color:#79c0ff } /* LiteralStringDoc */ .chroma .sd { color:#a5d6ff } /* LiteralStringDouble */ .chroma .s2 { color:#a5d6ff } /* LiteralStringEscape */ .chroma .se { color:#79c0ff } /* LiteralStringHeredoc */ .chroma .sh { color:#79c0ff } /* LiteralStringInterpol */ .chroma .si { color:#a5d6ff } /* LiteralStringOther */ .chroma .sx { color:#a5d6ff } /* LiteralStringRegex */ .chroma .sr { color:#79c0ff } /* LiteralStringSingle */ .chroma .s1 { color:#a5d6ff } /* LiteralStringSymbol */ .chroma .ss { color:#a5d6ff } /* LiteralNumber */ .chroma .m { color:#a5d6ff } /* LiteralNumberBin */ .chroma .mb { color:#a5d6ff } /* LiteralNumberFloat */ .chroma .mf { color:#a5d6ff } /* LiteralNumberHex */ .chroma .mh { color:#a5d6ff } /* LiteralNumberInteger */ .chroma .mi { color:#a5d6ff } /* LiteralNumberIntegerLong */ .chroma .il { color:#a5d6ff } /* LiteralNumberOct */ .chroma .mo { color:#a5d6ff } /* Operator */ .chroma .o { color:#ff7b72;font-weight:bold } /* OperatorWord */ .chroma .ow { color:#ff7b72;font-weight:bold } /* Punctuation */ .chroma .p { color: #fff } /* Comment */ .chroma .c { color:#8b949e;font-style:italic } /* CommentHashbang */ .chroma .ch { color:#8b949e;font-style:italic } /* CommentMultiline */ .chroma .cm { color:#8b949e;font-style:italic } /* CommentSingle */ .chroma .c1 { color:#8b949e;font-style:italic } /* CommentSpecial */ .chroma .cs { color:#8b949e;font-weight:bold;font-style:italic } /* CommentPreproc */ .chroma .cp { color:#8b949e;font-weight:bold;font-style:italic } /* CommentPreprocFile */ .chroma .cpf { color:#8b949e;font-weight:bold;font-style:italic } /* Generic */ .chroma .g { color: #fff } /* GenericDeleted */ .chroma .gd { color:#ffa198;background-color:#490202 } /* GenericEmph */ .chroma .ge { font-style:italic } /* GenericError */ .chroma .gr { color:#ffa198 } /* GenericHeading */ .chroma .gh { color:#79c0ff;font-weight:bold } /* GenericInserted */ .chroma .gi { color:#56d364;background-color:#0f5323 } /* GenericOutput */ .chroma .go { color:#8b949e } /* GenericPrompt */ .chroma .gp { color:#8b949e } /* GenericStrong */ .chroma .gs { font-weight:bold } /* GenericSubheading */ .chroma .gu { color:#79c0ff } /* GenericTraceback */ .chroma .gt { color:#ff7b72 } /* GenericUnderline */ .chroma .gl { text-decoration:underline } /* TextWhitespace */ .chroma .w { color:#6e7681 } } 我在上面提到的生成的样式中空缺的部分，可以看一下下面的样例，\n要记得把他们的颜色都补上。\n然后，修改一下配置即可，\nparams: assets: disableHLJS: true markup: goldmark: renderer: unsafe: true # 可以 unsafe，有些 html 标签和样式可能需要 highlight: anchorLineNos: false # 不要给行号设置锚标 codeFences: true # 代码围栏 noClasses: false # TODO: 不知道干啥的，暂时没必要了解，不影响展示 lineNos: true # 代码行 lineNumbersInTable: false # 不要设置成 true，否则如果文章开头是代码的话，摘要会由一大堆数字(即代码行号)开头文章 # 这里设置 style 没用，得自己加 css # style: \u0026#34;github-dark\u0026#34; # style: monokai 修改网页的 favicon 先到 flaticon 网站中找一个 icon 图片，然后放到 static 目录下，\n然后，修改配置，\nparams: # 设置网站的标签页的图标，即 favicon assets: favicon: \u0026#34;favicon.png\u0026#34; favicon16x16: \u0026#34;favicon.png\u0026#34; favicon32x32: \u0026#34;favicon.png\u0026#34; apple_touch_icon: \u0026#34;favicon.png\u0026#34; safari_pinned_tab: \u0026#34;favicon.png\u0026#34; 其他一些小的样式修改 这个就直接看我的代码仓库就可以了，修改的基本都是 css，代码都在 assets 目录下，\n部署到 Github Pages 这里其实有两种方式，一种是直接建立一个以 username.github.io 为名的 Github 仓库，然后，进行部署，另一种是在此基础上新建一个普通的项目，然后可以挂到 username.github.io 域名的后面。\n我们这里就选用简单的第一种比较直接的方式。\n新建一个仓库，没有什么好说的，然后把我们当前的这个仓库和远程仓库关联起来，然后推送过去。然后按照 Hugo 的文档指导来操作即可。\n对于官方给出的 .github/workflows/hugo.yaml， 把其中的分支名改一下即可，或者同时把其中的 Hugo 的版本信息改成和本地的一致也可以。之后，每次推送就可以看到 Github 在部署了。\n一些常用的 Hugo 命令 其实可能只有两个需要我们去记忆或者说熟悉，\nhugo new content content/posts/xxxxx.md hugo server 一些不足 PaperMod 的搜索十分简陋。但是，问题倒也不大。从好的方面来讲，该分享的内容是不影响分享的，而且，正式因为搜索不太好用，所以对个人的隐私可能反而会友好一点。\n验证与自查 hugo server 本地预览：首页、归档、分类、标签、搜索页均正常，搜索能搜到文章内容 文章页底部出现 giscus 评论框，且明暗主题切换时评论主题跟随变化 数学公式正确渲染；含三对以上花括号的公式用 \u0026lt;div\u0026gt; 包裹后解析正常 推送到 GitHub 后 Actions 构建成功，访问 https://\u0026lt;username\u0026gt;.github.io/ 与本地效果一致，favicon 正常显示 参考 https://pengfeixc.com/blogs/developer-handbook/git-submodules 添加 About 页面 修改字体 https://developer.mozilla.org/en-US/docs/Web/CSS/cursor https://cursor.in/ https://github.com/francoischalifour/medium-zoom https://shaohanyun.top/posts/env/hugo_mathjax/ ","permalink":"https://heihei0299.github.io/posts/blog/hugo-papermod-github-pages-guide/","summary":"\u003ch1 id=\"hugo--papermod--github-pages-搭建一个完善的个人博客以-windows11-为例\"\u003eHugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例)\u003c/h1\u003e\n\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e之前四五年的时间里也算写过五六百篇博客。以前的 Hexo 顶不住博客数量的压力了，所以，切到 Hugo 试一下。\u003c/p\u003e\n\u003ch3 id=\"涉及工具\"\u003e涉及工具\u003c/h3\u003e\n\u003cp\u003e除了搭建博客和介绍如何使用这个博客框架这样一个流程本身，还包含了对 PaperMod 这个主题的一些细节的定制。\u003c/p\u003e","title":"Hugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例)"},{"content":"VSCode 终端黑块字体解决方案 适用场景：Arch Linux + Niri 桌面环境下，VSCode 内置终端、opencode（TUI 程序）、zsh + starship 提示符出现大量黑块（■）、空白方块、字符断裂、中文路径显示异常，而 kitty 终端基本正常。你会学到字体 fallback 链的原理与三层修复方法。\n问题现象 在以下工具中出现字符显示异常：\nVSCode 内置终端 opencode（TUI 终端程序） zsh + starship 提示符 kitty 终端（基本正常） 具体表现：\n黑块（■） 空白方块 字符断裂 中文路径显示异常 Git/终端图标不显示（变方块） 例如：\nNerd Font 图标（  ）显示为方块 中文目录名称显示为 ■ 或空白 部分 ASCII 字符也异常断裂 原因分析 这个问题本质不是单一错误，而是字体系统不完整 + fallback 链错误导致的，可以拆成 3 个核心原因。\n原因一：Nerd Font 字符缺失（图标变黑块） starship / opencode / git prompt 使用：\nPowerline symbols Nerd Font private use area glyph box drawing symbols 当前系统中使用的 Nerd Font：\n版本不完整（非 complete patched） 或 VSCode 没正确加载 Mono 版本 结果：图标字符找不到 → 显示黑块。\n原因二：中文字体缺失（CJK fallback 不工作） 路径中包含中文，例如：\n配置 / learning-project 但 terminal 没有正确 fallback 到：\nNoto Sans CJK 结果：中文 glyph 缺失 → 显示 ■ 或空白。\n原因三：VSCode / opencode 字体 fallback 链错误（最关键） VSCode terminal 与 kitty 不同：\nkitty：支持 font fallback（自动补字体） VSCode terminal：只依赖手动配置的字体列表 当前问题是：VSCode terminal 没有正确配置多字体 fallback 顺序，导致英文 OK、Nerd font 显示异常、中文显示异常、emoji 显示异常，最终出现大量黑块。\n为什么 kitty 正常但 VSCode / opencode 不正常 终端 字体机制 结果 kitty 自动 fallback 正常 VSCode terminal 手动字体链 失败 opencode (TUI) 依赖终端字体 失败 kitty 掩盖了问题的严重性。\n解决办法 必须同时解决三层字体。\n1. 安装完整字体集合（系统层） sudo pacman -S \\ ttf-jetbrains-mono-nerd \\ noto-fonts \\ noto-fonts-cjk \\ noto-fonts-emoji \\ ttf-nerd-fonts-symbols-mono 2. VSCode terminal 字体配置（关键） 打开 settings.json，添加：\n\u0026#34;terminal.integrated.fontFamily\u0026#34;: \u0026#34;JetBrainsMono Nerd Font Mono, Symbols Nerd Font Mono, Noto Sans CJK SC, Noto Color Emoji\u0026#34; 3. 强制 Mono Nerd Font（避免 glyph 缺失） 确保使用：\nJetBrainsMono Nerd Font Mono 而不是：\nJetBrains Mono Nerd Font（非 Mono） 4. 重启 VSCode 必须执行：\nCtrl + Shift + P → Reload Window 验证与自查 fc-list | rg \u0026quot;Nerd Font\u0026quot; 与 fc-list | rg \u0026quot;Noto Sans CJK\u0026quot; 都有输出 在 VSCode 终端执行 echo \u0026quot;中文 test ABC    🌏 ▓▒░\u0026quot;，中文、图标、emoji 均正常显示无黑块 cd 配置 这类中文路径输入、Tab 补全显示正常 VSCode 终端与 kitty 显示效果一致 总结 问题的本质是：终端字体 fallback 链断裂 + Nerd Font / CJK / Emoji 字体未统一配置，导致 VSCode 与 TUI 程序无法正确渲染字符，从而出现大量黑块。\n参考 Nerd Fonts 官方仓库 VSCode 终端外观文档 ArchWiki - Font configuration ","permalink":"https://heihei0299.github.io/posts/linux/vscode%E7%BB%88%E7%AB%AF%E9%BB%91%E5%BF%AB%E5%AD%97%E4%BD%93%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","summary":"\u003ch1 id=\"vscode-终端黑块字体解决方案\"\u003eVSCode 终端黑块字体解决方案\u003c/h1\u003e\n\u003cp\u003e适用场景：Arch Linux + Niri 桌面环境下，VSCode 内置终端、opencode（TUI 程序）、zsh + starship 提示符出现大量黑块（■）、空白方块、字符断裂、中文路径显示异常，而 kitty 终端基本正常。你会学到字体 fallback 链的原理与三层修复方法。\u003c/p\u003e","title":"VSCode 终端黑块字体解决方案"},{"content":"安装 Niri 与 DMS 适用场景：Arch Linux 用户想从传统桌面切换到 Niri（滚动平铺 Wayland 合成器）并搭配 DMS（DankMaterialShell）桌面 Shell。你会学到 niri 的安装、首次运行与基础快捷键，以及用官方脚本一键配置 DMS。\n什么是 Niri Niri 是一个基于 Wayland 的滚动平铺窗口管理器，核心特点是横向无限卷轴式布局。不像传统桌面那样窗口只能在一个屏幕范围内排列，Niri 的工作区可以向左右无限延伸。\n更多特性建议看官方的 Demo 视频：Niri 演示视频\n安装 前提 已安装 Arch Linux 并启动了系统 已配置 archlinuxcn 源和 AUR 助手（yay / paru） 安装 niri sudo pacman -S niri xwayland-satellite xdg-desktop-portal-gnome fuzzel kitty 包名 作用 niri niri 窗口管理器本体 xwayland-satellite 在 Wayland 上运行 X11 应用的兼容层 xdg-desktop-portal-gnome 提供文件选择、屏幕分享等桌面门户功能 fuzzel 应用启动器，niri 默认快捷键 Super+D 调用 kitty 终端模拟器 第一次运行 niri-session 这会在一个纯文本 tty 中启动 niri 会话，自动生成默认配置文件 ~/.config/niri/config.kdl\nSuper+Shift+E 退出 niri。\nSuper 键就是 Win 键\n基础使用方法 快捷键 功能 Super+Shift+/ 快捷键教程菜单 Super+T 打开终端 Super+D 打开应用启动器 Super+Q 关闭当前窗口 Super+U / Super+I 上下切换工作区 Super+J / Super+K 左右切换窗口 Super+Shift+J / Super+Shift+K 左右移动窗口 Super+F 全屏当前窗口 Super+Shift+F 切换窗口浮动模式 Super+O 打开 Overview 视图 安装 DMS 桌面 Shell DankMaterialShell 是一套完整的桌面 Shell，适用于 niri、Hyprland、Sway 等 Wayland 合成器。它取代了 waybar、swaylock、swayidle、mako、fuzzel、polkit 等组件。\n使用一键安装脚本自动配置，桌面选择 niri，终端选择 kitty：\ncurl -fsSL https://install.danklinux.com | sh 详细配置（登录管理器 DMS 等）见 配置Arch.md 的 D 段。\n验证与自查 niri msg version 能输出版本号，niri-session 可正常进入会话 按 Super+T 能打开 kitty 终端，按 Super+D 能调出 fuzzel 按 Super+Shift+/ 能弹出快捷键教程菜单 DMS 安装后重启，能看到图形化登录界面 参考 Niri GitHub 仓库 Niri Wiki（配置、快捷键等） DankMaterialShell 一键安装脚本 ","permalink":"https://heihei0299.github.io/posts/linux/install-niri-dms/","summary":"\u003ch1 id=\"安装-niri-与-dms\"\u003e安装 Niri 与 DMS\u003c/h1\u003e\n\u003cp\u003e适用场景：Arch Linux 用户想从传统桌面切换到 Niri（滚动平铺 Wayland 合成器）并搭配 DMS（DankMaterialShell）桌面 Shell。你会学到 niri 的安装、首次运行与基础快捷键，以及用官方脚本一键配置 DMS。\u003c/p\u003e","title":"安装 Niri 与 DMS"},{"content":"配置 Arch Linux 适用场景：刚装完 Arch Linux，需要一套从基础环境到输入法、显示管理器的完整配置。你会学到按依赖顺序分四段完成配置：A 段基础配置 → B 段显卡驱动 → C 段 fcitx5 中文输入法 → D 段 DMS 显示管理器。\n执行顺序说明 本文件分为四段，按依赖关系排列：\nA段（基础配置） ────── 装完系统后立刻做 ↓ B段（显卡驱动） ────── 可选，按需阅读 ↓ 安装 Niri（详见 [install-niri+dms.md](install-niri-dms)） ↓ C段（输入法） ──────── 需要 niri 配置文件的环境变量 D段（DMS） ─────────── 需要 niri 的 .desktop 文件 建议流程：\n从 安装ArchLinux（Win双系统） 重启进入新系统 完成本文件的 A段 和 B段 安装 Niri 最后回来完成 C段 和 D段 A段：基础配置 设置默认编辑器 系统级的环境变量，很多程序会读取 EDITOR 来决定用什么编辑器打开文件：\nsudo vim /etc/environment 写入：\nEDITOR=nano 如果用 neovim 则填 nvim，nano 填 nano\n需要重新登录后生效：\nexit 开启 32 位源 Steam 和 Wine 等需要 32 位库：\nsudo vim /etc/pacman.conf 找到 [multilib]，去掉下面两行的注释：\n[multilib] Include = /etc/pacman.d/mirrorlist 同步数据库：\nsudo pacman -Syu 添加 archlinuxcn 源 archlinuxcn 是由社区维护的软件仓库，包含很多官方源没有的包。\nsudo vim /etc/pacman.conf 文件底部追加：\n[archlinuxcn] Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch 中科大和清华的源在国内速度很快。海外用户直接用官方源：Server = https://repo.archlinuxcn.org/$arch\n同步并安装密钥：\nsudo pacman -Syu archlinuxcn-keyring 安装 AUR 助手 AUR（Arch User Repository）是 Arch 最强大的软件仓库，AUR 助手可以一键从 AUR 安装软件。\nsudo pacman -S --needed base-devel yay paru 包名 说明 base-devel 编译 AUR 包必需的工具链 yay 最流行的 AUR 助手 paru 另一个 AUR 助手，某些 yay 安装失败的包可以换 paru 使用方式：\nyay -S 包名 paru -S 包名 两个都装，互相备份。日常使用一个即可。\n安装字体 sudo pacman -S noto-fonts noto-fonts-cjk noto-fonts-emoji ttf-jetbrains-mono-nerd 包名 说明 noto-fonts 大部分外文字体 noto-fonts-cjk 中日韩字体（注意需要正确配置 fontconfig，否则中文可能显示为日文字形） noto-fonts-emoji Emoji 支持 ttf-jetbrains-mono-nerd 等宽字体，带 Nerd Font 图标，终端和 waybar 常用 配置 kitty 终端和 vscode 终端字体 配置 kitty 终端字体，改为 JetBrainsMono Nerd Font：\nnano .config/kitty/kitty.conf 写入：\nfamily JetBrainsMono Nerd Font bold_font auto bold_italic_font auto italic_font auto symbol_map U+4E00-U+9FFF Noto Sans CJK SC emoji_font Noto Color Emoji 配置 vscode 终端字体，修改 settings.json，添加下面的配置：\n{ \u0026#34;editor.fontFamily\u0026#34;: \u0026#34;JetBrainsMono Nerd Font\u0026#34;, \u0026#34;terminal.integrated.fontFamily\u0026#34;: \u0026#34;JetBrainsMono Nerd Font Mono, Symbols Nerd Font Mono, Noto Sans CJK SC, Noto Color Emoji\u0026#34;, \u0026#34;terminal.integrated.fontLigatures\u0026#34;: true, \u0026#34;terminal.integrated.gpuAcceleration\u0026#34;: \u0026#34;off\u0026#34;, \u0026#34;chat.agent.enabled\u0026#34;: false, \u0026#34;chat.checkpoints.enabled\u0026#34;: false, \u0026#34;chat.viewSessions.enabled\u0026#34;: false, \u0026#34;workbench.secondarySideBar.defaultVisibility\u0026#34;: \u0026#34;hidden\u0026#34; } 配置 fontconfig 字体 编辑 ~/.config/fontconfig/fonts.conf：\nvim ~/.config/fontconfig/fonts.conf 写入（最终稳定版）：\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE fontconfig SYSTEM \u0026#34;fonts.dtd\u0026#34;\u0026gt; \u0026lt;fontconfig\u0026gt; \u0026lt;!-- ========== monospace 主字体链 ========== --\u0026gt; \u0026lt;match target=\u0026#34;pattern\u0026#34;\u0026gt; \u0026lt;test name=\u0026#34;family\u0026#34;\u0026gt; \u0026lt;string\u0026gt;monospace\u0026lt;/string\u0026gt; \u0026lt;/test\u0026gt; \u0026lt;edit name=\u0026#34;family\u0026#34; mode=\u0026#34;prepend\u0026#34;\u0026gt; \u0026lt;string\u0026gt;JetBrainsMono Nerd Font Mono\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;Noto Sans Mono CJK SC\u0026lt;/string\u0026gt; \u0026lt;/edit\u0026gt; \u0026lt;/match\u0026gt; \u0026lt;!-- ========== sans 中文 fallback ========== --\u0026gt; \u0026lt;match target=\u0026#34;pattern\u0026#34;\u0026gt; \u0026lt;test name=\u0026#34;lang\u0026#34; compare=\u0026#34;contains\u0026#34;\u0026gt; \u0026lt;string\u0026gt;zh\u0026lt;/string\u0026gt; \u0026lt;/test\u0026gt; \u0026lt;edit name=\u0026#34;family\u0026#34; mode=\u0026#34;prepend\u0026#34;\u0026gt; \u0026lt;string\u0026gt;Noto Sans CJK SC\u0026lt;/string\u0026gt; \u0026lt;/edit\u0026gt; \u0026lt;/match\u0026gt; \u0026lt;!-- ========== emoji ========== --\u0026gt; \u0026lt;alias\u0026gt; \u0026lt;family\u0026gt;emoji\u0026lt;/family\u0026gt; \u0026lt;prefer\u0026gt; \u0026lt;family\u0026gt;Noto Color Emoji\u0026lt;/family\u0026gt; \u0026lt;/prefer\u0026gt; \u0026lt;/alias\u0026gt; \u0026lt;/fontconfig\u0026gt; 安装音视频服务 PipeWire 是 Red Hat 主导开发的现代音视频服务：\nsudo pacman -S --needed pipewire wireplumber pipewire-pulse pipewire-alsa pipewire-jack 包名 说明 pipewire 核心音视频服务 wireplumber PipeWire 的智能会话管理 pipewire-pulse PulseAudio 兼容层 pipewire-alsa ALSA 兼容层 pipewire-jack JACK 兼容层 启用服务：\nsystemctl --user enable --now pipewire pipewire-pulse wireplumber 可选：安装音视频固件（部分笔记本需要）：\nsudo pacman -S --needed sof-firmware alsa-ucm-conf 安装蓝牙 sudo pacman -S --needed bluez 启用服务：\nsudo systemctl enable --now bluetooth 蓝牙管理工具推荐 bluetui：\nsudo pacman -S bluetui 安装性能模式切换 sudo pacman -S power-profiles-daemon sudo systemctl enable --now power-profiles-daemon 提供三个档位：performance（性能）、balanced（平衡）、power-saver（省电）。\n切换模式：\npowerprofilesctl set performance powerprofilesctl set balanced powerprofilesctl set power-saver B段：显卡驱动 根据你的显卡选择对应章节。Intel 用户按需阅读 AMD 和 Nvidia 部分即可。\nIntel（你的配置） sudo pacman -S mesa vulkan-intel libva-intel-driver intel-media-driver 包名 说明 mesa 开源图形驱动（Mesa3D） vulkan-intel Intel Vulkan 支持 libva-intel-driver Intel 视频编解码（旧款，VA-API） intel-media-driver Intel 视频编解码（新款，Media Driver），与上一项二选一或都装 验证驱动是否正常工作：\nsudo pacman -S glxinfo glxinfo | grep \u0026#34;OpenGL renderer\u0026#34; 应该输出 Intel 相关字样。\nAMD sudo pacman -S mesa vulkan-radeon libva-mesa-driver 包名 说明 mesa 开源图形驱动 vulkan-radeon AMD Vulkan 支持（也可以装 vulkan-amdgpu-pro AUR 版） libva-mesa-driver AMD 视频编解码 Nvidia Nvidia 在 Wayland 下的支持已经相当成熟，推荐使用开源内核模块。\nsudo pacman -S nvidia-dkms nvidia-utils libva-nvidia-driver 包名 说明 nvidia-dkms Nvidia 闭源驱动（dkms 版，内核更新时自动重编译） nvidia-utils Nvidia 工具和库（含 nvidia-smi） libva-nvidia-driver Nvidia 视频编解码（VA-API 转译） 如果是 RTX 20 及更新架构，也可以选择 nvidia-open-dkms（开源内核模块），性能和闭源版一致。\n在 /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT 中添加：\nnvidia_drm.modeset=1 重新生成 GRUB 配置：\nsudo grub-mkconfig -o /boot/grub/grub.cfg 在 niri 的 environment { } 中添加：\nGDK_BACKEND \u0026#34;wayland\u0026#34; CLUTTER_BACKEND \u0026#34;wayland\u0026#34; 部分 Electron 应用需要 --ozone-platform=wayland 启动参数才能正确使用 Wayland。\nC段：中文输入法 前提 已安装 Niri niri 配置文件中已创建 environment { } 代码块 安装 fcitx5 框架 sudo pacman -S fcitx5-im fcitx5-im 是一个元包，包含 fcitx5 主程序、配置工具、QT/GTK 模块等\n安装 RIME 引擎和雾凇方案 sudo pacman -S fcitx5-rime yay -S rime-ice-git 包名 说明 fcitx5-rime RIME 中州韵输入法引擎 rime-ice-git 雾凇拼音输入方案（含全拼、双拼等） 配置雾凇为默认方案 创建 RIME 用户配置目录：\nmkdir -p ~/.local/share/fcitx5/rime 编辑默认方案配置：\nvim ~/.local/share/fcitx5/rime/default.custom.yaml 写入：\npatch: __include: rime_ice_suggestion:/ 重启 fcitx5 使配置生效：\nfcitx5 -r 重启后默认就会使用雾凇拼音。\n在 fcitx5 中添加 RIME 打开 fcitx5 配置工具：\nfcitx5-configtool 在 输入法 列表中，把 中州韵 (rime) 添加到左侧已启用列表。\n配置 niri 环境变量 在 ~/.config/niri/config.kdl 的 environment { } 中添加：\nenvironment { XMODIFIERS \u0026#34;@im=fcitx\u0026#34; } 自启动 fcitx5 在 config.kdl 的 spawn-at-startup 中添加：\nspawn-at-startup \u0026#34;fcitx5\u0026#34; \u0026#34;-d\u0026#34; 重启输入法：\nfcitx5 -r 现在试试输入 苍茫的天涯是我的爱，效果应该好了很多。\n可选：设置输入法切换快捷键 在 niri 配置的 binds { } 中添加：\nMod+F1 { spawn-sh \u0026#34;pkill fcitx5 || fcitx5 -d\u0026#34;; } 输入法偶尔会卡住，这个快捷键可以快速重启输入法\n可选：美化 fcitx5 主题 下载主题放到 ~/.local/share/fcitx5/themes/，在 fcitx5 配置工具的 经典用户界面 中选择主题。\n输入法异常处理 如果在某些软件中无法使用输入法，可能原因：\nLC_CTYPE 问题：在 niri 配置中已设 LC_CTYPE=en_US.UTF-8，此值可能会导致 Steam 无法输入中文。解决办法是在 Steam 的 .desktop 文件中添加 env LC_CTYPE=zh_CN.UTF-8 前缀。 Electron 应用：需要添加启动参数： --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime D段：显示管理器 DMS 什么是 dms-greeter？ dms-greeter 是一款 greetd 登录界面程序，采用了与 DankMaterialShell 锁屏界面相同的美学风格。\n安装依赖 有两种安装方式可选。\n方式一：自动安装（推荐） dms greeter install 适用于任何发行版（Arch Linux 需要 paru 或 yay）。配置、权限、主题同步和 greetd 服务启用均自动处理。\n方式二：手动安装 sudo pacman -S greetd yay -S quickshell greetd-dms-greeter-git 包名 说明 greetd 登录管理守护进程 quickshell DMS 使用的 UI 渲染框架 greetd-dms-greeter-git DMS 登录界面（AUR） 启用欢迎界面 dms greeter enable 这将：\n配置 /etc/greetd/config.toml，使用正确的合成器命令 禁用冲突的显示管理器（gdm、lightdm、sddm） 启用并启动 greetd 服务 与用户主题同步 dms greeter sync 这将：\n安装 acl（如果尚未安装） 将用户添加到 greeter 组 设置 ACL 权限，以便 greeter 访问配置目录 创建符号链接以同步设置、壁纸和颜色主题 检查安装情况 dms greeter status 验证项：\n✓ 用户组成员身份 ✓ 缓存目录是否存在 ✓ 配置符号链接（设置、壁纸、颜色） ✓ 源文件是否存在且可读 示例输出：\n=== DMS Greeter Status === Group Membership: ✓ User is in greeter group Greeter Cache Directory: ✓ /var/cache/dms-greeter exists Configuration Symlinks: ✓ Settings: synced correctly ✓ Session state: synced correctly ✓ Color theme: synced correctly ✓ All checks passed! Greeter is properly configured. 如果出现部分错误，可以重启后再次执行 dms greeter status 查看情况。\n配置 greetd 创建 niri 登录合成器配置：\nsudo tee /etc/greetd/niri.kdl \u0026gt; /dev/null \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; hotkey-overlay { skip-at-startup } environment { DMS_RUN_GREETER \u0026#34;1\u0026#34; } gestures { hot-corners { off } } layout { background-color \u0026#34;#000000\u0026#34; } EOF 然后在 /etc/greetd/config.toml 中修改合成器命令：\ncommand = \u0026#34;dms-greeter --command niri -C /etc/greetd/niri.kdl\u0026#34; 再次检查安装情况 dms greeter status 验证项：\n✓ 用户组成员身份 ✓ 缓存目录是否存在 ✓ 配置符号链接（设置、壁纸、颜色） ✓ 源文件是否存在且可读 示例输出：\n=== DMS Greeter Status === Group Membership: ✓ User is in greeter group Greeter Cache Directory: ✓ /var/cache/dms-greeter exists Configuration Symlinks: ✓ Settings: synced correctly ✓ Session state: synced correctly ✓ Color theme: synced correctly ✓ All checks passed! Greeter is properly configured. 重启验证 reboot 重启后应该就能看到 DMS 的图形化登录界面了。\n验证与自查 fcitx5 -r 后在任意应用按 Ctrl+Space 可切换中文输入，输入 苍茫的天涯是我的爱 候选正常 dms greeter status 输出全部 ✓ glxinfo | grep \u0026quot;OpenGL renderer\u0026quot; 输出对应显卡厂商字样 powerprofilesctl get 返回当前性能档位；bluetoothctl 可扫描到蓝牙设备 参考 ArchWiki - pacman archlinuxcn 仓库说明 ArchWiki - AUR 助手 ArchWiki - PipeWire ArchWiki - 蓝牙 ArchWiki - NVIDIA ArchWiki - fcitx5 rime-ice 雾凇拼音 ArchWiki - greetd ArchWiki - Font configuration ","permalink":"https://heihei0299.github.io/posts/linux/%E9%85%8D%E7%BD%AEarch/","summary":"\u003ch1 id=\"配置-arch-linux\"\u003e配置 Arch Linux\u003c/h1\u003e\n\u003cp\u003e适用场景：刚装完 Arch Linux，需要一套从基础环境到输入法、显示管理器的完整配置。你会学到按依赖顺序分四段完成配置：A 段基础配置 → B 段显卡驱动 → C 段 fcitx5 中文输入法 → D 段 DMS 显示管理器。\u003c/p\u003e","title":"配置 Arch Linux"},{"content":"Arch Linux Timeshift 完整教程 什么是 Timeshift Timeshift 是 Linux 下最流行的系统快照工具之一，原由 Tony George 开发，现为 Linux Mint Xapp 项目的一部分。\n作用类似于：\nWindows 系统还原 macOS Time Machine（仅系统部分） 可在以下情况快速回滚：\npacman 更新后系统损坏 安装错误驱动导致无法进入桌面 修改配置导致系统异常 内核升级失败 引导器损坏 注意： Timeshift 设计用于保护系统文件和设置，不是个人数据备份工具。默认排除 /home/ 用户数据，确保恢复时不会覆盖你的文档。\n用户文件建议使用：\nrsync / borg / restic / Syncthing 安装 Timeshift sudo pacman -S timeshift Timeshift 依赖 cron 进行定时调度，安装后启用：\nsudo systemctl enable --now cronie.service 也可以使用 timeshift-systemd-timerAUR 替代 cronie。\n验证安装：\ntimeshift --version 查看磁盘布局 lsblk -f 输出示例（ext4）：\nnvme0n1 ├─nvme0n1p1 vfat ├─nvme0n1p2 ext4 / └─nvme0n1p3 ext4 /home Btrfs 示例：\nnvme0n1 ├─nvme0n1p1 vfat └─nvme0n1p2 btrfs /@ /@home Timeshift Btrfs 模式要求 @ 和 @home 子卷布局，其他布局不兼容。\n选择工作模式 RSYNC 模式（推荐新手） 适用于所有文件系统：ext4、xfs、f2fs、btrfs。\n特点：通用、稳定、不依赖文件系统。\nBTRFS 模式 仅适用于 Ubuntu 风格子卷布局（@ 和 @home）。\n特点：创建快照秒级完成、占用空间极小、恢复速度快。\n如果分区是 Btrfs 但没有 @/@home 布局，请先转换，参考 ArchWiki - Timeshift。\n首次配置 启动配置向导：\nsudo timeshift-launcher 或者直接启动 GTK 界面：\nsudo timeshift-gtk 首次运行会引导选择：\n快照类型 — RSYNC 或 BTRFS 快照存储位置 — 默认存储在系统根分区 /timeshift，推荐存到独立数据盘或大容量非系统分区 推荐快照策略 Timeshift 支持五个快照层级：Hourly、Daily、Weekly、Monthly、Boot。\n层级 保留数量 说明 Daily 7 最近 7 天 Weekly 4 最近 4 周 Monthly 3 最近 3 个月 Hourly 0 关闭 Boot 0 关闭 Boot 快照在系统启动 10 分钟后创建，适合频繁测试重启的场景。\n推荐过滤器 Timeshift → Settings → Filters\n排除以下目录（避免快照膨胀和污染恢复）：\n# 用户数据 /home/** # 临时/运行时 /tmp/** /var/tmp/** /run/** /proc/** /sys/** /dev/** # 缓存/日志（可重新生成） /var/cache/** /var/log/** /var/lib/systemd/coredump/** # 挂载点 /mnt/** /media/** # 应用数据 /var/lib/docker/** /var/lib/containerd/** /var/lib/flatpak/** /var/lib/libvirt/images/** /var/lib/lxd/** # 服务数据 /opt/** /srv/** /var/www/** /var/spool/** /var/mail/** # 回收站 /.local/share/Trash/** # Swap 文件 /swap.img 推荐 timeshift.json 配置文件位置：/etc/timeshift/timeshift.json\n首次配置后自动生成，也可以复制模板 /etc/timeshift/default.json 后手动编辑。\n{ \u0026#34;backup_device_uuid\u0026#34; : \u0026#34;ebe7d1b8-80ce-4390-81ed-ee34dbf0e63d\u0026#34;, \u0026#34;parent_device_uuid\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;do_first_run\u0026#34; : \u0026#34;false\u0026#34;, \u0026#34;btrfs_mode\u0026#34; : \u0026#34;false\u0026#34;, \u0026#34;include_btrfs_home_for_backup\u0026#34; : \u0026#34;false\u0026#34;, \u0026#34;include_btrfs_home_for_restore\u0026#34; : \u0026#34;false\u0026#34;, \u0026#34;stop_cron_emails\u0026#34; : \u0026#34;true\u0026#34;, \u0026#34;schedule_monthly\u0026#34; : \u0026#34;true\u0026#34;, \u0026#34;schedule_weekly\u0026#34; : \u0026#34;true\u0026#34;, \u0026#34;schedule_daily\u0026#34; : \u0026#34;true\u0026#34;, \u0026#34;schedule_hourly\u0026#34; : \u0026#34;false\u0026#34;, \u0026#34;schedule_boot\u0026#34; : \u0026#34;false\u0026#34;, \u0026#34;count_monthly\u0026#34; : \u0026#34;3\u0026#34;, \u0026#34;count_weekly\u0026#34; : \u0026#34;4\u0026#34;, \u0026#34;count_daily\u0026#34; : \u0026#34;7\u0026#34;, \u0026#34;count_hourly\u0026#34; : \u0026#34;0\u0026#34;, \u0026#34;count_boot\u0026#34; : \u0026#34;0\u0026#34;, \u0026#34;date_format\u0026#34; : \u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;, \u0026#34;exclude\u0026#34; : [ \u0026#34;/home/**\u0026#34;, \u0026#34;/proc/**\u0026#34;, \u0026#34;/sys/**\u0026#34;, \u0026#34;/dev/**\u0026#34;, \u0026#34;/run/**\u0026#34;, \u0026#34;/tmp/**\u0026#34;, \u0026#34;/var/tmp/**\u0026#34;, \u0026#34;/mnt/**\u0026#34;, \u0026#34;/media/**\u0026#34;, \u0026#34;/var/cache/**\u0026#34;, \u0026#34;/var/log/**\u0026#34;, \u0026#34;/var/lib/systemd/coredump/**\u0026#34;, \u0026#34;/swap.img\u0026#34;, \u0026#34;/var/lib/docker/**\u0026#34;, \u0026#34;/var/lib/containerd/**\u0026#34;, \u0026#34;/var/lib/flatpak/**\u0026#34;, \u0026#34;/var/lib/libvirt/images/**\u0026#34;, \u0026#34;/var/lib/lxd/**\u0026#34;, \u0026#34;/opt/**\u0026#34;, \u0026#34;/srv/**\u0026#34;, \u0026#34;/var/www/**\u0026#34;, \u0026#34;/.local/share/Trash/**\u0026#34;, \u0026#34;/var/spool/**\u0026#34;, \u0026#34;/var/mail/**\u0026#34;, \u0026#34;/.Trash-1000\u0026#34; ] } 创建快照 手动创建：\nsudo timeshift --create 添加备注：\nsudo timeshift --create --comments \u0026#34;Before pacman -Syu\u0026#34; 查看所有快照：\nsudo timeshift --list 脚本中使用建议加 --scripted 参数，避免交互提示。\n删除快照 查看快照列表：\nsudo timeshift --list 删除指定快照：\nsudo timeshift --delete --snapshot \u0026#39;2026-06-18_12-00-01\u0026#39; 删除全部：\nsudo timeshift --delete-all 系统恢复 图形界面恢复 sudo timeshift-launcher 选择快照 → Restore → 按向导完成。\n命令行恢复 sudo timeshift --restore 按提示选择快照，或直接指定：\nsudo timeshift --restore --snapshot \u0026#39;2026-06-18_12-00-01\u0026#39; 系统无法启动时恢复 方式一：Live 环境恢复（推荐） 用 Arch ISO 启动，安装 Timeshift：\nsudo pacman -S timeshift 运行 Timeshift GUI 选择快照恢复即可，无需 chroot。\n方式二：arch-chroot 恢复 sudo mount /dev/nvme0n1p2 /mnt arch-chroot /mnt timeshift --list timeshift --restore reboot 方式一更可靠，因为 Timeshift 在 chroot 环境下可能无法正确处理某些操作（如 EFI 分区挂载、GRUB 重装）。\nGRUB 与 Btrfs 快照 如果使用 Btrfs 模式，可安装 grub-btrfs 让快照出现在 GRUB 启动菜单：\nsudo pacman -S grub-btrfs inotify-tools 配置自动更新：\nsudo systemctl edit --full grub-btrfsd 将 ExecStart 改为：\nExecStart=/usr/bin/grub-btrfsd --syslog --timeshift-auto 启用服务：\nsudo systemctl enable --now grub-btrfsd 之后每次创建快照，GRUB 菜单自动多出一个\u0026quot;快照启动项\u0026quot;，可直接启动进快照状态。\nWayland 问题 timeshift-launcher 在 Wayland 下会报 xhost: command not found，因为该命令需要以 root 权限连接 X 显示服务。\n解决方法：\nsudo pacman -S xorg-xhost xhost +SI:localuser:root 这条命令赋予 root 用户访问当前 X 屏幕的权限。多用户系统需谨慎。\n安装后即可正常启动 sudo timeshift-launcher。\n常用命令 操作 命令 创建快照 sudo timeshift --create --comments \u0026quot;备注\u0026quot; 查看快照 sudo timeshift --list 恢复快照 sudo timeshift --restore --snapshot \u0026quot;名称\u0026quot; 删除快照 sudo timeshift --delete --snapshot \u0026quot;名称\u0026quot; 删除全部 sudo timeshift --delete-all 检查配置 sudo timeshift --check 查看配置 cat /etc/timeshift/timeshift.json 查看版本 timeshift --version 最佳实践 更新系统前：\nsudo timeshift --create --comments \u0026#34;Before update\u0026#34; 安装 NVIDIA 驱动前：\nsudo timeshift --create --comments \u0026#34;Before NVIDIA\u0026#34; 升级内核前：\nsudo timeshift --create --comments \u0026#34;Before kernel upgrade\u0026#34; 习惯：\n升级前创建快照 → 出问题恢复 → 继续工作 ","permalink":"https://heihei0299.github.io/posts/arch/%E5%BF%AB%E7%85%A7%E5%92%8C%E7%B3%BB%E7%BB%9F%E7%BB%B4%E6%8A%A4/","summary":"\u003ch1 id=\"arch-linux-timeshift-完整教程\"\u003eArch Linux Timeshift 完整教程\u003c/h1\u003e\n\u003ch2 id=\"什么是-timeshift\"\u003e什么是 Timeshift\u003c/h2\u003e\n\u003cp\u003eTimeshift 是 Linux 下最流行的系统快照工具之一，原由 Tony George 开发，现为 Linux Mint Xapp 项目的一部分。\u003c/p\u003e\n\u003cp\u003e作用类似于：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWindows 系统还原\u003c/li\u003e\n\u003cli\u003emacOS Time Machine（仅系统部分）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e可在以下情况快速回滚：\u003c/p\u003e","title":"Arch Linux Timeshift 完整教程"},{"content":"科学上网 文档整理报告 日期: 2026-06-13 范围: gfw-bypass/ 全部文件（.md 文档、图片资源）\n一、整理概况 统计项 数量 整理前文件总数（含图片） 62 重命名的 .md 文件 27 重命名的目录 5 保留原名的文件 2 (mihomo与tailcale冲突解决方法.md, 使用sing-box搭建ss via shadowtls.md) 发现的未索引文件 3 (Realm转发教程, TelegramBot配置记录, acme.sh申请Google公共证书) 二、重命名对照表 顶层 .md 文件 旧文件名 新文件名 说明 搭建hy2教程 1c899620...md 搭建Hysteria2教程.md 移除哈希，明确协议名 搭建naive d60bfec...md 搭建NaiveProxy教程.md 移除哈希 搭建snell.md 搭建Snell教程.md 统一命名风格 MTProxy手动安装最新（ mtg 2 0 ）代理教程 58f63d...md MTProxy安装教程.md 移除哈希，简化名称 机场搭建 c800a7429...md 机场搭建XBoard教程.md 补充具体项目名 sing-box 4890ab3b...md sing-box文档导航.md 明确文件性质（索引导航） meta-wiki 85cedb...md Meta内核/Meta内核文档导航.md 移入子目录，移除哈希 哪吒探针 328e80b...md 哪吒探针/哪吒探针文档导航.md 移入子目录，移除哈希 常用检查网站 7181c2e...md 常用检查网站.md 移除哈希 dd 系统 0c9c14ac...md DD重装系统教程.md 明确教程类型 debain 开启bbr c62bc...md Debian开启BBR教程.md 修正拼写，移除哈希 vless+vision+reality tcp reality失败解决方法 8ad22...md VLESS+Vision+Reality TCP Brutal失败解决方法.md 修正术语 纯ipv6机器装nezha-agent 59fd0...md 纯IPv6机器安装NezhaAgent.md 移除哈希 gitlab 远程订阅教程 604a0...md GitLab远程订阅教程.md 移除哈希 gitlab 订阅教程 e233c...md GitLab订阅教程.md 移除哈希 Serenity 学习记录 5a224...md Serenity学习记录.md 移除哈希 realm 转发教程 6a0b3...md Realm转发教程.md 移除哈希 TG_bot b87b5a...md TelegramBot配置记录.md 移除哈希，明确内容 111 907f24ab...md Loon进阶配置示例.md 文件名原为无意义数字，根据内容重命名 无标题 38c119...md acme.sh申请Google公共证书.md 根据实际内容重命名 Untitled.md 待整理文档-1.md 占位文件，标记待整理 Untitled (1).md 待整理文档-2.md 占位文件，标记待整理 无标题 87d9c95...md 待整理笔记.md 占位文件，标记待整理 子目录及嵌套文件 旧路径 新路径 说明 meta-wiki 85cedb.../ Meta内核/ 移除哈希 meta-wiki.../linux下clash安装 dc862b...md Meta内核/Linux下Clash安装.md 移除哈希 meta-wiki.../win上使用meta内核 e02203.../ Meta内核/Windows使用Meta内核/ 移除哈希 meta-wiki.../win上使用meta内核 ...md Meta内核/Windows使用Meta内核/Windows使用Meta内核.md 移除哈希 meta-wiki.../启用iis 8da3fb.../ Meta内核/Windows使用Meta内核/启用IIS/ 移除哈希 meta-wiki.../启用iis 8da3fb...md Meta内核/Windows使用Meta内核/启用IIS.md 移除哈希 meta-wiki.../为网页文件授权 42bbc5.../ Meta内核/Windows使用Meta内核/为网页文件授权/ 移除哈希 meta-wiki.../为网页文件授权 42bbc5...md Meta内核/Windows使用Meta内核/为网页文件授权.md 移除哈希 哪吒探针 328e80b.../ 哪吒探针/ 移除哈希 哪吒探针.../国内鸡安装哪吒探针 9f37f...md 哪吒探针/国内机器安装哪吒探针.md 修正名称，移除哈希 哪吒探针.../apline 安装哪吒探针 496d4...md 哪吒探针/Alpine安装哪吒探针.md 修正拼写，移除哈希 gitlab 远程订阅教程 604a0.../ GitLab远程订阅教程/ 移除哈希 MTProxy手动安装最新.../ MTProxy安装教程/ 移除哈希，与 .md 同名 三、修复的交叉引用 文件 修复内容 Meta内核/Meta内核文档导航.md 更新子文档链接（2处） Meta内核/Windows使用Meta内核/Windows使用Meta内核.md 更新图片路径（2处）、子页面链接（2处） Meta内核/Windows使用Meta内核/启用IIS.md 更新图片路径（6处） Meta内核/Windows使用Meta内核/为网页文件授权.md 更新图片路径（5处） 哪吒探针/哪吒探针文档导航.md 更新子文档链接（2处） MTProxy安装教程.md 更新图片路径（1处） GitLab远程订阅教程.md 更新图片路径（4处） GitLab订阅教程.md 更新 GitLab远程订阅教程链接（1处） README.md 全面重写，更新所有链接，补充遗漏文件索引 四、目录结构（整理后） gfw-bypass/ ├── README.md # 文档索引 ├── Meta内核/ # Meta 内核相关文档 │ ├── Meta内核文档导航.md │ ├── Linux下Clash安装.md │ └── Windows使用Meta内核/ │ ├── Windows使用Meta内核.md │ ├── 启用IIS.md │ ├── 启用IIS/ # 图片附件 │ ├── 为网页文件授权.md │ └── 为网页文件授权/ # 图片附件 ├── 哪吒探针/ # 哪吒探针相关文档 │ ├── 哪吒探针文档导航.md │ ├── 国内机器安装哪吒探针.md │ └── Alpine安装哪吒探针.md ├── GitLab远程订阅教程/ # 图片附件 ├── MTProxy安装教程/ # 图片附件 ├── 搭建Hysteria2教程.md ├── 搭建NaiveProxy教程.md ├── 搭建Snell教程.md ├── 使用sing-box搭建ss via shadowtls.md ├── MTProxy安装教程.md ├── 机场搭建XBoard教程.md ├── sing-box文档导航.md ├── 常用检查网站.md ├── DD重装系统教程.md ├── Debian开启BBR教程.md ├── VLESS+Vision+Reality TCP Brutal失败解决方法.md ├── mihomo与tailcale冲突解决方法.md ├── 纯IPv6机器安装NezhaAgent.md ├── Realm转发教程.md ├── GitLab远程订阅教程.md ├── GitLab订阅教程.md ├── Serenity学习记录.md ├── Loon进阶配置示例.md ├── TelegramBot配置记录.md ├── acme.sh申请Google公共证书.md ├── 待整理文档-1.md ├── 待整理文档-2.md └── 待整理笔记.md 五、已知问题 sing-box文档导航.md 中的 3 个子链接指向不存在的文件（sing-box常用命令、sing-box-linux安装、reality网站），这些文件不在仓库中。链接保持原样而非删除。 （2026-06-13 二次整理：已改为纯文本标注，注明原文来自 Notion 导出但未导入仓库。） 占位文件 3 个（待整理文档-1、待整理文档-2、待整理笔记）仍为空白占位，待后续补充内容。 图片目录中的 Untitled*.png 文件名未修改，保留导出时的原始名称（仅有意义的文件名才值得重命名）。 验证与自查 find content/posts/network -name \u0026quot;*[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]*\u0026quot; 确认已无带哈希残留的文件名 逐篇打开各子文档，确认交叉引用与图片路径无 404（重点：Meta内核/Windows使用Meta内核/ 下的三篇与 GitLab远程订阅教程 图片） 对照 README 文档索引与实际目录结构，确认无遗漏、无多余条目 三个占位文件（待整理文档-1、待整理文档-2、待整理笔记）状态已登记，待后续补充 ","permalink":"https://heihei0299.github.io/posts/network/%E6%95%B4%E7%90%86%E6%8A%A5%E5%91%8A/","summary":"\u003ch1 id=\"科学上网-文档整理报告\"\u003e科学上网 文档整理报告\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e日期\u003c/strong\u003e: 2026-06-13\n\u003cstrong\u003e范围\u003c/strong\u003e: \u003ccode\u003egfw-bypass/\u003c/code\u003e 全部文件（.md 文档、图片资源）\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一整理概况\"\u003e一、整理概况\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e统计项\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e数量\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e整理前文件总数（含图片）\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e62\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e重命名的 .md 文件\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e27\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e重命名的目录\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e5\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e保留原名的文件\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2 (\u003ccode\u003emihomo与tailcale冲突解决方法.md\u003c/code\u003e, \u003ccode\u003e使用sing-box搭建ss via shadowtls.md\u003c/code\u003e)\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e发现的未索引文件\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e3 (\u003ccode\u003eRealm转发教程\u003c/code\u003e, \u003ccode\u003eTelegramBot配置记录\u003c/code\u003e, \u003ccode\u003eacme.sh申请Google公共证书\u003c/code\u003e)\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"二重命名对照表\"\u003e二、重命名对照表\u003c/h2\u003e\n\u003ch3 id=\"顶层-md-文件\"\u003e顶层 .md 文件\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e旧文件名\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e新文件名\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e搭建hy2教程 1c899620...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e搭建Hysteria2教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希，明确协议名\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e搭建naive d60bfec...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e搭建NaiveProxy教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e搭建snell.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e搭建Snell教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e统一命名风格\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMTProxy手动安装最新（ mtg 2 0 ）代理教程 58f63d...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMTProxy安装教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希，简化名称\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e机场搭建 c800a7429...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e机场搭建XBoard教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e补充具体项目名\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esing-box 4890ab3b...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esing-box文档导航.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e明确文件性质（索引导航）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki 85cedb...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Meta内核文档导航.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移入子目录，移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针 328e80b...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针/哪吒探针文档导航.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移入子目录，移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e常用检查网站 7181c2e...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e常用检查网站.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003edd 系统 0c9c14ac...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eDD重装系统教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e明确教程类型\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003edebain 开启bbr c62bc...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eDebian开启BBR教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e修正拼写，移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003evless+vision+reality tcp reality失败解决方法 8ad22...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eVLESS+Vision+Reality TCP Brutal失败解决方法.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e修正术语\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e纯ipv6机器装nezha-agent 59fd0...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e纯IPv6机器安装NezhaAgent.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003egitlab 远程订阅教程 604a0...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eGitLab远程订阅教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003egitlab 订阅教程 e233c...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eGitLab订阅教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eSerenity 学习记录 5a224...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eSerenity学习记录.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003erealm 转发教程 6a0b3...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eRealm转发教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eTG_bot b87b5a...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eTelegramBot配置记录.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希，明确内容\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e111 907f24ab...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eLoon进阶配置示例.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e文件名原为无意义数字，根据内容重命名\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e无标题 38c119...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eacme.sh申请Google公共证书.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e根据实际内容重命名\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUntitled.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e待整理文档-1.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e占位文件，标记待整理\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUntitled (1).md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e待整理文档-2.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e占位文件，标记待整理\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e无标题 87d9c95...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e待整理笔记.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e占位文件，标记待整理\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"子目录及嵌套文件\"\u003e子目录及嵌套文件\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e旧路径\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e新路径\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki 85cedb.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../linux下clash安装 dc862b...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Linux下Clash安装.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../win上使用meta内核 e02203.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../win上使用meta内核 ...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/Windows使用Meta内核.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../启用iis 8da3fb.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/启用IIS/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../启用iis 8da3fb...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/启用IIS.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../为网页文件授权 42bbc5.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/为网页文件授权/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003emeta-wiki.../为网页文件授权 42bbc5...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/为网页文件授权.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针 328e80b.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针.../国内鸡安装哪吒探针 9f37f...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针/国内机器安装哪吒探针.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e修正名称，移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针.../apline 安装哪吒探针 496d4...md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针/Alpine安装哪吒探针.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e修正拼写，移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003egitlab 远程订阅教程 604a0.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eGitLab远程订阅教程/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMTProxy手动安装最新.../\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMTProxy安装教程/\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e移除哈希，与 .md 同名\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"三修复的交叉引用\"\u003e三、修复的交叉引用\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e文件\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e修复内容\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Meta内核文档导航.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新子文档链接（2处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/Windows使用Meta内核.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新图片路径（2处）、子页面链接（2处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/启用IIS.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新图片路径（6处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMeta内核/Windows使用Meta内核/为网页文件授权.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新图片路径（5处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e哪吒探针/哪吒探针文档导航.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新子文档链接（2处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eMTProxy安装教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新图片路径（1处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eGitLab远程订阅教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新图片路径（4处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eGitLab订阅教程.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e更新 GitLab远程订阅教程链接（1处）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eREADME.md\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e全面重写，更新所有链接，补充遗漏文件索引\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"四目录结构整理后\"\u003e四、目录结构（整理后）\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egfw-bypass/\n├── README.md                          # 文档索引\n├── Meta内核/                          # Meta 内核相关文档\n│   ├── Meta内核文档导航.md\n│   ├── Linux下Clash安装.md\n│   └── Windows使用Meta内核/\n│       ├── Windows使用Meta内核.md\n│       ├── 启用IIS.md\n│       ├── 启用IIS/                  # 图片附件\n│       ├── 为网页文件授权.md\n│       └── 为网页文件授权/            # 图片附件\n├── 哪吒探针/                          # 哪吒探针相关文档\n│   ├── 哪吒探针文档导航.md\n│   ├── 国内机器安装哪吒探针.md\n│   └── Alpine安装哪吒探针.md\n├── GitLab远程订阅教程/                # 图片附件\n├── MTProxy安装教程/                   # 图片附件\n├── 搭建Hysteria2教程.md\n├── 搭建NaiveProxy教程.md\n├── 搭建Snell教程.md\n├── 使用sing-box搭建ss via shadowtls.md\n├── MTProxy安装教程.md\n├── 机场搭建XBoard教程.md\n├── sing-box文档导航.md\n├── 常用检查网站.md\n├── DD重装系统教程.md\n├── Debian开启BBR教程.md\n├── VLESS+Vision+Reality TCP Brutal失败解决方法.md\n├── mihomo与tailcale冲突解决方法.md\n├── 纯IPv6机器安装NezhaAgent.md\n├── Realm转发教程.md\n├── GitLab远程订阅教程.md\n├── GitLab订阅教程.md\n├── Serenity学习记录.md\n├── Loon进阶配置示例.md\n├── TelegramBot配置记录.md\n├── acme.sh申请Google公共证书.md\n├── 待整理文档-1.md\n├── 待整理文档-2.md\n└── 待整理笔记.md\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch2 id=\"五已知问题\"\u003e五、已知问题\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003esing-box文档导航.md\u003c/code\u003e\u003c/strong\u003e 中的 3 个子链接指向不存在的文件（\u003ccode\u003esing-box常用命令\u003c/code\u003e、\u003ccode\u003esing-box-linux安装\u003c/code\u003e、\u003ccode\u003ereality网站\u003c/code\u003e），这些文件不在仓库中。\u003cdel\u003e链接保持原样而非删除。\u003c/del\u003e \u003cstrong\u003e（2026-06-13 二次整理：已改为纯文本标注，注明原文来自 Notion 导出但未导入仓库。）\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e占位文件\u003c/strong\u003e 3 个（\u003ccode\u003e待整理文档-1\u003c/code\u003e、\u003ccode\u003e待整理文档-2\u003c/code\u003e、\u003ccode\u003e待整理笔记\u003c/code\u003e）仍为空白占位，待后续补充内容。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e图片目录中的 \u003ccode\u003eUntitled*.png\u003c/code\u003e\u003c/strong\u003e 文件名未修改，保留导出时的原始名称（仅有意义的文件名才值得重命名）。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"验证与自查\"\u003e验证与自查\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003efind content/posts/network -name \u0026quot;*[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]*\u0026quot;\u003c/code\u003e 确认已无带哈希残留的文件名\u003c/li\u003e\n\u003cli\u003e逐篇打开各子文档，确认交叉引用与图片路径无 404（重点：\u003ccode\u003eMeta内核/Windows使用Meta内核/\u003c/code\u003e 下的三篇与 \u003ccode\u003eGitLab远程订阅教程\u003c/code\u003e 图片）\u003c/li\u003e\n\u003cli\u003e对照 README 文档索引与实际目录结构，确认无遗漏、无多余条目\u003c/li\u003e\n\u003cli\u003e三个占位文件（\u003ccode\u003e待整理文档-1\u003c/code\u003e、\u003ccode\u003e待整理文档-2\u003c/code\u003e、\u003ccode\u003e待整理笔记\u003c/code\u003e）状态已登记，待后续补充\u003c/li\u003e\n\u003c/ol\u003e","title":"科学上网 文档整理报告"},{"content":"为网页文件授权 适用场景：IIS 网站页面打开报权限错误（出现\u0026quot;这种情况是没有权限的问题\u0026quot;提示，如 401.3 未经授权访问），需要给网页目录添加读取权限。本文演示把网站目录授权给 Everyone 的完整步骤。\n授权步骤 打开 IIS，我这里添加的网站叫 testReact，选中后右键点击 编辑权限： 切换到 安全选项卡，点击编辑： 点击 添加： 输入 Everyone，点击确定： 选中 Everyone 并勾选权限允许： 验证与自查 在浏览器中刷新网站页面，权限报错消失，页面正常显示 在 IIS 中重启网站后再次访问，确认权限设置已生效 若仍报错，检查父目录是否被上级权限继承设置覆盖；不需要写入的目录只勾选\u0026quot;读取\u0026quot;即可 参考 IIS 官方文档（Microsoft Learn） NTFS 权限概述（Microsoft Learn） ","permalink":"https://heihei0299.github.io/posts/network/meta%E5%86%85%E6%A0%B8/windows%E4%BD%BF%E7%94%A8meta%E5%86%85%E6%A0%B8/%E4%B8%BA%E7%BD%91%E9%A1%B5%E6%96%87%E4%BB%B6%E6%8E%88%E6%9D%83/","summary":"\u003ch1 id=\"为网页文件授权\"\u003e为网页文件授权\u003c/h1\u003e\n\u003cp\u003e适用场景：IIS 网站页面打开报权限错误（出现\u0026quot;这种情况是没有权限的问题\u0026quot;提示，如 401.3 未经授权访问），需要给网页目录添加读取权限。本文演示把网站目录授权给 \u003ccode\u003eEveryone\u003c/code\u003e 的完整步骤。\u003c/p\u003e","title":"为网页文件授权"},{"content":"Alpine 安装哪吒探针 适用场景：在 Alpine Linux（使用 OpenRC 初始化系统）上安装哪吒探针 Agent。你会学到如何下载对应架构的 Agent 可执行文件，并通过 /etc/init.d 脚本把它注册为开机自启服务。\n获取官方教程与安装包 官方教程：\nhttps://nezha.wiki/guide/agent.html#在-其他-linux-如-alpine-使用-openrc-的发行版-安装-agent\n手动安装需要下载对应版本的可执行文件：\nhttps://github.com/naiba/nezha/releases\n手动安装步骤 解压文件到指定目录并赋予执行权限 chmod +x nezha-agent 下载的压缩包需先解压，将 nezha-agent 二进制放到固定目录（如 /opt/nezha/agent/），并确保有执行权限。\n创建开机自启任务 vi /etc/init.d/nezha-agent 内容：\n#!/sbin/openrc-run name=\u0026#34;nezha agent\u0026#34; command=\u0026#34;/opt/nezha/agent/nezha-agent\u0026#34; # 填写你解压出的文件对应的位置 command_background=true command_args=\u0026#34;-s servername:port -p token \u0026#34; # 修改server名，端口和token pidfile=\u0026#34;/run/${RC_SVCNAME}.pid\u0026#34; depend() { need net use dns logger netmount } 启动服务并加入开机自启 chmod +x /etc/init.d/nezha-agent rc-service nezha-agent start rc-update add nezha-agent 验证与自查 rc-service nezha-agent status 显示 started pgrep -a nezha-agent 能看到 Agent 进程与启动参数（检查 -s 地址和 -p token 是否填对） 哪吒 Dashboard 面板中该服务器状态为在线 参考 Nezha 官方文档：Agent 安装（Alpine / OpenRC） Nezha Agent 发布页（GitHub） ","permalink":"https://heihei0299.github.io/posts/network/alpine%E5%AE%89%E8%A3%85%E5%93%AA%E5%90%92%E6%8E%A2%E9%92%88/","summary":"\u003ch1 id=\"alpine-安装哪吒探针\"\u003eAlpine 安装哪吒探针\u003c/h1\u003e\n\u003cp\u003e适用场景：在 Alpine Linux（使用 OpenRC 初始化系统）上安装哪吒探针 Agent。你会学到如何下载对应架构的 Agent 可执行文件，并通过 \u003ccode\u003e/etc/init.d\u003c/code\u003e 脚本把它注册为开机自启服务。\u003c/p\u003e","title":"Alpine 安装哪吒探针"},{"content":"coverview 博客文章封面部署 本文适用于需要为博客文章批量生成封面的场景。你会学到：用 Coverview 搭建一个本地封面生成服务，并用 pm2 持久化运行、nginx 反向代理对外提供访问。\n因为 Typecho-Butterfly 主题需要文章封面，我就选择了这个快速生成文章封面的项目 Coverview。\n项目介绍 Coverview 是一个开源的封面生成工具，特性包括：\n🚀 快速且易用 🌈 4 种主题、多种字体 🌠 100+ 开发者图标，支持上传自定义图标 ✨ 15+ 背景图案 💾 按博客平台尺寸生成封面（如 hashnode、dev） 安装 Node.js 环境 项目基于 Node.js 运行，先安装 npm 环境。参考 nodesource 安装文档。\n安装前确保 curl 已安装：\napt-get install -y curl 下载安装脚本\ncurl -fsSL https://deb.nodesource.com/setup_current.x -o nodesource_setup.sh 运行安装脚本\nbash nodesource_setup.sh 安装 Node.js\napt-get install -y nodejs 验证安装\nnode -v 下载 Coverview mkdir -p /opt/npm/Coverview \u0026amp;\u0026amp; cd /opt/npm/Coverview git clone https://github.com/rutikwankhade/CoverView.git 持久化运行 Coverview 用 pm2 将服务常驻后台：\nnpm install -g pm2 pm2 start npm --name \u0026#34;coverview\u0026#34; -- start 查看运行状态：\npm2 list nginx 反代 nginx 反代配置文件 Coverview 默认监听 127.0.0.1:3000，通过 nginx 反代对外提供服务：\nserver { listen 30001; server_name yourname.com; #access_log /var/log/nginx/host.access.log main; location / { proxy_pass http://127.0.0.1:3000/; #coverview 监听 127.0.0.1:3000 proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; } } 测试并重载配置：\nnginx -t nginx -s reload 验证与自查 pm2 list 中 coverview 进程状态为 online curl http://127.0.0.1:3000 返回页面内容 curl -H \u0026quot;Host: yourname.com\u0026quot; http://127.0.0.1:30001 通过域名端口可访问 浏览器打开封面生成页，确认主题、图标、背景均可正常渲染 注意：新部署的 coverview 需要使用 nginx 反代。如果直接使用 cloudflared 将 coverview 映射到公网，会导致访问失败。\n参考 Coverview GitHub 仓库 nodesource 安装文档 pm2 官方文档 ","permalink":"https://heihei0299.github.io/posts/blog/coverview-%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0%E5%B0%81%E9%9D%A2/","summary":"\u003ch1 id=\"coverview-博客文章封面部署\"\u003ecoverview 博客文章封面部署\u003c/h1\u003e\n\u003cp\u003e本文适用于需要为博客文章批量生成封面的场景。你会学到：用 \u003ca href=\"https://github.com/rutikwankhade/CoverView\"\u003eCoverview\u003c/a\u003e 搭建一个本地封面生成服务，并用 pm2 持久化运行、nginx 反向代理对外提供访问。\u003c/p\u003e","title":"coverview 博客文章封面部署"},{"content":"DD 重装系统教程 适用场景：VPS 商家提供的系统镜像不干净或版本不合需求时，用 MoeClub DD 脚本一键重装为官方纯净系统。你会学到 CentOS / Debian / Ubuntu 各版本的重装命令、注意事项与重装后的安全处理。\n原文参考：MoeClub DD 脚本\n注意事项 Vicer 脚本目前不支持重装为 CentOS 7。 重装系统来源于官方发行版。 安装过程全自动进行，一般无需 VNC 或救援模式手动介入。 系统安装完成后的默认用户名为 root。 默认密码为 MoeClub.org，安装完成后应立即修改。 常用命令 重装为 CentOS # CentOS 6.10 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -c 6.10 -v 64 -a # CentOS 6.10 32 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -c 6.10 -v 32 -a 重装为 Debian # Debian 8 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -d 8 -v 64 -a # Debian 9 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -d 9 -v 64 -a # Debian 10 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -d 10 -v 64 -a # Debian 11 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -d 11 -v 64 -a # Debian 12 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -d 12 -v 64 -a 重装为 Ubuntu # Ubuntu 12.04 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -u 12.04 -v 64 -a # Ubuntu 14.04 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -u 14.04 -v 64 -a # Ubuntu 16.04 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -u 16.04 -v 64 -a # Ubuntu 18.04 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -u 18.04 -v 64 -a # Ubuntu 20.04 64 位 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -u 20.04 -v 64 -a 安装过程说明 运行命令后，系统会自动开始重装。正常情况下大约 10 分钟左右可以完成，期间可通过 VNC 观察进度。\n常见情况如下：\n如果尝试安装不受支持的 CentOS 7，会直接提示不支持。 如果输入了不存在或不支持的系统版本，脚本会中止执行。 重装后修改 root 密码 出于安全考虑，安装完成后建议立即修改默认密码：\npasswd root 然后按提示输入两次新密码即可。某些终端在输入密码时不会显示字符，这是正常现象。\n验证与自查 重启后用 cat /etc/os-release 确认系统版本与预期一致 用默认凭据（root / MoeClub.org）登录后，立即执行 passwd root 修改密码 uname -m 确认架构为 64/32 位与所选一致 检查磁盘与网络状态：lsblk、ip addr 参考 MoeClub DD 脚本（moeelf.com） MoeClub/Note：InstallNET.sh 脚本仓库（GitHub） ","permalink":"https://heihei0299.github.io/posts/network/dd%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"dd-重装系统教程\"\u003eDD 重装系统教程\u003c/h1\u003e\n\u003cp\u003e适用场景：VPS 商家提供的系统镜像不干净或版本不合需求时，用 MoeClub DD 脚本一键重装为官方纯净系统。你会学到 CentOS / Debian / Ubuntu 各版本的重装命令、注意事项与重装后的安全处理。\u003c/p\u003e","title":"DD 重装系统教程"},{"content":"Debian 12 非桌面环境字体安装指南 本文适合 Debian 12 最小化安装、服务器或无桌面环境用户。你会学到：如何安装字体管理核心工具、常见字体包、手动放置字体文件，以及用 fc-list/fc-match 验证字体是否生效。\n概述 本指南详细介绍了在 Debian 12 非桌面环境（最小化安装、服务器环境、命令行界面）中安装和管理字体的完整流程。\n完整安装流程 1. 基础工具安装（必需） # 安装字体管理核心工具 sudo apt install fontconfig sudo apt install fonts-dejavu-core # 基础字体包 2. 常用字体包安装（推荐） 基础组合（轻量）\nsudo apt install fonts-dejavu \\ fonts-liberation \\ fonts-noto-mono 完整组合（含中文字体）\nsudo apt install fonts-dejavu \\ fonts-liberation \\ fonts-noto \\ fonts-noto-cjk \\ fonts-wqy-microhei \\ fonts-wqy-zenhei 微软字体\nsudo apt install ttf-mscorefonts-installer 3. 手动安装字体文件 # 创建字体目录 sudo mkdir -p /usr/local/share/fonts/custom # 复制字体文件 sudo cp /path/to/fonts/*.ttf /usr/local/share/fonts/custom/ sudo cp /path/to/fonts/*.otf /usr/local/share/fonts/custom/ # 设置权限 sudo chmod 644 /usr/local/share/fonts/custom/* # 更新字体缓存 sudo fc-cache -f -v 关键命令参考 字体管理命令 命令 功能 示例 fc-cache 更新字体缓存 sudo fc-cache -f -v fc-list 列出已安装字体 fc-list : family fc-match 匹配字体 fc-match \u0026quot;Arial\u0026quot; fc-query 查询字体信息 fc-query /path/to/font.ttf 验证命令 # 检查安装 which fc-list fc-list : family | head -20 # 统计字体数量 fc-list : file | wc -l # 测试中文字体 fc-list : family | grep -i \u0026#34;noto\\|wenquanyi\\|microhei\u0026#34; 目录结构说明 系统字体目录 /usr/share/fonts/ # 系统预装字体 /usr/local/share/fonts/ # 用户安装字体（推荐） 用户字体目录 ~/.fonts/ # 当前用户专用字体 ~/.local/share/fonts/ # 用户本地字体（某些应用） 故障排除 常见问题及解决方案 1. 命令未找到\n# 重新安装fontconfig sudo apt install --reinstall fontconfig 2. 字体不生效\n# 强制重建字体缓存 sudo fc-cache -rf 3. 权限问题\n# 修复目录权限 sudo chmod 755 /usr/local/share/fonts/ sudo chmod 644 /usr/local/share/fonts/*/* 4. 特定应用不识别字体\n# 重启应用服务 sudo systemctl restart application.service # 或重新登录用户会话 logout 缓存清理 # 清理字体缓存 sudo fc-cache --clean # 查看缓存统计 fc-cache --stat 验证与自查 fc-list : family | grep -i \u0026quot;noto\\|wenquanyi\\|microhei\u0026quot; 能输出中文字体族名 fc-match \u0026quot;Arial\u0026quot; 能匹配到替代字体文件 fc-list : file | wc -l 统计的字体数量在安装后明显增加 在命令行/终端应用中中文字符不再显示为方块（豆腐块） 总结要点 必需步骤: 先安装 fontconfig，再安装字体 目录选择: 系统级字体使用 /usr/local/share/fonts/，用户级使用 ~/.fonts/ 缓存更新: 安装字体后必须运行 sudo fc-cache -f -v 中文字体: 推荐 fonts-noto-cjk + fonts-wqy-microhei 组合 验证方法: 使用 fc-list 和 fc-match 命令验证安装 参考 Debian 字体 Wiki Fontconfig 文档 Noto 字体项目 文泉驿字体 ","permalink":"https://heihei0299.github.io/posts/ops/debian-12-%E9%9D%9E%E6%A1%8C%E9%9D%A2%E7%8E%AF%E5%A2%83%E5%AD%97%E4%BD%93%E5%AE%89%E8%A3%85%E6%8C%87%E5%8D%97/","summary":"\u003ch1 id=\"debian-12-非桌面环境字体安装指南\"\u003eDebian 12 非桌面环境字体安装指南\u003c/h1\u003e\n\u003cp\u003e本文适合 Debian 12 最小化安装、服务器或无桌面环境用户。你会学到：如何安装字体管理核心工具、常见字体包、手动放置字体文件，以及用 \u003ccode\u003efc-list\u003c/code\u003e/\u003ccode\u003efc-match\u003c/code\u003e 验证字体是否生效。\u003c/p\u003e","title":"Debian 12 非桌面环境字体安装指南"},{"content":"Debian 开启 BBR 适用场景：需要提升服务器在高延迟、高丢包网络下的吞吐性能（如跨境线路）。本文记录在 Debian 系统中启用 BBR 的最简步骤，全程只需三条命令加一次验证。\n检查内核版本 建议使用 root 权限执行，内核版本一般需要高于 4.9：\nuname -r 启用 BBR 将以下内容追加到 /etc/sysctl.conf：\necho \u0026#34;net.core.default_qdisc=fq\u0026#34; \u0026gt;\u0026gt; /etc/sysctl.conf echo \u0026#34;net.ipv4.tcp_congestion_control=bbr\u0026#34; \u0026gt;\u0026gt; /etc/sysctl.conf 使配置生效 sysctl -p 验证是否开启成功 执行以下命令，如果输出中包含 bbr，说明已启用：\nsysctl net.ipv4.tcp_available_congestion_control 也可以再执行一次模块检查：\nlsmod | grep bbr 验证与自查 sysctl net.ipv4.tcp_congestion_control 输出 bbr sysctl net.core.default_qdisc 输出 fq lsmod | grep bbr 能看到 tcp_bbr 模块 重启后再次执行 sysctl net.ipv4.tcp_congestion_control，仍为 bbr（配置已持久化到 /etc/sysctl.conf） 参考 Google BBR（GitHub） Linux 内核文档：TCP BBR ","permalink":"https://heihei0299.github.io/posts/network/debian%E5%BC%80%E5%90%AFbbr%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"debian-开启-bbr\"\u003eDebian 开启 BBR\u003c/h1\u003e\n\u003cp\u003e适用场景：需要提升服务器在高延迟、高丢包网络下的吞吐性能（如跨境线路）。本文记录在 Debian 系统中启用 BBR 的最简步骤，全程只需三条命令加一次验证。\u003c/p\u003e","title":"Debian 开启 BBR"},{"content":"dstatus agent 安装 本文适合需要为 dstatus 监控面板部署客户端 agent 的服务器管理员。你会学到：下载 neko-status 二进制、写入配置文件、注册 systemd 服务并开机自启。\n下载 agent wget https://github.com/fev125/dstatus/releases/download/v1.1/neko-status_linux_amd64 安装到系统目录 mv neko-status_linux_amd64 neko-status chmod +x neko-status mv neko-status /usr/bin/ 编写配置文件 mkdir /etc/neko-status/ nano /etc/neko-status/config.yaml 写入以下内容（key 替换为面板分配的密钥）：\nkey: 密钥 port: 9999 debug: false 创建 systemd 服务 nano /etc/systemd/system/nekonekostatus.service [Unit] Description=DStatus客户端服务 After=network.target [Service] Restart=always RestartSec=5 ExecStart=/usr/bin/neko-status -c /etc/neko-status/config.yaml [Install] WantedBy=multi-user.target 启动服务 systemctl daemon-reload systemctl start nekonekostatus.service systemctl status nekonekostatus.service 验证与自查 systemctl status nekonekostatus.service 显示 active (running) 且无报错 检查监听端口：ss -tlnp | grep 9999 能看到 neko-status 进程 在 dstatus 面板上能看到该节点上报状态（密钥与面板配置一致） 重启服务器后服务自动拉起（systemctl is-enabled nekonekostatus.service 返回 enabled） 参考 dstatus 官方仓库 dstatus Releases 下载页 systemd.service 手册 ","permalink":"https://heihei0299.github.io/posts/ops/dstatus-agent-%E5%AE%89%E8%A3%85/","summary":"\u003ch1 id=\"dstatus-agent-安装\"\u003edstatus agent 安装\u003c/h1\u003e\n\u003cp\u003e本文适合需要为 dstatus 监控面板部署客户端 agent 的服务器管理员。你会学到：下载 neko-status 二进制、写入配置文件、注册 systemd 服务并开机自启。\u003c/p\u003e\n\u003ch2 id=\"下载-agent\"\u003e下载 agent\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ewget https://github.com/fev125/dstatus/releases/download/v1.1/neko-status_linux_amd64\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"安装到系统目录\"\u003e安装到系统目录\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emv neko-status_linux_amd64  neko-status\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003echmod +x neko-status \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emv neko-status /usr/bin/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"编写配置文件\"\u003e编写配置文件\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir  /etc/neko-status/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003enano  /etc/neko-status/config.yaml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e写入以下内容（\u003ccode\u003ekey\u003c/code\u003e 替换为面板分配的密钥）：\u003c/p\u003e","title":"dstatus agent 安装"},{"content":"GitLab 远程订阅教程 适用场景：把代理订阅或静态配置文件托管在 GitLab 私有仓库，通过带访问令牌的仓库直链生成一个可远程读取的订阅地址，供代理客户端定时拉取。你会学到令牌创建、URL 拼接与安全注意事项。\n操作步骤 进入目标 GitLab 项目。 打开 Settings \u0026gt; Access Tokens。 进入访问令牌页面，准备创建新令牌。 点击右上角“创建令牌”，权限按下图或按你的读取需求设置。 创建完成后立即复制令牌。这个令牌通常只会展示一次。 订阅地址格式 将以下模板中的占位符替换为你的实际值：\nhttps://gitlab.com/api/v4/projects/用户名称%2F项目名称/repository/files/文件名称/raw?ref=main\u0026amp;private_token=访问令牌 参数说明 用户名称%2F项目名称：项目路径，斜杠需要编码为 %2F。 文件名称：仓库中的目标文件名。 ref=main：目标分支名称，如果你的默认分支不是 main，需要改成对应分支。 private_token：刚创建的访问令牌。 注意事项 令牌泄露后，任何拿到链接的人都可能读取对应文件。 如果只是公开分享静态订阅，优先考虑使用公开仓库或中转服务，避免直接暴露私有令牌。 验证与自查 用 curl -I \u0026quot;\u0026lt;你的订阅地址\u0026gt;\u0026quot; 测试，返回 200 OK 即直链有效 将订阅地址粘贴到代理客户端（Clash / Surge / sing-box 等），能成功拉取到节点列表 用无痕窗口打开订阅链接，确认内容与仓库中的文件一致；分享前检查 URL 中没有多余字符 参考 GitLab 个人访问令牌文档 GitLab Repository files API ","permalink":"https://heihei0299.github.io/posts/network/gitlab%E8%BF%9C%E7%A8%8B%E8%AE%A2%E9%98%85%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"gitlab-远程订阅教程\"\u003eGitLab 远程订阅教程\u003c/h1\u003e\n\u003cp\u003e适用场景：把代理订阅或静态配置文件托管在 GitLab 私有仓库，通过带访问令牌的仓库直链生成一个可远程读取的订阅地址，供代理客户端定时拉取。你会学到令牌创建、URL 拼接与安全注意事项。\u003c/p\u003e","title":"GitLab 远程订阅教程"},{"content":"Linux Mint 修复引导 适用场景：Linux Mint（或其他基于 Ubuntu/Debian 的发行版）因引导器损坏、Windows 更新覆盖引导、误删 EFI 文件而无法开机。你会学到从 Live 环境挂载分区、chroot、重装 GRUB 的标准流程，以及用 Boot-Repair 工具自动修复的简易方案。\n核心思路 启动 Live 环境：从一个可启动的 U 盘或光盘进入一个临时的 Linux 系统。 挂载系统分区：将你硬盘上损坏的 Linux 系统的根分区（/）和其他必要分区（如 /boot/efi）挂载到 Live 环境中。 Chroot：将 Live 环境的根目录\u0026quot;切换\u0026quot;到你硬盘上的系统根目录，这样后续的命令就好像是在你自己的系统里执行一样。 修复 GRUB：在 chroot 环境中，重新安装和配置 GRUB。 清理并重启：退出 chroot，卸载分区，然后重启电脑。 准备工作 你需要一个 Linux Live CD 或 USB。推荐使用Ubuntu的安装盘，因为它工具齐全，用户量大，方便查找资料。\n步骤一：从 Live CD/USB 启动 将制作好的 Live U 盘插入电脑。 开机并进入 BIOS/UEFI 设置，选择从 U 盘启动。 选择 \u0026ldquo;Try Ubuntu\u0026rdquo; (或类似的 \u0026ldquo;Live\u0026rdquo; 选项)，进入桌面环境。 步骤二：确定并挂载分区 打开一个终端（Terminal）。 找出你的 Linux 系统安装在哪个分区。使用 lsblk 或 sudo fdisk -l 命令查看。 sudo lsblk 你可能会看到类似这样的输出：\nNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 238.5G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi \u0026lt;-- EFI分区 (如果是UEFI模式) ├─sda2 8:2 0 1K 0 part └─sda5 8:5 0 238G 0 part / \u0026lt;-- Linux根分区 sdb 8:16 1 14.9G 0 disk └─sdb1 8:17 1 14.9G 0 part /cdrom 在这个例子中，Linux 根分区是 /dev/sda5，EFI 分区是 /dev/sda1。请根据你自己的实际情况替换设备名。\n步骤三：挂载系统（分两种情况） 情况 A：UEFI 模式 如果你的系统是 UEFI 模式（有 EFI 分区），执行以下命令：\n挂载根分区 (/)： # 将 /dev/sda5 替换成你的根分区 sudo mount /dev/sda5 /mnt 挂载 EFI 分区 (/boot/efi)： # 将 /dev/sda1 替换成你的EFI分区 sudo mount /dev/sda1 /mnt/boot/efi 绑定系统运行所需目录： sudo mount --bind /dev /mnt/dev sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /dev/pts /mnt/dev/pts 情况 B：Legacy BIOS 模式 如果你的系统是传统的 Legacy BIOS 模式（没有单独的 EFI 分区），步骤会简单一些：\n挂载根分区 (/)： # 将 /dev/sda5 替换成你的根分区 sudo mount /dev/sda5 /mnt 如果你的 /boot 是一个独立分区（例如 /dev/sda1），那么也需要挂载它：\n# sudo mount /dev/sda1 /mnt/boot 绑定系统运行所需目录： sudo mount --bind /dev /mnt/dev sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys 步骤四：Chroot 并修复 GRUB 进入 Chroot 环境： sudo chroot /mnt 执行后，你的终端提示符可能会改变，现在你所有的命令都将在你硬盘上的系统中执行。\n重新安装 GRUB（再次分两种情况）： 情况 A：UEFI 模式 # 确认 /boot/efi 目录存在且有内容 ls /boot/efi # 重新安装GRUB到EFI分区 # \u0026lt;distro_name\u0026gt; 通常是 ubuntu, debian, arch 等，它会在EFI启动项里创建一个同名文件夹 grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu 情况 B：Legacy BIOS 模式 # 将GRUB安装到硬盘的主引导记录(MBR) # 注意：这里是整个硬盘设备名 (如 /dev/sda)，而不是分区名 (如 /dev/sda5) grub-install /dev/sda 更新 GRUB 配置文件： 无论哪种模式，最后都需要运行这个命令来生成 grub.cfg 文件，它会扫描你系统里安装的内核和其他操作系统（如 Windows）。\nupdate-grub 或者使用完整命令：\ngrub-mkconfig -o /boot/grub/grub.cfg 步骤五：清理并重启 退出 Chroot 环境： exit 卸载所有分区： 按挂载的相反顺序卸载，非常重要！\n# 如果是UEFI模式，先卸载这个 sudo umount /mnt/boot/efi # 卸载其他绑定的目录 sudo umount /mnt/dev/pts sudo umount /mnt/dev sudo umount /mnt/proc sudo umount /mnt/sys # 最后卸载根分区 sudo umount /mnt 重启电脑： reboot 重启前记得拔掉 Live U 盘。你的电脑现在应该能正常显示 GRUB 菜单并启动了。\n简易方案：使用 Boot-Repair 工具 如果你觉得手动操作太复杂，可以使用一个名为 Boot-Repair 的图形化工具，它能自动完成大部分修复工作。\n同样从 Ubuntu Live CD/USB 启动并连接到互联网。 打开终端，输入以下命令来安装 Boot-Repair： sudo add-apt-repository ppa:yannubuntu/boot-repair sudo apt-get update sudo apt-get install -y boot-repair 启动 Boot-Repair： boot-repair 打开后，它会扫描你的系统。通常你只需要点击 \u0026ldquo;Recommended repair\u0026rdquo;（推荐修复） 按钮，然后等待它完成即可。 完成后，按照提示重启电脑。 这个工具非常适合新手，成功率也很高。\n验证与自查 ls /boot/efi/EFI 能看到 ubuntu（或对应发行版名）目录，且其中有 grubx64.efi sudo efibootmgr 输出中存在对应发行版的引导项（UEFI 模式） update-grub 输出包含 Found linux image 以及 Found Windows Boot Manager（若有 Windows） 重启后 GRUB 菜单正常显示，可正常进入 Linux 参考 ArchWiki - GRUB Ubuntu Community - Grub2 修复 Ubuntu Community - Boot-Repair ","permalink":"https://heihei0299.github.io/posts/linux/linuxmint%E4%BF%AE%E5%A4%8D%E5%BC%95%E5%AF%BC/","summary":"\u003ch1 id=\"linux-mint-修复引导\"\u003eLinux Mint 修复引导\u003c/h1\u003e\n\u003cp\u003e适用场景：Linux Mint（或其他基于 Ubuntu/Debian 的发行版）因引导器损坏、Windows 更新覆盖引导、误删 EFI 文件而无法开机。你会学到从 Live 环境挂载分区、chroot、重装 GRUB 的标准流程，以及用 Boot-Repair 工具自动修复的简易方案。\u003c/p\u003e","title":"Linux Mint 修复引导"},{"content":"Linux 部署 OpenClaw 本文教你从零在 Linux 上部署 OpenClaw：先做环境准备（中科大源、Node.js、git、npm 国内镜像），再安装并初始化 openclaw。命令在 Debian / Ubuntu / Fedora 下均可使用。\n环境准备 切换为中科大源 Debian sudo sed -i \u0026#39;s/deb.debian.org/mirrors.ustc.edu.cn/g\u0026#39; /etc/apt/sources.list Ubuntu sudo sed -i \u0026#39;s@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g\u0026#39; /etc/apt/sources.list 安装 Node.js（fnm） 推荐用 fnm（Fast Node Manager）管理 Node.js 版本：\n# Download and install fnm: curl -o- https://fnm.vercel.app/install | bash # Download and install Node.js: fnm install 24 # Verify the Node.js version: node -v # Should print \u0026#34;v24.14.0\u0026#34;. # Verify npm version: npm -v # Should print \u0026#34;11.9.0\u0026#34;. 安装 git # Debian/Ubuntu # For the latest stable version for your release of Debian/Ubuntu # apt-get install git # For Ubuntu, this PPA provides the latest stable upstream Git version # add-apt-repository ppa:git-core/ppa # apt update; apt install git # Fedora # yum install git (up to Fedora 21) # dnf install git (Fedora 22 and later) npm 切换为国内镜像 1. 淘宝源（最新） 这是目前最常用的源。\n设置方法：\nnpm config set registry https://registry.npmmirror.com 检查是否配置成功：\nnpm config get registry 其他国内镜像源\n腾讯云：https://mirrors.cloud.tencent.com/npm/ 中科大：https://mirrors.ustc.edu.cn/npm/ 清华大学：https://mirrors.tuna.tsinghua.edu.cn/npm/ 恢复官方源\n如果需要切回官方源，执行：\nnpm config set registry https://registry.npmjs.org/ 2. 推荐的安装方式：cnpm 可以安装 cnpm 专门用于国内下载。\nnpm install -g cnpm --registry=https://registry.npmmirror.com # 之后使用 cnpm install 代替 npm install 3. 配置成功检查 执行以下命令，若输出 https://registry.npmmirror.com/ 则说明配置成功：\nnpm config get registry 环境验证 node -v npm -v git -v 三个命令均能正常输出版本号，说明环境准备完成。\n安装 OpenClaw npm i -g openclaw openclaw onboard openclaw onboard 是交互式配置向导，按提示选择模型、设置网关即可完成初始化。\n验证部署 openclaw --version # 输出版本号即安装成功 openclaw gateway # 网关正常启动，浏览器访问 http://127.0.0.1:18789/ 可打开控制台 参考 OpenClaw 官方文档 OpenClaw GitHub 仓库 fnm - GitHub npmmirror 国内镜像源 ","permalink":"https://heihei0299.github.io/posts/ai/openclaw-linux-deploy/","summary":"\u003ch1 id=\"linux-部署-openclaw\"\u003eLinux 部署 OpenClaw\u003c/h1\u003e\n\u003cp\u003e本文教你从零在 Linux 上部署 OpenClaw：先做环境准备（中科大源、Node.js、git、npm 国内镜像），再安装并初始化 openclaw。命令在 Debian / Ubuntu / Fedora 下均可使用。\u003c/p\u003e","title":"Linux 部署 OpenClaw"},{"content":"Linux 下 Clash 安装与配置 适用场景：在 Linux 服务器上部署 Clash 作为代理出口，支持订阅导入、系统代理、Web 面板与每周自动更新订阅。本文命令在 X86_64 平台下 CentOS 7 验证（对应 clash-linux-amd64 包）。\n注意：本文使用经典 Clash（Dreamacro/clash v1.6.5）。该仓库已归档，社区维护分支为 mihomo（MetaCubeX），配置方式兼容。\n安装 Clash 1. 下载当前操作系统与 CPU 架构对应的包文件 我这儿是 X86_64 平台下 CentOS 7，所以对应使用 clash-linux-amd64-v1.6.5.gz 包即可：\nwget -O clash.gz https://github.com/Dreamacro/clash/releases/download/v1.6.5/clash-linux-amd64-v1.6.5.gz 2. 下载好后解压安装包中 clash 到 /usr/local/bin/ 目录下，并删除压缩包文件 gzip -dc clash.gz \u0026gt; /usr/local/bin/clash chmod +x /usr/local/bin/clash rm -f clash.gz 3. 创建配置文件目录，并下载 MMDB 文件 mkdir /etc/clash wget -O /etc/clash/Country.mmdb https://github.com/Dreamacro/maxmind-geoip/releases/download/20220812/Country.mmdb 4. 创建 systemd 脚本，脚本文件路径为 /etc/systemd/system/clash.service，内容如下： [Unit] Description=clash daemon [Service] Type=simple User=root ExecStart=/usr/local/bin/clash -d /etc/clash Restart=on-failure [Install] WantedBy=multi-user.target 5. 重载 systemctl daemon systemctl daemon-reload 配置代理上网 1. 导入已有的科学上网 订阅链接 wget -O /etc/clash/config.yaml [你的订阅链接] 2. 设置系统代理，添加配置文件 /etc/profile.d/proxy.sh 并在其中写入如下内容： export http_proxy=\u0026#34;127.0.0.1:7890\u0026#34; export https_proxy=\u0026#34;127.0.0.1:7890\u0026#34; export no_proxy=\u0026#34;localhost, 127.0.0.1\u0026#34; 3. 重载 /etc/profile 配置 source /etc/profile 4. 启动 clash 服务，并设置为开机自动启动 systemctl start clash systemctl enable clash 5. 测试 google.com 访问 # curl google.com \u0026lt;HTML\u0026gt;\u0026lt;HEAD\u0026gt;\u0026lt;meta http-equiv=\u0026#34;content-type\u0026#34; content=\u0026#34;text/html;charset=utf-8\u0026#34;\u0026gt;\u0026lt;TITLE\u0026gt;301 Moved\u0026lt;/TITLE\u0026gt;\u0026lt;/HEAD\u0026gt;\u0026lt;BODY\u0026gt;\u0026lt;H1\u0026gt;301 Moved\u0026lt;/H1\u0026gt;The document has moved\u0026lt;A HREF=\u0026#34;http://www.google.com/\u0026#34;\u0026gt;here\u0026lt;/A\u0026gt;.\u0026lt;/BODY\u0026gt;\u0026lt;/HTML\u0026gt; 配置 Web UI 1. 克隆 clash-dashboard 项目到本地 git clone -b gh-pages --depth 1 https://github.com/Dreamacro/clash-dashboard /opt/clash-dashboard 2. 修改 clash 配置文件中 external-ui 的值为 /opt/clash-dashboard sed -i \u0026#34;s/^#\\{0,1\\} \\{0,1\\}external-ui.*/external-ui: \\/opt\\/clash-dashboard/\u0026#34; /etc/clash/config.yaml 3. 重启 clash 服务 systemctl restart clash 4. 通过浏览器访问 localhost:9090/ui，其中 localhost 替换为 clash 部署服务器的 IP 配置定时更新订阅 使用如下脚本填写相关配置项目并放入 /etc/cron.weekly 目录下，每周自动更新订阅配置文件即可：\n#!/usr/bin/env bash # 订阅链接地址 SUBSCRIBE=\u0026#34;\u0026#34; # web-ui存放目录，留空则保持默认不修改 WEB_UI=\u0026#34;\u0026#34; # API 端口，留空则保持默认不修改 CONTROLLER_API_PROT=\u0026#34;\u0026#34; # API 口令，留空则保持默认不修改 SECRET=\u0026#34;\u0026#34; CLASH_CONFIG=\u0026#34;/etc/clash/config.yaml\u0026#34; if [ -z \u0026#34;${SUBSCRIBE}\u0026#34; ]; then echo \u0026#34;Subscription address cannot be empty\u0026#34; exit 1 fi systemctl stop clash wget --no-proxy -O ${CLASH_CONFIG} ${SUBSCRIBE} if [ -n \u0026#34;${WEB_UI}\u0026#34; ]; then sed -i \u0026#34;s?^#\\{0,1\\} \\{0,1\\}external-ui.*?external-ui: ${WEB_UI}?\u0026#34; ${CLASH_CONFIG} fi if [ -n \u0026#34;${CONTROLLER_API_PROT}\u0026#34; ]; then sed -i \u0026#34;s?^external-controller.*?external-controller: \u0026#39;0.0.0.0:${CONTROLLER_API_PROT}\u0026#39;?\u0026#34; ${CLASH_CONFIG} fi if [ -n \u0026#34;${SECRET}\u0026#34; ]; then sed -i \u0026#34;s?^secret.*?secret: \u0026#39;${SECRET}\u0026#39;?\u0026#34; ${CLASH_CONFIG} fi systemctl start clash 上述脚本写入 /etc/cron.weekly/clash.sh 并配置好相关变量后，保存退出并赋予可执行权限：\nchmod 0755 /etc/cron.weekly/clash.sh 验证与自查 systemctl status clash 显示 active（running） curl -x http://127.0.0.1:7890 https://www.google.com -I 返回 200 浏览器打开 http://\u0026lt;服务器IP\u0026gt;:9090/ui 能显示 Web 面板，且能看到节点列表 检查订阅自动更新：ls -l /etc/cron.weekly/clash.sh 有执行权限，手动执行一次后 journalctl -u clash -e 无报错 参考 Dreamacro/clash 发布页（GitHub） clash-dashboard（GitHub） mihomo（Clash 社区维护分支） ","permalink":"https://heihei0299.github.io/posts/network/linux%E4%B8%8Bclash%E5%AE%89%E8%A3%85/","summary":"\u003ch1 id=\"linux-下-clash-安装与配置\"\u003eLinux 下 Clash 安装与配置\u003c/h1\u003e\n\u003cp\u003e适用场景：在 Linux 服务器上部署 Clash 作为代理出口，支持订阅导入、系统代理、Web 面板与每周自动更新订阅。本文命令在 X86_64 平台下 CentOS 7 验证（对应 \u003ccode\u003eclash-linux-amd64\u003c/code\u003e 包）。\u003c/p\u003e","title":"Linux 下 Clash 安装与配置"},{"content":"linux下禁止普通用户通过 sudo su 或 su root 切换到root用户 本文适合需要对服务器做提权管控的运维人员。你会学到：为什么只加 wheel 组限制不够，以及如何用两步配置同时封死 sudo su - 和 su - root 两条提权路径。\n网上搜出来的很多方法，都是说加那个什么 wheel 组的就行，不加的就不行。但那样仅仅只能禁止 su - root 切换，并不能禁止通过 sudo su -进行切换。\n步骤一：限制 su 提权（PAM 与 login.defs） 修改 /etc/pam.d/su 配置：\n打开这个配置文件，找到如下行，并将行首 \u0026ldquo;#\u0026rdquo; 去掉，保存文件\nauth required pam_wheel.so use_uid 修改 /etc/login.defs 文件：\nvi /etc/login.defs #在文件末尾添加 SU_WHEEL_ONLY yes 保存文件 步骤二：限制 sudo 调用 su（sudoers） vim /etc/sudoers 添加：\n## Allow root to run any commands anywhere mccok ALL=(ALL) ALL,!/bin/su 两个步骤做完，则可以实现标题所说目的。\n两种做法的效果对比 如果只做了第一步，则还是可以通过 sudo su - 或 sudo su 切换到 root 如果只做了第二步，则还是可以通过 su - root 切换到 root 放行指定用户 如果想要只允许某用户可以使用 su 切换到 root 上。\n则将该用户添加到 wheel 组即可\nusermod -g wheel mccok 验证与自查 普通用户执行 su - root 被拒绝（提示密码错误或权限不足） 普通用户执行 sudo su - 被拒绝（sudoers 规则生效） 普通用户执行 sudo -i 仍可用（未被限制） wheel 组内用户执行 su - root 可以正常切换 参考 ArchWiki: sudo 配置 pam_wheel 手册 sudoers(5) 手册 login.defs(5) 手册（SU_WHEEL_ONLY） ","permalink":"https://heihei0299.github.io/posts/ops/linux%E4%B8%8B%E7%A6%81%E6%AD%A2%E6%99%AE%E9%80%9A%E7%94%A8%E6%88%B7%E9%80%9A%E8%BF%87-sudo-su-%E6%88%96-su-root-%E5%88%87%E6%8D%A2%E5%88%B0root%E7%94%A8%E6%88%B7/","summary":"\u003ch1 id=\"linux下禁止普通用户通过-sudo-su-或-su-root-切换到root用户\"\u003elinux下禁止普通用户通过 sudo su 或 su root 切换到root用户\u003c/h1\u003e\n\u003cp\u003e本文适合需要对服务器做提权管控的运维人员。你会学到：为什么只加 wheel 组限制不够，以及如何用两步配置同时封死 \u003ccode\u003esudo su -\u003c/code\u003e 和 \u003ccode\u003esu - root\u003c/code\u003e 两条提权路径。\u003c/p\u003e","title":"linux下禁止普通用户通过 sudo su 或 su root 切换到root用户"},{"content":"macOS 部署 OpenClaw 本文教你从零在 macOS 上部署 OpenClaw：硬件与系统要求、Homebrew、nvm + Node.js、国内镜像源、git，最后安装并初始化 openclaw。\n硬件要求 CPU：M 系列芯片或 Intel i5 以上 内存：8GB 以上（推荐 16GB） 硬盘：10GB 以上空闲空间 系统版本：\nmacOS 12 Monterey 或更高版本 推荐 macOS 14 Sonoma 或 macOS 15 Sequoia 环境准备 安装 Homebrew 国内推荐使用 HomebrewCN 脚本（安装时选择阿里源）：\n/bin/zsh -c \u0026#34;$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)\u0026#34; 常见错误点击这里查看\n也可以使用官方安装脚本：\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; 验证 Homebrew：\nbrew -v 安装 Node.js（nvm） 安装 nvm：\nbrew install nvm 设置环境变量，编辑 ~/.zprofile：\nvim ~/.zprofile 将以下内容添加到 .zprofile 文件最后面：\nexport NVM_DIR=\u0026#34;$HOME/.nvm\u0026#34; [ -s \u0026#34;/opt/homebrew/opt/nvm/nvm.sh\u0026#34; ] \u0026amp;\u0026amp; \\. \u0026#34;/opt/homebrew/opt/nvm/nvm.sh\u0026#34; # 加载 NVM [ -s \u0026#34;/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm\u0026#34; ] \u0026amp;\u0026amp; \\. \u0026#34;/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm\u0026#34; 按 ESC + :wq 保存并退出编辑器。\n加载环境变量到当前终端：\nsource ~/.zprofile 接下来使用 nvm 安装 Node.js：\nnvm install 22 配置 NVM 镜像源（加速 Node 下载） 安装好 NVM 后，将节点镜像地址设置为国内源。\n临时设置（当前终端有效）：\nexport NVM_NODEJS_ORG_MIRROR=https://npmmirror.com/mirrors/node # 淘宝源 # 或 export NVM_NODEJS_ORG_MIRROR=https://mirrors.ustc.edu.cn/node/ # 中科大源 永久设置：\n将上述 export 命令添加到你的 Shell 配置文件（.bashrc、.zshrc 或 .bash_profile）中。\n验证 Node.js 版本 # Verify the Node.js version: node -v # Should print \u0026#34;v22.14.0\u0026#34;. # Verify npm version: npm -v # Should print \u0026#34;11.9.0\u0026#34;. 切换 npm 到国内淘宝镜像：\nnpm config set registry https://registry.npmmirror.com npm config get registry 安装 git brew install git 验证 git 安装：\ngit -v 安装 OpenClaw npm i -g openclaw 验证安装：\nopenclaw --version 如果显示版本号就是安装成功。\n运行以下命令初始化 openclaw：\nopenclaw onboard 验证部署 openclaw --version # 输出版本号即安装成功 openclaw gateway # 网关启动后，浏览器访问 http://127.0.0.1:18789/ openclaw onboard 向导完成后，在网页控制台能正常对话，说明部署成功 网关服务异常时用 openclaw gateway restart 重启 参考 OpenClaw 官方文档 Homebrew 官网 nvm - GitHub HomebrewCN 安装脚本（gitee） ","permalink":"https://heihei0299.github.io/posts/ai/openclaw-macos-deploy/","summary":"\u003ch1 id=\"macos-部署-openclaw\"\u003emacOS 部署 OpenClaw\u003c/h1\u003e\n\u003cp\u003e本文教你从零在 macOS 上部署 OpenClaw：硬件与系统要求、Homebrew、nvm + Node.js、国内镜像源、git，最后安装并初始化 openclaw。\u003c/p\u003e","title":"macOS 部署 OpenClaw"},{"content":"mihomo 与 Tailscale 冲突解决方法 适用场景：同时使用 mihomo（Clash Meta 内核）代理和 Tailscale 组网时出现网络异常（无法联网、Tailscale DNS 失效等）。本文给出冲突原因与三招解决方案。\n问题描述 mihomo 与 Tailscale 在运行时会分别创建虚拟网卡，二者可能发生冲突，从而引发多种网络问题，例如无法联网、Tailscale DNS 失效等。\n解决方案 先启动 Tailscale，确保其完全连接成功； 然后再启动 Clash（建议关闭 Clash 的开机自启动，改为手动启动）； Clash 配置文件中 tun 部分参考如下设置： tun: exclude-interface: - Tailscale - Rustdesk route-exclude-address: - 100.64.0.0/10 - 10.0.0.0/8 - 223.5.5.5/32 - 1.1.1.1/32 配置要点：\nexclude-interface 让 mihomo 的 TUN 不接管 Tailscale（及 Rustdesk）虚拟网卡的流量。 route-exclude-address 排除 Tailscale 使用的 100.64.0.0/10（CGNAT 段）与内网网段，避免路由被抢。 顺序很重要：先起 Tailscale 再起 Clash；把 Clash 从开机自启移除，需要时手动启动。 验证与自查 先启动 Tailscale 后，tailscale status 正常且 tailscale ping \u0026lt;对端\u0026gt; 可通 再启动 Clash 后，外网代理正常（curl ip.sb 为代理出口），Tailscale 内网依然可访问 ip addr 中 Tailscale 与 mihomo 的虚拟网卡同时存在、IP 不冲突 若仍有问题，检查 journalctl -u clash -e（或 Clash 日志）与 tailscale status 的报错信息 参考 clash-verge-rev 讨论：PrintNow 的解决方案（GitHub Issue #1626） mihomo 官方 Wiki Tailscale 官方文档 ","permalink":"https://heihei0299.github.io/posts/network/mihomo%E4%B8%8Etailcale%E5%86%B2%E7%AA%81%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/","summary":"\u003ch1 id=\"mihomo-与-tailscale-冲突解决方法\"\u003emihomo 与 Tailscale 冲突解决方法\u003c/h1\u003e\n\u003cp\u003e适用场景：同时使用 mihomo（Clash Meta 内核）代理和 Tailscale 组网时出现网络异常（无法联网、Tailscale DNS 失效等）。本文给出冲突原因与三招解决方案。\u003c/p\u003e","title":"mihomo 与 Tailscale 冲突解决方法"},{"content":"MTProxy 手动安装教程（mtg 2.0） 适用场景：为 Telegram 客户端搭建一个专属 MTProxy 代理。本文记录使用 mtg 搭建 MTProxy 的基本流程，从下载二进制到输出 tg:// / t.me 分享链接。\n1. 下载 mtg wget https://github.com/9seconds/mtg/releases/download/v2.1.7/mtg-2.1.7-linux-amd64.tar.gz 下载完成后，请自行解压并将 mtg 二进制文件放到系统可执行路径中，例如 /usr/local/bin/mtg。\n2. 生成密钥 mtg generate-secret --hex auca.com 示例输出：\nee3701c2a4b9d0362c042c5d9374619b2c617563612e636f6d 返回值会以 ee 开头，表示支持 Fake TLS。请保存好这个密钥，稍后要写入配置文件。\n3. 创建 systemd 服务 创建服务文件：\nvim /usr/lib/systemd/system/mtg.service 写入以下内容：\n[Unit] Description=mtg [Service] ExecStart=/usr/local/bin/mtg run /usr/local/etc/mtg/config.toml Restart=always RestartSec=3 [Install] WantedBy=multi-user.target 4. 编写配置文件 创建配置目录和配置文件：\nmkdir -p /usr/local/etc/mtg vim /usr/local/etc/mtg/config.toml 最简配置 只配置密钥和监听端口即可启动：\nsecret = \u0026#34;ee3701c2a4b9d0362c042c5d9374619b2c617563612e636f6d\u0026#34; bind-to = \u0026#34;127.0.0.1:10080\u0026#34; 完整示例配置 如果你需要更多可调参数，可以参考下面的示例：\nsecret = \u0026#34;ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d\u0026#34; bind-to = \u0026#34;0.0.0.0:443\u0026#34; concurrency = 8192 tcp-buffer = \u0026#34;4kb\u0026#34; prefer-ip = \u0026#34;prefer-ipv4\u0026#34; domain-fronting-port = 443 tolerate-time-skewness = \u0026#34;5s\u0026#34; [network] doh-ip = \u0026#34;9.9.9.9\u0026#34; proxies = [] [network.timeout] tcp = \u0026#34;5s\u0026#34; http = \u0026#34;10s\u0026#34; idle = \u0026#34;1m\u0026#34; [defense.anti-replay] enabled = true max-size = \u0026#34;1mib\u0026#34; error-rate = 0.001 [defense.blocklist] enabled = true download-concurrency = 2 urls = [] update-each = \u0026#34;24h\u0026#34; [stats.statsd] enabled = false address = \u0026#34;127.0.0.1:8888\u0026#34; metric-prefix = \u0026#34;mtg\u0026#34; tag-format = \u0026#34;datadog\u0026#34; [stats.prometheus] enabled = true bind-to = \u0026#34;127.0.0.1:3129\u0026#34; http-path = \u0026#34;/\u0026#34; metric-prefix = \u0026#34;mtg\u0026#34; 5. 启动服务 systemctl daemon-reload systemctl enable mtg systemctl start mtg 查看运行状态：\nsystemctl status mtg 6. 输出代理链接 支持生成 tg://、t.me 格式链接和二维码：\nmtg access /etc/mtg.toml 验证与自查 systemctl status mtg 显示 active（running） ss -lntp | grep 10080（或你配置的监听端口）确认端口监听正常 journalctl -u mtg -e 无报错 用生成的 tg:// 链接在 Telegram 客户端中连接，发送/接收消息正常 参考 mtg（9seconds/mtg，GitHub） Telegram MTProto 传输协议文档 ","permalink":"https://heihei0299.github.io/posts/network/mtproxy%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"mtproxy-手动安装教程mtg-20\"\u003eMTProxy 手动安装教程（mtg 2.0）\u003c/h1\u003e\n\u003cp\u003e适用场景：为 Telegram 客户端搭建一个专属 MTProxy 代理。本文记录使用 \u003ccode\u003emtg\u003c/code\u003e 搭建 MTProxy 的基本流程，从下载二进制到输出 \u003ccode\u003etg://\u003c/code\u003e / \u003ccode\u003et.me\u003c/code\u003e 分享链接。\u003c/p\u003e\n\u003ch2 id=\"1-下载-mtg\"\u003e1. 下载 mtg\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ewget https://github.com/9seconds/mtg/releases/download/v2.1.7/mtg-2.1.7-linux-amd64.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e下载完成后，请自行解压并将 \u003ccode\u003emtg\u003c/code\u003e 二进制文件放到系统可执行路径中，例如 \u003ccode\u003e/usr/local/bin/mtg\u003c/code\u003e。\u003c/p\u003e","title":"MTProxy 手动安装教程（mtg 2.0）"},{"content":"OpenClaw 接入飞书机器人 将 OpenClaw 接入飞书后，可以在飞书内直接与 AI 对话，让机器人帮你执行任务。本文覆盖从模型配置、飞书应用创建到插件安装的完整流程。\n前置准备 OpenClaw 已安装并启动（参考 部署教程） 拥有飞书开发者账号（开放平台） 一个 DeepSeek（或其他兼容模型）的 API Key ① 配置 AI 模型 OpenClaw 需要绑定一个 AI 模型才能回答问题。以 DeepSeek 为例：\nAPI 地址\nhttps://api.deepseek.com API Key\nsk-\u0026lt;YOUR_DEEPSEEK_API_KEY\u0026gt; 模型 ID\ndeepseek-chat 通用对话模型 deepseek-reasoner 推理模型（适合复杂问题） 在 OpenClaw 后台的模型配置页面填入上述信息，保存即可。\n② 创建飞书应用 打开 飞书开发者后台，点击「创建应用」 填写应用名称（如 OpenClaw 助手）和描述 创建后进入应用详情页，左侧找到 凭证与基础信息 记录以下两个值，后面配置 OpenClaw 时需要： App ID: cli_\u0026lt;YOUR_FEISHU_APP_ID\u0026gt; App Secret: \u0026lt;YOUR_FEISHU_APP_SECRET\u0026gt; ③ 配置权限 在飞书开发者后台左侧找到「权限管理」，点击「添加权限」，搜索并添加以下权限：\n{ \u0026#34;scopes\u0026#34;: { \u0026#34;tenant\u0026#34;: [ \u0026#34;aily:file:read\u0026#34;, \u0026#34;aily:file:write\u0026#34;, \u0026#34;application:application.app_message_stats.overview:readonly\u0026#34;, \u0026#34;application:application:self_manage\u0026#34;, \u0026#34;application:bot.menu:write\u0026#34;, \u0026#34;cardkit:card:read\u0026#34;, \u0026#34;cardkit:card:write\u0026#34;, \u0026#34;contact:contact.base:readonly\u0026#34;, \u0026#34;contact:user.employee_id:readonly\u0026#34;, \u0026#34;corehr:file:download\u0026#34;, \u0026#34;event:ip_list\u0026#34;, \u0026#34;im:chat.access_event.bot_p2p_chat:read\u0026#34;, \u0026#34;im:chat.members:bot_access\u0026#34;, \u0026#34;im:message\u0026#34;, \u0026#34;im:message.group_at_msg:readonly\u0026#34;, \u0026#34;im:message.p2p_msg:readonly\u0026#34;, \u0026#34;im:message:readonly\u0026#34;, \u0026#34;im:message:send_as_bot\u0026#34;, \u0026#34;im:resource\u0026#34; ], \u0026#34;user\u0026#34;: [ \u0026#34;aily:file:read\u0026#34;, \u0026#34;aily:file:write\u0026#34;, \u0026#34;contact:contact.base:readonly\u0026#34;, \u0026#34;im:chat.access_event.bot_p2p_chat:read\u0026#34; ] } } 可以直接在权限管理页面的「JSON 编辑器」中粘贴上述内容批量添加。\n关键权限说明：\n权限 用途 im:message 收发消息 im:message:send_as_bot 以机器人身份发送消息 im:message.p2p_msg:readonly 读取私聊消息 im:message.group_at_msg:readonly 读取群聊 @ 消息 添加完成后点击「发布」\u0026gt;「更新版本」，填写版本说明后提交审核。\n④ 配置事件订阅 机器人需要监听消息事件才能响应。\n在左侧找到「事件与回调」，添加事件 im.message.receive_v1（接收消息事件） 回调地址暂时留空，后面从 OpenClaw 获取 添加后保存 ⑤ 安装并配置飞书插件 回到 OpenClaw 终端，执行以下步骤：\n启用飞书插件\nopenclaw plugins enable feishu 重启网关使插件生效\nopenclaw gateway restart 添加飞书渠道\nopenclaw channels add 按提示填入前面记录的 App ID 和 App Secret。\n再次重启网关\nopenclaw gateway restart 获取回调地址\nopenclaw channels list 输出中会显示飞书渠道的 Webhook URL 或 回调地址，复制这个地址。\n回到飞书开发者后台「事件与回调」页面，将上一步获取的地址填入 回调地址，保存。\n⑥ 验证测试 打开飞书，搜索你创建的应用名称 进入私聊窗口，发送任意消息 机器人应回复 AI 生成的回答 如果收不到回复，按以下顺序排查：\nOpenClaw 网关是否运行 — openclaw gateway status 飞书插件是否启用 — openclaw plugins list 确认 feishu 在列表中 回调地址是否配置正确 — 检查飞书后台事件回调地址与 openclaw channels list 输出一致 权限是否已发布 — 飞书后台必须发布新版本后权限才生效 查看 OpenClaw 日志 — openclaw logs 定位具体错误 常见问题 Q: 添加渠道时提示凭证错误？\n检查 App ID 和 App Secret 是否复制完整，注意不要包含多余空格或换行。\nQ: 机器人能收到消息但不回复？\n检查 DeepSeek API Key 是否有效，可以在 OpenClaw 后台直接测试模型连接。\nQ: 飞书后台提示「事件订阅推送失败」？\n确认 OpenClaw 网关在公网可达的地址上运行。本地开发环境可以使用 ngrok 等工具暴露本地服务。\n参考 飞书开放平台 飞书开发者后台 OpenClaw 官方文档 DeepSeek API 文档 ","permalink":"https://heihei0299.github.io/posts/ai/openclaw-feishu-integration/","summary":"\u003ch1 id=\"openclaw-接入飞书机器人\"\u003eOpenClaw 接入飞书机器人\u003c/h1\u003e\n\u003cp\u003e将 OpenClaw 接入飞书后，可以在飞书内直接与 AI 对话，让机器人帮你执行任务。本文覆盖从模型配置、飞书应用创建到插件安装的完整流程。\u003c/p\u003e\n\u003ch2 id=\"前置准备\"\u003e前置准备\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eOpenClaw 已安装并启动（参考 \u003ca href=\"/posts/ai/openclaw-linux-deploy/\"\u003e部署教程\u003c/a\u003e）\u003c/li\u003e\n\u003cli\u003e拥有飞书开发者账号（\u003ca href=\"https://open.feishu.cn/\"\u003e开放平台\u003c/a\u003e）\u003c/li\u003e\n\u003cli\u003e一个 DeepSeek（或其他兼容模型）的 API Key\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"-配置-ai-模型\"\u003e① 配置 AI 模型\u003c/h2\u003e\n\u003cp\u003eOpenClaw 需要绑定一个 AI 模型才能回答问题。以 DeepSeek 为例：\u003c/p\u003e","title":"OpenClaw 接入飞书机器人"},{"content":"OpenSSH 使用方法 本文适合刚接触 SSH 或需要系统了解 OpenSSH 的用户。你会学到：OpenSSH 的安装与验证、服务端常用配置、密钥生成与公钥认证，以及 ssh/scp/sftp/ssh-agent 等工具的使用方法和安全要点。\n入门 下载和安装 在大多数基于 Linux 的操作系统中，OpenSSH 都已经预安装。如果没有可以使用系统的包管理器进行安装。\n# 在 Ubuntu 或 Debian 上 sudo apt-get update sudo apt-get install openssh-server # 在 CentOS 或 Fedora 上 sudo yum install openssh-server sudo dnf install openssh-server MacOS 系统：在 MacOS 上，OpenSSH 已经预安装。 Windows 系统：在 Windows 10 上，你可以阅读官方文档《安装 OpenSSH》 安装验证 如果 OpenSSH 已经成功安装，这个命令将输出你安装的 OpenSSH 版本。\n$ ssh -V OpenSSH_8.4p1 Debian-5+deb11u1, OpenSSL 1.1.1n 15 Mar 2022 配置 配置文件 OpenSSH 的配置文件在 /etc/ssh 目录中，以下是该目录中的部分重要文件及其功能的简述：\nmoduli：该文件包含了用于 Diffie-Hellman 密钥交换的大素数群，它们用于在客户端和服务器之间安全地协商出一个共享的对称密钥。 ssh_config：这是 SSH 客户端的全局配置文件，定义了默认的 SSH 客户端行为。每个用户可以在他们的 ~/.ssh/config 文件中覆盖这些默认设置。 ssh_config.d：这个目录包含了 ssh_config 文件的碎片，这些碎片在加载 ssh_config 时一起被读取，使得配置管理更加模块化。 sshd_config：这是 SSH 服务器的全局配置文件，定义了 SSH 服务器的行为。可以在这个文件中进行多项设置，如允许的身份验证方法，是否允许 root 登录，使用的端口号等。 sshd_config.d：类似于 ssh_config.d，这个目录包含了 sshd_config 文件的碎片，这些碎片在加载 sshd_config 时一起被读取。 ssh_host_ecdsa_key 和 ssh_host_ecdsa_key.pub：这是服务器的 ECDSA 密钥对。私钥 ssh_host_ecdsa_key 代表服务器的身份证明，不应该被公开，而公钥 ssh_host_ecdsa_key.pub 在客户端首次连接时发送给客户端，客户端将其存储为已知的主机密钥。 ssh_host_ed25519_key 和 ssh_host_ed25519_key.pub：这是服务器的 Ed25519 密钥对，其作用与 ECDSA 密钥对相似。 ssh_host_rsa_key 和 ssh_host_rsa_key.pub：这是服务器的 RSA 密钥对，其作用与 ECDSA 密钥对相似。 ssh_import_id：该文件用于导入其他系统中的公钥，例如，可以从 GitHub 中导入公钥。 常用配置 OpenSSH 的配置文件中有许多可用的选项，你可以根据自己的需要进行设置。以下是一些常用配置的详细介绍：\n配置项 描述 示例 Port 定义 OpenSSH 服务器监听的端口，通常为 22 Port 2222 PermitRootLogin 控制是否允许 root 用户通过 SSH 登录 PermitRootLogin no PubkeyAuthentication 控制是否允许使用公钥认证 PubkeyAuthentication yes PasswordAuthentication 控制是否允许使用密码认证，如果已使用公钥认证，可以关闭此项增加安全性 PasswordAuthentication no AllowUsers / AllowGroups 定义允许 SSH 登录的用户或用户组 AllowUsers user1 user2 AllowGroups group1 group2 DenyUsers / DenyGroups 定义禁止 SSH 登录的用户或用户组，如果用户同时出现在 Allow 和 Deny 列表中，Deny 优先 DenyUsers user3 user4 DenyGroups group3 group4 AuthorizedKeysFile 定义存储用户公钥的文件路径，用于公钥认证 AuthorizedKeysFile .ssh/authorized_keys 每次修改了配置文件后，你都需要手动重启 SSH 服务来使更改生效。\nsudo systemctl restart ssh 生成 SSH 密钥 使用 ssh-keygen 命令可以来生成 SSH 密钥对：\n$ ssh-keygen -t rsa -b 4096 Generating public/private rsa key pair. Enter file in which to save the key (/home/sevenwate/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/sevenwate/.ssh/id_rsa Your public key has been saved in /home/sevenwate/.ssh/id_rsa.pub The key fingerprint is: SHA256:pgYyQB1Xcw4eZPtY/7ejynt9u5svQWkiRksKfi4diIo sevenwate@ubuntu The key\u0026#39;s randomart image is: +---[RSA 4096]----+ | .....oB . | |. .. + B o | |. o = * . . | | . . o B = . + | | .o.. =S+ o + | |E .o ..oo . . | | o. ..o | | . . .oo+| | ++..BO| +----[SHA256]-----+ 这个命令会在 ~/.ssh/ 目录下生成两个文件：id_rsa（私钥）和 id_rsa.pub（公钥）。你可以将公钥添加到远程服务器的 ~/.ssh/authorized_keys 文件中，然后就可以使用密钥对进行认证了。\n防火墙配置 如果服务器启用了防火墙，需要服务器开放 SSH 端口（默认为 22）才能接受 SSH 连接。在使用 iptables 的系统中，可以使用以下命令：\nsudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT 工具 ssh ssh 是用于远程登录或者在远程主机上执行命令。\n# 远程登录 $ ssh [options] [user@]hostname # 在远程主机上执行命令 $ ssh [options] [user@]hostname [command] 选项 描述 -p \u0026lt;port\u0026gt; 指定连接服务器的端口，如果 SSH 服务器没有使用默认端口 22 -i \u0026lt;identity_file\u0026gt; 指定用于连接服务器的私钥文件 -l \u0026lt;user\u0026gt; 指定登录远程主机的用户名 -X 启用 X11 转发，这样可以在远程主机上运行图形程序 -N 不执行远程命令，这通常与 -L, -D, -w 等选项一起使用 -f 后台运行 SSH -q 安静模式，不显示连接、认证的相关信息 -v, -vv, -vvv 调试模式，输出详细的调试信息 -L \u0026lt;port:host:hostport\u0026gt; 设置本地端口转发，把本地的某个端口的流量转发到远程的某个端口 -R \u0026lt;port:host:hostport\u0026gt; 设置远程端口转发，把远程的某个端口的流量转发到本地的某个端口 -D \u0026lt;[bind_address:]port\u0026gt; 设置动态端口转发，创建 SOCKS 代理 scp scp是用于在本地主机和远程主机之间，或者两个远程主机之间复制文件的命令行工具。\n# 从本地复制文件到远程主机 $ scp [options] source_file user@remote:/path/to/destination # 从远程主机复制文件到本地 $ scp [options] user@remote:/path/to/source /path/to/local/destination 选项 描述 -P \u0026lt;port\u0026gt; 通过指定端口连接到远程主机 -p 保留原文件的修改时间和访问时间 -r 递归复制，用于目录的复制 -v 详细模式，显示出处理过程 -q 安静模式，不显示复制过程 -c \u0026lt;cipher\u0026gt; 指定加密算法，如 aes128-ctr, aes192-ctr, aes256-ctr, arcfour256, arcfour128, etc. -i \u0026lt;identity_file\u0026gt; 选择私钥文件，用于密钥认证 sftp sftp 是一个用于安全地传输文件的网络协议。可以使用 sftp 命令在你的计算机和远程服务器之间传输文件，就像使用 FTP 一样。\n# 连接到远程服务器 $ sftp [user@]hostname 选项 描述 -b \u0026lt;batchfile\u0026gt; 指定一个批处理文件执行一系列的 sftp 命令 -C 启用压缩功能 -l \u0026lt;limit\u0026gt; 限制使用的带宽，单位是 Kbit/s -o ssh_option 可以指定任何 ssh 命令接受的选项 -P \u0026lt;port\u0026gt; 指定连接服务器的端口，如果 SSH 服务器没有使用默认端口 22 -R \u0026lt;num_requests\u0026gt; 指定并行请求的最大数量 -s subsystem 指定要在远程主机上启动的 sftp 子系统 -v 显示详细的调试信息 在 sftp 命令行界面，可以使用一系列的命令来操作文件，比如 ls、cd、get、put 等。请注意，虽然这些命令和 Unix shell 中的命令相似，但它们实际上是 sftp 命令的一部分，可能会有一些差异。\n命令 示例 描述 ls sftp\u0026gt; ls 列出远程目录的文件 cd sftp\u0026gt; cd /path/to/remote/directory 更改远程目录 lcd sftp\u0026gt; lcd /path/to/local/directory 更改本地目录 pwd sftp\u0026gt; pwd 显示当前远程目录 get sftp\u0026gt; get remoteFile 下载单个文件 mget sftp\u0026gt; mget remoteFile1 remoteFile2 下载多个文件 put sftp\u0026gt; put localFile 上传单个文件 mput sftp\u0026gt; mput localFile1 localFile2 上传多个文件 mkdir sftp\u0026gt; mkdir /path/to/remote/directory 在远程服务器上创建目录 rmdir sftp\u0026gt; rmdir /path/to/remote/directory 在远程服务器上删除目录 rename sftp\u0026gt; rename oldname newname 在远程服务器上重命名文件或目录 rm sftp\u0026gt; rm /path/to/remote/file 在远程服务器上删除文件 bye 、 exit sftp\u0026gt; bye 、 sftp\u0026gt; exit 退出 sftp 会话 help sftp\u0026gt; help 显示帮助信息 ssh-keygen ssh-keygen 是一个用于创建、管理和转换认证密钥的工具，ssh-keygen 是 OpenSSH 套件中的一个重要组成部分。\n# 不带任何参数运行 ssh-keygen 会创建一个新的 RSA 密钥对 $ ssh-keygen # 或者你可以明确指定生成密钥的类型和密钥长度 $ ssh-keygen -t \u0026lt;type\u0026gt; -b \u0026lt;bits\u0026gt; 选项 描述 -t \u0026lt;type\u0026gt; 生成指定类型的密钥，比如 rsa、dsa、ecdsa、ed25519 等 -b \u0026lt;bits\u0026gt; 指定密钥长度，对于 RSA 密钥，建议至少使用 2048 位 -f \u0026lt;filename\u0026gt; 指定生成的私钥文件的名称和位置 -C \u0026lt;comment\u0026gt; 为密钥添加注释，这对于区分密钥非常有帮助 -N \u0026lt;new_passphrase\u0026gt; 为生成的私钥设置一个新的密码 -P \u0026lt;old_passphrase\u0026gt; 提供现有私钥的密码，用于更改私钥的密码或者生成无密码私钥 -y 输出私钥文件对应的公钥 -q 安静模式，不输出多余信息 -p 更改现有私钥的密码 -l -f \u0026lt;filename\u0026gt; 显示指定公钥文件的 fingerprint -e -f \u0026lt;filename\u0026gt; 以 RFC4716 SSH 公钥文件格式或者 PEM 公钥文件格式输出公钥 -i -f \u0026lt;filename\u0026gt; 读取未知类型的 SSH2 或者 PEM 公钥文件，转换为 SSH2 公钥文件格式 注意，如果你想要创建一个没有密码的私钥，你可以在 -N 选项后面留空，或者在运行 ssh-keygen 命令时直接按 Enter 跳过输入密码的步骤。\nssh-agent ssh-agent 是一个帮助管理 SSH 私钥的程序，用于保存所有私钥，并在需要进行 SSH 认证时提供这些私钥。ssh-agent 通过在后台运行并维护一个已解锁的私钥列表，可以让你在使用 SSH 客户端进行多个 SSH 连接或操作时避免频繁输入密码。\n# 在后台启动 `ssh-agent` $ ssh-agent # 在新的 shell 中启动 `ssh-agent` $ ssh-agent bash # 杀死当前运行的 `ssh-agent` 进程 $ ssh-agent -k ssh-add ssh-add 可以把你的私钥添加到 ssh-agent 的密钥列表中，这样你就可以避免频繁输入密码。一旦你的私钥被添加到 ssh-agent 中，你在使用 SSH 客户端进行认证时就可以直接从 ssh-agent 中获取私钥，无需再手动输入。\n# 尝试添加默认的私钥 $ ssh-add # 添加指定的私钥文件 $ ssh-add [options] [file ...] 选项 描述 -l 列出 ssh-agent 中所有已加载的私钥 -L 列出 ssh-agent 中所有已加载的私钥，同时输出公钥部分 -d \u0026lt;file\u0026gt; 从 ssh-agent 中删除指定的私钥 -D 删除 ssh-agent 中的所有私钥 -t \u0026lt;life\u0026gt; 为添加的私钥设置生存期，单位是秒 -x 锁定 ssh-agent，需要密码才能解锁 -X 解锁 ssh-agent ssh-keyscan ssh-keyscan 是一个方便的工具，它允许用户获取和管理公开的 SSH 密钥。当需要扫描和收集远程服务器的 SSH 公钥以用于以后的身份验证时，ssh-keyscan 是一个非常有用的工具。\nssh-keyscan [options] host [host2] [host3]... 选项 描述 -t \u0026lt;type\u0026gt; 指定要获取的密钥类型，如 rsa、dsa、ecdsa、ed25519 等 -p \u0026lt;port\u0026gt; 指定连接服务器的端口，如果 SSH 服务器没有使用默认端口 22 -T \u0026lt;timeout\u0026gt; 设置连接超时时间，单位是秒 -v 显示详细输出，有助于调试 -H 将结果中的主机名哈希化，这可以防止主机名被保存在已知主机文件中 -o \u0026lt;file\u0026gt; 将输出写入到指定的文件中 ssh-copy-id ssh-copy-id 是一个非常有用的命令，它可以把你的 SSH 公钥复制到远程服务器，以便实现无密码登录。这个命令会自动处理公钥的安装和权限设置。\nssh-copy-id [options] [user@]hostname 选项 描述 -i \u0026lt;identity_file\u0026gt; 指定要复制的身份文件，如果你不使用默认的 ~/.ssh/id_rsa.pub -p \u0026lt;port\u0026gt; 指定连接服务器的端口，如果 SSH 服务器没有使用默认端口 22 -f 强制复制，即使远程主机上已经存在相同的公钥 -n 不实际复制公钥，而是只检查和显示远程主机上的公钥 -h 显示帮助信息 使用 graph LR A[开始] --\u0026gt; B{获取服务器公钥} B --\u0026gt; C[生成公钥指纹并验证] C --\u0026gt; D{添加公钥到 known_hosts} D --\u0026gt; E[SSH 登录] E --\u0026gt; F[结束] 1. 获取服务器公钥 你可以在连接前预先使用 ssh-keyscan 命令来获取服务器的公钥：\nssh-keyscan hostname \u0026gt;\u0026gt; hostname.pub 这将把服务器的公钥保存在 hostname.pub 文件中。记住，这个步骤是可选的，如果你选择直接 SSH 连接到服务器，SSH 客户端将会在初次连接时自动获取服务器的公钥。\n2. 生成公钥指纹 使用 ssh-keygen 命令生成公钥的指纹：\nssh-keygen -lf hostname.pub 这将输出公钥的指纹，你需要通过一个安全的渠道（例如，通过 HTTPS 的网站或者电话等）获取服务器管理员提供的公钥指纹，然后手动比对这两个指纹是否一致。如果两者一致，你可以确认你将要连接的是正确的服务器。否则，可能存在中间人攻击的风险。\n3. 添加公钥到 known_hosts 一旦验证了服务器的公钥指纹，你可以把公钥添加到 ~/.ssh/known_hosts 文件中，这样在下次连接时，SSH 客户端就可以自动验证服务器的公钥了。\nssh-keyscan hostname \u0026gt;\u0026gt; ~/.ssh/known_hosts 4. SSH 登录 在公钥验证成功后，你可以通过 SSH 命令进行登录：\nssh username@example.com 在登录过程中，你可能需要输入用户的密码，或者如果你已经设置了 SSH 密钥对，那么可能需要输入私钥的密码。\n请注意，所有这些步骤都是为了确保你在 SSH 连接中的安全，通过预先验证服务器的公钥，**可以有效防止中间人攻击。**然而，这并不能替代其他的安全措施，如使用强密码、定期更新密码、使用防火墙和 IDS 等。\n验证与自查 ssh -V 能输出版本号（安装验证） 修改配置后执行 sudo systemctl restart ssh，并用 systemctl status ssh 确认服务正常 用 ssh -v user@host 能看到完整的连接与认证过程，出现 Authenticated 表示认证成功 ssh-keygen -lf ~/.ssh/id_rsa.pub 能输出指纹；ssh-add -l 能列出已加入 ssh-agent 的密钥 参考 OpenSSH 官方网站 ArchWiki: OpenSSH OpenSSH 手册（man ssh_config） 微软文档：安装 OpenSSH ","permalink":"https://heihei0299.github.io/posts/ops/openssh/","summary":"\u003ch1 id=\"openssh-使用方法\"\u003eOpenSSH 使用方法\u003c/h1\u003e\n\u003cp\u003e本文适合刚接触 SSH 或需要系统了解 OpenSSH 的用户。你会学到：OpenSSH 的安装与验证、服务端常用配置、密钥生成与公钥认证，以及 \u003ccode\u003essh\u003c/code\u003e/\u003ccode\u003escp\u003c/code\u003e/\u003ccode\u003esftp\u003c/code\u003e/\u003ccode\u003essh-agent\u003c/code\u003e 等工具的使用方法和安全要点。\u003c/p\u003e","title":"OpenSSH 使用方法"},{"content":"Oracle Cloud VPS 救砖恢复指南 本文适合 Oracle Cloud 实例系统损坏、需要重装系统的用户。你会学到：通过 netboot.xyz 启动 Alpine 临时系统，用 setup-alpine 安装真实系统，最后执行 DD 脚本部署目标发行版的完整救砖流程。\n原文链接: https://telegra.ph/OracleCloudInstancesNetbootReinstall-10-02\n概述 本指南介绍如何使用 netboot 恢复损坏的 Oracle Cloud VPS 实例，支持 AMD 和 ARM 架构重新安装系统。\n前置条件 在开始恢复操作前，必须先完成以下两个教程：\n控制台访问教程 Netboot 启动方法教程 恢复步骤 阶段一：进入 Alpine 临时系统 步骤 1：访问 netboot 界面\n按照前置教程进入 netboot.xyz 界面 选择 Linux Network Installs 从菜单中选择 Alpine Linux 步骤 2：登录临时系统\n系统启动后，使用以下凭据登录：\n用户名：root 密码：无需密码（直接按回车） 阶段二：安装真实的 Alpine 系统 步骤 1：下载配置文件\nwget --no-check-certificate -qO setup-alpine.config [配置文件URL] 步骤 2：执行安装\nsetup-alpine -f setup-alpine.config 步骤 3：设置 root 密码\n安装过程中会提示设置 root 密码，请输入并确认密码。\n步骤 4：确认磁盘擦除\n系统会提示确认擦除磁盘（通常显示为 sda - 50.5 GB），输入 yes 确认。\n步骤 5：重启系统\n安装完成后，执行重启：\nreboot 阶段三：完成 DD 操作 步骤 1：登录新系统\n使用在阶段二中设置的 root 密码登录 Alpine 系统。\n步骤 2：安装 bash\napk add bash 步骤 3：执行 DD 脚本\n使用 DD 脚本部署你想要的 Linux 发行版（Debian、Ubuntu 等）。\n常用 DD 脚本示例：\n# Debian bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;DD_SCRIPT_URL\u0026#39;) # Ubuntu bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;DD_SCRIPT_URL\u0026#39;) 步骤 4：等待 DD 完成\nDD 过程可能需要一段时间，请耐心等待。完成后系统会自动重启到新安装的系统。\n架构支持 本教程支持以下架构：\nAMD/x86_64： 标准 x86 架构 ARM/aarch64： ARM 架构 请确保在 netboot 阶段选择与你的实例架构匹配的 Alpine 版本。\n验证与自查 Alpine 安装完成后能用设置的 root 密码登录 uname -m 输出与实例架构一致（x86_64 或 aarch64） DD 完成后系统自动重启，并能通过 SSH/控制台登录新系统 新系统网络正常（ping 外网或 curl 测试） 注意事项 数据备份\n此操作会完全擦除磁盘 请在操作前备份所有重要数据 密码设置\n务必记住在阶段二设置的 root 密码 此密码用于登录 Alpine 系统执行 DD 操作 网络连接\n整个过程需要稳定的网络连接 DD 操作会下载系统镜像，需要较长时间 架构确认\n确认实例架构（AMD 或 ARM） 选择对应的 Alpine 版本 DD 脚本选择\n使用可信的 DD 脚本源 确保脚本支持你的目标系统 故障排除 Alpine 安装失败 如果 Alpine 安装失败，检查：\n配置文件是否正确下载 网络连接是否稳定 磁盘空间是否足够 DD 操作卡住 如果 DD 操作长时间无响应：\n检查网络连接 尝试更换 DD 脚本源 查看系统日志排查问题 无法登录新系统 DD 完成后无法登录：\n确认使用正确的默认密码 检查 SSH 服务是否启动 通过控制台连接排查问题 相关资源 密码重置教程 DD 安装详细步骤 Oracle Cloud 配置最佳实践 控制台连接指南 Netboot 启动方法 完整流程总结 准备阶段\n完成前置教程学习 备份重要数据 确认实例架构 临时系统阶段\n通过 netboot 启动 Alpine 无密码登录临时系统 安装阶段\n下载配置文件 执行 setup-alpine 设置 root 密码 确认磁盘擦除 DD 阶段\n登录 Alpine 系统 安装 bash 执行 DD 脚本 等待完成并重启 验证阶段\n登录新系统 验证系统功能 配置必要服务 ","permalink":"https://heihei0299.github.io/posts/ops/oracle-cloud-netboot-reinstall/","summary":"\u003ch1 id=\"oracle-cloud-vps-救砖恢复指南\"\u003eOracle Cloud VPS 救砖恢复指南\u003c/h1\u003e\n\u003cp\u003e本文适合 Oracle Cloud 实例系统损坏、需要重装系统的用户。你会学到：通过 netboot.xyz 启动 Alpine 临时系统，用 \u003ccode\u003esetup-alpine\u003c/code\u003e 安装真实系统，最后执行 DD 脚本部署目标发行版的完整救砖流程。\u003c/p\u003e","title":"Oracle Cloud VPS 救砖恢复指南"},{"content":"Oracle Cloud 实例控制台连接指南 本文适合无法通过 SSH 登录 Oracle Cloud 实例、需要系统恢复或进 BIOS 的用户。你会学到：CloudShell 串口连接、通过 Linux 跳板机用 VNC 连接实例，以及重启时进入 BIOS 的按键时机。\n原文链接: https://telegra.ph/OracleCloudInstancesConsoleConnection-10-02\n概述 本指南介绍如何通过控制台连接到 Oracle Cloud 实例，涵盖三种连接方法用于系统恢复和 BIOS 访问。\n连接方法 方法一：串行控制台 (CloudShell) 这是最快速的连接方法，直接通过浏览器访问。\n操作步骤：\n登录 OCI 控制台 导航到：实例 → OS 管理 → 控制台连接 启动 CloudShell 连接进行文本访问 特点：\n✅ 最快速的方法 ✅ 基于浏览器，无需额外工具 ⚠️ 可能存在权限限制 方法二：通过 Linux 跳板机使用 VNC（推荐） 这是最推荐的方法，涉及三台机器：\n机器角色：\n机器 A： 需要恢复的 Oracle 实例 机器 B： Ubuntu/Debian VPS 跳板机 机器 C： 你的本地计算机 步骤 1：在跳板机上生成 SSH 密钥\n在机器 B（跳板机）上执行：\nssh-keygen -t rsa -b 2048 查看公钥：\ncat ~/.ssh/id_rsa.pub 步骤 2：在 OCI 创建控制台连接\n进入 Oracle Cloud 控制台 找到目标实例 导航到：实例详情 → 资源 → 控制台连接 创建新的控制台连接 粘贴跳板机的公钥 记录连接信息（连接 OCID 和 VNC 连接字符串） 步骤 3：建立 VNC 隧道\n在机器 B（跳板机）上执行 VNC 连接命令（从 OCI 控制台复制）：\nssh -o ProxyCommand=\u0026#39;ssh -W %h:%p -p 443 \\ ocid1.instanceconsoleconnection.oc1...[YOUR_OCID]@instance-console.[region].oraclecloud.com\u0026#39; \\ -N -L 5900:ocid1.instance.oc1...[YOUR_INSTANCE_OCID]:5900 \\ ocid1.instanceconsoleconnection.oc1...[YOUR_CONNECTION_OCID]@instance-console.[region].oraclecloud.com 步骤 4：从本地连接到跳板机\n在机器 C（本地计算机）上建立 SSH 隧道：\nssh -L 5900:localhost:5900 user@jumphost-ip 步骤 5：使用 VNC 客户端连接\n在本地计算机上使用 VNC 客户端连接到：\nlocalhost:5900 方法三：Windows 直接 VNC 注意： 此方法较为复杂，文章建议避免使用此方法。\n访问 BIOS CloudShell 方法 在 OCI 控制台中启动实例重启 在启动过程中快速按 ESC 键 使用键盘方向键导航 BIOS 菜单 VNC 方法 在 OCI 控制台中启动实例重启 在启动过程中快速按 F2 键 使用键盘方向键导航 BIOS 菜单 SSH 配置问题 如果遇到 SSH 连接问题，可能需要在 SSH 配置文件中添加以下内容：\n编辑 ~/.ssh/config 文件：\nHost * HostKeyAlgorithms +ssh-rsa PubkeyAcceptedKeyTypes +ssh-rsa 这将启用对旧版 SSH-RSA 算法的支持。\n验证与自查 CloudShell 串口连接后能出现登录提示符（login:），说明实例系统仍在运行 VNC 连接 localhost:5900 能显示完整图形界面 重启后按住 ESC（CloudShell）或 F2（VNC）能进入 BIOS 菜单 实例能通过控制台连接正常关机/重启操作 注意事项 CloudShell 方法\n最简单快速 适合紧急情况 可能受权限限制 VNC 跳板机方法\n最稳定可靠 需要额外的 VPS 作为跳板机 提供完整的图形界面访问 BIOS 访问时机\n需要在实例重启后立即按键 CloudShell 使用 ESC 键 VNC 使用 F2 键 安全建议\n妥善保管 SSH 私钥 定期更新控制台连接密钥 使用强密码保护跳板机 相关资源 Netboot 恢复教程 Netboot 重装系统教程 Oracle Cloud 文档：串行控制台连接 Oracle Cloud 文档：VNC 控制台连接 ","permalink":"https://heihei0299.github.io/posts/ops/oracle-cloud-console-connection/","summary":"\u003ch1 id=\"oracle-cloud-实例控制台连接指南\"\u003eOracle Cloud 实例控制台连接指南\u003c/h1\u003e\n\u003cp\u003e本文适合无法通过 SSH 登录 Oracle Cloud 实例、需要系统恢复或进 BIOS 的用户。你会学到：CloudShell 串口连接、通过 Linux 跳板机用 VNC 连接实例，以及重启时进入 BIOS 的按键时机。\u003c/p\u003e","title":"Oracle Cloud 实例控制台连接指南"},{"content":"Rclone 安装与使用 Rclone 是一个命令行程序，用于管理云存储上的文件，打包了各大供应商云存储的接口，提供统一的访问界面。rclone 支持 40 多种云存储产品，包括对象存储、企业和消费者文件存储、服务以及标准传输协议。Rclone 符合 unix POSIX 规范，支持与常见的 shell 工具（rsync、cp、mv、mount、ls、ncdu、tree、rm 和 cat）交互。Rclone 使用我们耳熟能详的语法，支持 shell 管道，支持 --dry-run 保护，同时为了保护数据的可靠性，Rclone 可保留时间戳并始终进行文件验证校验和。Rclone 支持多种文件传输协议：SFTP、HTTP、WebDAV、FTP 和 DLNA。Rclone 是一个成熟的开源软件，最初受 rsync 的启发并采用 Golang 编写，其文档和社区也都非常好，提供广泛和友好的使用用例。\n安装 Script installation To install rclone on Linux/macOS/BSD systems, run:\nsudo -v ; curl https://rclone.org/install.sh | sudo bash For beta installation, run:\nsudo -v ; curl https://rclone.org/install.sh | sudo bash -s beta Note that this script checks the version of rclone installed first and won\u0026rsquo;t re-download if not needed.\nLinux installation Precompiled binary Fetch and unpack\ncurl -O https://downloads.rclone.org/rclone-current-linux-amd64.zip unzip rclone-current-linux-amd64.zip cd rclone-*-linux-amd64 Copy binary file\nsudo cp rclone /usr/bin/ sudo chown root:root /usr/bin/rclone sudo chmod 755 /usr/bin/rclone Install manpage\nsudo mkdir -p /usr/local/share/man/man1 sudo cp rclone.1 /usr/local/share/man/man1/ sudo mandb Run rclone config to setup. See rclone config docs for more details.\nrclone config 设置 命令 说明 rclone config 添加、删除、管理网盘等操作 rclone config file 显示配置文件的路径 rclone config show 显示配置文件信息 root@SunPma:~# rclone config Current remotes: Name Type ==== ==== DB dropbox GD drive e) Edit existing remote n) New remote d) Delete remote r) Rename remote c) Copy remote s) Set configuration password q) Quit config e/n/d/r/c/s/q\u0026gt; 语法 # 本地到网盘 rclone [功能选项] \u0026lt;本地路径\u0026gt; \u0026lt;配置名称:路径\u0026gt; [参数] [参数] # 网盘到本地 rclone [功能选项] \u0026lt;配置名称:路径\u0026gt; \u0026lt;本地路径\u0026gt; [参数] [参数] # 网盘到网盘 rclone [功能选项] \u0026lt;配置名称:路径\u0026gt; \u0026lt;配置名称:路径\u0026gt; [参数] [参数] # [参数]为可选项 示例\n# 复制到网盘，并显示实时传输进度，设置并行上传数为8 rclone copy -P /home/SunPma GD:/home/SunPma --transfers=8 # 如果需要服务端对服务端的传输可加以下参数（不消耗本地流量） rclone copy 配置名称:网盘路径 配置名称:网盘路径 --drive-server-side-across-configs 功能 命令 说明 rclone copy 复制 rclone move 移动，如果要在移动后删除空源目录，加上 --delete-empty-src-dirs 参数 rclone sync 同步：将源目录同步到目标目录，只更改目标目录 rclone size 查看网盘文件占用大小 rclone delete 删除路径下的文件内容 rclone purge 删除路径及其所有文件内容 rclone mkdir 创建目录 rclone rmdir 删除目录 rclone rmdirs 删除指定环境下的空目录。如果加上 --leave-root 参数，则不会删除根目录 rclone check 检查源和目的地址数据是否匹配 rclone ls 列出指定路径下的所有的文件以及文件大小和路径 rclone lsl 比上面多一个显示上传时间 rclone lsd 列出指定路径下的目录 rclone lsf 列出指定路径下的目录和文件 参数 命令 说明 -n = --dry-run 测试运行，查看 Rclone 在实际运行中会进行哪些操作 -P = --progress 显示实时传输进度，500ms 刷新一次，否则默认 1 分钟刷新一次 --cache-chunk-size 5M 块的大小，默认 5M，越大上传越快，占用内存越多，太大可能会导致进程中断 --onedrive-chunk-size 100M 提高 OneDrive 上传速度，适用于 G 口宽带服务器（默认为 320KB） --drive-chunk-size 64M 提高 Google Drive 上传速度，适用于 G 口宽带服务器（默认为 8M） --cache-chunk-total-size SizeSuffix 块可以在本地磁盘上占用的总大小，默认 10G --transfers=N 并行文件数，默认为 4 --config string 指定配置文件路径，string 为配置文件路径 --ignore-errors 跳过错误 --size-only 根据文件大小校验，不校验 hash --drive-server-side-across-configs 服务端对服务端传输 日志 有 4 个级别的日志记录：ERROR NOTICE INFO DEBUG 默认情况下 Rclone 将生成 ERROR NOTICE 日志\n命令 说明 -q rclone 将仅生成 ERROR 消息 -v rclone 将生成 ERROR NOTICE INFO 消息 -vv rclone 将生成 ERROR NOTICE INFO DEBUG 消息 --log-level LEVEL 标志控制日志级别 输出日志到文件\n使用 --log-file=FILE 选项，rclone 会将 Error Info Debug 消息以及标准错误重定向到 FILE。这里的 FILE 是你指定的日志文件路径。\n过滤 命令 说明 --exclude 排除文件或目录 --include 包含文件或目录 --filter 文件过滤规则，相当于上面两个选项的其它使用方式。包含规则以 + 开头，排除规则以 - 开头 文件类型过滤\n例如 --exclude \u0026quot;*.bak\u0026quot; --filter \u0026quot;- *.bak\u0026quot; 排除所有 bak 文件 例如 --include \u0026quot;*.{png,jpg}\u0026quot; --filter \u0026quot;+ *.{png,jpg}\u0026quot; 包含所有 png 和 jpg 文件，排除其他文件 例如 --delete-excluded 删除排除的文件。需配合过滤参数使用，否则无效 目录过滤\n目录过滤需要在目录名称后面加上 /，否则会被当做文件进行匹配。以 / 开头只会匹配根目录（指定目录下），否则匹配所有目录，这同样适用于文件。\n--exclude \u0026quot;.git/\u0026quot; 排除所有目录下的 .git 目录 --exclude \u0026quot;/.git/\u0026quot; 只排除根目录下的 .git 目录 --exclude \u0026quot;{Video,Software}/\u0026quot; 排除所有目录下的 Video 和 Software 目录 --exclude \u0026quot;/{Video,Software}/\u0026quot; 只排除根目录下的 Video 和 Software 目录 --include \u0026quot;/{Video,Software}/**\u0026quot; 仅包含根目录下的 Video 和 Software 目录的所有内容 大小过滤\n默认大小单位为 kBytes，但可以使用 k M 或 G 后缀。\n--min-size 过滤小于指定大小的文件。比如 --min-size 50 表示不会传输小于 50k 的文件 --max-size 过滤大于指定大小的文件。比如 --max-size 1G 表示不会传输大于 1G 的文件 实际使用中发现大小过滤两个选项不能同时使用。\n过滤规则文件\n--filter-from \u0026lt;规则文件\u0026gt; 从文件添加包含/排除规则 比如 --filter-from filter-file.txt 过滤规则文件示例：\n- secret*.jpg + *.jpg + *.png + file2.avi - /dir/Trash/** + /dir/** - * 环境变量 rclone 中的每个选项都可以通过环境变量设置。环境变量的名称可以通过长选项名称进行转换：删除 -- 前缀、更改 - 为 _、大写并添加前缀 RCLONE_。环境变量的优先级会低于命令行选项，即通过命令行追加相应的选项时会覆盖环境变量设定的值。\n比如设置最小上传大小 --min-size 50，使用环境变量是 RCLONE_MIN_SIZE=50。当环境变量设置后，在命令行中使用 --min-size 100，那么此时环境变量的值就会被覆盖。\n常用环境变量\n命令 说明 RCLONE_CONFIG 自定义配置文件路径 RCLONE_CONFIG_PASS 若 rclone 进行了加密设置，把此环境变量设置为密码，可自动解密配置文件 RCLONE_RETRIES 上传失败重试次数，默认 3 次 RCLONE_RETRIES_SLEEP 上传失败重试等待时间，默认禁用，单位 s、m、h 分别代表秒、分钟、小时 RCLONE_TRANSFERS 并行上传文件数 RCLONE_CACHE_CHUNK_SIZE 块的大小，默认 5M RCLONE_CACHE_CHUNK_TOTAL_SIZE 块可以在本地磁盘上占用的总大小，默认 10G RCLONE_IGNORE_ERRORS=true 跳过错误 验证与自查 rclone version 正常输出版本号 rclone config show 能看到已配置的远程（remote）及其类型 rclone lsd \u0026lt;配置名称\u0026gt;: 能列出网盘根目录内容 实际传输前先用 rclone copy --dry-run 试运行，确认将执行的操作符合预期 参考 Rclone 官网 Rclone 安装指南 Rclone 文档 ","permalink":"https://heihei0299.github.io/posts/ai/rclone-install-guide/","summary":"\u003ch1 id=\"rclone-安装与使用\"\u003eRclone 安装与使用\u003c/h1\u003e\n\u003cp\u003eRclone 是一个命令行程序，用于管理云存储上的文件，打包了各大供应商云存储的接口，提供统一的访问界面。rclone 支持 40 多种云存储产品，包括对象存储、企业和消费者文件存储、服务以及标准传输协议。Rclone 符合 unix POSIX 规范，支持与常见的 shell 工具（rsync、cp、mv、mount、ls、ncdu、tree、rm 和 cat）交互。Rclone 使用我们耳熟能详的语法，支持 shell 管道，支持 \u003ccode\u003e--dry-run\u003c/code\u003e 保护，同时为了保护数据的可靠性，Rclone 可保留时间戳并始终进行文件验证校验和。Rclone 支持多种文件传输协议：SFTP、HTTP、WebDAV、FTP 和 DLNA。Rclone 是一个成熟的开源软件，最初受 rsync 的启发并采用 Golang 编写，其文档和社区也都非常好，提供广泛和友好的使用用例。\u003c/p\u003e","title":"Rclone 安装与使用"},{"content":"Realm 转发教程 适用场景：用一台低配 VPS 做流量中转（中转机 → 落地机），把端口转发配置从 iptables 换成更容易维护的 Realm。你会学到安装、TOML 配置、systemd 托管与验证的完整流程。\n项目地址：realm\n特点 支持 DDNS，传入域名后可自动更新解析。 基于 Rust 编写，资源占用低，适合低配主机。 配置简单，相比 iptables 更容易维护。 同时支持 TCP 和 UDP 转发。 运行环境建议 推荐系统：\nDebian 11 及以上 Ubuntu 20 及以上 建议先检查 GLIBC 版本，要求 2.29 及以上：\nldd --version 如果版本过低，建议直接更换到较新的系统版本。\n1. 安装 Realm mkdir -p /home/realm/work touch /home/realm/work/config.toml wget -P /home/realm https://github.com/zhboner/realm/releases/download/v2.5.3/realm-x86_64-unknown-linux-gnu.tar.gz tar -zxvf /home/realm/realm-x86_64-unknown-linux-gnu.tar.gz -C /home/realm chmod +x /home/realm/realm 2. 配置文件 Realm 支持 json 和 toml 两种格式，这里更推荐 toml。\n参考示例地址： Realm examples\n中转机示例 vim /home/realm/work/config.toml [log] level = \u0026#34;warn\u0026#34; output = \u0026#34;/var/log/realm.log\u0026#34; [network] no_tcp = false use_udp = true [[endpoints]] listen = \u0026#34;[::]:50001\u0026#34; remote = \u0026#34;[2602:fce1:407::2f]:10080\u0026#34; remote_transport = \u0026#34;tls;sni=amd.com;insecure\u0026#34; 落地机示例 vim /home/realm/work/config.toml [log] level = \u0026#34;warn\u0026#34; [network] no_tcp = false use_udp = true [[endpoints]] listen = \u0026#34;[::]:10080\u0026#34; remote = \u0026#34;127.0.0.1:10089\u0026#34; listen_transport = \u0026#34;tls;servername=amd.com\u0026#34; 3. 配置开机启动 创建服务文件：\nvim /etc/systemd/system/realm.service 写入以下内容：\n[Unit] Description=realm After=network-online.target Wants=network-online.target systemd-networkd-wait-online.service [Service] Type=simple User=root Restart=on-failure RestartSec=5s DynamicUser=true ExecStart=/home/realm/realm -c /home/realm/work/config.toml [Install] WantedBy=multi-user.target 4. 启动服务 systemctl daemon-reload systemctl enable realm systemctl restart realm systemctl status realm 验证与自查 systemctl status realm 显示 active（running） ss -lntup | grep 50001（中转机监听端口）确认监听正常 用 nc -vz \u0026lt;中转机IP\u0026gt; 50001 或客户端经中转访问落地服务，确认转发链路通 查看转发日志：tail -f /var/log/realm.log（需配置 output） 参考 realm（zhboner/realm，GitHub） Realm examples（GitHub） ","permalink":"https://heihei0299.github.io/posts/network/realm%E8%BD%AC%E5%8F%91%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"realm-转发教程\"\u003eRealm 转发教程\u003c/h1\u003e\n\u003cp\u003e适用场景：用一台低配 VPS 做流量中转（中转机 → 落地机），把端口转发配置从 \u003ccode\u003eiptables\u003c/code\u003e 换成更容易维护的 Realm。你会学到安装、TOML 配置、systemd 托管与验证的完整流程。\u003c/p\u003e","title":"Realm 转发教程"},{"content":"Serenity 学习记录 适用场景：使用带鉴权的订阅地址拉取 Serenity 客户端配置，以及在 Telegram 注册/登录因手机号被风控时的排查思路。本文记录配置获取方式与 Telegram 账号异常的常见原因和解决建议。\n获取配置文件 curl -o config.json http://username:password@example.com:10001 如果订阅地址本身已包含鉴权信息，也可以直接在浏览器或客户端中访问：\nhttps://username:password@www.example.com/ Telegram 账号被封的排查 如果某个手机号被 Telegram 判定为异常，常见原因和排查方向如下。\n可能原因 号码被系统标记为异常：Telegram 可能将该号码识别为频繁使用、异常登录或与其他违规行为相关联。 IP 地址或设备限制：你所在的 IP 或使用的设备可能被 Telegram 认为与违规行为有关联。 账户安全风险：你的账户可能被系统判定存在安全风险，例如多次登录失败、账户被入侵等。 地区限制：某些地区可能会受到 Telegram 的限制，导致无法正常使用。 建议解决方案 更换设备或网络环境：尝试使用不同的设备或切换网络，以排除设备或 IP 地址问题。 检查号码是否被封：访问 Telegram 官方支持页面 确认号码状态。 联系 Telegram 客服：如果号码正常使用且被误封，可以提交申诉说明情况。 使用备用号码：如果长期无法恢复，可考虑使用新的合规号码重新注册。 注意事项 请确保号码来源合法，且未被用于异常注册或滥用行为。 虚拟号码或频繁切换出口环境，可能更容易触发风控。 不要在笔记中直接保存个人手机号等隐私信息。 验证与自查 curl -o config.json http://username:password@example.com:10001 后执行 ls -l config.json，确认文件非空且能正常解析 将配置文件导入客户端，确认节点列表能正常显示 若 Telegram 无法注册/登录，先按\u0026quot;可能原因\u0026quot;逐项核对号码状态、设备与网络出口，再决定是否申诉或更换号码 参考 Telegram 官方支持页面 Telegram FAQ（账号与安全） ","permalink":"https://heihei0299.github.io/posts/network/serenity%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","summary":"\u003ch1 id=\"serenity-学习记录\"\u003eSerenity 学习记录\u003c/h1\u003e\n\u003cp\u003e适用场景：使用带鉴权的订阅地址拉取 Serenity 客户端配置，以及在 Telegram 注册/登录因手机号被风控时的排查思路。本文记录配置获取方式与 Telegram 账号异常的常见原因和解决建议。\u003c/p\u003e","title":"Serenity 学习记录"},{"content":"smartctl 使用指南 适用场景：怀疑硬盘有坏道、开机变慢或想定期体检磁盘。你会学到如何安装 smartmontools，用 smartctl 查看硬盘健康状态、执行短/长自检、查看自检结果与历史错误日志。\nsmartctl 是一个命令行工具，用于通过读取硬盘的 S.M.A.R.T.（自我监测、分析与报告技术）信息来检测和监测硬盘的健康状况。您可以使用它来查看硬盘的详细信息（smartctl -a /dev/sda），执行自检测试（smartctl -t long /dev/sda），查看自检结果（smartctl -l selftest /dev/sda），或查看历史错误信息（smartctl -l error /dev/sda）。\n安装 smartctl sudo apt install smartmontools 主要功能和常用命令 查看全部 SMART 信息 smartctl -a /dev/sda：显示硬盘的全部 SMART 数据，包括温度、健康状态等。 smartctl -i /dev/sda：显示硬盘的身份信息，并检查是否已启用 SMART 功能。 检查硬盘健康状态 smartctl -H /dev/sda：提供硬盘的整体健康状况摘要。 smartctl -A /dev/sda：显示 SMART 厂商属性和值。 执行和查看自检 smartctl -t short /dev/sda：执行一个简短的后台自检。 smartctl -t long /dev/sda：执行一个长时间的后台自检。 smartctl -C -t short /dev/sda：执行一个简短的前台自检。 smartctl -C -t long /dev/sda：执行一个长时间的前台自检。 smartctl -l selftest /dev/sda：在自检完成后，查看测试结果日志。 查看错误日志 smartctl -l error /dev/sda：显示硬盘的历史错误信息。 中断测试 smartctl -X /dev/sda：中断正在运行的后台测试。 如何使用 识别硬盘：在运行 smartctl 命令前，需要知道要检测的硬盘设备名称（例如 /dev/sda、/dev/sdb）。您可以使用 lsblk 命令来列出系统中的所有硬盘设备。 运行命令：根据您想执行的操作，选择并运行相应的命令。例如，要查看 /dev/sda 的全部信息，您需要输入 smartctl -a /dev/sda。 分析结果：仔细查看命令的输出，特别是健康状况 (PASSED 或 Failure)，自检结果，以及错误日志。 验证与自查 sudo smartctl -H /dev/sda 输出 SMART overall-health self-assessment test result: PASSED 执行 sudo smartctl -t short /dev/sda 后，等待数分钟再执行 sudo smartctl -l selftest /dev/sda，确认自检结果为 Completed without error sudo smartctl -A /dev/sda 中 Reallocated_Sector_Ct（重映射扇区数）保持稳定不增长 检测前先用 lsblk 确认设备名，避免测错磁盘（尤其注意 NVMe 设备名为 /dev/nvme0n1 形式） 参考 smartmontools 官方网站 ArchWiki - S.M.A.R.T. ","permalink":"https://heihei0299.github.io/posts/linux/smartctl%E4%BD%BF%E7%94%A8/","summary":"\u003ch1 id=\"smartctl-使用指南\"\u003esmartctl 使用指南\u003c/h1\u003e\n\u003cp\u003e适用场景：怀疑硬盘有坏道、开机变慢或想定期体检磁盘。你会学到如何安装 smartmontools，用 \u003ccode\u003esmartctl\u003c/code\u003e 查看硬盘健康状态、执行短/长自检、查看自检结果与历史错误日志。\u003c/p\u003e","title":"smartctl 使用指南"},{"content":"Starship Pastel Powerline 预设 本文适合想快速获得高颜值终端提示符的 Starship 用户。你会学到：用一条命令应用官方 Pastel Powerline 预设、它的完整配置内容，以及路径替换（directory.substitutions）等特性。\n此预设的灵感来自于 M365Princess。 它同时也演示了 Starship 的路径替换功能。\n前置要求 在终端中安装并使用 Nerd Font字体（本示例使用Caskaydia Cove Nerd Font）。 配置方式 一键应用配置 starship preset pastel-powerline -o ~/.config/starship.toml 点这下载 TOML 文件\n完整配置内容 \u0026#34;$schema\u0026#34; = \u0026#39;https://starship.rs/config-schema.json\u0026#39; format = \u0026#34;\u0026#34;\u0026#34; [](#9A348E)\\ $os\\ $username\\ [](bg:#DA627D fg:#9A348E)\\ $directory\\ [](fg:#DA627D bg:#FCA17D)\\ $git_branch\\ $git_status\\ [](fg:#FCA17D bg:#86BBD8)\\ $c\\ $elixir\\ $elm\\ $golang\\ $gradle\\ $haskell\\ $java\\ $julia\\ $nodejs\\ $nim\\ $rust\\ $scala\\ [](fg:#86BBD8 bg:#06969A)\\ $docker_context\\ [](fg:#06969A bg:#33658A)\\ $time\\ [ ](fg:#33658A)\\ \u0026#34;\u0026#34;\u0026#34; # Disable the blank line at the start of the prompt # add_newline = false # You can also replace your username with a neat symbol like  or disable this # and use the os module below [username] show_always = true style_user = \u0026#34;bg:#9A348E\u0026#34; style_root = \u0026#34;bg:#9A348E\u0026#34; format = \u0026#39;[$user ]($style)\u0026#39; disabled = false # An alternative to the username module which displays a symbol that # represents the current operating system [os] style = \u0026#34;bg:#9A348E\u0026#34; disabled = true # Disabled by default [directory] style = \u0026#34;bg:#DA627D\u0026#34; format = \u0026#34;[ $path ]($style)\u0026#34; truncation_length = 3 truncation_symbol = \u0026#34;…/\u0026#34; # Here is how you can shorten some long paths by text replacement # similar to mapped_locations in Oh My Posh: [directory.substitutions] \u0026#34;Documents\u0026#34; = \u0026#34;󰈙 \u0026#34; \u0026#34;Downloads\u0026#34; = \u0026#34; \u0026#34; \u0026#34;Music\u0026#34; = \u0026#34; \u0026#34; \u0026#34;Pictures\u0026#34; = \u0026#34; \u0026#34; # Keep in mind that the order matters. For example: # \u0026#34;Important Documents\u0026#34; = \u0026#34; 󰈙 \u0026#34; # will not be replaced, because \u0026#34;Documents\u0026#34; was already substituted before. # So either put \u0026#34;Important Documents\u0026#34; before \u0026#34;Documents\u0026#34; or use the substituted version: # \u0026#34;Important 󰈙 \u0026#34; = \u0026#34; 󰈙 \u0026#34; [c] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [cpp] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [docker_context] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#06969A\u0026#34; format = \u0026#39;[ $symbol $context ]($style)\u0026#39; [elixir] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [elm] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [git_branch] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#FCA17D\u0026#34; format = \u0026#39;[ $symbol $branch ]($style)\u0026#39; [git_status] style = \u0026#34;bg:#FCA17D\u0026#34; format = \u0026#39;[$all_status$ahead_behind ]($style)\u0026#39; [golang] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [gradle] style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [haskell] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [java] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [julia] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [nodejs] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [nim] symbol = \u0026#34;󰆥 \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [rust] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [scala] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [time] disabled = false time_format = \u0026#34;%R\u0026#34; # Hour:Minute Format style = \u0026#34;bg:#33658A\u0026#34; format = \u0026#39;[ ♥ $time ]($style)\u0026#39; 效果说明 该预设是渐变色Powerline风格，从紫色到粉色、橙色、蓝色、青色的渐变配色，支持显示：\n操作系统/用户名 当前路径（支持目录别名替换） Git分支和状态 开发环境版本（C/C++/Go/Java/Node.js/Rust等主流语言） Docker上下文 当前时间 验证与自查 执行 source ~/.zshrc（或重新打开终端），提示符变为粉彩渐变 powerline 样式 Documents、Downloads 等目录名显示为对应图标 进入 Git 仓库时显示分支名与状态 在 Node/Rust/Java 项目目录中显示对应版本号，提示符末尾显示当前时间 参考 Starship 官方预设页 Starship Pastel Powerline 官方页面 Nerd Fonts 官网 ","permalink":"https://heihei0299.github.io/posts/ops/starship-pastel-powerline-%E9%A2%84%E8%AE%BE/","summary":"\u003ch1 id=\"starship-pastel-powerline-预设\"\u003eStarship Pastel Powerline 预设\u003c/h1\u003e\n\u003cp\u003e本文适合想快速获得高颜值终端提示符的 Starship 用户。你会学到：用一条命令应用官方 Pastel Powerline 预设、它的完整配置内容，以及路径替换（directory.substitutions）等特性。\u003c/p\u003e","title":"Starship Pastel Powerline 预设"},{"content":"Starship Pastel Powerline 整理文档 本文适合想深入理解或定制 Starship Pastel Powerline 预设的用户。你会学到：预设的快速安装方式、完整配置逐模块解析、配色说明，以及接入现有 zsh 环境的建议与验证方法。\n本文根据 Starship 官方 Pastel Powerline 预设页面整理，补充了可直接使用的命令和配置，适合直接套用到本机 starship 环境。\n官方页面：\nhttps://starship.rs/zh-cn/presets/pastel-powerline 1. 预设简介 Pastel Powerline 是 Starship 官方提供的社区预设之一。\n这个预设的特点很明确：\n使用柔和的粉彩配色 采用 powerline 风格的连续分段提示符 在不同模块之间用彩色分隔符连接 演示了 directory.substitutions 的路径替换能力 官方说明里提到，这个预设的灵感来自 M365Princess。\n2. 前置要求 这个预设依赖 Nerd Font 图标字体。\n如果终端没有启用 Nerd Font，你会看到方块、乱码，或者分隔符和图标显示异常。官方页面给出的示例字体是：\nCaskaydia Cove Nerd Font 你也可以使用其他 Nerd Font，例如：\nJetBrainsMono Nerd Font MesloLGS Nerd Font Hack Nerd Font 如果你还没安装字体，可以参考你现有的文档：\nzsh安装和美化指南.md 3. 快速安装方式 官方推荐的应用方式是直接用 Starship 生成预设配置：\nstarship preset pastel-powerline -o ~/.config/starship.toml 如果你想先查看内容，不直接覆盖配置，可以执行：\nstarship preset pastel-powerline 如果你当前已经有 ~/.config/starship.toml，建议先备份：\ncp ~/.config/starship.toml ~/.config/starship.toml.bak 2\u0026gt;/dev/null 4. 完整配置 下面是 starship preset pastel-powerline 生成的完整配置：\n\u0026#34;$schema\u0026#34; = \u0026#39;https://starship.rs/config-schema.json\u0026#39; format = \u0026#34;\u0026#34;\u0026#34; [](#9A348E)\\ $os\\ $username\\ [](bg:#DA627D fg:#9A348E)\\ $directory\\ [](fg:#DA627D bg:#FCA17D)\\ $git_branch\\ $git_status\\ [](fg:#FCA17D bg:#86BBD8)\\ $c\\ $elixir\\ $elm\\ $golang\\ $gradle\\ $haskell\\ $java\\ $julia\\ $nodejs\\ $nim\\ $rust\\ $scala\\ [](fg:#86BBD8 bg:#06969A)\\ $docker_context\\ [](fg:#06969A bg:#33658A)\\ $time\\ [ ](fg:#33658A)\\ \u0026#34;\u0026#34;\u0026#34; # Disable the blank line at the start of the prompt # add_newline = false # You can also replace your username with a neat symbol like  or disable this # and use the os module below [username] show_always = true style_user = \u0026#34;bg:#9A348E\u0026#34; style_root = \u0026#34;bg:#9A348E\u0026#34; format = \u0026#39;[$user ]($style)\u0026#39; disabled = false # An alternative to the username module which displays a symbol that # represents the current operating system [os] style = \u0026#34;bg:#9A348E\u0026#34; disabled = true # Disabled by default [directory] style = \u0026#34;bg:#DA627D\u0026#34; format = \u0026#34;[ $path ]($style)\u0026#34; truncation_length = 3 truncation_symbol = \u0026#34;…/\u0026#34; # Here is how you can shorten some long paths by text replacement # similar to mapped_locations in Oh My Posh: [directory.substitutions] \u0026#34;Documents\u0026#34; = \u0026#34;󰈙 \u0026#34; \u0026#34;Downloads\u0026#34; = \u0026#34; \u0026#34; \u0026#34;Music\u0026#34; = \u0026#34; \u0026#34; \u0026#34;Pictures\u0026#34; = \u0026#34; \u0026#34; [c] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [cpp] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [docker_context] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#06969A\u0026#34; format = \u0026#39;[ $symbol $context ]($style)\u0026#39; [elixir] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [elm] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [git_branch] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#FCA17D\u0026#34; format = \u0026#39;[ $symbol $branch ]($style)\u0026#39; [git_status] style = \u0026#34;bg:#FCA17D\u0026#34; format = \u0026#39;[$all_status$ahead_behind ]($style)\u0026#39; [golang] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [gradle] style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [haskell] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [java] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [julia] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [nodejs] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [nim] symbol = \u0026#34;󰆥 \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [rust] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [scala] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#86BBD8\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [time] disabled = false time_format = \u0026#34;%R\u0026#34; style = \u0026#34;bg:#33658A\u0026#34; format = \u0026#39;[ ♥ $time ]($style)\u0026#39; 5. 提示符结构说明 这个预设的提示符从左到右大致分成几段：\n系统/用户区 当前目录区 Git 信息区 编程语言运行时区 Docker 上下文区 时间区 具体对应关系如下：\n第一段：$os + $username 第二段：$directory 第三段：$git_branch + $git_status 第四段：语言模块，如 c、golang、java、nodejs、rust 第五段：$docker_context 第六段：$time 整体使用 、 这类 powerline 分隔符把每个模块连成一个连续色带。\n6. 配色说明 该预设主要使用这几组颜色：\n#9A348E：用户/系统段 #DA627D：目录段 #FCA17D：Git 段 #86BBD8：语言运行时段 #06969A：Docker 段 #33658A：时间段 视觉上属于偏柔和的粉彩风格，不像很多 powerline 主题那样强烈或高对比。\n7. 关键模块说明 7.1 username show_always = true 总是显示用户名 普通用户和 root 都使用同样背景色 如果你觉得用户名占空间，可以改成：\n[username] show_always = false 7.2 os 当前预设里：\n[os] disabled = true 也就是说系统图标模块默认关闭。\n如果你想显示操作系统图标，可改成：\n[os] style = \u0026#34;bg:#9A348E\u0026#34; disabled = false 7.3 directory 目录模块做了两件事：\n最多显示 3 级路径 把常见目录名替换成图标 配置如下：\ntruncation_length = 3 truncation_symbol = \u0026#34;…/\u0026#34; 路径替换示例：\nDocuments -\u0026gt; 󰈙 Downloads -\u0026gt;  Music -\u0026gt;  Pictures -\u0026gt;  这也是官方页面特别提到的一个演示点。\n7.4 Git 模块 Git 信息由两部分组成：\ngit_branch git_status 效果包括：\n显示当前分支名 显示文件状态和 ahead/behind 信息 7.5 语言运行时模块 这套预设会在检测到对应项目时显示语言和版本，例如：\nC C++ Elixir Elm Go Gradle Haskell Java Julia Node.js Nim Rust Scala 如果你不想显示这么多语言模块，可以删除不需要的模块，或者从 format 里移除对应变量。\n7.6 time 时间模块默认开启：\n[time] disabled = false time_format = \u0026#34;%R\u0026#34; format = \u0026#39;[ ♥ $time ]($style)\u0026#39; 这里 %R 表示 小时:分钟 格式。\n如果你不想显示时间，可以改成：\n[time] disabled = true 8. 适合什么场景 这个预设比较适合：\n终端需要兼顾美观和信息密度 经常在 Git 仓库里工作 经常切换多种语言项目 希望提示符风格统一且辨识度高 如果你更偏爱极简风格，这个预设可能会显得信息有点多。\n9. 建议的本地化调整 你当前环境已经使用 starship，如果要把这个预设接入你自己的 zsh 配置，建议这样处理：\n先备份现有 ~/.config/starship.toml 先运行 starship preset pastel-powerline \u0026gt; /tmp/pastel-powerline.toml 观察效果 再决定是全量替换，还是只拷贝其中的颜色、目录、时间模块 对于你的环境，我更建议优先改这几个点：\n打开 os 模块，方便区分系统环境 根据自己的开发栈删除不用的语言模块 保留 directory.substitutions 保留 git_branch、git_status 和 time 10. 应用后验证 应用配置后，执行：\nsource ~/.zshrc 或者重新打开终端。\n然后检查：\n分隔符是否正常显示 图标是否正常显示 Git 仓库里是否显示分支信息 在 Node、Rust、Java 等项目目录中是否显示对应版本 时间模块是否出现在提示符末尾 11. 常见问题 11.1 分隔符显示成方块 通常是 Nerd Font 没有安装或终端没有启用该字体。\n11.2 提示符太长 可以删除以下任意模块：\nusername os 部分语言模块 docker_context time 11.3 没有显示语言版本 Starship 只有在检测到对应项目或工具链时才会显示相关模块，这属于正常行为。\n12. 总结 Pastel Powerline 本质上是一套偏柔和风格的高信息密度提示符方案。\n它的重点不只是\u0026quot;好看\u0026quot;，还包括：\n用颜色区分不同信息区域 用 powerline 分段提升提示符可读性 用路径替换简化长目录显示 用语言模块和 Git 模块增强开发场景下的反馈 如果你想直接使用，最简单的方式就是：\nstarship preset pastel-powerline -o ~/.config/starship.toml 如果你想定制，建议先保留它的整体结构，再逐步删减不需要的模块。\n参考 Starship Pastel Powerline 官方页面 Starship 官方预设页 Starship 配置文档 Nerd Fonts 官网 ","permalink":"https://heihei0299.github.io/posts/ops/starship-pastel-powerline%E6%95%B4%E7%90%86%E6%96%87%E6%A1%A3/","summary":"\u003ch1 id=\"starship-pastel-powerline-整理文档\"\u003eStarship Pastel Powerline 整理文档\u003c/h1\u003e\n\u003cp\u003e本文适合想深入理解或定制 Starship Pastel Powerline 预设的用户。你会学到：预设的快速安装方式、完整配置逐模块解析、配色说明，以及接入现有 zsh 环境的建议与验证方法。\u003c/p\u003e","title":"Starship Pastel Powerline 整理文档"},{"content":"tcping 与 udping 使用指南 本文适合需要测试服务器端口连通性的运维人员。你会学到：ping（ICMP）、tcping（TCP 端口连通性）和 udping（UDP 端口连通性）三种工具的安装与用法，以及用 socat 监听 UDP 端口配合测试的方法。\n本指南介绍三种常用的网络连通性测试工具：ping、tcping 和 udping，适用于常规 ICMP 测试、TCP 端口连通性测试、以及 UDP 端口连通性测试。\n一、ICMP Ping（操作系统自带） 大多数操作系统都自带 ping 命令，用于测试目标主机是否可达。\nping example.com 二、TCPing（测试 TCP 连通性） 1. Linux 安装 # 从 GitHub 下载 wget https://github.com/cloverstd/tcping/releases/download/v0.1.1/tcping-linux-amd64-v0.1.1.tar.gz ## 国内镜像（更快） wget https://mirror.ghproxy.com/https://github.com/cloverstd/tcping/releases/download/v0.1.1/tcping-linux-amd64-v0.1.1.tar.gz ## 解压并安装 tar -xzvf tcping-linux-amd64-v0.1.1.tar.gz sudo install tcping /usr/local/bin 2. Windows 安装 打开 GitHub Release 页面：https://github.com/cloverstd/tcping/releases 下载带有 windows 的压缩包，解压获得 tcping.exe 文件 将其放入带有环境变量的目录（如 C:\\Windows\\System32） 打开 PowerShell 或 CMD 测试命令 3. TCPing 用法示例 ## 默认 TCP ping（端口 80） tcping google.com ## 指定端口 tcping google.com 443 ## HTTP 模式 ping（默认 GET 请求） tcping -H google.com ## 指定 URI tcping http://hui.lu 4. 常用参数说明 参数 说明 -c 指定 ping 次数，默认 4 -I 间隔时间，支持单位：ms, s 等（如 -I 500ms） -T 连接超时时间（如 -T 1s） -H 启用 HTTP 模式（忽略 URI） --post HTTP 模式中使用 POST 请求 --head HTTP 模式中使用 HEAD 请求 --user-agent 自定义 UA 字符串 -v 显示版本信息 三、UDPing（测试 UDP 连通性） 1. 下载 UDPing 脚本 ## 原始地址 wget https://raw.githubusercontent.com/wangyu-/UDPping/master/udpping.py ## 国内镜像 wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/wangyu-/UDPping/master/udpping.py 2. 配置和安装 ## 确保 python3 可用 python3 --version ## 安装（可选） sudo install udpping.py /usr/local/bin/udping ⚠️ 注意：脚本首行使用的解释器为 python，如需强制使用 python3，请修改为：\n#!/usr/bin/env python3 3. 使用 socat 监听 UDP 端口 ## 安装 socat sudo apt install -y socat # Debian/Ubuntu sudo yum install -y socat # RHEL/CentOS ## 监听 UDP 端口（如 4000） socat -v UDP-LISTEN:4000,fork PIPE 4. 使用 udping 测试 ## 最基础用法 ./udpping.py 192.168.1.100 4000 ## 指定参数 ./udpping.py 192.168.1.100 4000 \u0026#34;LEN=400;INTERVAL=2000\u0026#34; ## IPv6 示例 ./udpping.py fe80::1a2b:3c4d 4000 参数说明：\n参数 说明 LEN 发送数据长度（字节） INTERVAL 间隔时间/超时时间（单位：毫秒） 四、总结 工具 协议类型 说明 ping ICMP 操作系统自带，适合测试基本连通性 tcping TCP 可测试指定端口是否可连接，支持 HTTP 模式 udping UDP 使用 Python 编写，可灵活配置 UDP 测试 验证与自查 tcping google.com 443 输出 open（或成功响应），说明目标 TCP 端口可达 tcping 对未开放端口输出 filtered/closed，能区分防火墙拦截与端口未监听 对端 socat -v UDP-LISTEN:4000,fork PIPE 在终端输出收到的数据包，同时本机 ./udpping.py 有返回，说明 UDP 通路正常 ping 通但 tcping 不通时，优先检查目标防火墙规则 参考 cloverstd/tcping GitHub 仓库 wangyu-/UDPping GitHub 仓库 socat 官方文档 ","permalink":"https://heihei0299.github.io/posts/ops/tcping-%E4%B8%8Eudping-%E4%BD%BF%E7%94%A8/","summary":"\u003ch1 id=\"tcping-与-udping-使用指南\"\u003etcping 与 udping 使用指南\u003c/h1\u003e\n\u003cp\u003e本文适合需要测试服务器端口连通性的运维人员。你会学到：\u003ccode\u003eping\u003c/code\u003e（ICMP）、\u003ccode\u003etcping\u003c/code\u003e（TCP 端口连通性）和 \u003ccode\u003eudping\u003c/code\u003e（UDP 端口连通性）三种工具的安装与用法，以及用 socat 监听 UDP 端口配合测试的方法。\u003c/p\u003e","title":"tcping 与 udping 使用指南"},{"content":"tea in the snow 个人 zsh 配置整理 本文适合想参考一套轻量、快速启动的 zsh 配置的用户。你会学到：tea-in-the-snow 的 ~/.zshrc 完整结构——zinit 插件管理、Pure 提示符、9 个常用插件、代理快捷别名与 yazi 文件管理器集成。\n仓库地址：https://github.com/tea-in-the-snow/terminal-configuration/blob/main/zsh/zshrc\n📋 配置总览 插件管理器：Zinit（轻量化、高性能、支持异步加载） 终端主题：Pure 极简提示符 插件数量：9个常用插件，兼顾功能和启动速度 适用平台：Linux/macOS 通用 🔧 分模块说明 1. 基础配置 # 历史命令配置 HISTFILE=~/.zhistory HISTSIZE=1000 SAVEHIST=1000 setopt nomatch bindkey -v # 使用Vim键位模式 # 补全初始化 autoload -Uz compinit compinit 历史命令保存1000条 开启Vim模式，支持Vim快捷键操作命令行 2. 环境变量配置 # Rust环境 export PATH=$PATH:$HOME/.cargo/env # Go环境 export PATH=$PATH:~/go/bin # GCC彩色报错配置 export GCC_COLORS=\u0026#39;error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01\u0026#39; 预配置了Rust和Go开发环境路径 开启GCC编译输出彩色显示，错误警告更醒目 3. 别名配置 基础操作别名\n别名 命令 说明 c clear 清屏 e exit 退出终端 lg lazygit 启动LazyGit可视化Git工具 t tmux 启动Tmux终端复用 hx helix 启动Helix编辑器 ll ls -l 列表显示文件详情 la ls -A 显示所有文件（含隐藏文件） l ls -CF 简洁显示文件列表 彩色输出别名\n自动开启ls、grep等命令的彩色输出，不用额外配置。\n代理快捷别名\n别名 功能 proxy_on 开启终端HTTP/HTTPS代理（默认端口7890） proxy_off 关闭终端代理 git_proxy_on 开启Git全局代理 git_proxy_off 关闭Git全局代理 4. Zinit 插件管理器配置 ### Zinit 安装引导（首次打开自动安装Zinit） if [[ ! -f $HOME/.local/share/zinit/zinit.git/zinit.zsh ]]; then # 自动安装Zinit脚本... fi # 加载Zinit核心扩展 zinit light-mode for \\ zdharma-continuum/zinit-annex-as-monitor \\ zdharma-continuum/zinit-annex-bin-gem-node \\ zdharma-continuum/zinit-annex-patch-dl \\ zdharma-continuum/zinit-annex-rust 首次打开终端如果没装Zinit会自动安装，零配置上手 加载Zinit官方扩展，支持更多高级功能 5. 加载的插件列表 插件 功能 优化配置 sindresorhus/pure 极简风终端提示符，颜值高性能好 预编译优化 zsh-users/zsh-autosuggestions 历史命令自动提示，按→补全 异步延迟加载，不阻塞启动 zsh-users/zsh-completions 丰富的命令补全库，支持主流工具补全 立即加载 zsh-users/zsh-syntax-highlighting 命令语法高亮，输错命令变红 立即加载 mrjohannchang/zsh-interactive-cd 支持fzf模糊搜索切换目录 需先安装fzf agkozak/zsh-z 快速目录跳转，z 关键词直接跳转到常用目录 立即加载 OMZ::plugins/git Git常用命令别名和增强功能 直接复用OMZ插件 OMZ::plugins/safe-paste 粘贴长命令时不会自动执行，安全粘贴 直接复用OMZ插件 OMZ::plugins/command-not-found 输错命令时提示正确的包安装命令 直接复用OMZ插件 6. 自定义工具函数 # yazi 文件管理器集成，退出时自动切换到选中目录 function yy() { local tmp=\u0026#34;$(mktemp -t \u0026#34;yazi-cwd.XXXXXX\u0026#34;)\u0026#34; yazi \u0026#34;$@\u0026#34; --cwd-file=\u0026#34;$tmp\u0026#34; if cwd=\u0026#34;$(cat -- \u0026#34;$tmp\u0026#34;)\u0026#34; \u0026amp;\u0026amp; [ -n \u0026#34;$cwd\u0026#34; ] \u0026amp;\u0026amp; [ \u0026#34;$cwd\u0026#34; != \u0026#34;$PWD\u0026#34; ]; then builtin cd -- \u0026#34;$cwd\u0026#34; fi rm -f -- \u0026#34;$tmp\u0026#34; } 输入yy启动yazi文件管理器，退出时会自动cd到你最后浏览的目录，非常实用。\n📝 完整配置代码 # Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. # Initialization code that may require console input (password prompts, [y/n] # confirmations, etc.) must go above this block; everything else may go below. # if [[ -r \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; ]]; then # source \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; # fi # Lines configured by zsh-newuser-install # HISTFILE=~/.histfile HISTFILE=~/.zhistory HISTSIZE=1000 SAVEHIST=1000 setopt nomatch bindkey -v # End of lines configured by zsh-newuser-install # The following lines were added by compinstall zstyle :compinstall filename \u0026#39;/home/tea-in-the-snow/.zshrc\u0026#39; autoload -Uz compinit compinit # End of lines added by compinstall # export HISTFILE = \u0026#34;~/.histfile\u0026#34; # Homebrew config # eval \u0026#34;$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\u0026#34; # Load starship # eval \u0026#34;$(starship init zsh)\u0026#34; # rust environment export PATH=$PATH:$HOME/.cargo/env # add the path of go and those installed by go # export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin # alias alias c=\u0026#39;clear\u0026#39; alias e=\u0026#39;exit\u0026#39; alias lg=\u0026#39;lazygit\u0026#39; alias t=\u0026#39;tmux\u0026#39; alias hx=\u0026#39;helix\u0026#39; # enable color support of ls and also add handy aliases if [ -x /usr/bin/dircolors ]; then test -r ~/.dircolors \u0026amp;\u0026amp; eval \u0026#34;$(dircolors -b ~/.dircolors)\u0026#34; || eval \u0026#34;$(dircolors -b)\u0026#34; alias ls=\u0026#39;ls --color=auto\u0026#39; alias dir=\u0026#39;dir --color=auto\u0026#39; alias vdir=\u0026#39;vdir --color=auto\u0026#39; alias grep=\u0026#39;grep --color=auto\u0026#39; alias fgrep=\u0026#39;fgrep --color=auto\u0026#39; alias egrep=\u0026#39;egrep --color=auto\u0026#39; fi # some more ls aliases alias ll=\u0026#39;ls -l\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias l=\u0026#39;ls -CF\u0026#39; # colored GCC warnings and errors export GCC_COLORS=\u0026#39;error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01\u0026#39; ### Added by Zinit\u0026#39;s installer if [[ ! -f $HOME/.local/share/zinit/zinit.git/zinit.zsh ]]; then print -P \u0026#34;%F{33} %F{220}Installing %F{33}ZDHARMA-CONTINUUM%F{220} Initiative Plugin Manager (%F{33}zdharma-continuum/zinit%F{220})…%f\u0026#34; command mkdir -p \u0026#34;$HOME/.local/share/zinit\u0026#34; \u0026amp;\u0026amp; command chmod g-rwX \u0026#34;$HOME/.local/share/zinit\u0026#34; command git clone https://github.com/zdharma-continuum/zinit \u0026#34;$HOME/.local/share/zinit/zinit.git\u0026#34; \u0026amp;\u0026amp; \\ print -P \u0026#34;%F{33} %F{34}Installation successful.%f%b\u0026#34; || \\ print -P \u0026#34;%F{160} The clone has failed.%f%b\u0026#34; fi source \u0026#34;$HOME/.local/share/zinit/zinit.git/zinit.zsh\u0026#34; autoload -Uz _zinit (( ${+_comps} )) \u0026amp;\u0026amp; _comps[zinit]=_zinit # Load a few important annexes, without Turbo # (this is currently required for annexes) zinit light-mode for \\ zdharma-continuum/zinit-annex-as-monitor \\ zdharma-continuum/zinit-annex-bin-gem-node \\ zdharma-continuum/zinit-annex-patch-dl \\ zdharma-continuum/zinit-annex-rust ### End of Zinit\u0026#39;s installer chunk # zinit ice depth=1; zinit light romkatv/powerlevel10k # some oh-my-zsh library zinit snippet OMZ::lib/completion.zsh zinit snippet OMZ::lib/git.zsh # pure zsh prompt zinit ice compile\u0026#39;(pure|async).zsh\u0026#39; pick\u0026#39;async.zsh\u0026#39; src\u0026#39;pure.zsh\u0026#39; zinit light sindresorhus/pure # plugins zinit ice lucid wait=\u0026#39;0\u0026#39; atload=\u0026#39;_zsh_autosuggest_start\u0026#39; zinit light zsh-users/zsh-autosuggestions zinit light zsh-users/zsh-completions zinit light zsh-users/zsh-syntax-highlighting zinit light mrjohannchang/zsh-interactive-cd # need to install fzf zinit light agkozak/zsh-z zinit snippet OMZ::plugins/git/git.plugin.zsh zinit snippet OMZ::plugins/safe-paste/safe-paste.plugin.zsh zinit snippet OMZ::plugins/command-not-found/command-not-found.plugin.zsh # zsh-autosuggestions config ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=\u0026#34;fg=#87afff\u0026#34; # proxy # export http_proxy=http://127.0.0.1:7890 # export https_proxy=http://127.0.0.1:7890 alias proxy_on=\u0026#39;export http_proxy=http://127.0.0.1:7890 \u0026amp;\u0026amp; export https_proxy=http://127.0.0.1:7890 \u0026amp;\u0026amp; echo -e http and https proxy on\u0026#39; alias proxy_off=\u0026#39;unset https_proxy http_proxy \u0026amp;\u0026amp; echo -e http and https proxy off\u0026#39; alias git_proxy_on=\u0026#39;git config --global http.proxy 127.0.0.1:7890 \u0026amp;\u0026amp; git config --global https.proxy 127.0.0.1:7890 \u0026amp;\u0026amp; echo -e git proxy on\u0026#39; alias git_proxy_off=\u0026#39;git config --global --unset http.proxy \u0026amp;\u0026amp; git config --global --unset https.proxy \u0026amp;\u0026amp; echo -e git proxy off\u0026#39; # To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. # [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh # yazi configuration function yy() { local tmp=\u0026#34;$(mktemp -t \u0026#34;yazi-cwd.XXXXXX\u0026#34;)\u0026#34; yazi \u0026#34;$@\u0026#34; --cwd-file=\u0026#34;$tmp\u0026#34; if cwd=\u0026#34;$(cat -- \u0026#34;$tmp\u0026#34;)\u0026#34; \u0026amp;\u0026amp; [ -n \u0026#34;$cwd\u0026#34; ] \u0026amp;\u0026amp; [ \u0026#34;$cwd\u0026#34; != \u0026#34;$PWD\u0026#34; ]; then builtin cd -- \u0026#34;$cwd\u0026#34; fi rm -f -- \u0026#34;$tmp\u0026#34; } 验证与自查 新开终端执行 zinit times 能看到各插件加载耗时，启动无阻塞 输入历史命令前缀会出现灰色自动建议，按 → 补全 输入错误命令时显示红色高亮（syntax-highlighting 生效） z /tmp 能跳转目录；yy 打开 yazi 退出后自动 cd 到所选目录 参考 tea-in-the-snow/terminal-configuration 仓库 zinit 官方仓库 Pure 提示符 yazi 文件管理器 ","permalink":"https://heihei0299.github.io/posts/ops/tea-in-the-snow-%E4%B8%AA%E4%BA%BA-zsh-%E9%85%8D%E7%BD%AE%E6%95%B4%E7%90%86/","summary":"\u003ch1 id=\"tea-in-the-snow-个人-zsh-配置整理\"\u003etea in the snow 个人 zsh 配置整理\u003c/h1\u003e\n\u003cp\u003e本文适合想参考一套轻量、快速启动的 zsh 配置的用户。你会学到：tea-in-the-snow 的 \u003ccode\u003e~/.zshrc\u003c/code\u003e 完整结构——zinit 插件管理、Pure 提示符、9 个常用插件、代理快捷别名与 yazi 文件管理器集成。\u003c/p\u003e","title":"tea in the snow 个人 zsh 配置整理"},{"content":"Telegram Bot 配置记录 适用场景：把 Telegram Bot 配置写入公开仓库前必须遵守的安全规范。本文记录了一次凭据入库后的清理经验，以及防止再次泄露的做法。\n这个文件原本保存了 Bot Token、UUID 和用户 ID 等敏感信息，现已清理。\n建议做法 不要将 bot_token、用户 ID、UUID 等敏感配置直接写入仓库。 建议改为保存到本地环境变量、密码管理器，或未纳入版本控制的私有配置文件中。 如果旧 Token 已经泄露，应立即在 Telegram BotFather 中重新生成并废弃原 Token。 验证与自查 在仓库中搜索敏感字段：grep -rn \u0026quot;bot_token\u0026quot; . 应无残留 用新 Token 调用 curl https://api.telegram.org/bot\u0026lt;TOKEN\u0026gt;/getMe，返回 \u0026quot;ok\u0026quot;:true 即令牌有效 确认旧 Token 已在 BotFather 中通过 /revoke 废弃并重新生成 检查环境变量或私密配置文件中的 Token 与仓库内示例占位符格式一致 参考 Telegram Bot API 官方文档 BotFather（创建与管理 Bot） ","permalink":"https://heihei0299.github.io/posts/network/telegrambot%E9%85%8D%E7%BD%AE%E8%AE%B0%E5%BD%95/","summary":"\u003ch1 id=\"telegram-bot-配置记录\"\u003eTelegram Bot 配置记录\u003c/h1\u003e\n\u003cp\u003e适用场景：把 Telegram Bot 配置写入公开仓库前必须遵守的安全规范。本文记录了一次凭据入库后的清理经验，以及防止再次泄露的做法。\u003c/p\u003e\n\u003cp\u003e这个文件原本保存了 Bot Token、UUID 和用户 ID 等敏感信息，现已清理。\u003c/p\u003e","title":"Telegram Bot 配置记录"},{"content":"typecho butterfly 本文适用于正在为 Typecho 博客挑选主题的场景。你会学到：Typecho-Butterfly 的版本兼容性、功能特性，以及下载安装方法。\n我选择主题的刚性需求有以下三点：导航栏自定义二级目录、代码高亮、好用且好看的目录。而 Typecho-Butterfly 主题正好满足我的需求。Typecho-Butterfly 移植自 hexo 框架的 Butterfly 主题。\n版本要求 Typecho 1.20 与 php 7.4+ [!WARNING]\n主题没有适配 Typecho 1.21 及以上\n主题特色 方便原 hexo 的博文转移，因为都是 md 文档 原 butterfly 的用户可以直接使用原版 butterfly 主题的 css 文件，拥有原先同样的效果 注意：移植并非为最新版 butterfly，你的 index.css 可能会在本主题出现 bug 由于使用动态博客，大部分功能可以让程序去实现，不用借助第三方 api（可在主题外观设置中自定义） 在线编辑文档 搜索、加密和置顶文章可以直接使用（相比 hexo 无需太多额外配置） 相比 hexo，程序安装更加方便，使用更加高效 网站咨询显示同时在线人数（某些虚拟主机似乎无法使用） 可在后台设置侧边栏信息以及侧边栏的隐藏和显示 后续将开发更多功能 下载 wget https://github.com/wehaox/Typecho-Butterfly/releases/download/1.8.0/butterfly_Release1.8.0.zip 解压 unzip butterfly_Release1.8.0.zip 将解压后的目录上传到 Typecho 的 usr/themes/ 下，并在后台「外观」中启用主题。\n验证与自查 Typecho 后台「外观」中能看到并成功启用 Butterfly 主题 首页导航栏可自定义二级目录，代码块有高亮 文章目录侧边栏正常显示，样式与演示一致 若启用后样式错乱，检查是否为 index.css 与新版本 butterfly 的兼容问题 项目地址 Typecho-Butterfly GitHub 仓库 hexo Butterfly 主题文档 参考 Typecho-Butterfly GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/blog/typecho-butterfly/","summary":"\u003ch1 id=\"typecho-butterfly\"\u003etypecho butterfly\u003c/h1\u003e\n\u003cp\u003e本文适用于正在为 Typecho 博客挑选主题的场景。你会学到：\u003ccode\u003eTypecho-Butterfly\u003c/code\u003e 的版本兼容性、功能特性，以及下载安装方法。\u003c/p\u003e\n\u003cp\u003e我选择主题的刚性需求有以下三点：导航栏自定义二级目录、代码高亮、好用且好看的目录。而 \u003ccode\u003eTypecho-Butterfly\u003c/code\u003e 主题正好满足我的需求。\u003ccode\u003eTypecho-Butterfly\u003c/code\u003e 移植自 hexo 框架的 \u003ccode\u003eButterfly\u003c/code\u003e 主题。\u003c/p\u003e","title":"typecho butterfly"},{"content":"Typecho Docker 部署指南（基于 PHP 8.2） 本文适用于在 Debian/Ubuntu 服务器上用 Docker 部署 Typecho 的场景。你会学到：PHP 8.2 FPM 容器编排、SQLite 零依赖数据库、Nginx 虚拟主机配置、Cloudflare IP 白名单加固，以及修改后台路径增强安全。\n环境信息 操作系统：Debian / Ubuntu Web 服务器：Nginx PHP 版本：8.2（Docker） 数据库：SQLite（无需额外安装） 部署 PHP 8.2 容器 1. 创建工作目录 mkdir -p /opt/docker/php cd /opt/docker/php 2. 编写 docker-compose.yml vim docker-compose.yml services: php: image: lih66511/php-8.2-fpm-typecho:1.0 # 替换为你的镜像名称 container_name: php-8.2-fpm restart: unless-stopped volumes: - ./php/php.ini:/usr/local/etc/php/conf.d/custom.ini # 自定义 PHP 配置 - ./www.conf:/usr/local/etc/php-fpm.d/www.conf # FPM 配置文件 - /var/www/html/typecho:/var/www/html/typecho # Typecho 代码目录 environment: - TZ=Asia/Shanghai ports: - \u0026#34;127.0.0.1:5050:9000\u0026#34; # 将容器的 9000 端口映射到本地 5050 端口 3. 创建 www.conf（FPM 配置） vim /opt/docker/php/www.conf [www] user = www-data group = www-data listen = 9090 listen.owner = www-data listen.group = www-data pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 4. 启动容器 docker compose up -d 下载并部署 Typecho cd /var/www/html wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip unzip typecho.zip -d typecho chown -R www-data:www-data /var/www/html/typecho chmod -R 755 /var/www/html/typecho 配置 Nginx 1. 设置默认虚拟主机（拦截未授权访问） vim /etc/nginx/conf.d/default.conf server { listen 80 default_server; server_name _; include /etc/nginx/conf.d/cf-ip; deny all; return 404; } 2. 配置 Typecho 虚拟主机 vim /etc/nginx/conf.d/typecho.conf server { listen 80; server_name yourservername; # 替换为你的域名 root /var/www/html/typecho; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php?$args; } location ~ \\.php$ { fastcgi_pass 127.0.0.1:5050; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } 3. 允许 Cloudflare IP vim /etc/nginx/conf.d/cf-ip # Cloudflare IPv4 allow 173.245.48.0/20; allow 103.21.244.0/22; allow 103.22.200.0/22; allow 103.31.4.0/22; allow 141.101.64.0/18; allow 108.162.192.0/18; allow 190.93.240.0/20; allow 188.114.96.0/20; allow 197.234.240.0/22; allow 198.41.128.0/17; allow 162.158.0.0/15; allow 104.16.0.0/13; allow 104.24.0.0/14; allow 172.64.0.0/13; allow 131.0.72.0/22; # Cloudflare IPv6 allow 2400:cb00::/32; allow 2606:4700::/32; allow 2803:f800::/32; allow 2405:b500::/32; allow 2405:8100::/32; allow 2a06:98c0::/29; allow 2c0f:f248::/32; Cloudflare 的 IP 段会更新，建议定期从 Cloudflare 官方 IP 列表 核对。\n启动 Typecho 安装向导（SQLite 模式） 浏览器访问：\nhttp://yourdomain.com 选择数据库类型为 SQLite，默认路径为：\n/var/www/typecho/usr/database.db 如需手动创建数据库文件及权限：\ntouch /var/www/html/typecho/usr/database.db chown www-data:www-data /var/www/html/typecho/usr/database.db 修改后台路径（增强安全） 打开配置文件：\nvim /var/www/html/typecho/config.inc.php 添加或修改如下内容：\n\u0026lt;?php define(\u0026#39;__TYPECHO_ADMIN_DIR__\u0026#39;, \u0026#39;/your-new-admin-path/\u0026#39;); 重命名后台目录：\nmv /var/www/html/typecho/admin /var/www/html/typecho/your-new-admin-path 验证与自查 docker compose ps 中 php 容器状态为 running nginx -t 配置检查通过，nginx -s reload 生效 浏览器访问 http://yourdomain.com 能进入 Typecho 安装向导并完成 SQLite 安装 使用新后台路径能登录，旧路径 admin 返回 404 常见问题排查 问题 解决方法 403 权限错误 确保 Typecho 目录权限正确：chown -R www-data:www-data /var/www/html/typecho PHP 页面不解析 检查 Nginx 中 fastcgi_pass 设置是否为 127.0.0.1:5050 数据库无法写入 检查 usr/database.db 文件权限是否正确 参考 Typecho 官方文档 Docker Compose 文档 Cloudflare 官方 IP 列表 PHP Docker 官方镜像 ","permalink":"https://heihei0299.github.io/posts/blog/typecho-%E9%83%A8%E7%BD%B2-php-8-2-sqlite-nginx/","summary":"\u003ch1 id=\"typecho-docker-部署指南基于-php-82\"\u003eTypecho Docker 部署指南（基于 PHP 8.2）\u003c/h1\u003e\n\u003cp\u003e本文适用于在 Debian/Ubuntu 服务器上用 Docker 部署 Typecho 的场景。你会学到：PHP 8.2 FPM 容器编排、SQLite 零依赖数据库、Nginx 虚拟主机配置、Cloudflare IP 白名单加固，以及修改后台路径增强安全。\u003c/p\u003e","title":"Typecho Docker 部署指南（基于 PHP 8.2）"},{"content":"Typecho 部署（Docker 部署 PHP 8.2） 本文适用于在 Debian/Ubuntu 服务器上用 Docker 部署 Typecho 的场景。你会学到：PHP 8.2 FPM 容器编排与端口映射、Nginx 虚拟主机与 Cloudflare IP 白名单配置、SQLite 安装向导，以及修改默认后台登录路径。\n环境信息 操作系统：Debian / Ubuntu Web服务器：Nginx PHP版本：8.2 docker 数据库：SQLite（无需安装） 安装 php8.2 mkdir -p /opt/docker/php cd /opt/docker/php vim docker-compose.yml services: # PHP-FPM 服务 php: image: lih66511/php-8.2-fpm-typecho:1.0 # 替换为你的镜像名 container_name: php-8.2-fpm restart: unless-stopped volumes: # 挂载Typecho代码目录 - ./php/php.ini:/usr/local/etc/php/conf.d/custom.ini # 自定义PHP配置 - ./www.conf:/usr/local/etc/php-fpm.d/www.conf - /var/www/html/typecho:/var/www/html/typecho environment: - TZ=Asia/Shanghai # 设置时区 ports: - \u0026#34;127.0.0.1:5050:9000\u0026#34; vim /opr/docker/php/www.conf [www] ; 定义运行 PHP-FPM 的用户和组（通常为 www-data 或 nobody） user = www-data group = www-data ; 监听端口（确保和 docker-compose.yaml 的端口映射一致） listen = 9090 ; 其他必要配置（如进程管理） listen.owner = www-data listen.group = www-data pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 启动容器：\ndocker compose up -d 下载并解压 Typecho cd /var/www/html sudo wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip sudo unzip typecho.zip -d typecho sudo chown -R www-data:www-data /var/www/html/typecho sudo chmod -R 755 /var/www/html/typecho 配置 Nginx 只允许特定的 sni 或者 host 访问特定的服务，其他未知 sni 或者 host 直接返回404. 只允许cloudflare的 ip 访问80端口 默认虚拟主机 vim /etc/nginx/conf.d/default.conf # 默认 server，匹配所有未定义的 Host server { listen 80 default_server; server_name _; # 匹配任意 Host include /etc/nginx/conf.d/cf-ip; deny all; return 404; } Typecho 虚拟主机 vim /etc/nginx/conf.d/typecho.conf server { listen 80; server_name yourservername; root /var/www/html/typecho; index index.html index.htm index.php; location / { try_files $uri $uri/ /index.php?$args; } location ~ \\.php$ { fastcgi_pass 127.0.0.1:5050; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } Cloudflare IP 白名单 vim /etc/nginx/conf.d/cf-ip # 允许 Cloudflare 的 IP 地址访问 # 定义允许的 IPv4 地址范围 allow 173.245.48.0/20; allow 103.21.244.0/22; allow 103.22.200.0/22; allow 103.31.4.0/22; allow 141.101.64.0/18; allow 108.162.192.0/18; allow 190.93.240.0/20; allow 188.114.96.0/20; allow 197.234.240.0/22; allow 198.41.128.0/17; allow 162.158.0.0/15; allow 104.16.0.0/13; allow 104.24.0.0/14; allow 172.64.0.0/13; allow 131.0.72.0/22; # 定义允许的 IPv6 地址范围 allow 2400:cb00::/32; allow 2606:4700::/32; allow 2803:f800::/32; allow 2405:b500::/32; allow 2405:8100::/32; allow 2a06:98c0::/29; allow 2c0f:f248::/32; Cloudflare 的 IP 段会更新，建议定期从 Cloudflare 官方 IP 列表 核对。\n启动 Typecho 安装向导（使用 SQLite） 浏览器访问：\nhttp://yourdomain.com 选择数据库类型为 SQLite，默认数据库路径：\n/var/www/typecho/usr/database.db 如果需要可手动创建空数据库文件并设置权限：\nsudo touch /var/www/html/typecho/usr/database.db sudo chown www-data:www-data /var/www/html/typecho/usr/database.db 更改默认后台登录路径 更改 define('__TYPECHO_ADMIN_DIR__', '/Changed Path/'); vim /var/www/html/typecho/config.inc.php \u0026lt;?php // admin directory (relative path) define(\u0026#39;__TYPECHO_ADMIN_DIR__\u0026#39;, \u0026#39;/YOUR Changed Path/\u0026#39;); mv admin [OUR Changed Path] 验证与自查 docker compose ps 中 php 容器状态为 running nginx -t 通过，浏览器访问 http://yourdomain.com 能进入安装向导并完成 SQLite 安装 使用新后台路径能登录，旧路径 admin 返回 404 非 Cloudflare IP 直连 80 端口被拒绝，未知 Host 返回 404 常见问题处理 问题 解决方法 403 权限错误 检查 typecho 目录是否可写；chown -R www-data:www-data/var/www/html/typecho PHP 页面不执行 确认 Nginx 的 fastcgi_pass 设置正确（127.0.0.1：5050） 数据库写入失败 检查 typecho 目录是否可写；chown -R www-data:www-data/var/www/html/typecho 后续建议（可选） 启用 HTTPS（推荐使用 Let\u0026rsquo;s Encrypt） 使用 CDN 或缓存插件提升性能 定期备份 usr/ 文件夹（包含 SQLite 数据库和上传内容） 参考 Typecho 官方文档 Typecho GitHub Releases Docker Compose 文档 Cloudflare 官方 IP 列表 ","permalink":"https://heihei0299.github.io/posts/blog/%E9%83%A8%E7%BD%B2typecho-docker-%E9%83%A8%E7%BD%B2php-8-2/","summary":"\u003ch1 id=\"typecho-部署docker-部署-php-82\"\u003eTypecho 部署（Docker 部署 PHP 8.2）\u003c/h1\u003e\n\u003cp\u003e本文适用于在 Debian/Ubuntu 服务器上用 Docker 部署 Typecho 的场景。你会学到：PHP 8.2 FPM 容器编排与端口映射、Nginx 虚拟主机与 Cloudflare IP 白名单配置、SQLite 安装向导，以及修改默认后台登录路径。\u003c/p\u003e","title":"Typecho 部署（Docker 部署 PHP 8.2）"},{"content":"Typecho 文章批量导出工具 提供从 Typecho 数据库批量导出文章为 Markdown 文件的 Python 脚本，支持 SQLite 和 MySQL 两种数据库。\n功能特点 自动识别已发布的文章（status='publish'） 保留原始文章标题和内容 自动生成规范化文件名（替换特殊字符） 实时输出导出进度 SQLite 版本 import sqlite3 import os # 配置区 ================================== DB_FILE = \u0026#39;typecho.db\u0026#39; # SQLite 数据库文件路径 OUTPUT_DIR = \u0026#39;./exported_md\u0026#39; # 输出目录 # ========================================= def export_posts(): \u0026#34;\u0026#34;\u0026#34;导出已发布的文章到Markdown文件\u0026#34;\u0026#34;\u0026#34; if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR) with sqlite3.connect(DB_FILE) as conn: cursor = conn.cursor() cursor.execute(\u0026#34;\u0026#34;\u0026#34; SELECT cid, title, text FROM typecho_contents WHERE status=\u0026#39;publish\u0026#39; \u0026#34;\u0026#34;\u0026#34;) for cid, title, text in cursor.fetchall(): # 生成安全的文件名 filename = f\u0026#34;{cid}_{title.replace(\u0026#39;/\u0026#39;, \u0026#39;_\u0026#39;).replace(\u0026#39; \u0026#39;, \u0026#39;_\u0026#39;)}.md\u0026#34; filepath = os.path.join(OUTPUT_DIR, filename) with open(filepath, \u0026#39;w\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: f.write(f\u0026#34;# {title}\\n\\n{text}\u0026#34;) print(f\u0026#34;✓ 导出成功: {title} → {filename}\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: export_posts() MySQL 版本 import mysql.connector import os # 配置区 ================================== DB_CONFIG = { \u0026#39;host\u0026#39;: \u0026#39;localhost\u0026#39;, \u0026#39;user\u0026#39;: \u0026#39;typecho_user\u0026#39;, \u0026#39;password\u0026#39;: \u0026#39;your_password\u0026#39;, \u0026#39;database\u0026#39;: \u0026#39;typecho_db\u0026#39;, } OUTPUT_DIR = \u0026#39;./exported_md\u0026#39; # 输出目录 # ========================================= def export_posts(): \u0026#34;\u0026#34;\u0026#34;导出已发布的文章到Markdown文件\u0026#34;\u0026#34;\u0026#34; if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR) try: conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor() cursor.execute(\u0026#34;\u0026#34;\u0026#34; SELECT cid, title, text FROM typecho_contents WHERE status=\u0026#39;publish\u0026#39; \u0026#34;\u0026#34;\u0026#34;) for cid, title, text in cursor.fetchall(): # 生成安全的文件名 filename = f\u0026#34;{cid}_{title.replace(\u0026#39;/\u0026#39;, \u0026#39;_\u0026#39;).replace(\u0026#39; \u0026#39;, \u0026#39;_\u0026#39;)}.md\u0026#34; filepath = os.path.join(OUTPUT_DIR, filename) with open(filepath, \u0026#39;w\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: f.write(f\u0026#34;# {title}\\n\\n{text}\u0026#34;) print(f\u0026#34;✓ 导出成功: {title} → {filename}\u0026#34;) finally: cursor.close() conn.close() if __name__ == \u0026#39;__main__\u0026#39;: export_posts() 使用说明 准备工作：\n安装依赖库：\npip install mysql-connector-python 修改脚本中的数据库配置参数\n执行导出：\npython export_typecho.py 输出结果：\n所有文章将以 {文章ID}_{文章标题}.md 格式保存 每篇文章首行自动添加Markdown标题 注意事项：\n确保数据库连接信息正确 MySQL版本需要提前创建好数据库连接权限 文件名中的特殊字符会被替换为下划线 验证与自查 导出后统计数量：ls exported_md/ | wc -l，与数据库已发布文章数一致 抽查导出的 .md 文件，首行为 Markdown 标题 # 文章标题 用 head -5 exported_md/*.md 检查中文无乱码（UTF-8 编码） 将导出的 md 文件导入 Hugo/Hexo 前，先补 front matter 并检查正文中的特殊字符 参考 Typecho 官方文档 SQLite 官方文档 mysql-connector-python (PyPI) ","permalink":"https://heihei0299.github.io/posts/blog/%E4%BB%8Etypecho%E4%B8%AD%E5%AF%BC%E5%87%BA%E6%96%87%E7%AB%A0-sqlite%E6%95%B0%E6%8D%AE%E5%BA%93/","summary":"\u003ch1 id=\"typecho-文章批量导出工具\"\u003eTypecho 文章批量导出工具\u003c/h1\u003e\n\u003cp\u003e提供从 Typecho 数据库批量导出文章为 Markdown 文件的 Python 脚本，支持 SQLite 和 MySQL 两种数据库。\u003c/p\u003e\n\u003ch2 id=\"功能特点\"\u003e功能特点\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e自动识别已发布的文章（\u003ccode\u003estatus='publish'\u003c/code\u003e）\u003c/li\u003e\n\u003cli\u003e保留原始文章标题和内容\u003c/li\u003e\n\u003cli\u003e自动生成规范化文件名（替换特殊字符）\u003c/li\u003e\n\u003cli\u003e实时输出导出进度\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"sqlite-版本\"\u003eSQLite 版本\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003esqlite3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 配置区 ==================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eDB_FILE\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;typecho.db\u0026#39;\u003c/span\u003e        \u003cspan class=\"c1\"\u003e# SQLite 数据库文件路径\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;./exported_md\u0026#39;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 输出目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =========================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eexport_posts\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;导出已发布的文章到Markdown文件\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexists\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emakedirs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esqlite3\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDB_FILE\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003econn\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexecute\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e            SELECT cid, title, text \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e            FROM typecho_contents \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e            WHERE status=\u0026#39;publish\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e        \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ecid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efetchall\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"c1\"\u003e# 生成安全的文件名\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003efilename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ecid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e_\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereplace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;_\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereplace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39; \u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;_\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e.md\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003efilepath\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efilename\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"nb\"\u003eopen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efilepath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;w\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eencoding\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;utf-8\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewrite\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;# \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\\n\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;✓ 导出成功: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e → \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003efilename\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vm\"\u003e__name__\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;__main__\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexport_posts\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"mysql-版本\"\u003eMySQL 版本\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003emysql.connector\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 配置区 ==================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eDB_CONFIG\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s1\"\u003e\u0026#39;host\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;localhost\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s1\"\u003e\u0026#39;user\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;typecho_user\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s1\"\u003e\u0026#39;password\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;your_password\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s1\"\u003e\u0026#39;database\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;typecho_db\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;./exported_md\u0026#39;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 输出目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =========================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eexport_posts\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;导出已发布的文章到Markdown文件\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexists\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emakedirs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003econn\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emysql\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnector\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e**\u003c/span\u003e\u003cspan class=\"n\"\u003eDB_CONFIG\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexecute\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e            SELECT cid, title, text \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e            FROM typecho_contents \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e            WHERE status=\u0026#39;publish\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e        \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ecid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efetchall\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"c1\"\u003e# 生成安全的文件名\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003efilename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ecid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e_\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereplace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;_\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereplace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39; \u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;_\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e.md\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003efilepath\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOUTPUT_DIR\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efilename\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"nb\"\u003eopen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efilepath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;w\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eencoding\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;utf-8\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewrite\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;# \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\\n\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;✓ 导出成功: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e → \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003efilename\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efinally\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecursor\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003econn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vm\"\u003e__name__\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;__main__\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexport_posts\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"使用说明\"\u003e使用说明\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e准备工作\u003c/strong\u003e：\u003c/p\u003e","title":"Typecho 文章批量导出工具"},{"content":"VLESS + Vision + Reality 无法正常使用 TCP Brutal 的解决方法 适用场景：服务端已配置 vless + vision + reality 组合，但 tcp_brutal 流控不生效，表现为 YouTube 播放异常等。本文给出从系统层排查解决的三个步骤。\n某些 IDC 提供的系统镜像可能会导致 vless + vision + reality 组合无法正常使用 tcp_brutal 流控，例如 YouTube 播放异常。这类情况下，常见处理方式是先更换为更干净的系统环境。\n1. DD 为 Debian 11 纯净系统 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://www.moeelf.com/attachment/LinuxShell/InstallNET.sh\u0026#39;) -d 11 -v 64 -a 2. 更新内核到 5.10 先获取 root 权限：\nsu 更新软件包索引：\napt-get update 查看可用内核：\napt-cache search linux-image 安装目标内核包，示例：\napt-get install linux-image-5.10.0-26-cloud-amd64 安装完成后更新引导：\nupdate-grub 然后重启系统：\nreboot 重启后检查当前内核：\nuname -a 3. 安装 tcp_brutal 模块 bash \u0026lt;(curl -fsSL https://tcp.hy2.sh/) 验证与自查 uname -a 确认内核为 5.10 及以上（tcp_brutal 模块的兼容范围） lsmod | grep tcp_brutal 能看到模块已加载；sysctl net.ipv4.tcp_available_congestion_control 输出包含 brutal 重启服务端代理进程后，客户端 YouTube 播放恢复正常，速度曲线符合预期 客户端日志无 tcp_brutal 协商失败或流控回退报错 参考 tcp-brutal（apernet，GitHub） sing-box VLESS 出站文档 ","permalink":"https://heihei0299.github.io/posts/network/vless-vision-reality-tcp-brutal%E5%A4%B1%E8%B4%A5%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/","summary":"\u003ch1 id=\"vless--vision--reality-无法正常使用-tcp-brutal-的解决方法\"\u003eVLESS + Vision + Reality 无法正常使用 TCP Brutal 的解决方法\u003c/h1\u003e\n\u003cp\u003e适用场景：服务端已配置 \u003ccode\u003evless + vision + reality\u003c/code\u003e 组合，但 \u003ccode\u003etcp_brutal\u003c/code\u003e 流控不生效，表现为 YouTube 播放异常等。本文给出从系统层排查解决的三个步骤。\u003c/p\u003e","title":"VLESS + Vision + Reality 无法正常使用 TCP Brutal 的解决方法"},{"content":"VPS 常用脚本合集：DD 重装、测试与优化 本文适合经常折腾 VPS 的运维用户。你会学到：DD 重装 Linux/Windows 的脚本、服务器性能与流媒体解锁测试、测速与回程测试、BBR 与 SWAP 优化，以及 Docker/Python/WARP 等环境的一键安装命令。\n1. DD重装脚本 史上最强脚本 来源：史上最强脚本\nwget --no-check-certificate -qO InstallNET.sh \u0026#39;https://raw.githubusercontent.com/leitbogioro/Tools/master/Linux_reinstall/InstallNET.sh\u0026#39; \u0026amp;\u0026amp; chmod a+x InstallNET.sh \u0026amp;\u0026amp; bash InstallNET.sh -debian 12 -pwd \u0026#39;password\u0026#39; 萌咖大佬的脚本 bash \u0026lt;(wget --no-check-certificate -qO- \u0026#39;https://raw.githubusercontent.com/MoeClub/Note/master/InstallNET.sh\u0026#39;) -d 11 -v 64 -p 密码 -port 端口 -a -firmware DD windows（使用史上最强DD脚本） 支持Windows 10、Windows 11、Windows Server 2012 R2、Windows Server 2016、Windows Server 2019、Windows Server 2022\nwget --no-check-certificate -qO InstallNET.sh \u0026#39;https://raw.githubusercontent.com/leitbogioro/Tools/master/Linux_reinstall/InstallNET.sh\u0026#39; \u0026amp;\u0026amp; chmod a+x InstallNET.sh \u0026amp;\u0026amp; bash InstallNET.sh -windows 10 -lang \u0026#34;cn\u0026#34; 账户：Administrator 密码：Teddysun.com 2. 综合测试脚本 融合怪 来源：融合怪\nbash \u0026lt;(wget -qO- --no-check-certificate https://gitlab.com/spiritysdx/za/-/raw/main/ecs.sh) NodeBench 来源：NodeBench\nbash \u0026lt;(curl -sL https://raw.githubusercontent.com/LloydAsp/NodeBench/main/NodeBench.sh) yabs 来源：yabs\ncurl -sL yabs.sh | bash 使用gb5测试yabs\ncurl -sL yabs.sh | bash -5 3. 性能测试 gb5专测脚本 来源：gb5专测脚本\nbash \u0026lt;(curl -sL bash.icu/gb5) 4. 流媒体及IP质量测试 最常用版本 来源：最常用版本\nbash \u0026lt;(curl -L -s check.unlock.media) 原生检测脚本 来源：原生检测脚本\nbash \u0026lt;(curl -sL Media.Check.Place) 准确度最高 来源：准确度最高\nbash \u0026lt;(curl -L -s https://github.com/1-stream/RegionRestrictionCheck/raw/main/check.sh) IP质量体检脚本 来源：IP质量体检脚本\nbash \u0026lt;(curl -sL IP.Check.Place) ChatGPT APP 解锁检测 来源：ChatGPT APP 解锁检测\n安卓\ncurl android.chat.openai.com 苹果\ncurl ios.chat.openai.com 结果解析：https://www.nodeseek.com/post-31717-1\n5. 测速脚本 三网测速脚本 Speedtest\n来源：Speedtest\nbash \u0026lt;(curl -sL bash.icu/speedtest) Taier\n来源：Taier\nbash \u0026lt;(curl -sL res.yserver.ink/taier.sh) hyperspeed 来源：hyperspeed\nbash \u0026lt;(curl -Lso- https://bench.im/hyperspeed) 全球测速 来源：全球测速\ncurl -sL network-speed.xyz | bash 6. 回程测试 直接显示回程（小白用这个） 来源：直接显示回程\ncurl https://raw.githubusercontent.com/ludashi2020/backtrace/main/install.sh -sSf | sh 回程详细测试（推荐） 来源：回程详细测试\nwget https://ghproxy.com/https://raw.githubusercontent.com/vpsxb/testrace/main/testrace.sh -O testrace.sh \u0026amp;\u0026amp; bash testrace.sh 7. 功能脚本 Fail2ban 来源：Fail2ban\nwget --no-check-certificate https://raw.githubusercontent.com/FunctionClub/Fail2ban/master/fail2ban.sh \u0026amp;\u0026amp; bash fail2ban.sh 2\u0026gt;\u0026amp;1 | tee fail2ban.log 开启BBR 一键开启BBR，适用于较新的Debian、Ubuntu\necho \u0026#34;net.core.default_qdisc=fq\u0026#34; \u0026gt;\u0026gt; /etc/sysctl.conf echo \u0026#34;net.ipv4.tcp_congestion_control=bbr\u0026#34; \u0026gt;\u0026gt; /etc/sysctl.conf sysctl -p sysctl net.ipv4.tcp_available_congestion_control lsmod | grep bbr 多功能BBR安装脚本\nwget -N --no-check-certificate \u0026#34;https://gist.github.com/zeruns/a0ec603f20d1b86de6a774a8ba27588f/raw/4f9957ae23f5efb2bb7c57a198ae2cffebfb1c56/tcp.sh\u0026#34; \u0026amp;\u0026amp; chmod +x tcp.sh \u0026amp;\u0026amp; ./tcp.sh TCP窗口调优 wget http://sh.nekoneko.cloud/tools.sh -O tools.sh \u0026amp;\u0026amp; bash tools.sh 测试访问优先级 curl ip.sb 添加SWAP wget https://www.moerats.com/usr/shell/swap.sh \u0026amp;\u0026amp; bash swap.sh 25端口开放测试 telnet smtp.aol.com 25 8. 一键安装常用环境及软件 docker 国外专用\ncurl -sSL https://get.docker.com/ | sh 国内专用\ncurl -sSL https://get.daocloud.io/docker | sh Python 来源：Python\ncurl -O https://raw.githubusercontent.com/lx969788249/lxspacepy/master/pyinstall.sh \u0026amp;\u0026amp; chmod +x pyinstall.sh \u0026amp;\u0026amp; ./pyinstall.sh WARP 来源：WARP\nwget -N https://gitlab.com/fscarmen/warp/-/raw/main/menu.sh \u0026amp;\u0026amp; bash menu.sh Aria2一键安装脚本 来源：Aria2一键安装脚本\nwget -N git.io/aria2.sh \u0026amp;\u0026amp; chmod +x aria2.sh \u0026amp;\u0026amp; ./aria2.sh aaPanel（宝塔国际版） 来源：aaPanel\nURL=https://www.aapanel.com/script/install_7.0_en.sh \u0026amp;\u0026amp; if [ -f /usr/bin/curl ];then curl -ksSO \u0026#34;$URL\u0026#34; ;else wget --no-check-certificate -O install_7.0_en.sh \u0026#34;$URL\u0026#34;;fi;bash install_7.0_en.sh aapanel 宝塔 来源：宝塔\nurl=https://download.bt.cn/install/install_lts.sh;if [ -f /usr/bin/curl ];then curl -sSO $url;else wget -O install_lts.sh $url;fi;bash install_lts.sh ed8484bec 宝塔开心版 来源：宝塔开心版\n访问：https://bt.sb/bbs/forum-37-1.html\n9. 综合功能脚本 科技lion 来源：科技lion\ncurl -sS -O https://kejilion.pro/kejilion.sh \u0026amp;\u0026amp; chmod +x kejilion.sh \u0026amp;\u0026amp; ./kejilion.sh SKY-BOX 来源：SKY-BOX\nwget -O box.sh https://raw.githubusercontent.com/BlueSkyXN/SKY-BOX/main/box.sh \u0026amp;\u0026amp; chmod +x box.sh \u0026amp;\u0026amp; cl 验证与自查 任意脚本执行前先 bash -n 检查语法或查看脚本源码，避免运行来源不明的命令 测试脚本（融合怪/yabs）输出中能看到 CPU、内存、磁盘、网络四类成绩 流媒体检测输出包含 Netflix: Yes 或对应解锁地区，说明 IP 质量达标 开启 BBR 后 sysctl net.ipv4.tcp_congestion_control 输出 bbr，且 lsmod | grep bbr 有结果 DD 重装前确认数据已备份，重装后用 SSH 能正常登录 参考 leitbogioro/Tools（DD 脚本） MoeClub/Note（萌咖 DD 脚本） spiritLHLS/ecs（融合怪） masonr/yet-another-bench-script（yabs） xykt/RegionRestrictionCheck（流媒体检测） i-abc/GB5（Geekbench 5 测试） i-abc/Speedtest（测速脚本） vpsxb/testrace（回程测试） kejilion/sh（科技lion） BlueSkyXN/SKY-BOX ","permalink":"https://heihei0299.github.io/posts/ops/vps%E5%B8%B8%E7%94%A8%E8%84%9A%E6%9C%AC/","summary":"\u003ch1 id=\"vps-常用脚本合集dd-重装测试与优化\"\u003eVPS 常用脚本合集：DD 重装、测试与优化\u003c/h1\u003e\n\u003cp\u003e本文适合经常折腾 VPS 的运维用户。你会学到：DD 重装 Linux/Windows 的脚本、服务器性能与流媒体解锁测试、测速与回程测试、BBR 与 SWAP 优化，以及 Docker/Python/WARP 等环境的一键安装命令。\u003c/p\u003e","title":"VPS 常用脚本合集：DD 重装、测试与优化"},{"content":"Windows 使用 Meta 内核 适用场景：不依赖 GUI 客户端，在 Windows 上直接使用 Clash.Meta 内核（命令行启动），并用 Yacd 网页面板配合 IIS 进行可视化控制。本文按步骤记录完整流程。\n1. 下载并解压 Meta 内核 下载 meta 内核并解压缩：\n//meta 内核下载地址 https://github.com/MetaCubeX/Clash.Meta/releases/download/v1.15.1/clash.meta-windows-amd64-v1.15.1.zip 为 meta 内核授予管理员权限。\n在防火墙中放行 meta 内核（tun 模式下，参数 systack:system 需要此项）。\n2. 准备 working 目录与规则数据 在 meta 同目录下，创建名为 working 的目录。\n下载 geosite.dat、geoip.dat、country.mmdb 文件：\n//geoip.dat 下载地址 https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat //geosite.dat 下载地址 https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat //country.mmdb 下载地址 https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country.mmdb 将 geosite.dat、geoip.dat、country.mmdb、config.yaml 放入 working 文件夹中。\n💡 配置文件需要重新命名为 config.yaml\n3. 命令行启动内核 在 meta 内核所在文件夹的地址栏输入 cmd，调出 cmd 窗口：\ncmd 中输入如下命令：\nclash.meta.exe -d working 4. 配置 Yacd 面板 下载 yacd 网页文件：\nhttps://github.com/MetaCubeX/Yacd-meta/archive/refs/tags/v0.3.7.zip 在 working 文件夹下新建 dashboard 文件夹，将 yacd 网页文件（解压后）放入其中。\n💡 config.yaml 中的参数 external-ui 必须为 external-ui: dashboard（存放 yacd 文件的相对目录）\n启用 IIS 服务：启用IIS\n为 yacd 网页文件授权：为网页文件授权\n浏览器输入 127.0.0.1: port：\n验证与自查 cmd 窗口内 clash.meta.exe 运行无报错，日志显示内核已启动 浏览器打开 127.0.0.1:\u0026lt;external-controller 端口\u0026gt; 能打开 Yacd 面板，并显示代理与节点列表 面板中能切换节点、看到实时流量，说明内核与面板连通 若使用 tun 模式，关闭代理后测试系统网络恢复正常（防火墙放行已生效） 参考 Clash.Meta 发布页（GitHub） meta-rules-dat（GitHub） Yacd-meta（GitHub） ","permalink":"https://heihei0299.github.io/posts/network/meta%E5%86%85%E6%A0%B8/windows%E4%BD%BF%E7%94%A8meta%E5%86%85%E6%A0%B8/windows%E4%BD%BF%E7%94%A8meta%E5%86%85%E6%A0%B8/","summary":"\u003ch1 id=\"windows-使用-meta-内核\"\u003eWindows 使用 Meta 内核\u003c/h1\u003e\n\u003cp\u003e适用场景：不依赖 GUI 客户端，在 Windows 上直接使用 Clash.Meta 内核（命令行启动），并用 Yacd 网页面板配合 IIS 进行可视化控制。本文按步骤记录完整流程。\u003c/p\u003e\n\u003ch2 id=\"1-下载并解压-meta-内核\"\u003e1. 下载并解压 Meta 内核\u003c/h2\u003e\n\u003cp\u003e下载 meta 内核并解压缩：\u003c/p\u003e","title":"Windows 使用 Meta 内核"},{"content":"Windows 修复引导 适用场景：Windows 开机直接进入恢复模式、黑屏或提示找不到引导文件（多出现在双系统 EFI 分区被覆盖、ESP 分区被格式化之后）。你会学到在 WinPE 环境下用 bcdboot 重建 UEFI 启动项的方法，以及 BCDBoot 命令的工作原理。\nPE 环境下使用 cmd 命令修复 选择优盘的 WinPE 启动盘启动系统。 使用 DG 分区软件，可看到 Esp 分区，然后右键菜单选择分配盘符。这样可以挂载引导分区。 格式化已经分配盘符的 esp 分区。 以管理员身份打开 cmd 执行以下命令： bcdboot C:\\Windows /s F: /f uefi /l zh-cn 各参数的具体含义：\nc:\\windows 系统安装目录，打开我的电脑，查看你的系统是安装在那个盘，就输入相应的盘符和目录。 /s S: 指定esp分区所在磁盘，小编指定ESP分区为t盘。 /f uefi 指定启动方式为uefi，注意之间的空格一定要输入。 /l zh-cn 指定uefi启动界面语言为简体中 Bcdboot 命令的原理及作用 在 win8/10 系统中有一条 BCDboot.exe 命令，它是一种快速设置系统启动分区或修复系统启动环境的命令行工具。BCDboot.exe 命令是通过从已安装的 Windows 系统文件夹中复制一小部分启动环境文件来设置/修复系统的。BCDboot 还会在系统分区上创建引导配置 BCD 文件，该文件存储了启动引导项，可让您选择引导已安装的 Windows。\n当系统无法启动时，用 U 盘或光盘启动 Windows PE 环境、或者使用 win8/10 的高级修复模式启动到命令符环境（推荐），然后运行 BCDboot 命令来修复损坏的系统。BCDboot 命令通常在 %WINDIR%\\System32 文件夹内，它运行后从计算机上已有的 Windows 映像复制一套启动环境文件到启动分区（GPT）/目录(MBR)中。\n这些启动环境文件包括：从已安装的系统 %WINDIR%\\boot\\efi 文件夹和 %WINDIR%\\System32\\boot 文件夹复制到系统启动分区中。在 UEFI+GPT 环境下，BCDboot 将文件复制到固件所标识的默认系统启动分区（ESP）。另外，BCDboot 还使用 %WINDIR%\\System32\\Config\\BCD-Template 文件为模板，在系统启动分区上创建新的 BCD（启动菜单）文件，并初始化 BCD 启动环境文件。可以在 BCD-Template 文件中定义特定的 BCD 设置(需要 BCD 文件编辑器)。\n在基于 BIOS 的系统上，系统分区是使用主引导记录 (MBR) 磁盘格式的磁盘上的活动分区。BCDboot 会在系统分区上创建 \\Boot 目录，并将所有需要的引导环境文件都复制到此目录中。\n在基于 UEFI(统一可扩展固件接口) 的系统上，EFI 系统分区是使用 GUID 分区表 (GPT) 磁盘格式的磁盘上的系统启动分区（ESP）。BCDboot 会创建 \\Efi\\Microsoft\\Boot 目录，并将所有需要的引导环境文件都复制到此目录中。\nBCDboot 命令行格式：\nBCDBOOT source [/llocale] [/svolume-letter] [/v] [/m [{OS Loader GUID}]] 验证与自查 重启后出现 Windows Boot Manager 引导项，能正常进入 Windows ESP 分区中存在 \\EFI\\Microsoft\\Boot\\bootmgfw.efi 文件 进入系统后运行 bcdedit /enum 能看到 {bootmgr} 与系统引导项 双系统用户修复后确认 efibootmgr 中 Windows Boot Manager 与 Linux 引导项并存 参考 Microsoft Learn - BCDboot 命令行选项 Microsoft Learn - WinPE 简介 ","permalink":"https://heihei0299.github.io/posts/linux/win%E4%BF%AE%E5%A4%8D%E5%BC%95%E5%AF%BC/","summary":"\u003ch1 id=\"windows-修复引导\"\u003eWindows 修复引导\u003c/h1\u003e\n\u003cp\u003e适用场景：Windows 开机直接进入恢复模式、黑屏或提示找不到引导文件（多出现在双系统 EFI 分区被覆盖、ESP 分区被格式化之后）。你会学到在 WinPE 环境下用 \u003ccode\u003ebcdboot\u003c/code\u003e 重建 UEFI 启动项的方法，以及 BCDBoot 命令的工作原理。\u003c/p\u003e","title":"Windows 修复引导"},{"content":"WSL 安装与 systemd 配置 本文适合在 Windows 上使用 WSL2 的开发者和运维用户。你会学到：一条命令安装 WSL2、启用 systemd 并用它管理服务（如 Docker）、通过 .wslconfig 限制资源占用，以及解决 systemctl --user 不可用的排障方法。\n现在，可以使用单个命令安装运行 WSL 所需的一切内容。 在管理员模式下打开 PowerShell 或 Windows 命令提示符，输入 wsl --install 命令，然后重启计算机。\nwsl --install 这会启用\u0026quot;虚拟机平台\u0026quot;以及\u0026quot;适用于 Linux 的 Windows 子系统\u0026quot;功能，并安装 Ubuntu 为 WSL2 的默认发行版。\n重启后系统会自动安装并启动 Ubuntu，我们需要设置用户名及密码。\n安装完成后，我们可以在 powershell 里查看 WSL 中发行版的版本。\n\u0026gt; wsl -l -v NAME STATE VERSION * Ubuntu Running 2 我们可以看到运行的 Ubuntu 已经是 WSL2 版本。\n启用 systemd 在 WSL 中修改 /etc/wsl.conf 文件\n[boot] systemd=true 然后在 powershell 中重启 WSL。\nwsl.exe --shutdown 重启后可以在 WSL 中进行验证\nps --no-headers -o comm 1 如果返回 systemd 说明启用成功。\n我们现在就可以通过 systemd 来管理服务了。例如开机自启动 docker\nsudo systemctl enable docker.service 添加用户组\nsudo usermod -aG docker $USER 我们还可以通过输入命令来查看 systemd 管理的服务状态。\nsystemctl list-unit-files --type=service 限制 WSL2 资源占用 WSL2 默认会占用系统一半的内存，我们可以通过配置文件来限制 WSL2 占用的资源。\n修改 Windows 用户目录下 .wslconfig 文件，例如\n[wsl2] processors=4 memory=2GB swap=2GB 限制 WSL2 使用 4 核处理器，2GB 内存以及 2GB 的交换空间。\n问题：没有启用 user 级别的 systemd 报错 ateway service runtime ────────────────────────────────────────────╮ │ │ │ QuickStart uses Node for the Gateway service (stable + supported). │ ├──────────────────────────────────────────────────────────────────────╯ Error: systemctl is-enabled unavailable: Command failed: systemctl --user is-enabled openclaw-gateway.service 原因 出现此错误的原因通常是因为 WSL2 虽然启用了 systemd（系统级），但没有正确初始化\u0026quot;用户级\u0026quot; (User-level) 的 systemd 实例或 D-Bus 会话。\nsystemctl --user 命令依赖于 XDG_RUNTIME_DIR 环境变量和用户 D-Bus 服务。在 WSL2 中，这些环境有时不会自动配置。\n以下是解决步骤：\n步骤 1：开启用户驻留 (Linger) 这是最关键的一步。它允许 systemd 用户实例在用户没有登录时也能运行，并自动创建必要的运行时目录。\n在 WSL2 终端执行：\nsudo loginctl enable-linger $(whoami) 步骤 2：配置环境变量 你需要手动指向用户运行目录。将以下内容添加到你的 ~/.bashrc（或 ~/.zshrc）文件中：\n# 在终端执行以下命令 echo \u0026#39;export XDG_RUNTIME_DIR=/run/user/$(id -u)\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 步骤 3：验证用户服务状态 执行以下命令确认用户级 systemd 是否已就绪：\nsystemctl --user status 正常结果：显示 State: running 且没有错误。 如果报错 \u0026ldquo;Failed to connect to bus\u0026rdquo;：请确保你没有使用 sudo 运行此命令。systemctl \u0026ndash;user 严禁使用 sudo。 步骤 4：重新安装 OpenClaw Gateway 服务 由于之前的安装因为检测失败而中断，现在请强制重新安装：\n# 强制覆盖安装 openclaw gateway install --force # 或者使用修复工具 openclaw doctor --repair 验证与自查 ps --no-headers -o comm 1 返回 systemd，说明 WSL2 系统级 systemd 已启用 systemctl --user status 显示 State: running，说明用户级 systemd 正常 free -h 中内存总量约为 .wslconfig 中配置的 memory 值 sudo systemctl enable docker.service 后，重启 WSL（wsl.exe --shutdown）Docker 自动运行 参考 微软官方文档：WSL 安装 微软官方文档：WSL 中启用 systemd 微软官方文档：.wslconfig 配置 systemd 用户实例文档（loginctl enable-linger） ","permalink":"https://heihei0299.github.io/posts/ops/wsl/","summary":"\u003ch1 id=\"wsl-安装与-systemd-配置\"\u003eWSL 安装与 systemd 配置\u003c/h1\u003e\n\u003cp\u003e本文适合在 Windows 上使用 WSL2 的开发者和运维用户。你会学到：一条命令安装 WSL2、启用 systemd 并用它管理服务（如 Docker）、通过 \u003ccode\u003e.wslconfig\u003c/code\u003e 限制资源占用，以及解决 \u003ccode\u003esystemctl --user\u003c/code\u003e 不可用的排障方法。\u003c/p\u003e","title":"WSL 安装与 systemd 配置"},{"content":"Zsh 安装和美化指南 这份文档基于当前机器上的 ~/.zshrc 与 ~/.zsh_aliases 整理，目标是从零安装并复现一套兼顾颜值和效率的 zsh 环境。以下命令以 Debian/Ubuntu 系发行版为例。\n当前方案的核心组件如下：\nzsh：Shell 本体 zinit：插件管理器 starship：跨 Shell 提示符 fnm：Node.js 版本管理 zsh-autosuggestions：命令自动建议 zsh-syntax-highlighting：命令语法高亮 zsh-history-substring-search：历史记录子串搜索 zsh-z：目录跳转增强 fzf-tab：更友好的补全体验 lsd、batcat、tldr、ncdu、fdfind：常用命令增强工具 效果概览 这套配置完成后，你会得到：\n更现代的命令提示符 输入历史命令时的灰色自动建议 命令语法高亮 Tab 补全增强 常用目录快速跳转 更美观的 ls、cat、帮助查看和磁盘分析体验 安装基础软件 sudo apt update sudo apt install -y zsh git curl unzip wget 安装补全工具 fzf：\napt install fzf 安装常用增强工具：\nsudo apt install -y lsd bat fd-find ncdu 安装 tldr（需要配置 node 和 nvm）：\nnpm i -g tldr 如果系统仓库没有 tldr，可按官方方式单独安装。\n部分工具说明：\nbat：代替 cat，带语法高亮（Debian/Ubuntu 上命令名是 batcat） fd：代替 find（Debian/Ubuntu 上命令名是 fdfind） tldr：简版 man 手册，npm 全局安装 切换默认 Shell 为 zsh 先确认 zsh 路径：\nwhich zsh 切换默认 Shell：\nchsh -s \u0026#34;$(which zsh)\u0026#34; 执行后重新登录，或者临时进入：\nexec zsh 安装 Starship starship 用于生成更现代的提示符。\ncurl -sS https://starship.rs/install.sh | sh 安装完成后确认版本：\nstarship --version 安装 fnm fnm 是一个轻量的 Node.js 版本管理器。\ncurl -fsSL https://fnm.vercel.app/install | bash 安装完成后，重新打开终端，或手动加载环境后检查：\nfnm --version 示例：\nfnm install 24 准备配置文件 建议先备份现有配置：\ncp ~/.zshrc ~/.zshrc.bak 2\u0026gt;/dev/null cp ~/.zsh_aliases ~/.zsh_aliases.bak 2\u0026gt;/dev/null ~/.zshrc 将下面内容写入 ~/.zshrc：\n# ~/.zshrc # 项目或私有环境变量。 # 如需提高安全性，建议迁移到单独的私有文件中再 source。 export AUTH_TOKEN=123 export PROXY_TOKEN=123 # ----------------------------------------------------------------------------- # 工具链初始化 # ----------------------------------------------------------------------------- # fnm FNM_PATH=\u0026#34;/home/shial/.local/share/fnm\u0026#34; if [ -d \u0026#34;$FNM_PATH\u0026#34; ]; then export PATH=\u0026#34;$FNM_PATH:$PATH\u0026#34; eval \u0026#34;$(fnm env --shell zsh)\u0026#34; fi # 加载 .zsh_alias source .zsh_aliases # ----------------------------------------------------------------------------- # 历史记录与 shell 选项 # ----------------------------------------------------------------------------- # 控制历史记录的数量和保存位置。 HISTCONTROL=ignoreboth HISTSIZE=1000 SAVEHIST=2000 HISTFILE=\u0026#34;$HOME/.zsh_history\u0026#34; # 追加写入历史、忽略重复项和以空格开头的命令，并在多窗口间共享历史。 setopt APPEND_HISTORY setopt HIST_IGNORE_DUPS setopt HIST_IGNORE_SPACE setopt HIST_REDUCE_BLANKS setopt SHARE_HISTORY # ----------------------------------------------------------------------------- # 终端行为 # ----------------------------------------------------------------------------- # 在每次显示提示符前刷新终端尺寸变量，避免窗口缩放后行列数不准确。 autoload -Uz add-zsh-hook update_terminal_size() { export LINES COLUMNS } add-zsh-hook precmd update_terminal_size # ----------------------------------------------------------------------------- # Zinit 插件管理 # ----------------------------------------------------------------------------- # Zinit 安装目录；若不存在则自动克隆。 ZINIT_HOME=\u0026#34;${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git\u0026#34; [[ ! -d \u0026#34;$ZINIT_HOME\u0026#34; ]] \u0026amp;\u0026amp; mkdir -p \u0026#34;$(dirname \u0026#34;$ZINIT_HOME\u0026#34;)\u0026#34; [[ ! -d \u0026#34;$ZINIT_HOME/.git\u0026#34; ]] \u0026amp;\u0026amp; git clone https://github.com/zdharma-continuum/zinit.git \u0026#34;$ZINIT_HOME\u0026#34; source \u0026#34;$ZINIT_HOME/zinit.zsh\u0026#34; # 注册 Zinit 自身补全。 autoload -Uz _zinit (( ${+_comps} )) \u0026amp;\u0026amp; _comps[zinit]=_zinit # ==================== 插件配置（按需添加） ==================== # 🔹 必装基础插件 # 先加载补全扩展，再初始化 compinit，使扩展补全立即生效。 zinit light zsh-users/zsh-completions # 丰富的命令补全库 zinit light zsh-users/zsh-syntax-highlighting # 命令语法高亮 # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # 交互增强插件 # ----------------------------------------------------------------------------- # 🔹 异步延迟加载（不阻塞启动） zinit ice lucid wait=\u0026#39;0\u0026#39; atload=\u0026#39;_zsh_autosuggest_start\u0026#39; zinit light zsh-users/zsh-autosuggestions # 历史命令自动提示 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light agkozak/zsh-z # 快速目录跳转 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light Aloxaf/fzf-tab # fzf 命令补全 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light zsh-users/zsh-history-substring-search # ==================== 初始化补全 ==================== autoload -Uz compinit compinit # ----------------------------------------------------------------------------- # 使用 starship 作为提示符；放在最后初始化，避免提示符被后续配置覆盖。 eval \u0026#34;$(starship init zsh)\u0026#34; 配置 zsh 主题 使用 starship 快速配置 zsh 主题：\nstarship preset pastel-powerline -o ~/.config/starship.toml 配置说明 插件部分 当前插件组合分别解决不同问题：\nzsh-users/zsh-completions：补充更多命令补全定义 Aloxaf/fzf-tab：让补全选择界面更友好 zsh-users/zsh-autosuggestions：根据历史命令提供自动建议 zsh-users/zsh-history-substring-search：根据当前输入检索历史 agkozak/zsh-z：根据访问频率快速跳转目录 zsh-users/zsh-syntax-highlighting：命令输入时直接高亮 其中 zsh-syntax-highlighting 建议保持最后加载，否则容易被后续配置影响效果。\n历史记录配置 这部分配置解决的是命令历史的可用性问题：\nAPPEND_HISTORY：历史记录追加写入 HIST_IGNORE_DUPS：忽略重复命令 HIST_IGNORE_SPACE：忽略以空格开头的命令 SHARE_HISTORY：多个终端共享历史记录 别名部分 这些别名主要做了两类事情：命令替换和体验增强。\nls 替换成 lsd，目录展示更直观 cat 替换成 batcat，带语法高亮 man 映射到 tldr，适合快速查命令用法 du 映射到 ncdu，适合交互式查看磁盘占用 fd 映射到 fdfind -HI，默认显示隐藏文件并忽略系统命名差异 Nerd Font 字体设置 如果你希望 lsd 图标、提示符符号显示正常，终端需要使用 Nerd Font。\n常见可选字体：\nMesloLGS Nerd Font JetBrainsMono Nerd Font Hack Nerd Font 手动安装 Nerd Font 下面以 JetBrainsMono Nerd Font 为例。\n创建用户字体目录： mkdir -p ~/.local/share/fonts cd /tmp 下载字体压缩包： wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/0xProto.zip 如果你想装 Meslo 或 Hack，把文件名替换成：\nMeslo.zip Hack.zip 解压到本地字体目录： unzip -o JetBrainsMono.zip -d ~/.local/share/fonts/JetBrainsMonoNerdFont 刷新字体缓存： fc-cache -fv 检查字体是否已被系统识别： fc-list | grep \u0026#34;JetBrainsMono Nerd Font\u0026#34; 或者 fc-list | ge \u0026#34;JetBrainsMono Nerd Font\u0026#34; 如果能看到相关输出，说明字体已经安装成功。\n终端中启用字体 字体安装完成后，还需要在终端模拟器里手动切换字体，否则图标仍然不会正常显示。\n常见设置路径如下：\nGNOME Terminal：首选项 -\u0026gt; 你的配置 -\u0026gt; 文本 -\u0026gt; 自定义字体 Warp/Tabby/Termius：Settings -\u0026gt; Appearance -\u0026gt; Font Windows Terminal：Profiles -\u0026gt; Appearance -\u0026gt; Font face Alacritty：在配置文件中设置 font.family 推荐直接选择：\nJetBrainsMono Nerd Font MesloLGS Nerd Font Hack Nerd Font 一键安装脚本方式 如果你不想手动下载，也可以直接使用 Nerd Fonts 官方仓库脚本，但前提是本机已安装 git：\ngit clone --depth 1 https://github.com/ryanoasis/nerd-fonts.git /tmp/nerd-fonts cd /tmp/nerd-fonts ./install.sh JetBrainsMono 安装完成后同样执行：\nfc-cache -fv 常见问题排查 如果字体已经安装，但图标仍然显示异常，按这个顺序排查：\n确认终端里实际选中的字体就是 Nerd Font，而不是原版字体 关闭并重新打开终端 执行 fc-list | rg \u0026quot;Nerd Font\u0026quot;，确认系统能识别字体 如果是远程桌面或某些轻量终端，确认该终端本身支持图标字符显示 设置方法 下载并安装 Nerd Font 在终端模拟器设置中把字体切换为对应 Nerd Font 重新打开终端 如果图标显示成方块或乱码，通常就是字体没有切换成功。\n使配置生效 执行：\nsource ~/.zshrc 如果没有报错，再验证各项功能。\n验证命令 echo $SHELL zsh --version starship --version fnm --version 继续检查别名是否生效：\nls ll la lt cat ~/.zshrc tldr ls ncdu fd zsh 检查 z 是否可用：\nz /tmp 如果你还没有足够的目录访问历史，z 一开始效果不明显，属于正常现象。\n常见问题 source ~/.zshrc 报错找不到别名文件 请确认文件名是：\n~/.zsh_aliases 不是：\n~/.zsh_alias 当前配置实际使用的是 ~/.zsh_aliases。\nfnm 不生效 先确认目录存在：\nls ~/.local/share/fnm 再确认 ~/.zshrc 中已包含：\nFNM_PATH=\u0026#34;$HOME/.local/share/fnm\u0026#34; if [ -d \u0026#34;$FNM_PATH\u0026#34; ]; then export PATH=\u0026#34;$FNM_PATH:$PATH\u0026#34; eval \u0026#34;$(fnm env --shell zsh)\u0026#34; fi 图标乱码 优先排查两项：\n终端字体是否换成 Nerd Font 当前终端是否支持图标显示 batcat、fdfind 命令名和教程不同 这是 Debian/Ubuntu 系常见差异：\nbat 包名通常提供命令 batcat fd 包名通常提供命令 fdfind 所以通过别名统一成常见用法是合理做法。\n安全建议 你当前 ~/.zshrc 中包含：\nexport AUTH_TOKEN=123 export PROXY_TOKEN=123 如果这些变量是真实敏感信息，建议改成单独的私有文件，例如：\n[ -f \u0026#34;$HOME/.zsh_private\u0026#34; ] \u0026amp;\u0026amp; source \u0026#34;$HOME/.zsh_private\u0026#34; 然后把敏感变量放进 ~/.zsh_private，并确保该文件不被公开同步。\n总结 这套 zsh 配置的思路不是堆很多主题，而是用一组明确分工的工具完成体验升级：\nzinit 管插件 starship 管提示符 fnm 管 Node 版本 各类插件负责建议、高亮、补全和目录跳转 别名把日常命令替换成更友好的版本 如果你要在新机器上复刻环境，按本文顺序执行即可：先装 zsh 和依赖，再写入 ~/.zshrc 与 ~/.zsh_aliases，最后切换字体并重新加载配置。\n参考 Zsh 官方文档 zinit 插件管理器 Starship 官方文档 fnm 官方仓库 Nerd Fonts 官方仓库 ","permalink":"https://heihei0299.github.io/posts/linux/zsh%E5%AE%89%E8%A3%85%E5%92%8C%E7%BE%8E%E5%8C%96%E6%8C%87%E5%8D%97/","summary":"\u003ch1 id=\"zsh-安装和美化指南\"\u003eZsh 安装和美化指南\u003c/h1\u003e\n\u003cp\u003e这份文档基于当前机器上的 \u003ccode\u003e~/.zshrc\u003c/code\u003e 与 \u003ccode\u003e~/.zsh_aliases\u003c/code\u003e 整理，目标是从零安装并复现一套兼顾颜值和效率的 \u003ccode\u003ezsh\u003c/code\u003e 环境。以下命令以 Debian/Ubuntu 系发行版为例。\u003c/p\u003e\n\u003cp\u003e当前方案的核心组件如下：\u003c/p\u003e","title":"Zsh 安装和美化指南"},{"content":"Zsh 安装和美化指南 本文适合想在新机器上从零搭建一套高颜值 zsh 环境的用户。你会学到：安装 zsh 及增强工具、切换默认 shell、配置 Starship 提示符与 fnm、写入 ~/.zshrc 和 ~/.zsh_aliases，以及安装 Nerd Font 并逐项验证。\n这份文档基于当前机器上的 ~/.zshrc 与 ~/.zsh_aliases 整理，目标是从零安装并复现一套兼顾颜值和效率的 zsh 环境。\n当前方案的核心组件如下：\nzsh：Shell 本体 zinit：插件管理器 starship：跨 Shell 提示符 fnm：Node.js 版本管理 zsh-autosuggestions：命令自动建议 zsh-syntax-highlighting：命令语法高亮 zsh-history-substring-search：历史记录子串搜索 zsh-z：目录跳转增强 fzf-tab：更友好的补全体验 lsd、batcat、tldr、ncdu、fdfind：常用命令增强工具 1. 效果概览 这套配置完成后，你会得到：\n更现代的命令提示符 输入历史命令时的灰色自动建议 命令语法高亮 Tab 补全增强 常用目录快速跳转 更美观的 ls、cat、帮助查看和磁盘分析体验 2. 安装基础软件 以下命令以 Debian/Ubuntu 系发行版为例。\nsudo apt update sudo apt install -y zsh git curl unzip wget 安装补全工具fzf\napt install fzf 安装常用增强工具：\nsudo apt install -y lsd bat fd-find ncdu 安装 tldr：\n需要配置node和nvm\nnpm i -g tldr 如果系统仓库没有 tldr，可按官方方式单独安装。\n3. 切换默认 Shell 为 zsh 先确认 zsh 路径：\nwhich zsh 切换默认 Shell：\nchsh -s \u0026#34;$(which zsh)\u0026#34; 执行后重新登录，或者临时进入：\nexec zsh 4. 安装 Starship starship 用于生成更现代的提示符。\ncurl -sS https://starship.rs/install.sh | sh 安装完成后确认版本：\nstarship --version 5. 安装 fnm fnm 是一个轻量的 Node.js 版本管理器。\ncurl -fsSL https://fnm.vercel.app/install | bash 安装完成后，重新打开终端，或手动加载环境后检查：\nfnm --version 示例：\nfnm install 24 6. 准备配置文件 建议先备份现有配置：\ncp ~/.zshrc ~/.zshrc.bak 2\u0026gt;/dev/null cp ~/.zsh_aliases ~/.zsh_aliases.bak 2\u0026gt;/dev/null 6.1 ~/.zshrc 将下面内容写入 ~/.zshrc：\n# ~/.zshrc # 仅在交互式 zsh 会话中加载；用于初始化环境变量、别名、补全、插件和提示符。 # ----------------------------------------------------------------------------- # 基础环境变量 # ----------------------------------------------------------------------------- if [[ -z \u0026#34;${debian_chroot:-}\u0026#34; \u0026amp;\u0026amp; -r /etc/debian_chroot ]]; then debian_chroot=\u0026#34;$(\u0026lt; /etc/debian_chroot)\u0026#34; fi # 项目或私有环境变量。 # 更推荐迁移到单独的私有文件中再 source，避免敏感信息直接写入公开配置。 export AUTH_TOKEN=123 export PROXY_TOKEN=123 # ----------------------------------------------------------------------------- # PATH # ----------------------------------------------------------------------------- typeset -U path PATH # ----------------------------------------------------------------------------- # 工具链初始化 # ----------------------------------------------------------------------------- FNM_PATH=\u0026#34;$HOME/.local/share/fnm\u0026#34; if [ -d \u0026#34;$FNM_PATH\u0026#34; ]; then export PATH=\u0026#34;$FNM_PATH:$PATH\u0026#34; eval \u0026#34;$(fnm env --shell zsh)\u0026#34; fi source \u0026#34;$HOME/.zsh_aliases\u0026#34; # ----------------------------------------------------------------------------- # 历史记录与 shell 选项 # ----------------------------------------------------------------------------- HISTCONTROL=ignoreboth HISTSIZE=1000 SAVEHIST=2000 HISTFILE=\u0026#34;$HOME/.zsh_history\u0026#34; setopt APPEND_HISTORY setopt HIST_IGNORE_DUPS setopt HIST_IGNORE_SPACE setopt HIST_REDUCE_BLANKS setopt SHARE_HISTORY # ----------------------------------------------------------------------------- # 终端行为 # ----------------------------------------------------------------------------- autoload -Uz add-zsh-hook update_terminal_size() { export LINES COLUMNS } add-zsh-hook precmd update_terminal_size # ----------------------------------------------------------------------------- # Zinit 插件管理 # ----------------------------------------------------------------------------- ZINIT_HOME=\u0026#34;${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git\u0026#34; [[ ! -d \u0026#34;$ZINIT_HOME\u0026#34; ]] \u0026amp;\u0026amp; mkdir -p \u0026#34;$(dirname \u0026#34;$ZINIT_HOME\u0026#34;)\u0026#34; [[ ! -d \u0026#34;$ZINIT_HOME/.git\u0026#34; ]] \u0026amp;\u0026amp; git clone https://github.com/zdharma-continuum/zinit.git \u0026#34;$ZINIT_HOME\u0026#34; source \u0026#34;$ZINIT_HOME/zinit.zsh\u0026#34; autoload -Uz _zinit (( ${+_comps} )) \u0026amp;\u0026amp; _comps[zinit]=_zinit zinit light zsh-users/zsh-completions zstyle \u0026#39;:completion:*\u0026#39; menu select # ----------------------------------------------------------------------------- # 补全系统 # ----------------------------------------------------------------------------- autoload -Uz compinit compinit zinit light Aloxaf/fzf-tab # ----------------------------------------------------------------------------- # 交互增强插件 # ----------------------------------------------------------------------------- zinit light zsh-users/zsh-autosuggestions zinit light zsh-users/zsh-history-substring-search zinit light agkozak/zsh-z zinit light zsh-users/zsh-syntax-highlighting # ----------------------------------------------------------------------------- # 提示符 # ----------------------------------------------------------------------------- eval \u0026#34;$(starship init zsh)\u0026#34; 6.2 ~/.zsh_aliases 将下面内容写入 ~/.zsh_aliases：\n# lsd 替换 ls（带图标，需要 Nerd Fonts） alias ls=\u0026#39;lsd --group-dirs first\u0026#39; alias ll=\u0026#39;ls -l\u0026#39; alias la=\u0026#39;ls -al\u0026#39; alias lt=\u0026#39;ls --tree --depth=2\u0026#39; alias cat=\u0026#39;batcat\u0026#39; alias man=\u0026#39;tldr\u0026#39; alias du=\u0026#39;ncdu\u0026#39; alias fd=\u0026#39;fdfind -HI\u0026#39; 6.3 配置zsh主题 使用startship快速配置zsh主题\nstarship preset pastel-powerline -o ~/.config/starship.toml 7. 配置说明 7.1 插件部分 当前插件组合分别解决不同问题：\nzsh-users/zsh-completions：补充更多命令补全定义 Aloxaf/fzf-tab：让补全选择界面更友好 zsh-users/zsh-autosuggestions：根据历史命令提供自动建议 zsh-users/zsh-history-substring-search：根据当前输入检索历史 agkozak/zsh-z：根据访问频率快速跳转目录 zsh-users/zsh-syntax-highlighting：命令输入时直接高亮 其中 zsh-syntax-highlighting 建议保持最后加载，否则容易被后续配置影响效果。\n7.2 历史记录配置 这部分配置解决的是命令历史的可用性问题：\nAPPEND_HISTORY：历史记录追加写入 HIST_IGNORE_DUPS：忽略重复命令 HIST_IGNORE_SPACE：忽略以空格开头的命令 SHARE_HISTORY：多个终端共享历史记录 7.3 别名部分 这些别名主要做了两类事情：命令替换和体验增强。\nls 替换成 lsd，目录展示更直观 cat 替换成 batcat，带语法高亮 man 映射到 tldr，适合快速查命令用法 du 映射到 ncdu，适合交互式查看磁盘占用 fd 映射到 fdfind -HI，默认显示隐藏文件并忽略系统命名差异 8. Nerd Font 字体设置 如果你希望 lsd 图标、提示符符号显示正常，终端需要使用 Nerd Font。\n常见可选字体：\nMesloLGS Nerd Font JetBrainsMono Nerd Font Hack Nerd Font 8.1 手动安装 Nerd Font 下面以 JetBrainsMono Nerd Font 为例。\n创建用户字体目录：\nmkdir -p ~/.local/share/fonts cd /tmp 下载字体压缩包：\nwget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/0xProto.zip 如果你想装 Meslo 或 Hack，把文件名替换成：\nMeslo.zip Hack.zip 解压到本地字体目录：\nunzip -o JetBrainsMono.zip -d ~/.local/share/fonts/JetBrainsMonoNerdFont 刷新字体缓存：\nfc-cache -fv 检查字体是否已被系统识别：\nfc-list | grep \u0026#34;JetBrainsMono Nerd Font\u0026#34; 或者 fc-list | ge \u0026#34;JetBrainsMono Nerd Font\u0026#34; 如果能看到相关输出，说明字体已经安装成功。\n8.2 终端中启用字体 字体安装完成后，还需要在终端模拟器里手动切换字体，否则图标仍然不会正常显示。\n常见设置路径如下：\nGNOME Terminal：首选项 -\u0026gt; 你的配置 -\u0026gt; 文本 -\u0026gt; 自定义字体 Warp/Tabby/Termius：Settings -\u0026gt; Appearance -\u0026gt; Font Windows Terminal：Profiles -\u0026gt; Appearance -\u0026gt; Font face Alacritty：在配置文件中设置 font.family 推荐直接选择：\nJetBrainsMono Nerd Font MesloLGS Nerd Font Hack Nerd Font 8.3 一键安装脚本方式 如果你不想手动下载，也可以直接使用 Nerd Fonts 官方仓库脚本，但前提是本机已安装 git：\ngit clone --depth 1 https://github.com/ryanoasis/nerd-fonts.git /tmp/nerd-fonts cd /tmp/nerd-fonts ./install.sh JetBrainsMono 安装完成后同样执行：\nfc-cache -fv 8.4 常见问题排查 如果字体已经安装，但图标仍然显示异常，按这个顺序排查：\n确认终端里实际选中的字体就是 Nerd Font，而不是原版字体 关闭并重新打开终端 执行 fc-list | rg \u0026quot;Nerd Font\u0026quot;，确认系统能识别字体 如果是远程桌面或某些轻量终端，确认该终端本身支持图标字符显示 8.5 设置方法 下载并安装 Nerd Font 在终端模拟器设置中把字体切换为对应 Nerd Font 重新打开终端 如果图标显示成方块或乱码，通常就是字体没有切换成功。\n9. 使配置生效 执行：\nsource ~/.zshrc 如果没有报错，再验证各项功能。\n10. 验证命令 echo $SHELL zsh --version starship --version fnm --version 继续检查别名是否生效：\nls ll la lt cat ~/.zshrc tldr ls ncdu fd zsh 检查 z 是否可用：\nz /tmp 如果你还没有足够的目录访问历史，z 一开始效果不明显，属于正常现象。\n11. 常见问题 11.1 source ~/.zshrc 报错找不到别名文件 请确认文件名是：\n~/.zsh_aliases 不是：\n~/.zsh_alias 当前配置实际使用的是 ~/.zsh_aliases。\n11.2 fnm 不生效 先确认目录存在：\nls ~/.local/share/fnm 再确认 ~/.zshrc 中已包含：\nFNM_PATH=\u0026#34;$HOME/.local/share/fnm\u0026#34; if [ -d \u0026#34;$FNM_PATH\u0026#34; ]; then export PATH=\u0026#34;$FNM_PATH:$PATH\u0026#34; eval \u0026#34;$(fnm env --shell zsh)\u0026#34; fi 11.3 图标乱码 优先排查两项：\n终端字体是否换成 Nerd Font 当前终端是否支持图标显示 11.4 batcat、fdfind 命令名和教程不同 这是 Debian/Ubuntu 系常见差异：\nbat 包名通常提供命令 batcat fd 包名通常提供命令 fdfind 所以通过别名统一成常见用法是合理做法。\n12. 安全建议 你当前 ~/.zshrc 中包含：\nexport AUTH_TOKEN=123 export PROXY_TOKEN=123 如果这些变量是真实敏感信息，建议改成单独的私有文件，例如：\n[ -f \u0026#34;$HOME/.zsh_private\u0026#34; ] \u0026amp;\u0026amp; source \u0026#34;$HOME/.zsh_private\u0026#34; 然后把敏感变量放进 ~/.zsh_private，并确保该文件不被公开同步。\n13. 总结 这套 zsh 配置的思路不是堆很多主题，而是用一组明确分工的工具完成体验升级：\nzinit 管插件 starship 管提示符 fnm 管 Node 版本 各类插件负责建议、高亮、补全和目录跳转 别名把日常命令替换成更友好的版本 如果你要在新机器上复刻环境，按本文顺序执行即可：先装 zsh 和依赖，再写入 ~/.zshrc 与 ~/.zsh_aliases，最后切换字体并重新加载配置。\n参考 Zsh 官方文档 Starship 官网 zinit 官方仓库 fnm（Fast Node Manager） Nerd Fonts 官网 ","permalink":"https://heihei0299.github.io/posts/ops/zsh%E5%AE%89%E8%A3%85%E5%92%8C%E7%BE%8E%E5%8C%96%E6%8C%87%E5%8D%97/","summary":"\u003ch1 id=\"zsh-安装和美化指南\"\u003eZsh 安装和美化指南\u003c/h1\u003e\n\u003cp\u003e本文适合想在新机器上从零搭建一套高颜值 zsh 环境的用户。你会学到：安装 zsh 及增强工具、切换默认 shell、配置 Starship 提示符与 fnm、写入 \u003ccode\u003e~/.zshrc\u003c/code\u003e 和 \u003ccode\u003e~/.zsh_aliases\u003c/code\u003e，以及安装 Nerd Font 并逐项验证。\u003c/p\u003e","title":"Zsh 安装和美化指南"},{"content":"Zsh 安装和美化指南（Arch Linux 版） 这份文档基于当前机器上的 ~/.zshrc 与 ~/.zsh_aliases 整理，目标是从零安装并复现一套兼顾颜值和效率的 zsh 环境。以下命令以 Arch Linux 为例。\n当前方案的核心组件如下：\nzsh：Shell 本体 zinit：插件管理器 starship：跨 Shell 提示符 fnm：Node.js 版本管理 zsh-autosuggestions：命令自动建议 zsh-syntax-highlighting：命令语法高亮 zsh-history-substring-search：历史记录子串搜索 zsh-z：目录跳转增强 fzf-tab：更友好的补全体验 lsd、bat、tldr、ncdu、fd：常用命令增强工具 效果概览 这套配置完成后，你会得到：\n更现代的命令提示符 输入历史命令时的灰色自动建议 命令语法高亮 Tab 补全增强 常用目录快速跳转 更美观的 ls、cat、帮助查看和磁盘分析体验 安装基础软件 sudo pacman -S --noconfirm zsh git curl unzip wget fzf lsd bat fd ncdu tldr 部分工具说明：\nbat：代替 cat，带语法高亮（Arch 上命令名就是 bat，不是 batcat） fd：代替 find（Arch 上命令名就是 fd，不是 fdfind） tldr：来自 community 仓库的社区版 tldr，无需 npm 如果要用 npm 版 tldr 也可以：\nnpm i -g tldr 切换默认 Shell 为 zsh 先确认 zsh 路径：\nwhich zsh 切换默认 Shell：\nchsh -s \u0026#34;$(which zsh)\u0026#34; 执行后重新登录，或者临时进入：\nexec zsh 安装 Starship starship 用于生成更现代的提示符。\n方式一（推荐，从 Arch 官方仓库安装）：\nsudo pacman -S starship 方式二（官方安装脚本）：\ncurl -sS https://starship.rs/install.sh | sh 安装完成后确认版本：\nstarship --version 安装 fnm fnm 是一个轻量的 Node.js 版本管理器。\n方式一（AUR，需要已安装 AUR Helper 如 yay / paru）：\nyay -S fnm-bin 方式二（官方安装脚本）：\ncurl -fsSL https://fnm.vercel.app/install | bash 安装完成后，重新打开终端，或手动加载环境后检查：\nfnm --version 示例：\nfnm install 24 准备配置文件 建议先备份现有配置：\ncp ~/.zshrc ~/.zshrc.bak 2\u0026gt;/dev/null cp ~/.zsh_aliases ~/.zsh_aliases.bak 2\u0026gt;/dev/null ~/.zshrc 将下面内容写入 ~/.zshrc：\n# ~/.zshrc # 项目或私有环境变量。 # 如需提高安全性，建议迁移到单独的私有文件中再 source。 export AUTH_TOKEN=123 export PROXY_TOKEN=123 # ----------------------------------------------------------------------------- # 工具链初始化 # ----------------------------------------------------------------------------- # fnm FNM_PATH=\u0026#34;$HOME/.local/share/fnm\u0026#34; if [ -d \u0026#34;$FNM_PATH\u0026#34; ]; then export PATH=\u0026#34;$FNM_PATH:$PATH\u0026#34; eval \u0026#34;$(fnm env --shell zsh)\u0026#34; fi # 加载 .zsh_alias source .zsh_aliases # ----------------------------------------------------------------------------- # 历史记录与 shell 选项 # ----------------------------------------------------------------------------- # 控制历史记录的数量和保存位置。 # 注意：HISTCONTROL 是 bash 变量，在 zsh 中无效。下面通过 setopt 实现同样的效果。 HISTSIZE=1000 SAVEHIST=2000 HISTFILE=\u0026#34;$HOME/.zsh_history\u0026#34; # 追加写入历史、忽略重复项和以空格开头的命令，并在多窗口间共享历史。 setopt APPEND_HISTORY setopt HIST_IGNORE_DUPS setopt HIST_IGNORE_SPACE setopt HIST_REDUCE_BLANKS setopt SHARE_HISTORY # ----------------------------------------------------------------------------- # 终端行为 # ----------------------------------------------------------------------------- # 在每次显示提示符前刷新终端尺寸变量，避免窗口缩放后行列数不准确。 autoload -Uz add-zsh-hook update_terminal_size() { export LINES COLUMNS } add-zsh-hook precmd update_terminal_size # ----------------------------------------------------------------------------- # Zinit 插件管理 # ----------------------------------------------------------------------------- # Zinit 安装目录；若不存在则自动克隆。 ZINIT_HOME=\u0026#34;${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git\u0026#34; [[ ! -d \u0026#34;$ZINIT_HOME\u0026#34; ]] \u0026amp;\u0026amp; mkdir -p \u0026#34;$(dirname \u0026#34;$ZINIT_HOME\u0026#34;)\u0026#34; [[ ! -d \u0026#34;$ZINIT_HOME/.git\u0026#34; ]] \u0026amp;\u0026amp; git clone https://github.com/zdharma-continuum/zinit.git \u0026#34;$ZINIT_HOME\u0026#34; source \u0026#34;$ZINIT_HOME/zinit.zsh\u0026#34; # 注册 Zinit 自身补全。 autoload -Uz _zinit (( ${+_comps} )) \u0026amp;\u0026amp; _comps[zinit]=_zinit # ==================== 插件配置（按需添加） ==================== # 🔹 必装基础插件 # 先加载补全扩展，再初始化 compinit，使扩展补全立即生效。 zinit light zsh-users/zsh-completions # 丰富的命令补全库 zinit light zsh-users/zsh-syntax-highlighting # 命令语法高亮 # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # 交互增强插件 # ----------------------------------------------------------------------------- # 🔹 异步延迟加载（不阻塞启动） zinit ice lucid wait=\u0026#39;0\u0026#39; atload=\u0026#39;_zsh_autosuggest_start\u0026#39; zinit light zsh-users/zsh-autosuggestions # 历史命令自动提示 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light agkozak/zsh-z # 快速目录跳转 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light Aloxaf/fzf-tab # fzf 命令补全 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light zsh-users/zsh-history-substring-search # ==================== 初始化补全 ==================== autoload -Uz compinit compinit # ----------------------------------------------------------------------------- # 使用 starship 作为提示符；放在最后初始化，避免提示符被后续配置覆盖。 eval \u0026#34;$(starship init zsh)\u0026#34; 配置 zsh 主题 使用 starship 快速配置 zsh 主题：\nstarship preset pastel-powerline -o ~/.config/starship.toml 配置说明 插件部分 当前插件组合分别解决不同问题：\nzsh-users/zsh-completions：补充更多命令补全定义 Aloxaf/fzf-tab：让补全选择界面更友好 zsh-users/zsh-autosuggestions：根据历史命令提供自动建议 zsh-users/zsh-history-substring-search：根据当前输入检索历史 agkozak/zsh-z：根据访问频率快速跳转目录 zsh-users/zsh-syntax-highlighting：命令输入时直接高亮 其中 zsh-syntax-highlighting 建议保持最后加载，否则容易被后续配置影响效果。\n历史记录配置 这部分配置解决的是命令历史的可用性问题：\nAPPEND_HISTORY：历史记录追加写入 HIST_IGNORE_DUPS：忽略重复命令 HIST_IGNORE_SPACE：忽略以空格开头的命令 SHARE_HISTORY：多个终端共享历史记录 别名部分 这些别名主要做了两类事情：命令替换和体验增强。\nls 替换成 lsd，目录展示更直观 cat 替换成 bat，带语法高亮（Arch 上命令名就是 bat，无需别名） man 映射到 tldr，适合快速查命令用法 du 映射到 ncdu，适合交互式查看磁盘占用 fd 映射到 fd -HI，默认显示隐藏文件 Nerd Font 字体设置 如果你希望 lsd 图标、提示符符号显示正常，终端需要使用 Nerd Font。\n常见可选字体：\nMesloLGS Nerd Font JetBrainsMono Nerd Font Hack Nerd Font 通过 pacman 安装 Arch Linux 官方仓库直接提供了 Nerd Font 包，一行命令搞定：\n# JetBrainsMono Nerd Font（推荐） sudo pacman -S ttf-jetbrains-mono-nerd # 或者 Meslo Nerd Font sudo pacman -S ttf-meslo-nerd # 或者 Hack Nerd Font sudo pacman -S ttf-hack-nerd 安装完成后刷新字体缓存：\nfc-cache -fv 检查是否安装成功：\nfc-list | grep \u0026#34;Nerd Font\u0026#34; 终端中启用字体 字体安装完成后，还需要在终端模拟器里手动切换字体，否则图标仍然不会正常显示。\n常见设置路径如下：\nKitty：编辑 ~/.config/kitty/kitty.conf，设置 font_family（见下方优化配置） GNOME Terminal：首选项 -\u0026gt; 你的配置 -\u0026gt; 文本 -\u0026gt; 自定义字体 Konsole（KDE 默认）：设置 -\u0026gt; 编辑当前方案 -\u0026gt; 外观 -\u0026gt; 字体 Warp/Tabby/Termius：Settings -\u0026gt; Appearance -\u0026gt; Font Windows Terminal：Profiles -\u0026gt; Appearance -\u0026gt; Font face Alacritty：在配置文件中设置 font.family 推荐直接选择：\nJetBrainsMono Nerd Font MesloLGS Nerd Font Hack Nerd Font 常见问题排查 如果字体已经安装，但图标仍然显示异常，按这个顺序排查：\n确认终端里实际选中的字体就是 Nerd Font，而不是原版字体 关闭并重新打开终端 执行 fc-list | rg \u0026quot;Nerd Font\u0026quot;，确认系统能识别字体 如果是远程桌面或某些轻量终端，确认该终端本身支持图标字符显示 设置方法 通过 pacman -S 安装对应 Nerd Font 包 在终端模拟器设置中把字体切换为对应 Nerd Font 关闭并重新打开终端 如果图标显示成方块或乱码，通常就是字体没有切换成功。\nKitty 终端优化配置 Kitty 是一款 GPU 加速的终端模拟器，配置路径为 ~/.config/kitty/kitty.conf。\nmkdir -p ~/.config/kitty 将以下内容写入 ~/.config/kitty/kitty.conf：\n# ============================================================================= # Kitty 终端配置 # 适配 ttf-jetbrains-mono-nerd + zsh + starship # ============================================================================= # ── 字体 ──────────────────────────────────────────────────────────────────── font_family JetBrainsMono Nerd Font bold_font auto italic_font auto bold_italic_font auto font_size 12.0 # ── 窗口 ──────────────────────────────────────────────────────────────────── remember_window_size yes initial_window_width 900 initial_window_height 600 window_border_width 0.5px window_margin_width 0 window_padding_width 8 # ── 颜色 ──────────────────────────────────────────────────────────────────── # Tokyo Night 配色（适配 dark 主题） foreground #a9b1d6 background #1a1b26 selection_foreground #c0caf5 selection_background #2f3346 # 黑色 color0 #1d202f color8 #414868 # 红色 color1 #f7768e color9 #f7768e # 绿色 color2 #9ece6a color10 #9ece6a # 黄色 color3 #e0af68 color11 #e0af68 # 蓝色 color4 #7aa2f7 color12 #7aa2f7 # 紫色 color5 #bb9af7 color13 #bb9af7 # 青色 color6 #7dcfff color14 #7dcfff # 白色 color7 #a9b1d6 color15 #c0caf5 # ── 光标 ───────────────────────────────────────────────────────────────────── cursor #c0caf5 cursor_text_color #1a1b26 cursor_shape block cursor_beam_thickness 1.5 # ── Tab 栏 ────────────────────────────────────────────────────────────────── tab_bar_style powerline tab_bar_margin_width 0.0 tab_bar_edge top tab_bar_style separator tab_separator \u0026#34; | \u0026#34; active_tab_foreground #1a1b26 active_tab_background #7aa2f7 inactive_tab_foreground #a9b1d6 inactive_tab_background #1d202f # ── 滚动与性能 ─────────────────────────────────────────────────────────────── scrollback_lines 10000 scrollback_pager_history_size 100 # GPU 渲染（Kitty 默认已开启，这里显式确认） renderer gl # ── Shell 集成 ────────────────────────────────────────────────────────────── # 让 Kitty 识别 Shell 提示符，支持点击跳转输出 shell_integration enabled # ── 快捷键 ─────────────────────────────────────────────────────────────────── # 让 Ctrl+Shift+↑/↓ 也能滚动 map ctrl+shift+up scroll_line_up map ctrl+shift+down scroll_line_down # 新建窗口 / 标签页 map ctrl+shift+enter new_window map ctrl+shift+t new_tab # 字体放大/缩小 map ctrl+plus change_font_size all +2.0 map ctrl+minus change_font_size all -2.0 map ctrl+0 change_font_size all 0 配置说明：\n字体：直接指定 JetBrainsMono Nerd Font，与你安装的包 ttf-jetbrains-mono-nerd 对应 配色：Tokyo Night 主题，与 starship 的 pastel-powerline 预设风格一致 窗口边距：window_padding_width 8 让内容不贴边，阅读更舒适 Tab 栏：顶部显示带分隔符的标签页 GPU：Kitty 默认使用 OpenGL 渲染，滚动流畅 应用配置后重启 Kitty 即可生效。\n使配置生效 执行：\nsource ~/.zshrc 如果没有报错，再验证各项功能。\n验证命令 echo $SHELL zsh --version starship --version fnm --version 继续检查别名是否生效：\nls ll la lt cat ~/.zshrc tldr ls ncdu fd zsh 检查 z 是否可用：\nz /tmp 如果你还没有足够的目录访问历史，z 一开始效果不明显，属于正常现象。\n常见问题 fnm 不生效 先确认目录存在：\nls ~/.local/share/fnm 再确认 ~/.zshrc 中已包含：\nFNM_PATH=\u0026#34;$HOME/.local/share/fnm\u0026#34; if [ -d \u0026#34;$FNM_PATH\u0026#34; ]; then export PATH=\u0026#34;$FNM_PATH:$PATH\u0026#34; eval \u0026#34;$(fnm env --shell zsh)\u0026#34; fi kitty 终端或者 vscode 终端图标乱码 解决方法：使用支持 powerline 的字体。\n优先排查两项：\n终端字体是否换成 Nerd Font 当前终端是否支持图标显示 bat / fd 命令名说明 Arch Linux 上 bat 和 fd 的包名与命令名一致，无需额外别名：\nbat 包提供 bat 命令（Debian/Ubuntu 上叫 batcat） fd 包提供 fd 命令（Debian/Ubuntu 上叫 fdfind） 如果你是从 Debian 迁移过来，之前的别名 alias cat=batcat 需要改成 alias cat=bat。\n安全建议 你当前 ~/.zshrc 中包含：\nexport AUTH_TOKEN=123 export PROXY_TOKEN=123 如果这些变量是真实敏感信息，建议改成单独的私有文件，例如：\n[ -f \u0026#34;$HOME/.zsh_private\u0026#34; ] \u0026amp;\u0026amp; source \u0026#34;$HOME/.zsh_private\u0026#34; 然后把敏感变量放进 ~/.zsh_private，并确保该文件不被公开同步。\n总结 这套 zsh 配置的思路不是堆很多主题，而是用一组明确分工的工具完成体验升级：\nzinit 管插件 starship 管提示符 fnm 管 Node 版本 各类插件负责建议、高亮、补全和目录跳转 别名把日常命令替换成更友好的版本 如果你要在新机器上复刻环境，按本文顺序执行即可：先装 zsh 和依赖，再写入 ~/.zshrc 与 ~/.zsh_aliases，最后切换字体并重新加载配置。\n参考 Zsh 官方文档 zinit 插件管理器 Starship 官方文档 fnm 官方仓库 Nerd Fonts 官方仓库 Kitty 官方文档 ","permalink":"https://heihei0299.github.io/posts/linux/arch-zsh%E5%AE%89%E8%A3%85%E5%92%8C%E7%BE%8E%E5%8C%96%E6%8C%87%E5%8D%97/","summary":"\u003ch1 id=\"zsh-安装和美化指南arch-linux-版\"\u003eZsh 安装和美化指南（Arch Linux 版）\u003c/h1\u003e\n\u003cp\u003e这份文档基于当前机器上的 \u003ccode\u003e~/.zshrc\u003c/code\u003e 与 \u003ccode\u003e~/.zsh_aliases\u003c/code\u003e 整理，目标是从零安装并复现一套兼顾颜值和效率的 \u003ccode\u003ezsh\u003c/code\u003e 环境。以下命令以 Arch Linux 为例。\u003c/p\u003e\n\u003cp\u003e当前方案的核心组件如下：\u003c/p\u003e","title":"Zsh 安装和美化指南（Arch Linux 版）"},{"content":"zsh 部署与 Bash 配置迁移指南 本文适合从 bash 切换到 zsh 的用户。你会学到：zsh 与 bash 配置文件的对应关系、安装与切换默认 shell、把 ~/.bashrc 中的别名/环境变量/函数安全迁移到 ~/.zshrc，以及哪些 Bash 专用语法不能直接照搬。\n如果你已经习惯了 bash，但想切换到 zsh，最稳妥的方式不是\u0026quot;直接覆盖旧配置\u0026quot;，而是先安装 zsh，再把 bash 中真正有用的配置逐步迁移过去。\n这篇文档适合下面几种场景：\n新机器部署 zsh 服务器或桌面环境从 bash 切换到 zsh 将 ~/.bashrc、~/.bash_profile 中的别名、环境变量、函数迁移到 zsh 一、先了解 Bash 和 zsh 的配置文件差异 bash 常见配置文件：\n~/.bashrc：交互式 shell 常用配置，通常放别名、环境变量、函数 ~/.bash_profile：登录 shell 配置，常用于加载 ~/.bashrc ~/.profile：更通用的登录环境配置 zsh 常见配置文件：\n~/.zshrc：最常用，交互式 shell 配置基本都放这里 ~/.zprofile：登录 shell 配置，类似 ~/.bash_profile ~/.zshenv：所有 zsh 启动都会加载，不建议放复杂逻辑 ~/.zlogin：登录后执行，较少使用 可以简单理解为：\n~/.bashrc 大部分内容迁移到 ~/.zshrc ~/.bash_profile 里的登录环境配置迁移到 ~/.zprofile 二、安装 zsh Debian / Ubuntu sudo apt update sudo apt install -y zsh git curl CentOS / Rocky / AlmaLinux sudo dnf install -y zsh git curl 如果是较老系统：\nsudo yum install -y zsh git curl Arch Linux sudo pacman -S --noconfirm zsh git curl macOS macOS 一般自带 zsh，可以先确认版本：\nzsh --version which zsh 三、确认 zsh 路径并切换默认 shell 先查看系统允许使用的 shell：\ncat /etc/shells which zsh 如果 which zsh 输出的路径已经在 /etc/shells 中，就可以切换：\nchsh -s \u0026#34;$(which zsh)\u0026#34; 有些发行版需要重新登录后才会生效。可以退出当前会话再登录，或者直接执行：\nexec zsh 检查当前 shell：\necho $SHELL echo $0 四、先备份原有 Bash 配置 切换前建议先备份，避免误操作：\ncp ~/.bashrc ~/.bashrc.bak 2\u0026gt;/dev/null cp ~/.bash_profile ~/.bash_profile.bak 2\u0026gt;/dev/null cp ~/.profile ~/.profile.bak 2\u0026gt;/dev/null 如果你之前已经有 zsh 配置，也顺手备份：\ncp ~/.zshrc ~/.zshrc.bak 2\u0026gt;/dev/null cp ~/.zprofile ~/.zprofile.bak 2\u0026gt;/dev/null 五、把 Bash 配置迁移到 zsh 1. 先筛选哪些内容可以直接迁移 ~/.bashrc 中通常有以下几类内容：\nalias 别名 export 环境变量 PATH 路径追加 shell 函数 提示符 PS1 bash-completion 只适用于 Bash 的语法或插件 其中最容易迁移的是：\nalias export PATH 大部分函数 需要额外处理的是：\nshopt、complete、bind 等 Bash 专用语法 [[ ... ]] 大多兼容，但复杂脚本建议单独测试 PROMPT_COMMAND 通常要改写 bash-completion 不能直接原样照搬 2. 推荐的迁移方式 不要直接把整个 ~/.bashrc 原封不动复制到 ~/.zshrc。\n推荐做法：\n先创建一个干净的 ~/.zshrc 手动迁移常用别名、环境变量、函数 单独处理自动补全和提示符 打开一个新终端测试是否报错 3. 一个常见的迁移示例 假设原来的 ~/.bashrc 内容大致如下：\nexport EDITOR=vim export PATH=\u0026#34;$HOME/bin:$HOME/.local/bin:$PATH\u0026#34; alias ll=\u0026#39;ls -alF\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias gs=\u0026#39;git status\u0026#39; if [ -f /etc/bash_completion ]; then . /etc/bash_completion fi parse_git_branch() { git branch 2\u0026gt;/dev/null | sed -n \u0026#39;/\\* /s///p\u0026#39; } PS1=\u0026#39;\\u@\\h:\\w $(parse_git_branch)\\$ \u0026#39; 迁移到 ~/.zshrc 后，可以写成：\nexport EDITOR=vim export PATH=\u0026#34;$HOME/bin:$HOME/.local/bin:$PATH\u0026#34; alias ll=\u0026#39;ls -alF\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias gs=\u0026#39;git status\u0026#39; autoload -Uz compinit compinit parse_git_branch() { git branch 2\u0026gt;/dev/null | sed -n \u0026#39;/\\* /s///p\u0026#39; } setopt PROMPT_SUBST PROMPT=\u0026#39;%n@%m:%~ $(parse_git_branch)%# \u0026#39; 这里最关键的变化有两点：\nbash-completion 改成 autoload -Uz compinit \u0026amp;\u0026amp; compinit PS1 改成 zsh 常用的 PROMPT 六、建议直接使用的基础版 .zshrc 如果你想快速落地，可以先写一个基础版：\n# 基础环境变量 export EDITOR=vim export LANG=zh_CN.UTF-8 export PATH=\u0026#34;$HOME/bin:$HOME/.local/bin:$PATH\u0026#34; # 常用别名 alias ll=\u0026#39;ls -alF\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias l=\u0026#39;ls -CF\u0026#39; alias gs=\u0026#39;git status\u0026#39; alias gp=\u0026#39;git pull\u0026#39; alias ..=\u0026#39;cd ..\u0026#39; alias ...=\u0026#39;cd ../..\u0026#39; # 启用补全 autoload -Uz compinit compinit # 历史记录 HISTSIZE=5000 SAVEHIST=5000 HISTFILE=~/.zsh_history setopt HIST_IGNORE_DUPS setopt SHARE_HISTORY # 常用选项 setopt AUTO_CD setopt CORRECT # 提示符 PROMPT=\u0026#39;%F{green}%n@%m%f:%F{blue}%~%f %# \u0026#39; 写入后重新加载：\nsource ~/.zshrc 七、登录环境如何迁移 如果你原来把登录环境写在 ~/.bash_profile 中，例如：\nexport JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export GOPATH=$HOME/go export PATH=\u0026#34;$JAVA_HOME/bin:$GOPATH/bin:$PATH\u0026#34; 那么更推荐迁移到 ~/.zprofile：\nexport JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export GOPATH=$HOME/go export PATH=\u0026#34;$JAVA_HOME/bin:$GOPATH/bin:$PATH\u0026#34; 重新加载：\nsource ~/.zprofile 如果你不想区分那么细，也可以先统一放进 ~/.zshrc，后续再整理。\n八、Bash 配置里哪些内容不要直接搬 下面这些内容迁移时要特别留意：\n1. bash-completion 原来的 Bash 写法：\nif [ -f /etc/bash_completion ]; then . /etc/bash_completion fi zsh 一般改成：\nautoload -Uz compinit compinit 2. shopt 例如：\nshopt -s histappend shopt -s checkwinsize 这类是 Bash 专用，不能直接照搬。要改用 setopt 对应实现。\n3. PROMPT_COMMAND 如果你的 bash 用了：\nPROMPT_COMMAND=\u0026#39;history -a\u0026#39; 在 zsh 中通常需要改成钩子函数或 setopt 实现，不建议直接复制。\n4. complete 如果你手动写过 Bash 补全：\ncomplete -W \u0026#34;start stop restart\u0026#34; mycmd 这也是 Bash 风格，zsh 需要用自己的 completion 机制重新定义。\n九、如果你想顺手装 Oh My Zsh zsh 本身就能用，不安装 Oh My Zsh 也完全没问题。\n如果你想更快获得主题、插件和更友好的默认体验，可以安装：\nsh -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; 安装后主要修改 ~/.zshrc 里的这几项：\nZSH_THEME=\u0026#34;robbyrussell\u0026#34; plugins=(git sudo) 如果是服务器环境，建议先保持精简，避免装太多插件导致启动变慢。\n十、迁移完成后的检查命令 检查 shell 是否切换成功 echo $SHELL ps -p $$ -o comm= 检查配置是否报错 zsh -n ~/.zshrc source ~/.zshrc 检查别名和环境变量 alias echo $PATH echo $EDITOR 十一、常见问题 1. chsh 后重新登录还是 Bash 可能原因：\n当前终端工具强制指定了 shell 你登录的不是当前用户 zsh 路径不在 /etc/shells 可以先检查：\ngrep \u0026#34;$(which zsh)\u0026#34; /etc/shells getent passwd \u0026#34;$USER\u0026#34; 2. source ~/.zshrc 报错 通常是因为直接复制了 Bash 专用语法。优先排查这些关键字：\nshopt complete PROMPT_COMMAND bind 3. 颜色、提示符、补全和 Bash 不一样 这是正常的，因为 zsh 和 bash 的默认行为就不同。优先保证：\n命令能执行 环境变量正常 别名正常 补全可用 界面样式可以后面再慢慢调。\n十二、一个稳妥的迁移顺序 推荐按这个顺序操作：\n安装 zsh 备份 bash 配置 切换默认 shell 先写最小可用版 ~/.zshrc 逐项迁移 alias、PATH、export、函数 再处理主题、补全、提示符 最后清理旧的无用配置 十三、最小化迁移方案 如果你只想先跑起来，可以直接执行下面这套：\nsudo apt update \u0026amp;\u0026amp; sudo apt install -y zsh cp ~/.bashrc ~/.bashrc.bak 2\u0026gt;/dev/null cp ~/.bash_profile ~/.bash_profile.bak 2\u0026gt;/dev/null chsh -s \u0026#34;$(which zsh)\u0026#34; 然后把下面内容写入 ~/.zshrc：\nexport PATH=\u0026#34;$HOME/bin:$HOME/.local/bin:$PATH\u0026#34; alias ll=\u0026#39;ls -alF\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias gs=\u0026#39;git status\u0026#39; autoload -Uz compinit compinit HISTFILE=~/.zsh_history HISTSIZE=5000 SAVEHIST=5000 setopt HIST_IGNORE_DUPS setopt SHARE_HISTORY PROMPT=\u0026#39;%n@%m:%~ %# \u0026#39; 重新打开终端，基本就可以用了。\n十四、总结 从 bash 迁移到 zsh 并不复杂，核心就三件事：\n装好 zsh 切换默认 shell 把 ~/.bashrc 中真正有用的内容迁移到 ~/.zshrc 不要一次性把整个 bash 配置文件无脑复制过去。先让 alias、PATH、export、函数这些基础能力跑通，再补主题和插件，成功率会高很多。\n参考 Zsh 官方文档 ArchWiki: zsh Oh My Zsh 官方仓库 ","permalink":"https://heihei0299.github.io/posts/ops/zsh%E9%83%A8%E7%BD%B2%E4%B8%8Ebash%E9%85%8D%E7%BD%AE%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97/","summary":"\u003ch1 id=\"zsh-部署与-bash-配置迁移指南\"\u003ezsh 部署与 Bash 配置迁移指南\u003c/h1\u003e\n\u003cp\u003e本文适合从 bash 切换到 zsh 的用户。你会学到：zsh 与 bash 配置文件的对应关系、安装与切换默认 shell、把 \u003ccode\u003e~/.bashrc\u003c/code\u003e 中的别名/环境变量/函数安全迁移到 \u003ccode\u003e~/.zshrc\u003c/code\u003e，以及哪些 Bash 专用语法不能直接照搬。\u003c/p\u003e","title":"zsh 部署与 Bash 配置迁移指南"},{"content":"Zsh 启动优化指南：卸载 Oh My Zsh 改用 zinit 本文适合觉得终端打开慢（等 12 秒才能输入命令）的 zsh 用户。你会学到：卸载 Oh My Zsh、安装 zinit 并迁移插件配置，用异步延迟加载把启动时间从 8001500ms 优化到 50~150ms。\n🎯 优化背景 很多人从 Oh My Zsh（OMZ）入坑 Zsh，它开箱即用、功能丰富、文档齐全，但加载大量插件后会出现明显的启动延迟，甚至打开终端需要等1~2秒才能输入命令。\n本方案通过改用 zinit 插件管理器，既能保留你习惯的OMZ插件和功能，又能将启动速度优化到几十毫秒，实现终端秒开。\n⏱️ 优化前后对比 配置 插件数量 启动时间 体验 Oh My Zsh 默认配置 10个插件 800~1500ms 打开终端有明显延迟感 zinit 优化配置 相同10个插件 50~150ms 终端秒开，完全感知不到加载延迟 🚀 迁移步骤 步骤1：卸载 Oh My Zsh # 执行官方卸载脚本 uninstall_oh_my_zsh # （可选）删除残留文件 rm -rf ~/.oh-my-zsh rm -rf ~/.zshrc.pre-oh-my-zsh ⚠️ 注意：卸载前请备份你的.zshrc中的自定义配置（别名、环境变量等）。\n步骤2：安装 zinit（社区维护版本） 原版zinit已停止维护，推荐使用社区活跃的fork版本：\n# 自动安装脚本 bash -c \u0026#34;$(curl --fail --show-error --silent --location https://raw.githubusercontent.com/zdharma-continuum/zinit/HEAD/scripts/install.sh)\u0026#34; # 手动安装（网络不好时使用） mkdir -p ~/.zinit git clone https://github.com/zdharma-continuum/zinit.git ~/.zinit/bin 安装完成后重启终端，输入zinit --version确认安装成功。\n步骤3：基础配置（写入 ~/.zshrc） # ==================== 加载zinit ==================== source ~/.zinit/bin/zinit.zsh # ==================== 兼容OMZ核心库 ==================== # 加载OMZ的核心功能，不需要完全安装OMZ也能使用它的库和插件 zinit snippet OMZ::lib/key-bindings.zsh # 快捷键绑定 zinit snippet OMZ::lib/theme-and-appearance.zsh # 主题和外观配置 zinit snippet OMZ::lib/completion.zsh # 补全功能 zinit snippet OMZ::lib/history.zsh # 历史命令管理 zinit snippet OMZ::lib/git.zsh # Git增强功能 # ==================== 插件配置（按需添加） ==================== # 🔹 必装基础插件 zinit light zsh-users/zsh-syntax-highlighting # 命令语法高亮 zinit light zsh-users/zsh-completions # 丰富的命令补全库 # 🔹 异步延迟加载（不阻塞启动） zinit ice lucid wait=\u0026#39;0\u0026#39; atload=\u0026#39;_zsh_autosuggest_start\u0026#39; zinit light zsh-users/zsh-autosuggestions # 历史命令自动提示 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light agkozak/zsh-z # 快速目录跳转 zinit ice lucid wait=\u0026#39;0\u0026#39; zinit light Aloxaf/fzf-tab # fzf模糊补全 # 🔹 加载你习惯的OMZ插件 zinit snippet OMZ::plugins/git/git.plugin.zsh # Git插件 zinit snippet OMZ::plugins/safe-paste/safe-paste.plugin.zsh # 安全粘贴 zinit snippet OMZ::plugins/command-not-found/command-not-found.plugin.zsh # 命令未找到提示 # ==================== 主题配置 ==================== # 方案1：Pure极简主题（推荐，性能好颜值高） zinit ice compile\u0026#39;(pure|async).zsh\u0026#39; pick\u0026#39;async.zsh\u0026#39; src\u0026#39;pure.zsh\u0026#39; zinit light sindresorhus/pure # 方案2：Powerlevel10k主题（功能丰富，支持 instant prompt） # zinit ice depth=1; zinit light romkatv/powerlevel10k # ==================== 初始化补全 ==================== autoload -Uz compinit compinit 步骤4：性能优化进阶 1. 异步懒加载非关键插件\n对非启动必须的插件使用wait='0'参数，让它们在终端启动完成后后台异步加载，不阻塞主流程：\nzinit ice lucid wait=\u0026#39;0\u0026#39; zinit light \u0026lt;插件名\u0026gt; 2. 优化NVM等慢加载工具\n很多人启动慢的罪魁祸首是NVM加载，可以用zinit的延迟加载功能：\nzinit ice lucid wait=\u0026#39;1\u0026#39; atload=\u0026#39;[[ ! -f ~/.nvm/nvm.sh ]] || source ~/.nvm/nvm.sh\u0026#39; zinit light zdharma-continuum/null 这样NVM会在终端启动1秒后再加载，不影响启动速度。\n3. 禁用不需要的功能\n如果不需要OMZ的某些功能，就不要加载对应的库和插件，按需加载是性能优化的核心。\n📊 性能调试命令 命令 功能 zinit times 显示每个插件的加载耗时 zinit list 列出所有已加载的插件 zinit report \u0026lt;插件名\u0026gt; 查看插件加载详情（仅load模式支持） time zsh -i -c exit 测试Zsh启动总耗时 验证与自查 time zsh -i -c exit 实测启动耗时在 50~150ms 量级（优化前 800ms+） zinit times 能看到插件加载耗时，wait 延迟加载的插件不阻塞主流程 原有用习惯保留：Git 别名、语法高亮、自动建议、fzf 补全均正常 zsh -n ~/.zshrc 语法检查无报错 💡 最佳实践建议 只加载需要的插件：不用的功能坚决不加载，每多一个插件都会增加启动时间 优先使用原生zinit插件：比复用OMZ插件性能更好 定期清理无用插件：用zinit times找出加载慢的插件，考虑替换或删除 开启Turbo模式：对非关键插件都用wait参数异步加载 通过上述优化，你的Zsh启动速度会有质的提升，同时还能保留Oh My Zsh的所有常用功能和使用习惯~\n参考 zinit（zdharma-continuum）官方仓库 Oh My Zsh 官方仓库（含卸载说明） Zsh 官方文档 ","permalink":"https://heihei0299.github.io/posts/ops/zsh-optimization-guide/","summary":"\u003ch1 id=\"zsh-启动优化指南卸载-oh-my-zsh-改用-zinit\"\u003eZsh 启动优化指南：卸载 Oh My Zsh 改用 zinit\u003c/h1\u003e\n\u003cp\u003e本文适合觉得终端打开慢（等 1\u003cdel\u003e2 秒才能输入命令）的 zsh 用户。你会学到：卸载 Oh My Zsh、安装 zinit 并迁移插件配置，用异步延迟加载把启动时间从 800\u003c/del\u003e1500ms 优化到 50~150ms。\u003c/p\u003e","title":"Zsh 启动优化指南：卸载 Oh My Zsh 改用 zinit"},{"content":"安装 MCP 并使用 MCP 在配置 MCP 服务器之前，先安装一个神器——mcporter CLI。本文教你安装 mcporter 并在界面中启用它的技能，为后续配置、测试、管理 MCP 服务器打好基础。\n准备工作：安装 mcporter CLI mcporter 是一个专门用于管理 MCP 服务器的命令行工具，可以快速配置、测试、管理 MCP 服务器。\n安装很简单：\nnpm install -g mcporter 安装完成后，会自动创建配置文件和技能框架，这为后续配置打好了基础。在 wabui 界面启用 mcporter skills。\n或者直接安装 mcporter 技能：\nclawhb install mcporter 验证安装 # 确认 mcporter 已作为全局包安装 npm ls -g mcporter # 查看 mcporter 命令帮助，确认 CLI 可用 mcporter --help npm ls -g mcporter 输出中应能看到 mcporter@\u0026lt;版本号\u0026gt;，说明全局安装成功 mcporter --help 正常打印用法说明，说明命令已进入 $PATH 参考 mcporter - npm OpenClaw 官方文档 ","permalink":"https://heihei0299.github.io/posts/ai/install-mcp/","summary":"\u003ch1 id=\"安装-mcp-并使用-mcp\"\u003e安装 MCP 并使用 MCP\u003c/h1\u003e\n\u003cp\u003e在配置 MCP 服务器之前，先安装一个神器——mcporter CLI。本文教你安装 mcporter 并在界面中启用它的技能，为后续配置、测试、管理 MCP 服务器打好基础。\u003c/p\u003e","title":"安装 MCP 并使用 MCP"},{"content":"常用检查网站 适用场景：排查代理或服务器网络问题时，需要快速定位出口 IP、检测 DNS 泄露、评估线路质量。下面整理了一些常用的网络检测与线路排查网站，按用途分类。\nIP 与出口信息 bgp.tools ip.sb ip.skk.moe DNS 与泄露检测 ipleak.net 连通性与延迟测试 ITDog ping.pe ping0.cc 其他网络工具 gubo.org 验证与自查 执行 curl ip.sb 查看当前出口 IP，确认与代理节点所在地区一致 打开 ipleak.net 检查 DNS 泄露：应显示代理提供的 DNS，而非本地运营商 DNS 用 ping.pe 或 ping0.cc 检测目标线路的延迟与丢包，定位回程路径问题 用 ITDog 从多个国内节点测试网站连通性，判断是本地问题还是服务端问题 参考 bgp.tools — BGP 与 IP 归属查询 ipleak.net — IP / DNS / WebRTC 泄露检测 ping.pe — 全球多节点 Ping 测试 ITDog — 国内多节点连通性测试 ","permalink":"https://heihei0299.github.io/posts/network/%E5%B8%B8%E7%94%A8%E6%A3%80%E6%9F%A5%E7%BD%91%E7%AB%99/","summary":"\u003ch1 id=\"常用检查网站\"\u003e常用检查网站\u003c/h1\u003e\n\u003cp\u003e适用场景：排查代理或服务器网络问题时，需要快速定位出口 IP、检测 DNS 泄露、评估线路质量。下面整理了一些常用的网络检测与线路排查网站，按用途分类。\u003c/p\u003e","title":"常用检查网站"},{"content":"创建新用户并启用ssh登录 本文适合需要在服务器上创建专用账号（如备份用户）的运维人员。你会学到：创建用户并禁用其 sudo/su 提权能力，配置 SSH 公钥登录及正确的目录权限。\n1. 创建用户 1.1 创建用户 首先创建一个名为 rclone-backup 的用户，并为其设置密码：\nsudo adduser rclone-backup 按照提示设置密码并填写其他信息。\n1.2 禁用 sudo 权限 为了禁用 rclone-backup 用户使用 sudo -i，可以通过将该用户从 sudo 组中移除来实现：\nsudo deluser rclone-backup sudo 1.3 禁用 su 命令 su 命令通常由 /etc/pam.d/su 文件控制。可以通过修改该文件来禁止某些用户使用 su。编辑 /etc/pam.d/su 文件：\nsudo nano /etc/pam.d/su 然后在文件中找到类似于以下的行：\n# Uncomment the following line to enable \u0026#39;su\u0026#39; access for wheel group. # auth required pam_wheel.so use_uid 将该行注释掉或确保没有其他组能够使用 su，从而禁止 rclone-backup 用户使用 su。\n此外，可以使用 pam_wheel.so 来限制哪些用户可以使用 su。例如，编辑 /etc/group 文件并确保 rclone-backup 用户不属于 wheel 组。\n1.4 确认权限 为了确保 rclone-backup 用户无法使用 sudo 或 su，可以通过以下命令进行测试：\nsudo -u rclone-backup sudo -i sudo -u rclone-backup su 这两条命令应该都会被拒绝。如果一切正常，rclone-backup 用户就只能访问其权限范围内的资源，不能切换到 root 用户。\n2. 配置ssh登录 配置公钥 mkdir /home/rclone-backup/.ssh vim authorized_keys 配置权限 OpenSSH 出于安全考虑，它要求私钥和权限受限。\n# 设置 home 目录权限 chmod 755 /home/rclone-backup # 设置 .ssh 目录权限 chmod 700 /home/rclone-backup/.ssh # 设置 authorized_keys 文件权限 chmod 600 /home/rclone-backup/.ssh/authorized_keys # 更改所有权为 rclone-backup 用户 chown -R rclone-backup:rclone-backup /home/rclone-backup/.ssh 3. 登录ssh 使用ssh客户端登录ssh：\nssh -i 私钥路径 rclone-backup@服务器IP 验证与自查 sudo -u rclone-backup sudo -i 与 sudo -u rclone-backup su 均被拒绝 用私钥登录 rclone-backup@服务器IP 成功，且无需密码 ls -ld /home/rclone-backup/.ssh 权限为 700，authorized_keys 权限为 600 登录后执行 sudo -i 提示无权限，说明无法提权 参考 ArchWiki: OpenSSH pam_wheel 手册 Debian 手册：用户与组管理 ","permalink":"https://heihei0299.github.io/posts/ops/%E5%88%9B%E5%BB%BA%E6%96%B0%E7%94%A8%E6%88%B7%E5%B9%B6%E5%90%AF%E7%94%A8ssh%E7%99%BB%E5%BD%95/","summary":"\u003ch1 id=\"创建新用户并启用ssh登录\"\u003e创建新用户并启用ssh登录\u003c/h1\u003e\n\u003cp\u003e本文适合需要在服务器上创建专用账号（如备份用户）的运维人员。你会学到：创建用户并禁用其 \u003ccode\u003esudo\u003c/code\u003e/\u003ccode\u003esu\u003c/code\u003e 提权能力，配置 SSH 公钥登录及正确的目录权限。\u003c/p\u003e\n\u003ch2 id=\"1-创建用户\"\u003e1. 创建用户\u003c/h2\u003e\n\u003ch3 id=\"11-创建用户\"\u003e1.1 创建用户\u003c/h3\u003e\n\u003cp\u003e首先创建一个名为 \u003ccode\u003erclone-backup\u003c/code\u003e 的用户，并为其设置密码：\u003c/p\u003e","title":"创建新用户并启用ssh登录"},{"content":"纯 IPv6 机器安装 Nezha Agent 适用场景：只有 IPv6 地址的 VPS（无 IPv4）接入哪吒探针面板。本文记录从下载 Agent 到 systemd 托管的全流程，共五步。\n1. 下载并安装 Agent wget https://github.com/nezhahq/agent/releases/download/v0.16.2/nezha-agent_linux_amd64.zip mv nezha-agent /usr/local/bin/ chmod 744 /usr/local/bin/nezha-agent 下载的是 linux_amd64 架构包，其他架构请到 GitHub Releases 选择对应文件；纯 IPv6 机器需保证能访问 GitHub（必要时借助代理下载后上传）。\n2. 测试启动 nezha-agent -s \u0026lt;Dashboard服务器地址\u0026gt;:\u0026lt;端口\u0026gt; -p \u0026lt;连接密码\u0026gt; 如果面板中显示机器在线，说明测试成功。\n注意：Dashboard 服务器地址需填写其可达地址（如 IPv6 地址或域名）；\u0026lt;连接密码\u0026gt; 为 Dashboard 中该服务器的连接密钥。\n3. 配置 systemd 服务 在 /usr/lib/systemd/system 下创建 nezha-agent.service：\nvim /usr/lib/systemd/system/nezha-agent.service 内容如下：\n[Unit] Description=Nezha Agent After=syslog.target [Service] Type=simple User=root Group=root ExecStart=/usr/local/bin/nezha-agent -s \u0026lt;Dashboard服务器地址\u0026gt;:\u0026lt;端口\u0026gt; -p \u0026lt;连接密码\u0026gt; Restart=always [Install] WantedBy=multi-user.target 4. 启动服务 systemctl daemon-reload systemctl enable nezha-agent systemctl start nezha-agent 5. 查看状态 systemctl status nezha-agent 验证与自查 systemctl status nezha-agent 显示 active（running） journalctl -u nezha-agent -e 无报错 哪吒 Dashboard 面板中该机器显示为在线，数据正常上报 参考 Nezha Agent 发布页（GitHub） Nezha 官方文档 ","permalink":"https://heihei0299.github.io/posts/network/%E7%BA%AFipv6%E6%9C%BA%E5%99%A8%E5%AE%89%E8%A3%85nezhaagent/","summary":"\u003ch1 id=\"纯-ipv6-机器安装-nezha-agent\"\u003e纯 IPv6 机器安装 Nezha Agent\u003c/h1\u003e\n\u003cp\u003e适用场景：只有 IPv6 地址的 VPS（无 IPv4）接入哪吒探针面板。本文记录从下载 Agent 到 systemd 托管的全流程，共五步。\u003c/p\u003e\n\u003ch2 id=\"1-下载并安装-agent\"\u003e1. 下载并安装 Agent\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ewget https://github.com/nezhahq/agent/releases/download/v0.16.2/nezha-agent_linux_amd64.zip\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emv nezha-agent /usr/local/bin/\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003echmod \u003cspan class=\"m\"\u003e744\u003c/span\u003e /usr/local/bin/nezha-agent\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e下载的是 \u003ccode\u003elinux_amd64\u003c/code\u003e 架构包，其他架构请到 \u003ca href=\"https://github.com/nezhahq/agent/releases\"\u003eGitHub Releases\u003c/a\u003e 选择对应文件；纯 IPv6 机器需保证能访问 GitHub（必要时借助代理下载后上传）。\u003c/p\u003e","title":"纯 IPv6 机器安装 Nezha Agent"},{"content":"搭建 DERP 服务器与 Headscale 组网 适用场景：家里没有公网 IP 也想随时访问家里设备，或不想依赖官方中继服务器。本文教你搭建自建 Derper 中继服务器与 Headscale 控制服务器，把全部设备拉进自己的私有局域网。\n对大部分用户来说，自建一个 Derper 服务器，然后使用官方的 Tailscale 就够用了（即本文第 2 节内容）；把自建 Derper 加入官方 Tailscale 的方法请自行搜索，不难的。愿意折腾的可以继续往下看，建议先搞清楚第 1 节的内容，尤其是端口和配置文件。\n简介 Tailscale（Headscale）就是组建一个大的局域网，可以将你手里头的所有设备都拉到这个局域网内，进而使用局域网 IP 进行互联。 此外，Tailscale（Headscale）还有一个作用就是内网穿透，局域网内的设备之间的访问会通过穿透进行打洞（成功概率挺高），进而实现公网的点对点互联。而且就算打洞失败，也可以利用中转服务器进行互联。 应用场景举例：家里没有公网 IP，但是可以通过 Tailscale 组网和穿透的方式实现在任意网络下对家里设备的访问。 1. 准备 1.1 一个域名 后文以 headscale.example.com 为例，记得改成自己的域名，并域名解析到 IP 上。\n1.2 一台带公网 IP 的服务器 以 Debian 12 系统为例，后文的 IP 以 123.123.123.123 为例，记得改成自己的 IP。\n1.3 一个邮箱 申请证书，后文以 example@gmail.com 为例，记得改成自己的邮箱。\n1.4 需要用到的端口 Derper 端口：13445（自己改成喜欢的端口） Https 端口：13446（自己改成喜欢的端口） Headscale 端口：8080 80 443 端口 1.5 需要用到的配置文件 Derper：/var/www/derp.json，/etc/systemd/system/derp.service Headscale：/etc/headscale/config.yaml Nginx: /etc/nginx/sites-available/default 2. 搭建 Derper 由于官方新版本的 Derp 支持自签证书了，所以方法方便很多了，直接开始。\n2.1 安装最新版 Go 更新软件包，安装依赖 apt update \u0026amp;\u0026amp; apt upgrade apt install -y wget git openssl curl 下载最新版 Go wget https://go.dev/dl/go1.23.5.linux-amd64.tar.gz rm -rf /usr/local/go \u0026amp;\u0026amp; tar -C /usr/local -xzf go1.23.5.linux-amd64.tar.gz 目前最新版为 1.23.5，后续若有更新则可以去 https://go.dev/dl/ 查看最新版并替换下载即可，记得后面的版本号都要改。\n配置环境变量 export PATH=$PATH:/usr/local/go/bin go version echo \u0026#34;export PATH=$PATH:/usr/local/go/bin\u0026#34; \u0026gt;\u0026gt; /etc/profile source /etc/profile go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct 2.2 安装最新版 Derper 安装 go install tailscale.com/cmd/derper@latest 拷贝二进制文件 mkdir /etc/derp/ cp ~/go/bin/derper /etc/derp/ 查看是否拷贝成功 ls /etc/derp 2.3 生成 Derper 自签证书 DERP_IP=\u0026#34;123.123.123.123\u0026#34; openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout ${DERP_IP}.key -out ${DERP_IP}.crt -subj \u0026#34;/CN=${DERP_IP}\u0026#34; -addext \u0026#34;subjectAltName=IP:${DERP_IP}\u0026#34; 这时候 /root 文件夹下会有两个证书文件：/root/123.123.123.123.crt 和 /root/123.123.123.123.key，拷贝到 /etc/derp/：\nmv /root/123.123.123.123.crt /etc/derp mv /root/123.123.123.123.key /etc/derp 2.4 启动 Derper 服务器 写入后台：新建文件 derp.service touch /etc/systemd/system/derp.service 写入以下内容\n[Unit] Description=TS Derper After=network.target Wants=network.target [Service] User=root Restart=always ExecStart=/etc/derp/derper -hostname 123.123.123.123 -a :13445 -http-port 13446 -certmode manual -certdir /etc/derp RestartPreventExitStatus=1 [Install] WantedBy=multi-user.target 启动 systemctl enable derp systemctl restart derp systemctl status derp 2.5 验证 Derper 是否搭建成功 浏览器打开 https://123.123.123.123:13445，忽略不安全，看看是不是显示下面的内容：\nDERP This is a Tailscale DERP server.\nIt provides STUN, interactive connectivity establishment, and relaying of end-to-end encrypted traffic for Tailscale clients.\nDocumentation:\nAbout DERP Protocol \u0026amp; Go docs How to run a DERP server\n3. 搭建 Headscale 和 Headscale-ui 3.1 ACME 申请证书 安装依赖 apt update -y\u0026amp;\u0026amp;apt install -y curl\u0026amp;\u0026amp;apt install -y socat 申请证书 curl https://get.acme.sh | sh -s email=example@gmail.com ~/.acme.sh/acme.sh --issue -d headscale.example.com --standalone ~/.acme.sh/acme.sh --installcert -d headscale.example.com --key-file /root/private.key --fullchain-file /root/cert.crt 3.2 安装最新版 Headscale 下载 wget --output-document=headscale.deb \\ https://github.com/juanfont/headscale/releases/download/v0.24.1/headscale_0.24.1_linux_amd64.deb 目前最新版为 0.24.1，后续若有更新则可以去 https://github.com/juanfont/headscale/releases 查看最新版并替换下载即可，记得后面的版本号都要改。\n安装 mv headscale_0.24.1_linux_amd64.deb headscale.deb dpkg --install headscale.deb 修改配置文件（/etc/headscale/config.yaml） 仅列出需要修改的地方：\nserver_url: https://headscale.example.com listen_addr: 0.0.0.0:8080 metrics_listen_addr: 0.0.0.0:9090 prefixes: v4: 100.64.0.0/10 # v6: fd7a:115c:a1e0::/48 # List of externally available DERP maps encoded in JSON urls: - http://127.0.0.1/d/derp.json base_domain: headscale.example.com # List of DNS servers to expose to clients.（建议改成国内适合自己的DNS） nameservers: global: - 114.114.114.114 启动 systemctl enable headscale systemctl restart headscale systemctl status headscale 3.3 安装最新版 Headscale-ui 下载 wget https://github.com/gurucomputing/headscale-ui/releases/download/2025.01.20/headscale-ui.zip 目前最新版为 2025.01.20，后续若有更新则可以去 https://github.com/gurucomputing/headscale-ui 查看最新版并替换下载即可，记得后面的版本号都要改。\n安装 apt-get install unzip unzip -d /var/www headscale-ui.zip 3.4 配置 Derper 服务器 配置 /var/www/derp.json 文件，新建： touch /var/www/derp.json 写入以下内容：\n{ \u0026#34;Regions\u0026#34;: { \u0026#34;901\u0026#34;: { \u0026#34;RegionID\u0026#34;: 901, \u0026#34;RegionCode\u0026#34;: \u0026#34;Myself\u0026#34;, \u0026#34;RegionName\u0026#34;: \u0026#34;Myself Derper\u0026#34;, \u0026#34;Nodes\u0026#34;: [ { \u0026#34;Name\u0026#34;: \u0026#34;901a\u0026#34;, \u0026#34;RegionID\u0026#34;: 901, \u0026#34;DERPPort\u0026#34;: 13445, \u0026#34;IPv4\u0026#34;: \u0026#34;123.123.123.123\u0026#34;, \u0026#34;InsecureForTests\u0026#34;: true } ] } } } 3.5 安装并配置 Nginx 下载 apt install -y nginx 打开 /etc/nginx/sites-available/default 并添加以下内容： map $http_upgrade $connection_upgrade { default upgrade; \u0026#39;\u0026#39; close; } server { listen 80; listen [::]:80; listen 443 ssl http2; listen [::]:443 ssl http2; server_name headscale.example.com; ssl_certificate /root/cert.crt; ssl_certificate_key /root/private.key; ssl_protocols TLSv1.2 TLSv1.3; location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $server_name; proxy_redirect http:// https://; proxy_buffering off; 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; add_header Strict-Transport-Security \u0026#34;max-age=15552000; includeSubDomains\u0026#34; always; } location /web { index index.html; alias /var/www/web; } } server { listen 80; listen [::]:80; server_name 127.0.0.1; root /var/www; index index.html index.htm index.nginx-debian.html; location /d { alias /var/www; autoindex on; } location / { try_files $uri $uri/ =404; } } 启动 systemctl enable nginx systemctl restart nginx systemctl status nginx 3.6 验证安装 重启 Derper、Headscale 和 Nginx 服务 systemctl restart derp systemctl status derp systemctl restart headscale systemctl status headscale systemctl restart nginx systemctl status nginx 打开网站 https://headscale.example.com/web 如无意外应该是三个服务的状态应该是全绿的，网页也能正常打开，有报错的话建议按教程自查或在帖子底下留言。\n3.7 配置 Headscale-ui 生成 API Key headscale apikeys create --expiration 9999d 并将其写入到 https://headscale.example.com/web/settings.html 的 Headscale API Key 里面，点击 Save API Key 提交，见到右侧有小对勾即可。\n新建用户 Default 打开 https://headscale.example.com/web/users.html，点击 New User，输入 Default 并提交。\n4. 将设备添加到 Headscale 局域网中 4.1 下载最新客户端 https://tailscale.com/download\n4.2 启动指令解析 --login-server: 指定使用的 Headscale 服务器地址，即 https://headscale.example.com --advertise-routes: 向 Headscale 服务器报告当前客户端处于哪个内网网段下, 便于 Headscale 服务器让同内网设备直接内网直连(可选的)或者将其他设备指定流量路由到当前内网(可选)，多条路由英文逗号隔开 --accept-routes: 是否接受 Headscale 服务器下发的用于路由到其他客户端内网的路由规则(可选) --accept-dns: 是否使用 Headscale 服务器下发的 DNS 相关配置(可选, 推荐关闭) --force-reauth：强制重新认证 --advertise-exit-node：作为出口节点\n4.3 不同客户端的启动方法 Windows：以管理员身份启动 PowerShell，输入以下代码： tailscale login --login-server https://headscale.example.com iOS：用非国区 Apple ID 下载，配置 V P N 文件。点击 右上角头像，点击 Log In...，点击 右上角三个点，点击 Use a custom coordination server，输入 https://headscale.example.com，点击 Login in\nLinux：安装\ncurl -fsSL https://tailscale.com/install.sh | sh 运行\ntailscale up --reset --advertise-routes=192.168.x.0/24 --accept-routes=true --login-server=https://headscale.example.com 4.4 到 Headscale-ui 中允许设备加入局域网 上面在客户端执行了 tailscale up 之后，在客户都会显示一个带 Device Key 的代码，格式类似于：\nheadscale nodes register --user USERNAME --key mkey:ed8f19e22f51c9c231c8bc8ccbxxxxxxxxxxxxxxf86c8211e4ad32b6c6e 拷贝 mkey:ed8f19e22f51c9c231c8bc8ccbxxxxxxxxxxxxxxf86c8211e4ad32b6c6e，去 https://headscale.example.com/web/devices.html 中导入 Device Key。\n4.5 开启 Derper 防白嫖功能 将 Derper 服务器加入到 Headscale 局域网中。 修改 /etc/systemd/system/derp.service 文件，在 ExecStart=/etc/derp/derper -hostname 123.123.123.123 -a :13445 -http-port 13446 -certmode manual -certdir /etc/derp 的后面加入参数（注意前面先加一个空格）。 官方提供的防滥用参数为 -verify-clients，开启后 Derper 只允许已注册的客户端使用中继，详见文末参考的 DERP 官方文档。\n重启 Derper、Headscale 和 Nginx 服务 systemctl restart derp systemctl status derp systemctl restart headscale systemctl status headscale systemctl restart nginx systemctl status nginx 验证与自查 systemctl status derp headscale nginx 三个服务均为 active（running） 浏览器打开 https://123.123.123.123:13445，页面显示 \u0026ldquo;This is a Tailscale DERP server.\u0026rdquo; https://headscale.example.com/web 能正常打开，且三个服务状态为绿色 客户端执行 tailscale up 后，在 headscale-ui 的 devices 页面能看到设备上线，tailscale ping \u0026lt;对端\u0026gt; 可通 参考 Tailscale 官方文档：自定义 DERP 服务器 Derper 源码（tailscale.com/cmd/derper） Headscale（GitHub） headscale-ui（GitHub） Go 下载页 ","permalink":"https://heihei0299.github.io/posts/network/derp%E6%90%AD%E5%BB%BA/","summary":"\u003ch1 id=\"搭建-derp-服务器与-headscale-组网\"\u003e搭建 DERP 服务器与 Headscale 组网\u003c/h1\u003e\n\u003cp\u003e适用场景：家里没有公网 IP 也想随时访问家里设备，或不想依赖官方中继服务器。本文教你搭建自建 Derper 中继服务器与 Headscale 控制服务器，把全部设备拉进自己的私有局域网。\u003c/p\u003e","title":"搭建 DERP 服务器与 Headscale 组网"},{"content":"搭建 Hysteria2 教程 适用场景：搭建一个基于 QUIC 的 Hysteria2 代理服务端，利用 sing-box 的 ACME 功能自动签发 TLS 证书，无需手动管理证书文件。\n1. 安装 sing-box bash \u0026lt;(curl -fsSL https://sing-box.app/deb-install.sh) 2. 设置开机自启 sudo systemctl enable sing-box 3. 编辑配置文件 cd /etc/sing-box vim config.json 4. Hysteria2 配置示例 { \u0026#34;log\u0026#34;: { \u0026#34;disabled\u0026#34;: false, \u0026#34;level\u0026#34;: \u0026#34;error\u0026#34;, \u0026#34;timestamp\u0026#34;: true }, \u0026#34;inbounds\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;hysteria2\u0026#34;, \u0026#34;tag\u0026#34;: \u0026#34;hysteria2-in\u0026#34;, \u0026#34;listen\u0026#34;: \u0026#34;::\u0026#34;, \u0026#34;listen_port\u0026#34;: 你的端口, \u0026#34;users\u0026#34;: [ { \u0026#34;password\u0026#34;: \u0026#34;你的密码\u0026#34; } ], \u0026#34;tls\u0026#34;: { \u0026#34;enabled\u0026#34;: true, \u0026#34;alpn\u0026#34;: [ \u0026#34;h3\u0026#34; ], \u0026#34;certificate_path\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;key_path\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;acme\u0026#34;: { \u0026#34;domain\u0026#34;: [ \u0026#34;你的域名\u0026#34; ], \u0026#34;dns01_challenge\u0026#34;: { \u0026#34;provider\u0026#34;: \u0026#34;cloudflare\u0026#34;, \u0026#34;api_token\u0026#34;: \u0026#34;你的dns_token\u0026#34; } } } } ], \u0026#34;outbounds\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;direct\u0026#34;, \u0026#34;tag\u0026#34;: \u0026#34;direct\u0026#34; } ] } 配置要点：\nlisten_port、password、domain、api_token 都要替换成你自己的值。 使用 acme + dns01_challenge（cloudflare）时无需手动放行 80 端口，证书由 sing-box 自动申请并续期。 也可以改为 certificate_path / key_path 指向手动获取的证书文件。 5. 重启 sing-box sudo systemctl restart sing-box 常用命令 启用 sudo systemctl enable sing-box 禁用 sudo systemctl disable sing-box 启动 sudo systemctl start sing-box 停止 sudo systemctl stop sing-box 强行停止 sudo systemctl kill sing-box 重启 sudo systemctl restart sing-box 查看日志 sudo journalctl -u sing-box --output cat -e 实时日志 sudo journalctl -u sing-box --output cat -f 验证与自查 sudo systemctl status sing-box 显示 active（running） sudo ss -lntup | grep \u0026lt;你的端口\u0026gt; 确认 UDP 端口监听正常 sudo journalctl -u sing-box --output cat -e 无报错，能看到 ACME 证书签发成功日志 客户端（v2rayN / sing-box / Clash.Meta 等）用 Hysteria2 + 你的密码连接成功，curl ip.sb 出口为服务端 IP 参考 sing-box 安装说明 sing-box hysteria2 入站文档 Hysteria 官方文档 ","permalink":"https://heihei0299.github.io/posts/network/%E6%90%AD%E5%BB%BAhysteria2%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"搭建-hysteria2-教程\"\u003e搭建 Hysteria2 教程\u003c/h1\u003e\n\u003cp\u003e适用场景：搭建一个基于 QUIC 的 Hysteria2 代理服务端，利用 sing-box 的 ACME 功能自动签发 TLS 证书，无需手动管理证书文件。\u003c/p\u003e\n\u003ch2 id=\"1-安装-sing-box\"\u003e1. 安装 sing-box\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebash \u0026lt;\u003cspan class=\"o\"\u003e(\u003c/span\u003ecurl -fsSL https://sing-box.app/deb-install.sh\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"2-设置开机自启\"\u003e2. 设置开机自启\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo systemctl \u003cspan class=\"nb\"\u003eenable\u003c/span\u003e sing-box\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"3-编辑配置文件\"\u003e3. 编辑配置文件\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e /etc/sing-box\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003evim config.json\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"4-hysteria2-配置示例\"\u003e4. Hysteria2 配置示例\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;log\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;disabled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;level\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;error\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;timestamp\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;inbounds\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;hysteria2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;tag\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;hysteria2-in\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;listen\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;::\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;listen_port\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"err\"\u003e你的端口\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;users\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"nt\"\u003e\u0026#34;password\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;你的密码\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;tls\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nt\"\u003e\u0026#34;enabled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nt\"\u003e\u0026#34;alpn\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"s2\"\u003e\u0026#34;h3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nt\"\u003e\u0026#34;certificate_path\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nt\"\u003e\u0026#34;key_path\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nt\"\u003e\u0026#34;acme\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"nt\"\u003e\u0026#34;domain\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;你的域名\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"nt\"\u003e\u0026#34;dns01_challenge\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;provider\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;cloudflare\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;api_token\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;你的dns_token\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;outbounds\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;direct\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;tag\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;direct\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e配置要点：\u003c/p\u003e","title":"搭建 Hysteria2 教程"},{"content":"搭建 Naive Proxy 适用场景：搭建一个基于 HTTP/2 与 Chromium 网络栈的 NaiveProxy 代理服务端，客户端流量伪装为普通 HTTPS 访问。本文记录使用 sing-box 搭建的完整流程。\n1. 安装 sing-box bash \u0026lt;(curl -fsSL https://sing-box.app/deb-install.sh) sudo systemctl enable sing-box 2. 生成自签证书 sing-box generate tls-keypair bing.com -m 1000 执行后会输出一份私钥和证书。你可以将输出内容分别保存到以下文件中：\nvim /root/fullchain.cer vim /root/private.key 3. 配置 sing-box 将以下内容写入 config.json：\n{ \u0026#34;inbounds\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;naive\u0026#34;, \u0026#34;listen\u0026#34;: \u0026#34;::\u0026#34;, \u0026#34;listen_port\u0026#34;: 你的端口, \u0026#34;users\u0026#34;: [ { \u0026#34;username\u0026#34;: \u0026#34;用户名\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;密码\u0026#34; } ], \u0026#34;tls\u0026#34;: { \u0026#34;enabled\u0026#34;: true, \u0026#34;certificate_path\u0026#34;: \u0026#34;/root/fullchain.cer\u0026#34;, \u0026#34;key_path\u0026#34;: \u0026#34;/root/private.key\u0026#34; } } ], \u0026#34;outbounds\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;direct\u0026#34; } ] } 配置要点：\nlisten_port、username、password 替换为你自己的值。 certificate_path / key_path 指向第 2 步生成的自签证书。 生产环境建议改用正式证书（如 acme.sh 签发），自签证书需在客户端额外信任。 4. 重启服务 sudo systemctl restart sing-box 常用命令 启用 sudo systemctl enable sing-box 禁用 sudo systemctl disable sing-box 启动 sudo systemctl start sing-box 停止 sudo systemctl stop sing-box 强行停止 sudo systemctl kill sing-box 重启 sudo systemctl restart sing-box 查看日志 sudo journalctl -u sing-box --output cat -e 实时日志 sudo journalctl -u sing-box --output cat -f 验证与自查 sudo systemctl status sing-box 显示 active（running） sudo ss -lntup | grep \u0026lt;你的端口\u0026gt; 确认端口监听正常 sudo ls -l /root/fullchain.cer /root/private.key 确认证书文件存在且非空 客户端用 Naive 协议（用户名/密码 + 服务端地址）连接成功，curl ip.sb 出口为服务端 IP 参考 sing-box naive 入站文档 NaiveProxy（klzgrad，GitHub） ","permalink":"https://heihei0299.github.io/posts/network/%E6%90%AD%E5%BB%BAnaiveproxy%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"搭建-naive-proxy\"\u003e搭建 Naive Proxy\u003c/h1\u003e\n\u003cp\u003e适用场景：搭建一个基于 HTTP/2 与 Chromium 网络栈的 NaiveProxy 代理服务端，客户端流量伪装为普通 HTTPS 访问。本文记录使用 sing-box 搭建的完整流程。\u003c/p\u003e\n\u003ch2 id=\"1-安装-sing-box\"\u003e1. 安装 sing-box\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebash \u0026lt;\u003cspan class=\"o\"\u003e(\u003c/span\u003ecurl -fsSL https://sing-box.app/deb-install.sh\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo systemctl \u003cspan class=\"nb\"\u003eenable\u003c/span\u003e sing-box\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"2-生成自签证书\"\u003e2. 生成自签证书\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esing-box generate tls-keypair bing.com -m \u003cspan class=\"m\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e执行后会输出一份私钥和证书。你可以将输出内容分别保存到以下文件中：\u003c/p\u003e","title":"搭建 Naive Proxy"},{"content":"搭建 Snell Server 适用场景：为 Surge 客户端搭建一个轻量的自建代理服务端。本文参考 Surge | 部署 Snell Server – DivineEngine，并结合实际部署过程做了简化整理，从安装依赖到 systemd 托管全流程覆盖。\n1. 获取管理权限 sudo -i 2. 安装依赖 APT 系统 sudo apt update sudo apt install wget unzip vim DNF 系统 sudo dnf install unzip 3. 下载 Snell Server 如果你的机器是 linux-amd64：\nwget https://dl.nssurge.com/snell/snell-server-v4.0.1-linux-amd64.zip 如果是 ARM 机器：\nwget https://dl.nssurge.com/snell/snell-server-v4.0.1-linux-aarch64.zip 更多版本可参考官方页面： Snell Protocol 文档\n4. 解压并赋权 AMD64：\nsudo unzip snell-server-v4.0.1-linux-amd64.zip -d /usr/local/bin ARM64：\nsudo unzip snell-server-v4.0.1-linux-aarch64.zip -d /usr/local/bin 赋予执行权限：\nchmod +x /usr/local/bin/snell-server 5. 编写配置文件 创建配置目录：\nsudo mkdir -p /etc/snell 可以使用向导生成配置：\nsudo snell-server --wizard -c /etc/snell/snell-server.conf 也可以手动创建：\nsudo vim /etc/snell/snell-server.conf 示例配置：\n[snell-server] listen = 0.0.0.0:11807 psk = AijHCeos15IvqDZTb1cJMX5GcgZzIVE ipv6 = false 参数说明：\nlisten：监听地址和端口 psk：连接密钥 ipv6：是否启用 IPv6 6. 配置 systemd 服务 sudo vim /lib/systemd/system/snell.service 写入以下内容：\n[Unit] Description=Snell Proxy Service After=network.target [Service] Type=simple User=nobody Group=nogroup LimitNOFILE=32768 ExecStart=/usr/local/bin/snell-server -c /etc/snell/snell-server.conf AmbientCapabilities=CAP_NET_BIND_SERVICE StandardOutput=syslog StandardError=syslog SyslogIdentifier=snell-server [Install] WantedBy=multi-user.target 注意：\n某些 Linux 发行版中没有 nogroup，可以改成 Group=nobody。 如果需要绑定特权端口，AmbientCapabilities=CAP_NET_BIND_SERVICE 很有用。 7. 启动服务 sudo systemctl daemon-reload sudo systemctl enable snell sudo systemctl start snell 常用命令：\nsudo systemctl stop snell sudo systemctl status snell cat /etc/snell/snell-server.conf 8. 客户端示例 将服务端信息填入 Surge：\nAWS-EC2-SG = snell, XXX.XXX.XXX.XXX, 11807, psk=AijHCeos15IvqDZTb1cJMX5GcgZzIVE, version=4, tfo=true 9. 一键脚本 如果你不想手动部署，也可以尝试以下脚本：\nwget -O snell.sh --no-check-certificate https://raw.githubusercontent.com/getsomecat/Snell/master/snell_new.sh chmod +x snell.sh ./snell.sh 验证与自查 sudo systemctl status snell 显示 active（running） ss -lntup | grep 11807 确认监听端口正常（若改了端口以实际为准） 在 Surge 中添加节点后延迟测试通过，可正常访问外网 sudo journalctl -u snell -e 无报错 参考 Snell Protocol 官方文档 Surge | 部署 Snell Server – DivineEngine getsomecat/Snell 一键脚本（GitHub） ","permalink":"https://heihei0299.github.io/posts/network/%E6%90%AD%E5%BB%BAsnell%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"搭建-snell-server\"\u003e搭建 Snell Server\u003c/h1\u003e\n\u003cp\u003e适用场景：为 Surge 客户端搭建一个轻量的自建代理服务端。本文参考 \u003ca href=\"https://divineengine.net/article/deploying-a-snell-server/\"\u003eSurge | 部署 Snell Server – DivineEngine\u003c/a\u003e，并结合实际部署过程做了简化整理，从安装依赖到 systemd 托管全流程覆盖。\u003c/p\u003e","title":"搭建 Snell Server"},{"content":"构建并推送自定义 PHP 8.2 FPM 镜像（适用于 Typecho） 本文档介绍如何在本地构建一个适用于 Typecho 的自定义 PHP 8.2 FPM 镜像，并将其推送至 Docker Hub。\n准备工作目录 mkdir -p /opt/php cd /opt/php 创建 Dockerfile 创建并编辑 Dockerfile 文件：\nvim Dockerfile 添加以下内容：\nFROM php:8.2-fpm # 安装系统依赖和 PHP 扩展依赖 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y --no-install-recommends \\ git unzip libzip-dev libpng-dev libjpeg62-turbo-dev \\ libfreetype6-dev libwebp-dev libxml2-dev libicu-dev \\ libonig-dev libpq-dev libcurl4-openssl-dev sqlite3 libsqlite3-dev \\ \u0026amp;\u0026amp; rm -rf /var/lib/apt/lists/* # 配置并安装 PHP 扩展 RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \\ \u0026amp;\u0026amp; docker-php-ext-install \\ gd \\ pdo_mysql \\ mysqli \\ pdo_sqlite \\ zip \\ bcmath \\ exif \\ intl \\ opcache \\ sockets \\ mbstring \\ xml \\ curl # 安装 Composer COPY --from=composer:2 /usr/bin/composer /usr/bin/composer # 设置工作目录 WORKDIR /var/www/html # 配置 php.ini 参数优化 RUN mv \u0026#34;$PHP_INI_DIR/php.ini-production\u0026#34; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; sed -i \u0026#39;s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/\u0026#39; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; sed -i \u0026#39;s/memory_limit = .*/memory_limit = 128M/\u0026#39; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; sed -i \u0026#39;s/upload_max_filesize = .*/upload_max_filesize = 32M/\u0026#39; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; sed -i \u0026#39;s/post_max_size = .*/post_max_size = 32M/\u0026#39; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.enable=1\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.enable_cli=1\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.memory_consumption=128\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.interned_strings_buffer=8\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.max_accelerated_files=10000\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.validate_timestamps=1\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; \\ \u0026amp;\u0026amp; echo \u0026#34;opcache.revalidate_freq=2\u0026#34; \u0026gt;\u0026gt; \u0026#34;$PHP_INI_DIR/php.ini\u0026#34; # 设置文件权限 RUN chown -R www-data:www-data /var/www/html # 暴露端口 EXPOSE 9000 # 启动 php-fpm CMD [\u0026#34;php-fpm\u0026#34;] 构建镜像 在包含 Dockerfile 的目录下执行以下命令：\ndocker build --no-cache -t php-8.2-fpm-typecho . 推送镜像到 Docker Hub 1. 登录 Docker Hub docker login 2. 标记（Tag）镜像 将镜像打上你的 Docker Hub 用户名标签：\ndocker tag php-8.2-fpm-typecho YOUR_USERNAME/php-8.2-fpm-typecho:1.0 3. 推送镜像 执行推送操作：\ndocker push YOUR_USERNAME/php-8.2-fpm-typecho:1.0 后续操作（可选） 可以将该镜像用于 Nginx + PHP-FPM 的 Typecho 部署，结合 Docker Compose 使用将更方便。\n验证与自查 docker images | grep php-8.2-fpm-typecho 确认镜像已构建 检查关键扩展已启用：docker run --rm php-8.2-fpm-typecho php -m | grep -E 'gd|pdo_mysql|pdo_sqlite|intl|zip' 验证 php.ini 优化参数：docker run --rm php-8.2-fpm-typecho php -i | grep -E 'memory_limit|upload_max_filesize|opcache.enable' 推送后在其他机器执行 docker pull YOUR_USERNAME/php-8.2-fpm-typecho:1.0 能拉取成功 参考 PHP Docker 官方镜像文档 Composer 官方镜像 ","permalink":"https://heihei0299.github.io/posts/blog/php8-2-fpm-typecho%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA/","summary":"\u003ch1 id=\"构建并推送自定义-php-82-fpm-镜像适用于-typecho\"\u003e构建并推送自定义 PHP 8.2 FPM 镜像（适用于 Typecho）\u003c/h1\u003e\n\u003cp\u003e本文档介绍如何在本地构建一个适用于 Typecho 的自定义 PHP 8.2 FPM 镜像，并将其推送至 Docker Hub。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"准备工作目录\"\u003e准备工作目录\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir -p /opt/php\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e /opt/php\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ch2 id=\"创建-dockerfile\"\u003e创建 Dockerfile\u003c/h2\u003e\n\u003cp\u003e创建并编辑 \u003ccode\u003eDockerfile\u003c/code\u003e 文件：\u003c/p\u003e","title":"构建并推送自定义 PHP 8.2 FPM 镜像（适用于 Typecho）"},{"content":"国内机器安装哪吒探针 适用场景：为境内 VPS 安装哪吒探针 Agent，接入自己的 Dashboard 面板统一监控。本文记录下载 Agent、上传、编写 systemd 服务与启动验证的完整流程。\n1. 下载 Agent 并上传到国内机器 下载地址：\nhttps://github.com/nezhahq/agent/releases\nmkdir /opt/nezha/agent 将文件上传到 /opt/nezha/agent，然后赋予执行权限：\nchmod +x /opt/nezha/agent/nezha-agent 注意：GitHub Releases 下载在境内可能不稳定，可先下载再通过任意方式上传，或使用加速镜像。\n2. 配置 systemd 服务 vim /etc/systemd/system/nezha-agent.service 内容如下：\n[Unit] Description=哪吒探针监控端 ConditionFileIsExecutable=/opt/nezha/agent/nezha-agent [Service] StartLimitInterval=5 StartLimitBurst=10 ExecStart=/home/nezha/nezha-agent -s \u0026lt;Dashboard服务器地址\u0026gt;:\u0026lt;端口\u0026gt; -p \u0026lt;连接密码\u0026gt; WorkingDirectory=/root Restart=always RestartSec=120 EnvironmentFile=-/etc/sysconfig/nezha-agent [Install] WantedBy=multi-user.target 请把 \u0026lt;Dashboard服务器地址\u0026gt;:\u0026lt;端口\u0026gt; 与 \u0026lt;连接密码\u0026gt; 替换为你的 Dashboard 信息（Dashboard 后台 → 服务器 → 安装命令中可查看）。 注意：ExecStart 中的 Agent 路径需与文件实际存放位置一致。上面保留了原文示例路径 /home/nezha/nezha-agent，如果你按第 1 步上传到 /opt/nezha/agent/，请同步修改 ExecStart 路径。\n3. 启动服务 systemctl daemon-reload systemctl enable nezha-agent systemctl start nezha-agent 验证与自查 systemctl status nezha-agent 显示 active（running） journalctl -u nezha-agent -e 无报错，能看到成功连接 Dashboard 的日志 Dashboard 面板中该服务器状态为在线，能正常显示 CPU / 内存 / 流量数据 参考 Nezha Agent 发布页（GitHub） Nezha 官方文档 ","permalink":"https://heihei0299.github.io/posts/network/%E5%9B%BD%E5%86%85%E6%9C%BA%E5%99%A8%E5%AE%89%E8%A3%85%E5%93%AA%E5%90%92%E6%8E%A2%E9%92%88/","summary":"\u003ch1 id=\"国内机器安装哪吒探针\"\u003e国内机器安装哪吒探针\u003c/h1\u003e\n\u003cp\u003e适用场景：为境内 VPS 安装哪吒探针 Agent，接入自己的 Dashboard 面板统一监控。本文记录下载 Agent、上传、编写 systemd 服务与启动验证的完整流程。\u003c/p\u003e\n\u003ch2 id=\"1-下载-agent-并上传到国内机器\"\u003e1. 下载 Agent 并上传到国内机器\u003c/h2\u003e\n\u003cp\u003e下载地址：\u003c/p\u003e","title":"国内机器安装哪吒探针"},{"content":"机场搭建：XBoard Docker Compose 部署 适用场景：快速部署一套 XBoard 机场面板，管理节点、套餐与用户。本文记录使用 Docker Compose + SQLite 快速部署 XBoard 的基本流程。\n如果你需要使用 MySQL，请自行准备数据库环境后再调整部署配置。\n1. 安装 Docker curl -sSL https://get.docker.com | bash systemctl enable docker systemctl start docker 2. 获取 Docker Compose 项目文件 git clone -b docker-compose --depth 1 https://github.com/cedar2025/Xboard cd Xboard 3. 初始化数据库 安装过程中选择启用 SQLite 和 Docker 内置 Redis：\ndocker compose run -it --rm xboard php artisan xboard:install 执行完成后会返回后台地址和管理员账号密码，请及时记录。\n4. 启动 XBoard docker compose up -d 5. 访问站点 默认端口为 7001，可直接访问：\nhttp://你的IP:7001/ 如果需要使用 80 或 443 端口，建议额外配置 Nginx 反向代理。\n更新 修改版本 cd Xboard nano -m docker-compose.yaml 将 image 后面的版本号改为目标版本。如果使用 latest，可跳过这一步。\n执行更新 docker compose pull docker compose down docker compose run -it --rm xboard php artisan xboard:update docker compose up -d 回滚 注意：下面的操作不会回滚数据库，只会回退镜像版本。\n修改 docker-compose.yaml 中的镜像版本号。 重新启动服务： docker compose up -d 说明 启用 Webman 后，如果修改了相关代码，通常需要重启服务才会生效。 如果后续需要对接 v2bx，通常需要先让一个用户购买一次零元套餐，触发 XBoard 在数据库中生成用户记录，否则 v2bx 可能无法正常获取用户数据。 验证与自查 docker compose ps 显示所有服务为 Up 状态 curl -I http://你的IP:7001/ 返回 200 用安装时返回的管理员账号登录后台，能创建套餐并添加第一个用户 docker compose logs --tail 50 无 ERROR 级别报错 参考 Xboard（cedar2025/Xboard，GitHub） Docker Compose 官方文档 ","permalink":"https://heihei0299.github.io/posts/network/%E6%9C%BA%E5%9C%BA%E6%90%AD%E5%BB%BAxboard%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"机场搭建xboard-docker-compose-部署\"\u003e机场搭建：XBoard Docker Compose 部署\u003c/h1\u003e\n\u003cp\u003e适用场景：快速部署一套 XBoard 机场面板，管理节点、套餐与用户。本文记录使用 Docker Compose + SQLite 快速部署 XBoard 的基本流程。\u003c/p\u003e\n\u003cp\u003e如果你需要使用 MySQL，请自行准备数据库环境后再调整部署配置。\u003c/p\u003e","title":"机场搭建：XBoard Docker Compose 部署"},{"content":"启用 IIS 适用场景：需要在 Windows 上托管 asp / PHP 网站或静态网页（如 Yacd 面板）。本文说明 IIS 的作用，以及从启用功能到添加网站的全流程。\nIIS 服务作用 IIS（Internet Information Server，互联网信息服务）是一种 Web 服务组件，其中包括 Web 服务器、FTP 服务器、NNTP 服务器和 SMTP 服务器，分别用于网页浏览、文件传输、新闻服务和邮件发送等方面，它使得在网络（包括互联网和局域网）上发布信息成了一件很容易的事。\n使用微软的编程语言和框架（ASP.NET）写的网站可以部署到 IIS 服务器上。比如你访问了一个使用 ASP.NET 开发的网站的某个页面 index.aspx，请求提交到对方服务器上，IIS 就会收到你的请求，并且解析运行 index.aspx，并把结果输出出来，返回给你，显示到你的浏览器上。\nIIS 同类产品主流的有 Apache 和 Nginx，后二者多用于 PHP、Python 等。当然 IIS 也支持使用 PHP 开发的网站。如果你用 asp 写了一个网站，并且需要部署在一台服务器上给别人访问，最方便的办法就是在 Windows Server 上添加 IIS 功能，并且创建一个应用程序，再把你用 asp 写的网站放进去，别人就可以访问你的服务器上的这个网站了。\n打开步骤 打开 IIS 服务功能 依次进入\u0026quot;控制面板\u0026quot;-\u0026quot;程序\u0026quot;-\u0026quot;启用或关闭 Windows 功能\u0026quot;，或者\u0026quot;设置面板\u0026quot;-\u0026quot;应用\u0026quot;-\u0026quot;可选功能\u0026quot;-\u0026quot;更多 Windows 功能\u0026quot;。\n在 Windows 操作面板中，依次展开\u0026quot;Internet Information Service\u0026quot;-\u0026quot;World Wide Web Services\u0026quot;-\u0026quot;应用程序开发功能（Application Development Features）\u0026quot;。\n在展开的列表中找到 CGI 勾选，如果需要运行 asp 网站，在 asp 前勾选。这里只勾选 CGI，为 IIS 支持 PHP 做准备。\n也可以把 应用程序开发（Application Development Features） 下所有项都勾选。\n单击\u0026quot;确定\u0026quot;等待 Windows 开启完成。\n测试结果 在浏览器页面输入 localhost 能直接显示默认加载的页面：\n管理 IIS 服务 打开 IIS 管理工具 等待 IIS 服务安装完毕之后，可以通过系统的 IIS 管理工具进行管理。\n依次进入\u0026quot;控制面板\u0026quot;-\u0026quot;系统和安全\u0026quot;-\u0026quot;管理工具\u0026quot;，在文件夹中找到 IIS 管理工具：\n接下来就可以在管理工具内进行管理了，管理页面如下：\nIIS 管理操作 最左边点到网站，然后点击最右边 添加网站：\n输入网站名称，物理路径为 存放 yacd 网页文件的文件夹，并输入端口：\n💡 端口号不能与 \u0026ldquo;external-controller: 0.0.0.0:9090\u0026rdquo; 参数冲突\n验证与自查 浏览器访问 localhost 显示 IIS 默认页面，说明服务已启用 IIS 管理工具能正常打开并列出网站 添加网站后，通过 http://localhost:\u0026lt;端口\u0026gt; 能访问对应网页（端口不与 external-controller 的 9090 冲突） 部署 PHP 前确认已勾选 CGI；部署 asp 前确认已勾选 asp 参考 IIS 官方文档（Microsoft Learn） ASP.NET 文档（Microsoft Learn） ","permalink":"https://heihei0299.github.io/posts/network/meta%E5%86%85%E6%A0%B8/windows%E4%BD%BF%E7%94%A8meta%E5%86%85%E6%A0%B8/%E5%90%AF%E7%94%A8iis/","summary":"\u003ch1 id=\"启用-iis\"\u003e启用 IIS\u003c/h1\u003e\n\u003cp\u003e适用场景：需要在 Windows 上托管 asp / PHP 网站或静态网页（如 Yacd 面板）。本文说明 IIS 的作用，以及从启用功能到添加网站的全流程。\u003c/p\u003e\n\u003ch2 id=\"iis-服务作用\"\u003eIIS 服务作用\u003c/h2\u003e\n\u003cp\u003eIIS（Internet Information Server，互联网信息服务）是一种 Web 服务组件，其中包括 Web 服务器、FTP 服务器、NNTP 服务器和 SMTP 服务器，分别用于网页浏览、文件传输、新闻服务和邮件发送等方面，它使得在网络（包括互联网和局域网）上发布信息成了一件很容易的事。\u003c/p\u003e","title":"启用 IIS"},{"content":"使用 efibootmgr 管理 UEFI 启动项：修复丢失的启动项 适用场景：Linux/Windows 双系统环境中 Linux 启动项从 BIOS 启动菜单消失（重装 Windows、清空 NVRAM、主板更换后）。你会学到用 efibootmgr 查看 NVRAM 启动项、定位 EFI 分区、创建/删除启动项与调整启动顺序的完整流程。\n概述 UEFI（统一可扩展固件接口）已取代传统 BIOS 成为现代计算机的标准引导方式。掌握 UEFI 启动项管理技术对多系统用户至关重要。本文将详细介绍如何使用 efibootmgr 工具修复丢失的启动项，特别是在 Linux/Windows 双系统环境中。\nUEFI 基础概念 UEFI：统一可扩展固件接口，现代计算机标准固件 CSM：兼容性支持模块，用于启动传统 MBR 设备 安全启动：仅支持 UEFI 启动的操作系统，Windows 7 等系统需关闭此功能 纯 UEFI 模式：关闭 CSM 后仅支持 UEFI 启动设备 准备工作 当 Linux 启动项丢失时，推荐使用 Linux LiveCD 环境进行修复：\n制作 Ubuntu LiveCD 启动 U 盘（比光盘启动更快） 从 U 盘启动进入 LiveCD 环境 打开终端准备操作 查看现有启动项 $ efibootmgr BootCurrent: 0012 Timeout: 0 seconds BootOrder: 0012,0010,0011,000F,000B,000A,0006,0007,0008,0009,000C,000D,000E Boot0000 Setup Boot0001 Boot Menu # ... 更多启动项 ... Boot000F* Windows Boot Manager Boot0010* ubuntu Boot0011* ubuntushim Boot0012* opensuse-secureboot 输出说明：\nBootCurrent：当前启动项 Timeout：启动菜单等待时间 BootOrder：启动顺序优先级 各启动项按编号列出，星号表示有效项 定位 EFI 系统分区 $ sudo fdisk -l 设备 Start 末尾 扇区 Size 类型 /dev/sdb1 2048 206847 204800 100M EFI System # EFI系统分区 /dev/sdb2 206848 468991 262144 128M Microsoft reserved # ... 其他分区 ... 挂载并探索 EFI 分区 $ sudo mount /dev/sdb1 /mnt/ # 挂载EFI分区 $ sudo -i # 切换root用户 $ cd /mnt/EFI # 进入EFI目录 $ ls # 查看内容 Boot Microsoft opensuse ubuntu 各目录内容概览：\nBoot/：通用启动加载程序（bootx64.efi） Microsoft/：Windows 启动文件 opensuse/：openSUSE 启动文件（grub.efi, shim.efi 等） ubuntu/：Ubuntu 启动文件（shimx64.efi, grubx64.efi 等） 添加启动项 基本命令格式：\nefibootmgr -c -w -L \u0026#34;描述标签\u0026#34; -d /dev/sdX -p Y -l \\\\EFI\\\\路径\\\\文件.efi 参数说明：\n-c：创建新启动项 -w：立即写入 NVRAM -L：启动项显示名称 -d：磁盘设备（如 /dev/sdb） -p：EFI 分区号（通常是 1） -l：EFI 文件路径（使用双反斜杠） 实际操作示例：\nefibootmgr -c -w -L \u0026#34;Linux Rescue\u0026#34; -d /dev/sdb -p 1 -l \\\\EFI\\\\opensuse\\\\grubx64.efi 重要注意事项 避免使用通用启动文件：不要添加 /EFI/Boot/bootx64.efi，此文件可能被 Windows 覆盖 选择正确的 EFI 文件：应使用发行版特定目录下的文件（如 /EFI/ubuntu/shimx64.efi） 多系统引导顺序：最后安装的系统 GRUB 通常会管理所有系统启动项 测试添加的启动项：重启系统验证启动项是否正常工作 管理启动项 删除启动项 efibootmgr -b 0013 -B # 删除编号0013的启动项 调整启动顺序 efibootmgr -o 0012,0010,0011,000F # 设置启动顺序 设置超时时间 efibootmgr -t 5 # 设置5秒超时 验证与自查 efibootmgr 输出中包含新添加的启动项编号，且带 * 星号（有效项） BootOrder 第一项就是你想默认启动的系统编号 重启后 BIOS 启动菜单/GRUB 界面出现对应系统，可正常进入 操作前先 efibootmgr -v 记录原有启动项，便于出错时恢复 总结 efibootmgr 是管理 UEFI 启动项的强大命令行工具，特别适合解决双系统启动项丢失问题。通过本文介绍的方法，您可以有效修复启动问题，维护多系统引导环境的稳定性。\n提示：操作前请确保备份重要数据，修改启动项有一定风险，建议在了解各参数含义后再进行生产环境操作。\n参考 ArchWiki - Unified Extensible Firmware Interface efibootmgr man page ArchWiki - GRUB ","permalink":"https://heihei0299.github.io/posts/linux/%E7%94%A8efibootmgr%E7%AE%A1%E7%90%86uefi%E5%90%AF%E5%8A%A8%E9%A1%B9%E6%B7%BB%E5%8A%A0%E4%B8%A2%E5%A4%B1%E7%9A%84%E5%90%AF%E5%8A%A8%E9%A1%B9/","summary":"\u003ch1 id=\"使用-efibootmgr-管理-uefi-启动项修复丢失的启动项\"\u003e使用 efibootmgr 管理 UEFI 启动项：修复丢失的启动项\u003c/h1\u003e\n\u003cp\u003e适用场景：Linux/Windows 双系统环境中 Linux 启动项从 BIOS 启动菜单消失（重装 Windows、清空 NVRAM、主板更换后）。你会学到用 \u003ccode\u003eefibootmgr\u003c/code\u003e 查看 NVRAM 启动项、定位 EFI 分区、创建/删除启动项与调整启动顺序的完整流程。\u003c/p\u003e","title":"使用 efibootmgr 管理 UEFI 启动项：修复丢失的启动项"},{"content":"使用 Mihomo (Clash Meta) + WARP 为 VPS 添加 IPv4/IPv6 支持 适用场景:\nIPv6-only VPS 获取 IPv4 访问能力 IPv4-only VPS 获取 IPv6 访问能力 通过 Mihomo 实现灵活的流量路由和代理 相关文档:\nCloudflare WARP 教程 Mihomo WireGuard 配置文档 Mihomo TUN 配置文档 前置准备 1. 安装 Mihomo 下载 Mihomo\n# 下载最新版本（Linux amd64） wget -O /usr/local/bin/mihomo https://github.com/MetaCubeX/mihomo/releases/latest/download/mihomo-linux-amd64 # 或使用 curl curl -fsSL https://github.com/MetaCubeX/mihomo/releases/latest/download/mihomo-linux-amd64 -o /usr/local/bin/mihomo # 添加执行权限 chmod +x /usr/local/bin/mihomo 其他架构下载\nLinux ARM64: mihomo-linux-arm64 Linux ARMv7: mihomo-linux-armv7 Linux 386: mihomo-linux-386 创建配置目录\n# 创建 Mihomo 配置目录 mkdir -p /etc/mihomo # 创建日志目录 mkdir -p /var/log/mihomo 2. 获取 WARP 配置信息 安装 wgcf 工具\n# 下载 wgcf wget -O /usr/local/bin/wgcf https://github.com/ViRb3/wgcf/releases/latest/download/wgcf_2.2.20_linux_amd64 # 添加执行权限 chmod +x /usr/local/bin/wgcf 注册 WARP 账户并生成配置\n# 注册 WARP 账户 wgcf register # 生成 WireGuard 配置文件 wgcf generate 查看生成的配置\ncat wgcf-profile.conf 示例输出：\n[Interface] PrivateKey = YourPrivateKeyHere== Address = 172.16.0.2/32, fd01:5ca1:ab1e:xxxx:xxxx:xxxx:xxxx:xxxx/128 DNS = 1.1.1.1 [Peer] PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = engage.cloudflareclient.com:2408 生成的配置文件包含以下关键信息：\nPrivateKey: 客户端私钥 Address: 分配的 IPv4 和 IPv6 地址 PublicKey: WARP 服务端公钥 方案一：使用 TUN 模式（推荐） TUN 模式是一种虚拟网卡技术，可以实现全局透明代理，无需手动设置系统代理或环境变量。\nIPv6-only VPS 获取 IPv4 访问 创建 /etc/mihomo/config.yaml：\n# Mihomo 基础配置 mixed-port: 7890 allow-lan: true mode: rule log-level: info external-controller: 127.0.0.1:9090 # TUN 配置 tun: enable: true stack: mixed # system/gvisor/mixed，推荐 mixed device: mihomo-tun auto-route: true auto-redirect: true auto-detect-interface: true dns-hijack: - any:53 mtu: 1280 # DNS 配置 dns: enable: true listen: 0.0.0.0:53 enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 nameserver: - 1.1.1.1 - 8.8.8.8 # 代理配置 proxies: - name: \u0026#34;WARP-IPv4\u0026#34; type: wireguard server: 2606:4700:d0::a29f:c001 # IPv6-only 机器使用 IPv6 端点 port: 2408 ip: 172.16.0.2 # 从 wgcf-profile.conf 获取 ipv6: fd01:5ca1:ab1e:xxxx:xxxx:xxxx:xxxx:xxxx # 替换为实际地址 private-key: \u0026#34;YourPrivateKeyHere==\u0026#34; # 从 wgcf-profile.conf 获取 public-key: \u0026#34;bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=\u0026#34; allowed-ips: - 0.0.0.0/0 # 仅路由 IPv4 流量 mtu: 1280 udp: true # 代理组 proxy-groups: - name: \u0026#34;PROXY\u0026#34; type: select proxies: - WARP-IPv4 - DIRECT # 路由规则 rules: # IPv4 流量走 WARP - IP-CIDR,0.0.0.0/0,WARP-IPv4,no-resolve # IPv6 流量直连 - IP-CIDR6,::/0,DIRECT,no-resolve IPv4-only VPS 获取 IPv6 访问 创建 /etc/mihomo/config.yaml：\n# Mihomo 基础配置 mixed-port: 7890 allow-lan: true mode: rule log-level: info external-controller: 127.0.0.1:9090 # TUN 配置 tun: enable: true stack: mixed device: mihomo-tun auto-route: true auto-redirect: true auto-detect-interface: true dns-hijack: - any:53 mtu: 1280 # DNS 配置 dns: enable: true listen: 0.0.0.0:53 enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 nameserver: - 1.1.1.1 - 8.8.8.8 # 代理配置 proxies: - name: \u0026#34;WARP-IPv6\u0026#34; type: wireguard server: 162.159.192.1 # IPv4-only 机器使用 IPv4 端点 port: 2408 ip: 172.16.0.2 ipv6: fd01:5ca1:ab1e:xxxx:xxxx:xxxx:xxxx:xxxx private-key: \u0026#34;YourPrivateKeyHere==\u0026#34; public-key: \u0026#34;bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=\u0026#34; allowed-ips: - ::/0 # 仅路由 IPv6 流量 mtu: 1280 udp: true # 代理组 proxy-groups: - name: \u0026#34;PROXY\u0026#34; type: select proxies: - WARP-IPv6 - DIRECT # 路由规则 rules: # IPv4 流量直连 - IP-CIDR,0.0.0.0/0,DIRECT,no-resolve # IPv6 流量走 WARP - IP-CIDR6,::/0,WARP-IPv6,no-resolve TUN 模式注意事项 权限要求: TUN 模式需要 root 权限运行 内核支持: 确保系统内核支持 TUN 设备 防火墙: 需要放行内核进程才能使用 system/mixed 栈 DNS 劫持: dns-hijack 会拦截所有 DNS 请求 方案二：使用系统代理方式 如果不想使用 TUN 模式，可以通过设置系统代理或环境变量的方式使用 Mihomo。\nIPv6-only VPS 获取 IPv4 访问 创建 /etc/mihomo/config.yaml：\n# Mihomo 基础配置 mixed-port: 7890 allow-lan: true mode: rule log-level: info external-controller: 127.0.0.1:9090 # 代理配置 proxies: - name: \u0026#34;WARP-IPv4\u0026#34; type: wireguard server: 2606:4700:d0::a29f:c001 # IPv6-only 机器使用 IPv6 端点 port: 2408 ip: 172.16.0.2 ipv6: fd01:5ca1:ab1e:xxxx:xxxx:xxxx:xxxx:xxxx private-key: \u0026#34;YourPrivateKeyHere==\u0026#34; public-key: \u0026#34;bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=\u0026#34; allowed-ips: - 0.0.0.0/0 mtu: 1280 udp: true # 代理组 proxy-groups: - name: \u0026#34;PROXY\u0026#34; type: select proxies: - WARP-IPv4 - DIRECT # 路由规则 rules: - IP-CIDR,0.0.0.0/0,WARP-IPv4,no-resolve - IP-CIDR6,::/0,DIRECT,no-resolve 配置系统代理 使用环境变量设置代理：\n# 临时设置 export http_proxy=http://127.0.0.1:7890 export https_proxy=http://127.0.0.1:7890 export all_proxy=socks5://127.0.0.1:7890 # 永久设置（添加到 ~/.bashrc 或 /etc/profile） echo \u0026#39;export http_proxy=http://127.0.0.1:7890\u0026#39; \u0026gt;\u0026gt; ~/.bashrc echo \u0026#39;export https_proxy=http://127.0.0.1:7890\u0026#39; \u0026gt;\u0026gt; ~/.bashrc echo \u0026#39;export all_proxy=socks5://127.0.0.1:7890\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 启动和管理 Mihomo 手动启动 # 前台运行（用于测试） mihomo -d /etc/mihomo # 后台运行 nohup mihomo -d /etc/mihomo \u0026gt; /var/log/mihomo/mihomo.log 2\u0026gt;\u0026amp;1 \u0026amp; 创建 systemd 服务 创建 /etc/systemd/system/mihomo.service：\n[Unit] Description=Mihomo Service After=network.target [Service] Type=simple User=root ExecStart=/usr/local/bin/mihomo -d /etc/mihomo Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target 管理服务 # 重载 systemd 配置 systemctl daemon-reload # 启动服务 systemctl start mihomo # 查看服务状态 systemctl status mihomo # 启用开机自启 systemctl enable mihomo # 重启服务 systemctl restart mihomo # 停止服务 systemctl stop mihomo # 查看日志 journalctl -u mihomo -f 验证配置 测试 IPv4 连接 # 通过代理测试 IPv4 curl -x http://127.0.0.1:7890 -4 ip.p3terx.com curl -x http://127.0.0.1:7890 -4 ifconfig.me # 或使用环境变量 export http_proxy=http://127.0.0.1:7890 curl -4 ip.p3terx.com 测试 IPv6 连接 # 通过代理测试 IPv6 curl -x http://127.0.0.1:7890 -6 ip.p3terx.com curl -x http://127.0.0.1:7890 -6 ifconfig.me # 或使用环境变量 export http_proxy=http://127.0.0.1:7890 curl -6 ip.p3terx.com 故障排查 1. Mihomo 无法启动 # 检查配置文件语法 mihomo -t -d /etc/mihomo # 查看详细错误日志 mihomo -d /etc/mihomo # 检查端口占用 netstat -tlnp | grep 7890 lsof -i :7890 2. 无法连接到 WARP # 检查 WireGuard 密钥是否正确 cat wgcf-profile.conf # 测试端点连通性（IPv4 机器） ping -c 4 162.159.192.1 # 测试端点连通性（IPv6 机器） ping6 -c 4 2606:4700:d0::a29f:c001 # 检查防火墙 iptables -L -n -v ufw status 配置参数说明 端点地址选择规则 IPv4-only 机器（获取 IPv6）:\n使用 IPv4 端点：162.159.192.1 或 162.159.193.1 配置 allowed-ips: [\u0026quot;::/0\u0026quot;] 仅路由 IPv6 流量 IPv6-only 机器（获取 IPv4）:\n使用 IPv6 端点：2606:4700:d0::a29f:c001 或 2606:4700:d0::a29f:c101 配置 allowed-ips: [\u0026quot;0.0.0.0/0\u0026quot;] 仅路由 IPv4 流量 双栈机器:\n可使用任意端点 配置 allowed-ips: [\u0026quot;0.0.0.0/0\u0026quot;, \u0026quot;::/0\u0026quot;] 路由所有流量 使用场景示例 场景 1：仅 ChatGPT 走 WARP rules: - DOMAIN-SUFFIX,openai.com,WARP-IPv4 - DOMAIN-SUFFIX,chatgpt.com,WARP-IPv4 - DOMAIN-KEYWORD,openai,WARP-IPv4 - MATCH,DIRECT 场景 2：仅流媒体走 WARP rules: - DOMAIN-SUFFIX,netflix.com,WARP-IPv4 - DOMAIN-SUFFIX,hulu.com,WARP-IPv4 - DOMAIN-SUFFIX,disneyplus.com,WARP-IPv4 - MATCH,DIRECT 注意事项 端点地址选择: IPv4 机器使用 IPv4 端点（162.159.192.1），IPv6 机器使用 IPv6 端点（2606:4700:d0::a29f:c001） 密钥安全: 妥善保管 private-key，不要泄露 配置备份: 定期备份 wgcf-account.toml 和配置文件 合规使用: 遵守 Cloudflare 服务条款，避免滥用 性能影响: WARP 会增加延迟，不适合低延迟场景 IP 限制: WARP IP 可能被部分服务封禁 相关资源 官方文档 Mihomo 官方文档 Mihomo WireGuard 配置 Mihomo TUN 配置 Cloudflare WARP 工具项目 Mihomo GitHub wgcf GitHub 相关教程 Cloudflare WARP 教程 本文档整理自 Mihomo 官方文档和社区实践，仅供学习和参考使用。\n","permalink":"https://heihei0299.github.io/posts/ops/mihomo-warp-%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"使用-mihomo-clash-meta--warp-为-vps-添加-ipv4ipv6-支持\"\u003e使用 Mihomo (Clash Meta) + WARP 为 VPS 添加 IPv4/IPv6 支持\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e适用场景:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIPv6-only VPS 获取 IPv4 访问能力\u003c/li\u003e\n\u003cli\u003eIPv4-only VPS 获取 IPv6 访问能力\u003c/li\u003e\n\u003cli\u003e通过 Mihomo 实现灵活的流量路由和代理\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e相关文档:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/posts/ops/cloudflare-warp-%E6%95%99%E7%A8%8B/\"\u003eCloudflare WARP 教程\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://wiki.metacubex.one/config/proxies/wg/\"\u003eMihomo WireGuard 配置文档\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://wiki.metacubex.one/config/inbound/tun/\"\u003eMihomo TUN 配置文档\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"前置准备\"\u003e前置准备\u003c/h2\u003e\n\u003ch3 id=\"1-安装-mihomo\"\u003e1. 安装 Mihomo\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e下载 Mihomo\u003c/strong\u003e\u003c/p\u003e","title":"使用 Mihomo (Clash Meta) + WARP 为 VPS 添加 IPv4/IPv6 支持"},{"content":"使用 rclone 自动备份 Linux 服务器重要数据 本文适合需要把服务器关键目录定期备份到远程服务器的运维人员。你会学到：创建受限备份用户（禁 sudo/su）、配置 SSH 公钥认证、安装配置 rclone 的 sftp 远程，并用脚本 + crontab 定时备份 /etc、/var/www、/opt，远程只保留最近 3 份。\n本文介绍如何在 Linux 服务器上创建一个受限的备份用户，并使用 rclone 工具定期将 /etc、/var/www 和 /opt 三个目录备份至远程服务器。内容涵盖用户权限控制、SSH 公钥登录、rclone 配置及定时任务设置。\n一、目标服务器创建受限的 rclone-backup 用户 1. 创建用户 sudo adduser rclone-backup 按提示设置密码，其他信息可留空。\n2. 禁用 sudo 权限 将该用户从 sudo 组中移除，避免其获得 root 权限：\nsudo deluser rclone-backup sudo 3. 禁用 su 权限 编辑 PAM 配置文件，限制用户使用 su 命令：\nsudo nano /etc/pam.d/su 找到以下行并取消注释（或添加）：\nauth required pam_wheel.so use_uid 然后编辑 /etc/group 文件，确保 rclone-backup 不属于 wheel 组。\n4. 测试与验证 执行以下命令，确认 rclone-backup 用户无权限使用 sudo 和 su：\nsudo -u rclone-backup sudo -i sudo -u rclone-backup su 如果输出为\u0026quot;权限被拒绝\u0026quot;或类似信息，则权限设置成功。\n5. 创建备份文件夹 mkdir /opt/backup chown -R rclone-backup:rclone-backup /opt/backup chmod -R 755 /opt/backup 二、目标服务器配置 SSH 登录（公钥认证） 1. 配置目标服务器上的公钥 mkdir -p /home/rclone-backup/.ssh nano /home/rclone-backup/.ssh/authorized_keys 粘贴你的公钥：\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGjFShLsR9+8ksNfNli76sxbLRxw4TKKLdgH7J/9xz8h 2. 设置权限（必须） chmod 755 /home/rclone-backup chmod 700 /home/rclone-backup/.ssh chmod 600 /home/rclone-backup/.ssh/authorized_keys chown -R rclone-backup:rclone-backup /home/rclone-backup/.ssh 三、源服务器配置 SSH 私钥 nano /root/.ssh/rclone-backup.key 将私钥粘贴进去后，openssh 出于安全限制，要求私钥权限为600：\nchmod 600 /root/.ssh/rclone-backup.key 四、源服务器安装并配置 rclone 1. 安装 rclone wget https://downloads.rclone.org/v1.69.2/rclone-v1.69.2-linux-amd64.deb dpkg -i rclone-v1.69.2-linux-amd64.deb 2. 配置 rclone 远程连接 编辑配置文件：\nmkdir -p /root/.config/rclone nano /root/.config/rclone/rclone.conf 示例配置（两个远程服务器）：\n[us-bwg] type = sftp host = xx.x.x.x port = 5020 user = rclone-backup key_file = /root/.ssh/rclone-backup.key shell_type = unix md5sum_command = md5sum sha1sum_command = sha1sum [us-dmit] type = sftp host = x.x.x.x port = 5020 user = rclone-backup key_file = /root/.ssh/rclone-backup.key shell_type = unix md5sum_command = md5sum sha1sum_command = sha1sum 五、编写定时备份脚本 创建备份脚本：\nnano /root/backup.sh 脚本内容如下：\n#!/bin/bash # 步骤 1：准备备份目录 mkdir -p /opt/backcup mkdir -p /home/backup/ mkdir -p /opt/backcup/var/www/ rsync -aq --delete /etc/ /opt/backcup/etc/ rsync -aq --delete /var/www/ /opt/backcup/var/www/ # 步骤 2：打包 /opt 目录 timestamp=$(date +\u0026#34;%Y%m%d%H%M%S\u0026#34;) backup_file=\u0026#34;/home/backup/opt-${timestamp}.tar.gz\u0026#34; tar -czvf \u0026#34;$backup_file\u0026#34; /opt # 步骤 3：同步至远程并保留最新 3 个备份 remotes=(\u0026#34;us-bwg:/opt/backup/us-phx\u0026#34; \u0026#34;us-dmit:/opt/backup/us-phx\u0026#34;) for remote in \u0026#34;${remotes[@]}\u0026#34;; do # 上传备份文件 remote_file=() rclone copy \u0026#34;$backup_file\u0026#34; \u0026#34;$remote\u0026#34; -L -u -v -P \\ --transfers=20 --ignore-errors --buffer-size=128M \\ --check-first --checkers=10 --drive-acknowledge-abuse # 获取远程文件列表 mapfile -t remote_files \u0026lt; \u0026lt;(rclone lsf \u0026#34;$remote\u0026#34; | grep \u0026#39;opt-\u0026#39; | sort -r) # 删除旧备份，只保留最新 3 个 if [ ${#remote_files[@]} -gt 3 ]; then for ((i=3; i\u0026lt;${#remote_files[@]}; i++)); do echo \u0026#34;删除旧备份：${remote_files[$i]}\u0026#34; rclone delete \u0026#34;$remote/${remote_files[$i]}\u0026#34; done fi done # 步骤 4：可选 - 删除本地打包文件 rm -f \u0026#34;$backup_file\u0026#34; 给予执行权限：\nchmod +x /root/backup.sh 六、设置定时任务（crontab） 执行以下命令：\ncrontab -e 添加以下内容，每周一凌晨 2 点执行：\n0 2 * * 1 /root/backup.sh \u0026gt;\u0026gt; /var/log/backup.sh.log 2\u0026gt;\u0026amp;1 验证与自查 手动执行 bash /root/backup.sh 无报错，且 rclone lsf us-bwg:/opt/backup/us-phx 能看到 opt-时间戳.tar.gz 文件 crontab -l 能看到定时任务，date 确认任务时间正确 远程目录中旧备份被轮换删除，始终只保留最新 3 份 抽查远程备份包完整性：tar -tzf 或解压后与源目录对比 参考 rclone 官方文档：SFTP 后端 rclone 官方文档：copy/lsf/delete 命令 crontab(5) 手册 rsync 官方文档 ","permalink":"https://heihei0299.github.io/posts/ops/%E5%A4%87%E4%BB%BD%E6%96%B9%E6%A1%88/","summary":"\u003ch1 id=\"使用-rclone-自动备份-linux-服务器重要数据\"\u003e使用 rclone 自动备份 Linux 服务器重要数据\u003c/h1\u003e\n\u003cp\u003e本文适合需要把服务器关键目录定期备份到远程服务器的运维人员。你会学到：创建受限备份用户（禁 sudo/su）、配置 SSH 公钥认证、安装配置 rclone 的 sftp 远程，并用脚本 + crontab 定时备份 \u003ccode\u003e/etc\u003c/code\u003e、\u003ccode\u003e/var/www\u003c/code\u003e、\u003ccode\u003e/opt\u003c/code\u003e，远程只保留最近 3 份。\u003c/p\u003e","title":"使用 rclone 自动备份 Linux 服务器重要数据"},{"content":"为typecho启用cloudflare 本文适用于 Typecho 站点接入 Cloudflare 后加固源站 nginx 的场景。你会学到：如何让 nginx 只响应已知域名、只接受来自 Cloudflare 的访问，避免攻击者绕过 CDN 直接打源站。\n站点启用 Cloudflare 后，源站 IP 仍然公网可达。需要做两层限制：\n只允许特定的 sni 或 host 访问特定的服务，其他未知 sni 或 host 直接返回 404。 只允许 cloudflare 的 IP 访问 80 端口。 配置 nginx 默认 server：拒绝未知 Host 编辑 /etc/nginx/conf.d/default.conf：\nvim /etc/nginx/conf.d/default.conf # 默认 server，匹配所有未定义的 Host server { listen 80 default_server; server_name _; # 匹配任意 Host include /etc/nginx/conf.d/cf-ip.conf; deny all; return 404; } include /etc/nginx/conf.d/cf-ip.conf; 引入了 Cloudflare IP 白名单配置（见下文），让非 Cloudflare 来源直接被拒绝。\nTypecho 站点配置 编辑 /etc/nginx/conf.d/typecho.conf：\nvim /etc/nginx/conf.d/typecho.conf server { listen 80; server_name yourservername; root /var/www/html/typecho; index index.html index.htm index.php; location / { try_files $uri $uri/ /index.php?$args; } location ~ \\.php$ { fastcgi_pass 127.0.0.1:5050; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } 生成 Cloudflare IP 白名单 cf-ip.conf 需要从 Cloudflare 官方 IP 列表生成。参考 Cloudflare IP 列表：\n# IPv4 curl -s https://www.cloudflare.com/ips-v4 -o /tmp/cf-ips-v4 # IPv6 curl -s https://www.cloudflare.com/ips-v6 -o /tmp/cf-ips-v6 将 allow \u0026lt;ip\u0026gt;; 逐行写入 /etc/nginx/conf.d/cf-ip.conf，并在末尾加 deny all;。注意此文件只能用于「仅 Cloudflare 可访问」的端口（如 80），否则会阻断真实用户。\n验证与自查 nginx -t nginx -s reload curl -H \u0026quot;Host: yourservername\u0026quot; http://\u0026lt;源站IP\u0026gt;/ 能正常返回 Typecho 页面 直接访问源站 IP（不带正确 Host）应返回 404 非 Cloudflare IP 直接请求 80 端口应被拒绝（403 或连接失败） 经 Cloudflare 域名访问站点一切正常 参考 Cloudflare 官方 IP 列表 Nginx 官方文档 ","permalink":"https://heihei0299.github.io/posts/blog/%E4%B8%BAtypecho%E5%90%AF%E7%94%A8cloudflare/","summary":"\u003ch1 id=\"为typecho启用cloudflare\"\u003e为typecho启用cloudflare\u003c/h1\u003e\n\u003cp\u003e本文适用于 Typecho 站点接入 Cloudflare 后加固源站 nginx 的场景。你会学到：如何让 nginx 只响应已知域名、只接受来自 Cloudflare 的访问，避免攻击者绕过 CDN 直接打源站。\u003c/p\u003e\n\u003cp\u003e站点启用 Cloudflare 后，源站 IP 仍然公网可达。需要做两层限制：\u003c/p\u003e","title":"为typecho启用cloudflare"},{"content":"自建 Bing 每日图片 API 本文适合想给自己的站点提供 Bing 每日图片接口的开发者。你会学到：用一段 PHP 脚本代理 Bing 每日壁纸接口，支持随机模式（rand=true）、自定义分辨率（size）和 JSON 信息返回（info=true），并用 Nginx 部署。\nPHP 代码 \u0026lt;?php // 判断是否启用随机模式（rand=true） $idx = (isset($_GET[\u0026#39;rand\u0026#39;]) \u0026amp;\u0026amp; $_GET[\u0026#39;rand\u0026#39;] === \u0026#39;true\u0026#39;) ? rand(0, 7) : 0; // 获取 Bing 图片 JSON 数据 $json = @file_get_contents(\u0026#34;https://www.bing.com/HPImageArchive.aspx?format=js\u0026amp;idx=$idx\u0026amp;n=1\u0026#34;); if ($json === false) { http_response_code(500); echo \u0026#34;无法获取 Bing 图片数据\u0026#34;; exit; } // 解码 JSON $data = json_decode($json); $image = $data-\u0026gt;images[0]; // 获取分辨率参数，默认 1920x1080 $size = empty($_GET[\u0026#39;size\u0026#39;]) ? \u0026#34;1920x1080\u0026#34; : $_GET[\u0026#39;size\u0026#39;]; // 拼接完整图片 URL $imageUrl = \u0026#34;https://www.bing.com\u0026#34; . $image-\u0026gt;urlbase . \u0026#34;_{$size}.jpg\u0026#34;; // 获取其他信息 $title = $image-\u0026gt;copyright; $link = $image-\u0026gt;copyrightlink; $time = $image-\u0026gt;startdate; // 判断是否仅返回信息（info=true） if (isset($_GET[\u0026#39;info\u0026#39;]) \u0026amp;\u0026amp; $_GET[\u0026#39;info\u0026#39;] === \u0026#39;true\u0026#39;) { header(\u0026#34;Content-Type: application/json\u0026#34;); echo json_encode([ \u0026#34;title\u0026#34; =\u0026gt; $title, \u0026#34;url\u0026#34; =\u0026gt; $imageUrl, \u0026#34;link\u0026#34; =\u0026gt; $link, \u0026#34;time\u0026#34; =\u0026gt; $time ]); } else { // 否则重定向到图片地址 header(\u0026#34;Location: $imageUrl\u0026#34;); exit; } Nginx 配置文件 server { listen 127.0.0.1:60080; root /var/www/html; index index.php index.html; location / { try_files $uri $uri/ =404; } # 处理 PHP 文件 location ~ \\.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; # 这里根据你的 PHP 版本可能不同 } location ~ /\\.ht { deny all; } } 使用方法 使用 url https://yourdomain.com?rand=true\n验证与自查 curl -I 'https://yourdomain.com' 返回 302，Location 指向 https://www.bing.com/... 图片地址 curl -I 'https://yourdomain.com?rand=true' 多次执行返回不同的图片 URL curl 'https://yourdomain.com?info=true' 返回包含 title/url/link/time 的 JSON 带 size 参数（如 ?size=1920x1200）能拿到对应分辨率图片 参考 PHP 官方文档（file_get_contents / json_decode / header） Nginx 官方文档：FastCGI 模块 Bing HPImageArchive 接口说明 ","permalink":"https://heihei0299.github.io/posts/ops/%E8%87%AA%E5%BB%BAbing-%E6%AF%8F%E6%97%A5%E5%9B%BE%E7%89%87api/","summary":"\u003ch1 id=\"自建-bing-每日图片-api\"\u003e自建 Bing 每日图片 API\u003c/h1\u003e\n\u003cp\u003e本文适合想给自己的站点提供 Bing 每日图片接口的开发者。你会学到：用一段 PHP 脚本代理 Bing 每日壁纸接口，支持随机模式（\u003ccode\u003erand=true\u003c/code\u003e）、自定义分辨率（\u003ccode\u003esize\u003c/code\u003e）和 JSON 信息返回（\u003ccode\u003einfo=true\u003c/code\u003e），并用 Nginx 部署。\u003c/p\u003e","title":"自建 Bing 每日图片 API"},{"content":"zsh 与 zinit 启动时间测试方法 本文适合想定位 zsh 启动慢、或想验证 zinit 延迟加载效果的用户。你会学到：用 time zsh -i -c exit 测总耗时、用 zprof 找慢点、对 zinit 插件与 nvm/compinit 做对照实验的科学排查流程。\n目标只有一件事：先测量，再定位，再做对照实验。\n参考文章：\nhttps://spencer-blog-legacy.vercel.app/2020/07/remove-nvm-to-speed-up-zsh/ 一、什么时候用 适合这些情况：\n打开终端明显变慢 ~/.zshrc 越来越大，不确定慢点在哪 使用了 zinit，想判断哪些插件该延迟加载 想验证 nvm、compinit、主题或补全是否拖慢启动 二、先测总启动时间 先拿基线：\ntime zsh -i -c exit -i：交互式启动 -c exit：启动后立即退出，只测初始化 不要只跑一次，建议连续测试：\nfor i in {1..10}; do time zsh -i -c exit done 记录三件事：\n平均耗时 最快和最慢 修改前后的变化 三、保留一份基线记录 可以直接记成这样：\n测试时间：2026-04-03 机器环境：本机 / VPS / WSL zsh 版本：zsh --version 插件管理器：zinit 测试命令：time zsh -i -c exit 优化前： - 第 1 次：0.92s - 第 2 次：0.88s - 第 3 次：0.95s 优化后： - 第 1 次：0.41s - 第 2 次：0.39s - 第 3 次：0.43s 不要只写\u0026quot;感觉快了\u0026quot;。\n四、用 zprof 找慢点 在 ~/.zshrc 顶部加入：\nzmodload zsh/zprof 在末尾加入：\nzprof 然后执行：\nzsh -i -c exit 重点看：\nself 高的函数 total 高的函数 调用次数异常多的函数 插件、补全、版本管理器相关函数 常见慢点：\nnvm compinit compaudit 主题初始化 zinit 加载的补全插件 各类 eval \u0026quot;$(xxx init zsh)\u0026quot; 五、zinit 的对照测试 不要一次性重写配置，直接做对照实验。\n原始配置例如：\nzinit light zsh-users/zsh-autosuggestions zinit light zsh-users/zsh-completions zinit light Aloxaf/fzf-tab zinit light zsh-users/zsh-syntax-highlighting 改成延迟加载再测：\nzinit ice wait lucid zinit light zsh-users/zsh-autosuggestions zinit ice wait lucid zinit light zsh-users/zsh-completions 然后重新执行：\ntime zsh -i -c exit 比较三件事：\n同步加载耗时 wait 后是否明显下降 是否影响实际使用 六、推荐排查顺序 1. 测原始配置 for i in {1..5}; do time zsh -i -c exit done 2. 打开 zprof 先找最慢的前几个对象。\n3. 优先查高概率慢点 通常先看：\nnvm pyenv rbenv conda compinit 提示符主题 zinit 中的大插件或补全插件 4. 每次只改一类 不要同时改多个变量，否则很难判断因果关系。\n5. 每改一次就重测 time zsh -i -c exit 必要时再跑一次 zprof。\n七、专项测试 nvm 保留原配置先测，再临时注释下面这类内容重测：\nexport NVM_DIR=\u0026#34;$HOME/.nvm\u0026#34; [ -s \u0026#34;$NVM_DIR/nvm.sh\u0026#34; ] \u0026amp;\u0026amp; \\. \u0026#34;$NVM_DIR/nvm.sh\u0026#34; [ -s \u0026#34;$NVM_DIR/bash_completion\u0026#34; ] \u0026amp;\u0026amp; \\. \u0026#34;$NVM_DIR/bash_completion\u0026#34; 如果差距明显，说明 nvm 是主要慢点之一。常见处理方式：\n改成按需加载 进入 Node 项目目录时再加载 换成更轻的工具，例如 fnm compinit 典型写法：\nautoload -Uz compinit compinit 保留原配置测一轮，再临时注释后重测：\ntime zsh -i -c exit 如果差距明显，再考虑：\n精简补全插件 避免重复初始化 优化补全缓存 把部分补全插件改成延迟加载 八、准备最小化配置做对照 复杂配置里来回删改容易混乱，建议单独建测试目录：\nmkdir -p ~/zsh-benchmark cp ~/.zshrc ~/zsh-benchmark/.zshrc 也可以先换成最小配置：\n# ~/zsh-benchmark/.zshrc autoload -Uz compinit compinit 用独立环境测试：\nZDOTDIR=\u0026#34;$HOME/zsh-benchmark\u0026#34; zsh -i -c exit 这样不会污染主配置。\n九、记录模板 # zsh 启动时间测试记录 日期： 系统： 终端： zsh 版本： 插件管理器： 原始配置： - 第 1 次： - 第 2 次： - 第 3 次： zprof 重点耗时： - - - 第一次修改： - 修改内容： - 第 1 次： - 第 2 次： - 第 3 次： 第二次修改： - 修改内容： - 第 1 次： - 第 2 次： - 第 3 次： 结论： - 最主要慢点： - 有效优化项： - 无明显收益项： 十、总结 最实用的排查流程就是：\n用 time zsh -i -c exit 测总耗时 用 zprof 找主要慢点 对 zinit 插件、nvm、补全系统逐项做对照实验 保留记录，只接受能证明有效的修改 通常查到最后，问题都集中在版本管理器、补全系统、主题和少数同步加载插件上。\n验证与自查 优化前后各跑 5 次 time zsh -i -c exit，取中位数对比，收益可量化 zprof 输出的前几名与预期慢点（nvm/compinit/主题）一致 延迟加载改动后，zinit times 确认非关键插件在启动后异步加载 功能无损：补全、提示符、插件在交互使用中表现正常 参考 参考文章：Remove NVM to Speed Up Your Zsh zsh/zprof 模块文档 zinit 官方仓库 ","permalink":"https://heihei0299.github.io/posts/ops/zsh%E4%B8%8Ezinit%E5%90%AF%E5%8A%A8%E6%97%B6%E9%97%B4%E6%B5%8B%E8%AF%95%E6%96%B9%E6%B3%95/","summary":"\u003ch1 id=\"zsh-与-zinit-启动时间测试方法\"\u003ezsh 与 zinit 启动时间测试方法\u003c/h1\u003e\n\u003cp\u003e本文适合想定位 zsh 启动慢、或想验证 zinit 延迟加载效果的用户。你会学到：用 \u003ccode\u003etime zsh -i -c exit\u003c/code\u003e 测总耗时、用 \u003ccode\u003ezprof\u003c/code\u003e 找慢点、对 zinit 插件与 nvm/compinit 做对照实验的科学排查流程。\u003c/p\u003e","title":"zsh 与 zinit 启动时间测试方法"},{"content":"QMD 记忆优化系统 本文介绍 QMD（Quantum Memory Database）是什么，以及如何在 OpenClaw 中安装、配置并验证它。QMD 用本地语义搜索代替「把整个记忆文件塞进上下文」的做法，能大幅削减 token、提升响应速度、降低 API 成本。\nQMD 是什么 QMD (Quantum Memory Database) 是 Shopify 联合创始人兼 CEO Tobias Lütke (Tobi) 开发的本地语义搜索引擎。\n核心价值：\n不要把整个文件塞给 AI，而是先用本地搜索找到最相关的片段（通常只有 2-3 句话），再把这些精准内容传给 AI 解决传统记忆系统把整个 MEMORY.md 文件直接塞进上下文导致的\u0026quot;上下文爆炸\u0026quot;问题 实际效果：\n📊 Token 削减：60-97%（平均 95% 以上） ⚡ 响应速度提升：5-50 倍 💰 成本降低：90-99% 🎯 精准度：93%（纯语义搜索仅 59%） 底层技术：\n基于 TypeScript + Bun 开发，使用 node-llama-cpp 运行本地模型 三层混合检索：BM25 全文搜索 + 向量语义搜索 + LLM 重排序 所有模型在本地运行（GGUF 格式），完全离线 核心架构：\n在 OpenClaw 中安装 QMD 前提条件 ⚠️ OpenClaw 版本需要 ≥ 2026.2.2\n检查你的版本：\nopenclaw --version 如果版本低于 2026.2.2，需要先更新到最新版本。\n第一步：安装 QMD 1.1 选择包管理器 推荐使用 Bun（速度更快）：\n# 使用 Bun 安装 bun install -g @tobilu/qmd 或使用 npm：\n# 使用 npm 安装 npm install -g @tobilu/qmd 或直接运行（无需安装）：\nnpx @tobilu/qmd ... # 或 bunx @tobilu/qmd ... 1.2 安装支持扩展的 SQLite QMD 需要支持 vector 扩展的 SQLite。\nmacOS 用户：\nbrew install sqlite 验证安装：\nsqlite3 --version # 应该显示版本号 ≥ 3.40.0 Linux 用户（Ubuntu/Debian）：\nsudo apt update sudo apt install sqlite3 验证安装：\nsqlite3 --version Windows 用户：\n访问 SQLite 官网下载页面：https://www.sqlite.org/download.html 下载 \u0026ldquo;Precompiled Binaries for Windows\u0026rdquo; 中的 sqlite-tools-win-x64-*.zip 解压到任意目录（例如 C:\\sqlite） 将该目录添加到系统 PATH 环境变量： 右键\u0026quot;此电脑\u0026quot; → \u0026ldquo;属性\u0026rdquo; → \u0026ldquo;高级系统设置\u0026rdquo; \u0026ldquo;环境变量\u0026rdquo; → 编辑\u0026quot;Path\u0026quot;变量 添加解压路径（例如 C:\\sqlite） 重启终端，验证安装： sqlite3 --version 1.3 验证 QMD 安装 qmd --version 如果显示版本号，说明安装成功。\n第二步：配置 OpenClaw 使用 QMD 2.1 找到配置文件 根据你使用的版本和操作系统，配置文件位置：\nOpenClaw 用户：\nmacOS/Linux: ~/.openclaw/openclaw.json Windows: C:\\Users\\你的用户名\\.openclaw\\openclaw.json 2.2 修改配置 在配置文件中添加或修改以下内容：\n{ \u0026#34;memory\u0026#34;:{ \u0026#34;backend\u0026#34;:\u0026#34;qmd\u0026#34;, \u0026#34;qmd\u0026#34;:{ \u0026#34;limits\u0026#34;:{ \u0026#34;timeoutMs\u0026#34;:8000 } } } } 配置说明：\nbackend: \u0026quot;qmd\u0026quot; — 切换到 QMD 记忆后端 timeoutMs: 8000 — 设置超时时间为 8 秒（默认 4 秒可能不够） 💡 提示：所有操作系统的配置内容完全相同，只是文件路径不同。\n第三步：重启 OpenClaw 所有操作系统使用相同命令：\n# 重启 OpenClaw Gateway 服务 openclaw gateway restart 重启后的行为：\nOpenClaw 会自动使用 QMD 进行记忆检索 如果 QMD 出现问题，会自动回退到内置的 SQLite 记忆系统 不影响正常使用 验证 QMD 是否正常工作：\n查看 OpenClaw 日志，确认 QMD 后端已启用：\nopenclaw memory status 如果显示以下内容，则表示配对成功：\n🦞 OpenClaw 2026.3.12 (6472949) — I\u0026#39;ve seen your commit messages. We\u0026#39;ll work on that together. Memory Search (main) Provider: qmd (requested: qmd) Model: qmd Sources: memory Indexed: 0/0 files · 0 chunks Dirty: no Store: ~/.openclaw/agents/main/qmd/xdg-cache/qmd/index.sqlite Workspace: ~/.openclaw/workspace By source: memory · 0/0 files · 0 chunks Vector: ready Batch: disabled (failures 0/0) Issues: no memory files found in ~/.openclaw/workspace 实测对比效果 我在启用 QMD 前后做了对比测试，结果让人惊喜。\n场景一：长期会话记忆查询 测试问题：\u0026ldquo;我们三个月前讨论的那个项目，最后用的什么方案？\u0026rdquo;\n对比项 启用前 启用后 改善幅度 上下文大小 8 万+ tokens 削减 95%+ - 响应时间 45 秒（超时失败） 2 秒 快 20+ 倍 API 成本 $2.4 $0.01 降低 200+ 倍 成功率 失败 成功 ✅ 结论：速度快了 20+ 倍，成本降低 200+ 倍，而且不会失败。\n场景二：跨文件知识检索 测试问题：\u0026ldquo;我们之前所有项目用过哪些技术栈？\u0026rdquo;\n对比项 启用前 启用后 改善幅度 上下文大小 15000+ tokens 削减 90%+ - 响应时间 25-30 秒 3 秒 快 10 倍 稳定性 容易触发 rate limit 卡死 从不卡死 ✅ 结论：速度提升 10 倍，再也没卡死过。\n场景三：日常对话 测试问题：\u0026ldquo;帮我写个函数\u0026rdquo;\n对比项 启用前 启用后 改善幅度 上下文大小 5000+ tokens 削减 95%+ - 响应时间 8-10 秒 1 秒 快 8-10 倍 体验 感觉慢 秒级响应 🚀 结论：日常使用体验天差地别。\n技术深度：为什么上下文变小，速度就快那么多？ 大模型的推理时间和输入 token 数量基本成正比关系：\n上下文大小 平均响应时间 成本水平 稳定性 200 tokens 0.5-1 秒 💰 ✅ 2,000 tokens 5-8 秒 💰💰💰 ✅ 10,000 tokens 25-40 秒 💰💰💰💰 ⚠️ 50,000 tokens 1-2 分钟 💰💰💰💰💰💰 ❌ 容易超时 100,000+ tokens 2-5 分钟 💰💰💰💰💰💰💰💰 ❌ 基本失败 我的极端案例：那个 20 万 token 的会话，单次请求成本高达 $6-8，而且基本上都是超时失败，钱白花了。\n启用 QMD 后：无论历史记录有多长，每次只提取最相关的几句话（通常削减 95% 以上）。\n✅ 响应快了 5-50 倍 ✅ 成本降低 90-99% ✅ 精准度反而更高（因为噪音少了） ✅ 再也不会因为上下文太长而卡死或超时\n全面对比：启用 QMD 前后 未启用 QMD 启用 QMD 响应速度 5-120 秒（长会话直接超时） 1-3 秒（快 5-50 倍） Token 削减 完整上下文（5K-200K tokens） 削减 60-97%（平均 95%+） 单次 API 成本 $0.05-8（长会话） 降低 90-99% 精准度 容易被干扰 93% 准确率 稳定性 长会话必卡死 从不卡死 隐私 数据本地 完全本地 成本 持续消耗 API 完全免费 适用场景 必须启用的情况 🔴 会话历史超过 1 万 token（基本上运行一周就会超过） 🔴 经常被慢速响应或卡死困扰（特别是长期会话） 🔴 单次请求成本超过 $1 常见问题 Q1: QMD 和传统记忆系统的区别是什么？ 传统系统：\n把整个 MEMORY.md 文件直接塞进上下文 其中 90% 的内容可能和当前问题毫无关系 上下文越长，请求越慢，成本越高，AI 还容易被无关信息干扰 QMD 系统：\n先用本地搜索找到最相关的片段（通常只有 2-3 句话） 再把这些精准内容传给 AI 大幅减少上下文，提升速度和精准度 Q2: QMD 是否安全？ 完全安全：\n✅ 完全免费 ✅ 完全本地运行 ✅ 数据永远不出你的电脑 ✅ 不消耗任何 API 配额 Q3: QMD 的准确率如何？ 根据实测数据：\n混合搜索精准度：93% 纯语义搜索精准度：59% 混合搜索明显更准确 Q4: 如果 QMD 出现问题怎么办？ OpenClaw 有自动回退机制：\n如果 QMD 出现问题，会自动回退到内置的 SQLite 记忆系统 不影响正常使用 可以查看日志了解具体问题 Q5: QMD 需要联网吗？ 不需要：\n首次安装时会下载模型（需要联网） 之后完全离线运行 所有处理都在本地完成 Q6: QMD 支持哪些文件格式？ 主要支持：\n✅ Markdown 文件（.md） ✅ 文本文件（.txt） 其他格式需要先转换为 Markdown 或文本格式。\nQ7: QMD 的性能如何？ 非常高效：\n12 个文件的索引只需几秒钟 搜索响应通常在 1-3 秒内 完全适合实时交互场景 Q8: 如何优化 QMD 的性能？ 可以调整以下参数：\n{ \u0026#34;memory\u0026#34;:{ \u0026#34;backend\u0026#34;:\u0026#34;qmd\u0026#34;, \u0026#34;qmd\u0026#34;:{ \u0026#34;limits\u0026#34;:{ \u0026#34;timeoutMs\u0026#34;:8000 // 根据需要调整超时时间 } } } } Q9: QMD 占用多少存储空间？ 存储空间取决于：\n索引的文件数量和大小 模型文件（约几百 MB） 通常总占用在 1-2 GB 左右 Q10: 如何卸载 QMD？ 修改配置文件，移除 QMD 配置： { \u0026#34;memory\u0026#34;: { \u0026#34;backend\u0026#34;: \u0026#34;sqlite\u0026#34; // 改回默认的 SQLite 后端 } } 重启 OpenClaw： openclaw gateway restart 卸载 QMD 包（可选）： bun uninstall -g @tobilu/qmd # 或 npm uninstall -g @tobilu/qmd 验证与自查 qmd --version 正常输出版本号 openclaw memory status 输出中 Provider: qmd (requested: qmd)，说明 QMD 后端已启用 工作区目录（~/.openclaw/workspace）有 Markdown/文本文件后，索引文件数不再为 0/0 长会话中发起一次记忆查询，响应应在 1-3 秒内返回 参考 QMD GitHub OpenClaw 官网 SQLite 官网 ","permalink":"https://heihei0299.github.io/posts/ai/qmd-memory-optimization/","summary":"\u003ch1 id=\"qmd-记忆优化系统\"\u003eQMD 记忆优化系统\u003c/h1\u003e\n\u003cp\u003e本文介绍 QMD（Quantum Memory Database）是什么，以及如何在 OpenClaw 中安装、配置并验证它。QMD 用本地语义搜索代替「把整个记忆文件塞进上下文」的做法，能大幅削减 token、提升响应速度、降低 API 成本。\u003c/p\u003e","title":"QMD 记忆优化系统"},{"content":"常见问题 本文汇总 OpenClaw 部署与使用中遇到的高频问题：飞书循环提示配对码、Windows 安装报错 3221225477（node-llama-cpp 编译失败）、安装时 Git SSH 网络错误，均附实测解决方案。\n飞书循环提示配对码 现象：OpenClaw 连接飞书后，飞书循环提示配对码。在终端执行了 openclaw pairing approve feishu ARM8ZC3U 以后，再跟飞书聊天，仍然是以下提示，只不过配对码换了：\nOpenClaw: access not configured. Your Feishu user id: ou_d5bd43b12f7a1a35bd814e403d663bfe Pairing code: ARM8ZC3U Ask the bot owner to approve with: openclaw pairing approve feishu ARM8ZC3U 解决方法：在 openclaw control 网页里左侧找到「频道」，Dm Policy 选择 open，下拉点击 save。\nWindows 安装报错 3221225477（node-llama-cpp 编译失败） 现象：npm install -g openclaw 报错：\nnpm warn cleanup ] npm error code 3221225477 npm error path C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\openclaw\\node_modules\\node-llama-cpp npm error command failed npm error command C:\\Windows\\system32\\cmd.exe /d /s /c node ./dist/cli/cli.js postinstall npm error [node-llama-cpp] A prebuilt binary was not found, falling back to using no GPU npm error A complete log of this run can be found in: C:\\Users\\Administrator\\AppData\\Local\\npm-cache\\_logs\\2026-03-07T05_26_51_040Z-debug-0.log 目前的社区反馈显示，Node.js 24 在 Windows 上运行此类原生模块时极易出现 3221225477 错误。\n方案一：改用 Node.js 22.x LTS 建议卸载当前的 Node.js，安装 Node.js 22.x LTS 版本 重新安装后，清理缓存并重试： npm cache clean --force npm install -g openclaw@latest 方案二：绕过本地编译（如果你只使用云端模型） 如果你不需要在本地运行大模型（比如你只打算用 Claude/GPT 的 API），可以跳过导致崩溃的 postinstall 脚本：\nnpm install -g openclaw@2026.3.2 --ignore-scripts 注意：这会导致 node-llama-cpp 无法正常工作，即你不能在本地运行 GGUF 模型，但 OpenClaw 的其他功能（云端模型、网关等）仍可正常使用。\n安装时 Git SSH 网络错误 现象：npm i -g openclaw 报 git 错误：\nnpm error code 128 npm error An unknown git error occurred npm error command git --no-replace-objects ls-remote ssh://git@github.com/whiskeysockets/libsignal-node.git npm error git@github.com: Permission denied (publickey). npm error fatal: Could not read from remote repository. npm error Please make sure you have the correct access rights npm error and the repository exists. 原因：openclaw 的某个依赖包（libsignal-node）试图通过 SSH 协议从 GitHub 下载代码，但你的电脑没有配置 GitHub 的 SSH Key（公钥）。系统想通过 git@github.com 这个\u0026quot;加密通道\u0026quot;去抓取代码，但 GitHub 不认识你的电脑，所以拒绝了访问。在你的网络环境下，连接 GitHub 的 443 端口（HTTPS/SSH）超时也会触发同样的问题。\n以下是几种有效的解决方法，按推荐程度排序：\n方法一：强制将 Git 的 SSH/Git 协议替换为 HTTPS（最推荐） 很多时候，npm 会尝试用 SSH 协议连接 GitHub，而你可能没有配置 SSH Key 或者 SSH 端口被封。运行以下命令让 Git 始终使用 HTTPS：\ngit config --global url.\u0026#34;https://github.com/\u0026#34;.insteadOf \u0026#34;git://github.com/\u0026#34; git config --global url.\u0026#34;https://github.com/\u0026#34;.insteadOf \u0026#34;ssh://git@github.com/\u0026#34; npm cache clean --force 然后再运行：\nnpm i -g openclaw 方法二：更改系统 DNS 如果 HTTPS 方式仍然超时，可以尝试更换系统 DNS（如 223.5.5.5、119.29.29.29 等国内公共 DNS）后重试。\n方法三：手动安装依赖项 如果 libsignal-node 依然报错，可能是因为它需要编译环境。你可以尝试先安装这个特定的依赖包，有时 npm 会跳过 Git 源码去寻找预编译版本：\nnpm install -g libsignal-node npm install -g openclaw 补充问题：清理 npm 缓存 如果运行方法一后依然报错 \u0026ldquo;git check-ignore\u0026hellip;\u0026rdquo; 或权限问题，请尝试清理一下 npm 缓存后再安装：\nnpm cache clean --force npm i -g openclaw 验证与自查 配对码问题：Dm Policy 设为 open 后，重新给飞书机器人发消息，不再循环提示配对码 3221225477 错误：换用 Node.js 22.x LTS 或 --ignore-scripts 安装后，npm install -g openclaw 不再报错，openclaw --version 正常输出版本号 SSH 网络错误：执行 git config --global url.\u0026quot;https://github.com/\u0026quot;.insteadOf \u0026quot;ssh://git@github.com/\u0026quot; 后，npm i -g openclaw 能正常完成 本地 GGUF 模型（如用了 --ignore-scripts）：确认云端模型与网关功能正常，本地模型不可用属预期 参考 OpenClaw 官方文档 OpenClaw GitHub 仓库 ","permalink":"https://heihei0299.github.io/posts/ai/openclaw-faq/","summary":"\u003ch1 id=\"常见问题\"\u003e常见问题\u003c/h1\u003e\n\u003cp\u003e本文汇总 OpenClaw 部署与使用中遇到的高频问题：飞书循环提示配对码、Windows 安装报错 3221225477（node-llama-cpp 编译失败）、安装时 Git SSH 网络错误，均附实测解决方案。\u003c/p\u003e","title":"常见问题"},{"content":"OpenClaw 浏览器自动化教程 本文教你用 OpenClaw 做浏览器自动化：先理解 CDP 架构，再掌握托管浏览器与 Chrome 扩展中继两种模式的使用方法。\n原文链接：https://www.cnblogs.com/jinjiangongzuoshi/p/19673570 整理时间：2026-03-05 21:27 GMT+8\n概述 浏览器自动化一般主要靠代码框架、零代码工具和 AI 辅助工具实现，常用于功能测试、回归测试和跨浏览器兼容性测试。\n常见的代码框架类工具 Playwright：微软开发，速度快，自动等待机制强，支持 Python、JS、Java、.NET，适合复杂的跨浏览器测试。 Puppeteer：Google 开发，专为 Chrome/Chromium 设计，适合前端开发者。 Selenium：老牌工具，生态成熟，支持几乎所有浏览器和主流编程语言。 在 AI Agent 时代，浏览器自动化已成为必备技能，而上述传统的 Puppeteer/Playwright/Selenium 框架存在以下痛点：\n❌ 需要编写大量代码 ❌ 难以与 AI Agent 集成 ❌ 缺少统一的管理界面 ❌ 远程调试配置复杂 OpenClaw 的出现改变了这一切，它是一个自托管的 AI Agent 网关，内置了强大的浏览器控制能力，可以通过 CDP 协议实现：\n✅ CLI + AI Agent 双模式控制 ✅ 开箱即用的浏览器管理 ✅ 支持远程 CDP 连接 一、OpenClaw 连接 CDP 架构 1. OpenClaw OpenClaw 是一个多通道 AI Agent 网关，提供：\n🦞 独立浏览器配置文件（隔离于个人浏览器） 🔌 Chrome 扩展接力（控制现有 Chrome 标签） 🌐 远程 CDP 支持（连接云端浏览器） 📊 Web 控制面板（可视化管理） 2. CDP（Chrome DevTools Protocol） CDP 是 Chrome 浏览器的调试协议，允许开发者通过编程方式：\n控制页面导航、点击、输入 捕获网络请求和响应 执行 JavaScript 截图和生成 PDF 所有现代浏览器自动化工具（Puppeteer、Playwright）都基于 CDP 构建。\n3. CDP 连接架构 ┌─────────────┐ │ AI Agent │ │ (OpenClaw) │ └──────┬──────┘ │ HTTP API ▼ ┌─────────────────┐ │ Browser Control │ │ Service │ └────────┬────────┘ │ CDP Protocol ▼ ┌─────────────────┐ │ Chromium │ │ (CDP Port) │ └─────────────────┘ 架构分三部分：\n浏览器控制服务：运行在 OpenClaw Gateway 或节点上的 API Chrome MV3 插件：用 chrome.debugger API 附加到标签页，把 CDP 消息传给本地中继 本地中继服务器：在控制服务器和插件之间搭桥，默认端口 127.0.0.1:18792 二、环境准备 如果你是新手，想学习 OpenClaw 完整安装配置方法及如何接入飞书，可以查阅上一篇文章：为什么我拖了一个多月才开始使用OpenClaw？（附接入飞书完整详细教程，新手必看）\n这里主要以浏览器自动化为主，简化一下步骤，主要分两步：\n1. 安装 OpenClaw # 全局安装（需要 Node.js 22+） npm install -g openclaw@latest # 验证安装 openclaw --version # 输出：2026.3.2（或更新版本） 2. 初始化配置 # 交互式配置向导 openclaw onboard --install-daemon # 启动 Gateway（网关服务） openclaw gateway --port 18789 三、快速上手 目前 OpenClaw 提供了两种浏览器模式：\nOpenClaw 自带的托管浏览器 Chrome 扩展中继 说白了，就是：让 AI 直接用我的 Chrome，带着我所有的登录状态，稳定地、像我本人一样去操作网页。\n这类需求其实有一个更底层、更可靠的解决方案：通过 Chrome 的远程调试协议（CDP）直接连接。\n1. 浏览器（OpenClaw 托管） 它会启动一个全新的 Chromium 实例，有自己独立的用户数据目录。但问题很明显：这是一台\u0026quot;全新的电脑\u0026quot;，什么登录状态都没有。你想让 AI 帮你操作后台管理页面？不好意思，先登录。碰到二次验证、短信验证码的？那基本没戏。而且这个浏览器的连接稳定性也一般，时不时会断。\n这种方式，它是怎么工作的呢？\nChrome 内置了一个叫 DevTools Protocol（CDP）的调试接口。平时你按 F12 打开的开发者工具，底层用的就是这个协议。我们要做的，就是让 Chrome 把这个接口通过一个本地端口暴露出来，然后让 OpenClaw 连上去。\n连上之后，OpenClaw 就能像人一样操作浏览器：点击按钮、填写表单、读取页面内容，截图快照全都可以。而且因为走的是底层协议，比扩展中继稳定得多。\n新手视角：\n把它想象成一个独立的、仅供智能体使用的浏览器。 openclaw 配置文件不会触及你的个人浏览器配置文件。 智能体可以在安全的通道中打开标签页、读取页面、点击和输入。 默认的 chrome 配置文件通过扩展中继使用系统默认的 Chromium 浏览器；切换到 openclaw 可使用隔离的托管浏览器。 此浏览器不是你的日常浏览器。它是一个安全、隔离的界面，用于智能体自动化和验证。\n具体使用方法：\n查看浏览器状态（默认是关闭的）\nopenclaw browser status # 或 openclaw browser --browser-profile openclaw status 启动独立浏览器（openclaw 配置文件），完全隔离的浏览器实例，适合自动化任务：\nopenclaw browser start # 或使用独立浏览器 openclaw browser --browser-profile openclaw start 打开指定网页：\nopenclaw browser open https://testfather.cn # 或 openclaw browser --browser-profile openclaw open https://testfather.cn OpenClaw 默认是无头（headless）无界面模式，看不到窗口。\n截图（基于第 3 步打开的网页页面）：\nopenclaw browser screenshot # 或 openclaw browser --browser-profile openclaw snapshot 修改浏览器配置（位于 ~/.openclaw/openclaw.json）：\n{ \u0026#34;browser\u0026#34;: { \u0026#34;enabled\u0026#34;: true, \u0026#34;remoteCdpTimeoutMs\u0026#34;: 1500, \u0026#34;remoteCdpHandshakeTimeoutMs\u0026#34;: 3000, \u0026#34;defaultProfile\u0026#34;: \u0026#34;chrome\u0026#34;, \u0026#34;color\u0026#34;: \u0026#34;#FF4500\u0026#34;, \u0026#34;headless\u0026#34;: false, \u0026#34;noSandbox\u0026#34;: false, \u0026#34;attachOnly\u0026#34;: false, \u0026#34;executablePath\u0026#34;: \u0026#34;/Applications/Brave Browser.app/Contents/MacOS/Brave Browser\u0026#34;, \u0026#34;profiles\u0026#34;: { \u0026#34;openclaw\u0026#34;: { \u0026#34;cdpPort\u0026#34;: 18800, \u0026#34;color\u0026#34;: \u0026#34;#FF4500\u0026#34; }, \u0026#34;work\u0026#34;: { \u0026#34;cdpPort\u0026#34;: 18801, \u0026#34;color\u0026#34;: \u0026#34;#0066CC\u0026#34; }, \u0026#34;remote\u0026#34;: { \u0026#34;cdpUrl\u0026#34;: \u0026#34;http://10.0.0.42:9222\u0026#34;, \u0026#34;color\u0026#34;: \u0026#34;#00AA00\u0026#34; } } } } 注意事项：\n浏览器控制服务绑定到 loopback 上的端口，该端口从 gateway.port 派生（默认：18791，即 gateway + 2）。中继使用下一个端口（18792）。 如果你覆盖了 Gateway 网关端口（gateway.port 或 OPENCLAW_GATEWAY_PORT），派生的浏览器端口会相应调整以保持在同一\u0026quot;系列\u0026quot;中。 未设置时，cdpUrl 默认为中继端口。 remoteCdpTimeoutMs 适用于远程（非 loopback）CDP 可达性检查。 remoteCdpHandshakeTimeoutMs 适用于远程 CDP WebSocket 可达性检查。 attachOnly: true 表示\u0026quot;永不启动本地浏览器；仅在浏览器已运行时附加\u0026quot;。 color + 每个配置文件的 color 为浏览器 UI 着色，以便你能看到哪个配置文件处于活动状态。 默认配置文件是 chrome（扩展中继）。使用 defaultProfile: \u0026ldquo;openclaw\u0026rdquo; 来使用托管浏览器。 自动检测顺序：如果系统默认浏览器是基于 Chromium 的则使用它；否则 Chrome → Brave → Edge → Chromium → Chrome Canary。 本地 openclaw 配置文件会自动分配 cdpPort/cdpUrl — 仅为远程 CDP 设置这些。 更多使用，可查询帮助命令：\nopenclaw browser --help 2. Chrome 扩展中继 OpenClaw Chrome（chrome extension relay）扩展让代理能控制你现有的 Chrome 标签页，通过 Chrome 扩展控制你正在使用的 Chrome 标签，而不是启动一个单独的浏览器配置文件，附加/分离通过一个工具栏按钮完成。\n具体流程：\nGateway 网关在本地运行（同一台机器）或节点主机在浏览器所在机器上运行。 本地中继服务器在 loopback 的 cdpUrl 上监听（默认：http://127.0.0.1:18792）。 你点击标签页上的 OpenClaw Browser Relay 扩展图标来附加（它不会自动附加）。 智能体通过选择正确的配置文件，使用普通的 browser 工具控制该标签页。 如果 Gateway 网关在其他地方运行，请在浏览器所在机器上运行节点主机，以便 Gateway 网关可以代理浏览器操作。\n安装步骤：\n整个操作过程还是非常简单的，总共分两步，第一步安装 OpenClaw 的插件，第二步安装谷歌浏览器的拓展程序。\n方法一：通过 web store 商店安装 扩展已经发布到 Chrome 商店，搜索 \u0026ldquo;openclaw-browser-relay\u0026rdquo; 或直接访问：\nhttps://chromewebstore.google.com/detail/openclaw-browser-relay/nglingapjinhecnfejdcpihlpneeadjp?pli=1\n扩展作为静态文件包含在 OpenClaw 发行版中，没有单独的\u0026quot;构建\u0026quot;步骤。升级 OpenClaw 后：\n重新运行 openclaw browser extension install 刷新文件 在 chrome://extensions 页面点击扩展的\u0026quot;重新加载\u0026quot;按钮 方法二：手工安装 如果无法访问 Chrome Web Store，可以手工安装\n# 安装 Chrome 扩展 openclaw browser extension install # 查看扩展路径 openclaw browser extension path 在 Chrome 中加载扩展，打开 Chrome → 访问 chrome://extensions\n访问 chrome://extensions 启用\u0026quot;开发者模式\u0026quot; 点击\u0026quot;加载未打包的扩展程序\u0026quot; 选择扩展路径（选择上面命令打印的目录） 插件安装好之后，在扩展程序列表中，可以看到名称为：OpenClaw Browser Relay 插件\n访问：http://127.0.0.1:18789/overview，获取网关令牌\n在 OpenClaw Browser Relay 插件配置页面，配置 OpenClaw Token\n保存成功，提示：Relay reachable and authenticated at http://127.0.0.1:18792/，说明已连接到本地的中继服务\n插件安装完成后，需要重启一下 OpenClaw 网关使配置生效：\nopenclaw gateway restart 使用扩展 OpenClaw 附带一个名为 chrome 的内置浏览器配置文件，它指向默认端口上的扩展中继。使用它：\nCLI：openclaw browser --browser-profile chrome tabs 智能体工具：browser 配合 profile=\u0026quot;chrome\u0026quot; 如果你想要不同的名称或不同的中继端口，创建你自己的配置文件：\nopenclaw browser create-profile \\ --name my-chrome \\ --driver extension \\ --cdp-url http://127.0.0.1:18792 \\ --color \u0026#34;#00AA00\u0026#34; 安装完成后，Chrome 工具栏会出现 OpenClaw 图标。\n附加控制：打开你想要操作的网页，点击扩展图标。当图标上显示 \u0026ldquo;ON\u0026rdquo; 时，表示该标签页已受 OpenClaw 控制。 切换标签页：只需在其他标签页点击图标，即可将控制权切换过去。 点击工具栏上的插件图标就能授权控制。图标上会显示状态：ON 是已连接，\u0026hellip; 是正在连接，! 是中继不可达或认证失败（通常是 Gateway 令牌不匹配）。\n固定扩展到工具栏，点击扩展图标将其固定，方便后续操作。 3. 如何使用 把 openclaw 插件固定到工具栏后，比如访问 weibo.com 网站，点击插件，显示红色 \u0026ldquo;ON\u0026rdquo; 字样表示已 attached：\n接下来就可以用 openclaw 操作浏览器了，我还是通过飞书进行交互，比如：\n获取微博热搜：我已经打开并登录了微博，获取今天最新 10 条微博热搜消息 搜索信息：访问 http://127.0.0.1:18789/chat，在聊天窗中，让它帮我搜一下林俊旸的信息 它会自动帮我打开浏览器，访问百度，并且在输入框搜索：\u0026ldquo;林俊旸\u0026rdquo;，最后将信息结果汇总一并返回。\n这种操控浏览器的方式体验下来就是快跟准两个字，但是搜索毕竟是简单的操作。比如来个复杂一点的任务：打开 B 站，搜索 OpenClaw，给第一条视频点赞。当然这些操作，在这篇教程中我就不演示了，感兴趣的话，可自行实践吧~\n验证与自查 openclaw browser status 能显示浏览器状态（关闭/运行中）与当前配置文件 托管浏览器：openclaw browser start 后执行 openclaw browser open \u0026lt;url\u0026gt; 再 openclaw browser screenshot，能拿到页面截图 扩展中继：点击插件图标显示 \u0026ldquo;ON\u0026rdquo;，且提示 Relay reachable and authenticated at http://127.0.0.1:18792/ 让 AI 执行一次完整的「打开网页 → 搜索 → 返回结果」任务，确认端到端可用 四、写在最后 虽然，OpenClaw 能自动化浏览器，简单场景够用了，但说实话，不太好用，插件有时不太稳定，经常断开，还得我手工点重连，但毕竟人无完人，工具也一样，OpenClaw 还在进化中，有点小 Bug 可以理解，所以大家在养虾的过程中遇到点挫折时，多点耐心，折腾 AI 就得心态好点。\n不管是 browser-use 还是 openclaw 的浏览器功能，未来都会有很多应用场景，先熟悉起来。\n🎯 记住：浏览器自动化的核心不是工具，而是理解 CDP 协议和网页结构。掌握 OpenClaw，你就掌握了新一代自动化的钥匙！\n参考 OpenClaw 官方文档: https://docs.openclaw.ai OpenClaw 浏览器插件: https://docs.openclaw.ai/zh-CN/tools/chrome-extension GitHub 仓库: https://github.com/openclaw/openclaw Chrome DevTools Protocol: https://chromedevtools.github.io/devtools-protocol/ 文档整理完成于：2026-03-05 21:27 GMT+8\n","permalink":"https://heihei0299.github.io/posts/ai/openclaw-browser-automation/","summary":"\u003ch1 id=\"openclaw-浏览器自动化教程\"\u003eOpenClaw 浏览器自动化教程\u003c/h1\u003e\n\u003cp\u003e本文教你用 OpenClaw 做浏览器自动化：先理解 CDP 架构，再掌握托管浏览器与 Chrome 扩展中继两种模式的使用方法。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接：https://www.cnblogs.com/jinjiangongzuoshi/p/19673570\n整理时间：2026-03-05 21:27 GMT+8\u003c/p\u003e","title":"OpenClaw 浏览器自动化教程"},{"content":"OpenClaw + 飞书（Feishu）环境搭建指南 一个全面、实用、可直接上手的 Windows 环境下 OpenClaw × 飞书插件安装与调试指南。\n涵盖从安装 OpenClaw、配置本地网关、到接入飞书机器人（含插件安装、国内环境 npm 安装困难的解决方案）、常见问题排查等完整步骤。实际的部署环境为 💻 win11 25H2，非 WSL 方案。\n前提条件 仅需在管理员 PowerShell 窗口执行一次，在管理员权限的 PowerShell 窗口中依次运行：\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass 说明：\n第一条命令允许当前用户运行本地与下载的脚本。 第二条命令在当前 PowerShell 会话中临时跳过脚本执行策略的限制。 安全提示： 这两条命令仅影响当前用户或当前会话，不会修改系统全局策略或其他用户的环境。\n环境说明与约定 本文的所有 openclaw 相关命令与演示均在 PowerShell 7（Pwsh7）下执行。除特别说明外，操作以非管理员权限运行为主。\n关于模型选择：部署时可选择如 Qiwen 或者 智谱 的模型（两者均可快速注册并接入；实测 Qiwen 登录一次即可，智谱需要复制 API Key）；两种方式都实际接入过且成功。\n安装 OpenClaw + 飞书 部署流程 1. 安装 OpenClaw 在 PowerShell 中执行：\nnpm install -g openclaw@latest // 安装，耗时略久，稍等片刻 安装完成后，可按需执行 openclaw --help 查看可用子命令。\n2. 初次启动与向导（onboard） 启动并安装守护进程：\nopenclaw onboard --install-daemon // 启动 向导会让你选择 AI 模型、是否连接即时通讯平台等选项。若只想先跑通本地流程，可以选择跳过连接即时通讯平台，后续再单独安装飞书插件。\n参考教程：保姆级教程（手把手安装 OpenClaw 并接入飞书）提供了较详尽的步骤，适合初学者对照操作。\n3. 网页网关（Web Gateway） 3.1 重置或首次配置建议使用 如果没配置过（或者一开始选择重置 Reset 选项；推荐这种）：\n打开 \u0026quot;C:\\Users\\Venn\\.openclaw\\openclaw.json\u0026quot; 文件，选中 gateway - token 拷贝到网页里面。这样就能够在网页里面直接聊天。右上角也会显示 online 绿色图标。\n当控制台服务启动后，直接浏览器输入 http://127.0.0.1:18789/ 就可以进入这个页面。\n3.2 如果以前配置过，但是没有配置好（解决方案如下） 则可能打开网页失败。\n这里会自动打开浏览器，网址为 http://127.0.0.1:18789/#token=48cc2621xxxxxxxxxxxxx5942aebfe31cbf5\n但是会显示网页显示不对：\n无法访问此网站\n127.0.0.1 拒绝了我们的连接请求。\n配置 Openclaw 网页网关\nopenclaw gateway install // 必须管理员权限，用的 pwsh7 openclaw gateway // 启动 4. 配置飞书（Feishu）后台与权限 飞书后台配置和操作参考 【保姆级教程】手把手教你安装OpenClaw并接入飞书，让AI在聊天软件里帮你干活 的 【五、接入飞书机器人】章节。非常详细。\n常见坑：必须发布两次应用版本。第一次发布后机器人可能没有聊天输入框；完成回调配置并再次发布后，聊天界面才会出现输入框并能正常输入文字。\n飞书开放平台：https://open.feishu.cn（在飞书后台完成应用的创建与事件回调设置）。\n5. 安装飞书插件（extensions / feishu） 执行：\nopenclaw plugins install @m1heng-clawd/feishu // 安装命令 一定会遇到的问题：\n一定会遇到 openclaw 安装飞书插件时一定会遇到的报错 spawn：\n[openclaw] Failed to start CLI: Error: spawn EINVAL\n插件官网 m1heng/clawdbot-feishu，里面自带的一篇比较详细的，也可以对比着参考 OpenClaw 到底怎么装？一篇纯小白也能成功部署的超详细教程，说的比较详细，更适合纯小白扫盲，且由于 AI 迭代太快，但时间内里面有些步骤已经属于过时了，但是仍有一定借鉴意义。\n推荐的实测解决方案（一）：本地先安装，再拷贝到扩展目录（推荐） 在 C:\\Users\\\u0026lt;用户名\u0026gt;\\.openclaw 路径下执行：\nnpm config set registry https://registry.npmmirror.com // 先设置国内镜像 npm cache clean --force npm install @m1heng-clawd/feishu --verbose // 显示下载详细，主要此包安装比较困难 // 确认安装成功 npm list @m1heng-clawd/feishu ls node_modules/@m1heng-clawd/feishu 然后将安装好的包复制到 OpenClaw 的扩展目录：\n// 创建扩展目录并复制文件 C:\\Users\\Venn\\.openclaw\u0026gt; mkdir extensions C:\\Users\\Venn\\.openclaw\u0026gt; mkdir extensions\\feishu C:\\Users\\Venn\\.openclaw\u0026gt; xcopy /E /Y \u0026#34;node_modules\\@m1heng-clawd\\feishu\\*\u0026#34; \u0026#34;extensions\\feishu\\\u0026#34; // 安装依赖 PS C:\\Users\\Venn\\.openclaw\u0026gt; cd extensions\\feishu PS C:\\Users\\Venn\\.openclaw\\extensions\\feishu\u0026gt; npm install --prod // 很重要一步，不换源也很慢或者失败 完成后重启 OpenClaw 服务：\nopenclaw gateway restart 并将 npm registry 还原为官方镜像（可选）：\nnpm config set registry https://registry.npmjs.org/ // 还原官方镜像源 npm config get registry // 验证 替代方案（二）：在扩展目录中直接补装依赖 如果已经通过 openclaw plugins install 安装了插件但缺少依赖，可以直接在扩展目录中补装：\ncd C:\\Users\\\u0026lt;用户名\u0026gt;\\.openclaw\\extensions\\feishu npm install --prod 6. 使用 openclaw config 完成飞书相关配置 执行配置命令：\nopenclaw config 按照提示填入飞书应用的 App ID、App Secret 等信息。配置完成后，OpenClaw 会自动连接飞书机器人。\n7. 控制演示（启动并在飞书中控制电脑，效果演示） 配置完成后，在飞书中找到你的机器人，发送消息即可开始对话。OpenClaw 会响应你的指令，并可以执行各种任务，如文件操作、代码执行等。\n测试步骤：\n在飞书中打开机器人对话 发送测试消息，如\u0026quot;你好\u0026quot; 观察机器人是否正常响应 尝试发送更复杂的指令进行测试 验证与自查 openclaw plugins list 能看到 feishu 插件 浏览器访问 http://127.0.0.1:18789/ 能打开控制台，右上角显示 online 绿色图标 在飞书中给机器人发\u0026quot;你好\u0026quot;，能收到 AI 回复 npm config get registry 确认 registry 已还原为官方源（如按上文操作） 常见问题与排查建议 问题 1：网关无法访问 现象： 浏览器访问 http://127.0.0.1:18789/ 显示\u0026quot;无法访问此网站\u0026quot;\n解决方案：\n以管理员权限重新安装网关：openclaw gateway install 重启网关服务：openclaw gateway restart 检查防火墙是否阻止了端口 18789 问题 2：插件安装失败（spawn EINVAL） 现象： 执行 openclaw plugins install @m1heng-clawd/feishu 时报错\n解决方案：\n使用本文推荐的\u0026quot;本地先安装，再拷贝\u0026quot;方案 确保使用国内镜像源：npm config set registry https://registry.npmmirror.com 在扩展目录中手动执行 npm install --prod 问题 3：飞书机器人没有输入框 现象： 飞书中打开机器人，但看不到聊天输入框\n解决方案：\n确保在飞书后台完成了事件回调配置 在飞书开放平台重新发布应用版本 等待几分钟后刷新飞书客户端 问题 4：机器人不响应消息 现象： 在飞书中发送消息，机器人没有任何反应\n解决方案：\n检查 OpenClaw 服务是否正常运行：openclaw gateway 查看日志文件排查错误 确认飞书后台的权限配置是否完整 验证 openclaw config 中的配置信息是否正确 参考资料 OpenClaw 官方文档 【保姆级教程】手把手教你安装OpenClaw并接入飞书 m1heng/clawdbot-feishu 插件仓库 OpenClaw 到底怎么装？超详细教程 飞书开放平台 系列地址 本文为 OpenClaw 系列教程的一部分，更多相关内容请访问：\n原文地址：https://xmuli.tech/posts/4e21d8f5/ 作者博客：https://xmuli.tech/ 作者： 偕臧 最后更新： 2026-02-24 环境： Windows 11 25H2 + PowerShell 7\n","permalink":"https://heihei0299.github.io/posts/ai/openclaw-feishu-setup/","summary":"\u003ch1 id=\"openclaw--飞书feishu环境搭建指南\"\u003eOpenClaw + 飞书（Feishu）环境搭建指南\u003c/h1\u003e\n\u003cp\u003e一个全面、实用、可直接上手的 Windows 环境下 OpenClaw × 飞书插件安装与调试指南。\u003c/p\u003e\n\u003cp\u003e涵盖从安装 OpenClaw、配置本地网关、到接入飞书机器人（含插件安装、国内环境 npm 安装困难的解决方案）、常见问题排查等完整步骤。实际的部署环境为 💻 \u003ccode\u003ewin11 25H2\u003c/code\u003e，非 WSL 方案。\u003c/p\u003e","title":"OpenClaw + 飞书（Feishu）环境搭建指南"},{"content":"Windows 部署 OpenClaw 本文教你从零在 Windows 上部署 OpenClaw：无需科学上网，本地部署、无需 WSL。涵盖 Node.js / Git / Visual Studio 安装、npm 国内镜像、openclaw 安装与常用命令。\n一、部署环境说明 无需科学上网 本地部署，无需 WSL 二、安装步骤 1. 安装 Node.js、Git 和 Visual Studio （1）下载安装包\nNode.js 下载地址： Node.js — Download Node.js® Git 地址下载 https://git-scm.com/install/windows 安装 git 和 nodejs 安装 Visual Studio：https://visualstudio.microsoft.com/zh-hans/ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser node -v npm -v git -v 2. npm 切换为国内镜像 淘宝源（最新）：这是目前最常用的源。\n设置方法：\nnpm config set registry https://registry.npmmirror.com 检查是否配置成功：\nnpm config get registry 其他国内镜像源\n腾讯云：https://mirrors.cloud.tencent.com/npm/ 中科大：https://mirrors.ustc.edu.cn/npm/ 清华大学：https://mirrors.tuna.tsinghua.edu.cn/npm/ 恢复官方源\n如果需要切回官方源，执行：\nnpm config set registry https://registry.npmjs.org/ 推荐的安装方式：cnpm\n可以安装 cnpm 专门用于国内下载：\nnpm install -g cnpm --registry=https://registry.npmmirror.com # 之后使用 cnpm install 代替 npm install 配置成功检查\n执行以下命令，若输出 https://registry.npmmirror.com/ 则说明配置成功：\nnpm config get registry 3. 安装 OpenClaw npm i -g openclaw 验证安装：\nopenclaw --version 如果显示版本号就是安装成功。\n三、常用命令 openclaw gateway start # 启动网关 openclaw gateway stop # 停止网关 openclaw gateway restart # 重启网关 openclaw channels login # WhatsApp QR 配对登录 openclaw channels add # 添加 Telegram/Discord/Slack 机器人（可加 --token） 四、常见问题 SSH 网络错误：临时绕过 git SSH 问题\ngit config --global url.\u0026#34;https://github.com/\u0026#34;.insteadOf \u0026#34;ssh://git@github.com/\u0026#34; 更多问题参见 OpenClaw 常见问题\n验证与自查 openclaw --version 正常输出版本号 openclaw gateway start 后，浏览器访问 http://127.0.0.1:18789/ 可打开控制台 npm config get registry 输出 https://registry.npmmirror.com/（若已配置国内镜像） 在网关控制台发起一次对话，能收到模型回复即部署成功 参考 OpenClaw 官方文档 Node.js 官网 Git for Windows npmmirror 国内镜像源 ","permalink":"https://heihei0299.github.io/posts/ai/openclaw-windows-deploy/","summary":"\u003ch1 id=\"windows-部署-openclaw\"\u003eWindows 部署 OpenClaw\u003c/h1\u003e\n\u003cp\u003e本文教你从零在 Windows 上部署 OpenClaw：无需科学上网，本地部署、无需 WSL。涵盖 Node.js / Git / Visual Studio 安装、npm 国内镜像、openclaw 安装与常用命令。\u003c/p\u003e","title":"Windows 部署 OpenClaw"},{"content":"Claude Code 部署与配置指南 本文记录在 Linux 环境下安装和配置 Claude Code、CC Switch、VS Code 插件，以及常用 MCP 工具的基本流程。\n一、安装环境 1. 安装 Node.js 可先访问 Node.js 官网 了解安装方式。这里使用 nvm 安装：\n# 下载并安装 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash # 当前终端加载 nvm \\. \u0026#34;$HOME/.nvm/nvm.sh\u0026#34; # 安装 Node.js nvm install 24 # 验证版本 node -v npm -v 安装完成后，可用以下命令查看 node 的位置：\nwhereis node 如果某些图形界面程序无法读取当前用户的 nvm 环境变量，可以将 Node.js 的安装目录追加到系统级 PATH：\nsudo nano /etc/profile 追加类似如下内容：\nexport PATH=\u0026#34;$PATH:/home/xxxx/.nvm/versions/node/v24.12.0/bin\u0026#34; 注意将 /home/xxxx/ 替换为你自己的实际用户名路径。\n2. 全局安装 Claude Code npm install -g @anthropic-ai/claude-code 3. 安装 CC Switch 可访问 CC Switch 发布页 下载对应版本。\n以 Linux .deb 安装包为例：\nwget https://github.com/farion1231/cc-switch/releases/download/v3.9.1/CC-Switch-v3.9.1-Linux.deb sudo apt install ./CC-Switch-*.deb 4. 安装 VS Code 访问 VS Code 官网 下载对应平台版本，并安装插件 Claude Code for VS Code。\n二、配置环境 1. 配置 CC Switch 在 CC Switch 的界面中添加 API Key 和请求地址。\n按下图完成相关设置：\n2. 配置 Claude Code 跳过首次登录界面 在终端中执行以下命令：\nexport ANTHROPIC_AUTH_TOKEN=\u0026#34;xxxx\u0026#34; 设置完成后，可以跳过首次登录页面。\n也可以直接编辑 ~/.claude.json，将 hasCompletedOnboarding 设置为 true。\n新版本 VS Code 插件强制登录的解决方案 创建文件 ~/.claude/config.json：\n{ \u0026#34;primaryApiKey\u0026#34;: \u0026#34;xxxxx\u0026#34; } 三、进阶使用 1. 安装常用 MCP 安装\u0026quot;寸止\u0026quot; 访问 寸止项目地址，下载对应平台的二进制文件，并将程序路径加入系统环境变量。\n例如：\nwget https://github.com/imhuso/cunzhi/releases/download/v0.4.0/cunzhi-cli-v0.4.0-linux-x86_64.tar.gz tar -zxvf cunzhi-cli-v0.4.0-linux-x86_64.tar.gz sudo mv 寸止 /usr/local/bin sudo mv 等一下 /usr/local/bin 在 CC Switch 中配置\u0026quot;寸止\u0026quot; 按下图添加对应配置：\n配置提示词 运行以下命令：\n等一下 在设置界面的\u0026quot;参考提示词\u0026quot;标签页中：\n查看自动生成的提示词。 点击复制按钮。 将提示词追加到 ~/.claude/CLAUDE.md 中。这个文件是全局默认提示词文件。 测试\u0026quot;寸止\u0026quot;是否安装成功 执行：\nclaude mcp list 如果输出中包含类似内容，说明安装成功：\nChecking MCP server health... context7: /home/shial/.nvm/versions/node/v24.12.0/bin/npx -y @upstash/context7-mcp - ✓ Connected fetch: uvx mcp-server-fetch - ✗ Failed to connect 寸止: 寸止 - ✓ Connected 四、常见问题 1. Claude Code 插件无法使用 MCP 服务 在 Linux Mint 下，Claude Code 插件有时无法直接调用 npm 或 npx，通常是因为插件启动时没有正确读取当前用户的环境变量。\n这类情况下，可以确认 node 路径：\nwhereis node 然后将 Node.js 的实际安装目录加入系统级 PATH，例如编辑 /etc/profile：\nexport PATH=\u0026#34;$PATH:/home/shial/.nvm/versions/node/v24.12.0/bin\u0026#34; 修改完成后，重新登录系统或重新启动相关程序，再次测试 MCP 是否可用。\n验证与自查 claude --version 正常输出版本号，claude mcp list 中已配置的 MCP（如\u0026quot;寸止\u0026quot;）显示 ✓ Connected 在终端执行 claude 能正常进入对话，且未再弹出首次登录页面 CC Switch 中切换 Provider 后，claude 使用对应的 API 配置 在 VS Code 插件中发起一次对话，确认 MCP 工具可被调用 参考 Node.js 官网 CC Switch 发布页 VS Code 官网 寸止项目地址 nvm - GitHub ","permalink":"https://heihei0299.github.io/posts/ai/claude-code-deploy/","summary":"\u003ch1 id=\"claude-code-部署与配置指南\"\u003eClaude Code 部署与配置指南\u003c/h1\u003e\n\u003cp\u003e本文记录在 Linux 环境下安装和配置 Claude Code、CC Switch、VS Code 插件，以及常用 MCP 工具的基本流程。\u003c/p\u003e\n\u003ch2 id=\"一安装环境\"\u003e一、安装环境\u003c/h2\u003e\n\u003ch3 id=\"1-安装-nodejs\"\u003e1. 安装 Node.js\u003c/h3\u003e\n\u003cp\u003e可先访问 \u003ca href=\"https://nodejs.org/en/download\"\u003eNode.js 官网\u003c/a\u003e 了解安装方式。这里使用 \u003ccode\u003envm\u003c/code\u003e 安装：\u003c/p\u003e","title":"Claude Code 部署与配置指南"},{"content":"Oracle Cloud Shell AMD/ARM 系统救砖教程 本文适合 Oracle Cloud 实例系统损坏（DD 脚本失败、无法 SSH/VNC）需要救砖的用户。你会学到：通过 CloudShell 串口进入 EFI Shell，配置网络后用 TFTP 下载 netboot.xyz 引导文件并网络安装系统。\n原文链接: https://www.74110.net/tutorial/linux/2025-oracle-netboot/\n概述 本教程介绍如何通过 netboot.xyz 在 Oracle Cloud 实例上进行网络安装系统，解决 DD 脚本失败且无法使用 VNC 访问的情况。\n一、连接 Cloud Shell 访问 Oracle Cloud 控制台（建议使用旧版界面） 找到故障实例并打开控制台连接 启动 Cloud Shell 进行远程访问 二、下载 netboot.xyz 引导文件 2.1 进入 BIOS 强制重启实例，同时按住 Esc 键进入 BIOS 导航到 Boot Manager → EFI Internal Shell 2.2 配置网络 在 EFI Shell 中执行以下命令获取 IP 地址：\nifconfig -s eth0 dhcp 查看 IP 分配情况：\nifconfig -l eth0 2.3 下载引导文件 根据系统架构选择对应的命令：\nAMD 架构：\ntftp 132.226.239.122 amd.efi amd.efi ARM 架构：\ntftp 132.226.239.122 arm.efi arm.efi 三、TFTP 服务器地址列表 主要节点 以下是可用的 TFTP 服务器地址（按地区分布）：\n日本节点：\n132.226.239.122 140.238.43.93 韩国节点：\n132.226.18.134 129.154.196.63 新加坡节点：\n129.159.77.227 150.230.36.227 其他备用节点：\n129.154.196.63 150.230.36.227 自建 TFTP 代理 如果官方服务器不可用，可以使用 Docker 自建 TFTP 代理：\ndocker run -d --name tftp-proxy \\ -p 69:69/udp \\ -e UPSTREAM_SERVER=132.226.239.122 \\ tftp-proxy:latest 四、系统安装 4.1 启动引导文件 重启系统并再次进入 BIOS（按住 Esc） 导航到 Boot Maintenance Manager → Boot from File 选择之前下载的 netboot 镜像文件（amd.efi 或 arm.efi） 4.2 选择安装源 在 netboot.xyz 菜单中选择 Linux Network Installs 选择你想要安装的 Linux 发行版 按照标准安装流程进行操作 五、常见问题 5.1 系统启动卡顿 如果系统在启动过程中卡住，需要修改内核参数：\nconsole=ttyS0,9600 在引导参数中添加此参数可以解决启动卡顿问题。\n5.2 架构识别 重要提示： 在下载引导文件前，务必确认你的实例架构：\nAMD/x86_64 架构： 使用 amd.efi ARM/aarch64 架构： 使用 arm.efi 可以在 Oracle Cloud 控制台的实例详情中查看架构信息。\n5.3 TFTP 下载失败 如果 TFTP 下载失败，可以尝试：\n更换其他地区的 TFTP 服务器 检查网络连接是否正常（ifconfig -l eth0） 使用自建 TFTP 代理服务器 验证与自查 ifconfig -l eth0 能看到已分配的 IP 地址 tftp 下载的 amd.efi/arm.efi 文件大小不为 0（可执行 ls 确认） 从引导文件启动后能进入 netboot.xyz 菜单 网络安装完成后系统能正常登录（SSH 或控制台） 六、注意事项 备份数据： 在进行系统重装前，请确保已备份重要数据 网络连接： 整个过程需要稳定的网络连接 BIOS 操作： 按 Esc 键的时机很重要，需要在重启后立即按住 架构匹配： 确保下载的引导文件与系统架构匹配 服务器可用性： 如果某个 TFTP 服务器不可用，及时切换到备用服务器 七、参考资源 netboot.xyz 官方网站 Oracle Cloud 文档 原文教程 总结 通过 netboot.xyz 进行网络安装是救援 Oracle Cloud 实例的有效方法，特别是在 DD 脚本失败且无法使用 VNC 的情况下。关键步骤包括：\n连接 Cloud Shell 进入 EFI Shell 并配置网络 通过 TFTP 下载对应架构的引导文件 从引导文件启动并进行系统安装 整个过程需要耐心操作，注意架构匹配和网络稳定性。\n","permalink":"https://heihei0299.github.io/posts/ops/oracle-cloud-netboot-recovery/","summary":"\u003ch1 id=\"oracle-cloud-shell-amdarm-系统救砖教程\"\u003eOracle Cloud Shell AMD/ARM 系统救砖教程\u003c/h1\u003e\n\u003cp\u003e本文适合 Oracle Cloud 实例系统损坏（DD 脚本失败、无法 SSH/VNC）需要救砖的用户。你会学到：通过 CloudShell 串口进入 EFI Shell，配置网络后用 TFTP 下载 netboot.xyz 引导文件并网络安装系统。\u003c/p\u003e","title":"Oracle Cloud Shell AMD/ARM 系统救砖教程"},{"content":"Ventoy 启动盘制作 适用场景：在 Linux 系统（尤其是没有图形界面的服务器/命令行环境）上制作可引导 U 盘。你会学到 Ventoy 的 WebUI 使用方法、指定监听 IP 与端口，以及如何安全退出。\nLinux 系统图形化界面 — WebUI 背景介绍 为了方便操作，Ventoy 从 1.0.36 版本开始，在 Linux 系统下提供了基于浏览器的图形化操作界面。界面布局和操作方式与 Windows 下的安装程序保持一致。\nLinux 发行版有很多，桌面环境也多种多样，比如 Gnome2/Gnome3/KDE/XFCE/LXDE\u0026hellip; 等等，没有一个通用的图形界面方案。因此基于浏览器的图形界面，相对来讲是一种比较简单和通用的解决方案。而且本方案同时支持 x86_64/i386/arm64/mips64 系统。\n注意：使用过程中如果出错，你可以切换到命令行模式（使用说明）或者 GTK/QT 模式（使用说明）进行安装或升级\n界面截图 使用说明 使用方法 在安装包解压后的目录下，打开终端执行 sudo bash VentoyWeb.sh 打开浏览器，直接访问 http://127.0.0.1:24680 提示：执行第 1 步后会在终端上打印出对应的 http 地址。很多系统中都可以按下 Ctrl 键，同时鼠标点击链接即可。无需再手动打开浏览器。\nVentoyWeb.sh 默认情况下监听 127.0.0.1 地址的 24680 端口。此时只能通过本机的浏览器进行访问。\n你也可以像这样 sudo bash VentoyWeb.sh -H 192.168.0.100 -P 8080 指定 IP 地址和端口号。此时你可以通过同网络内的另一台电脑上的浏览器来访问这个界面进行操作。这在有些情况下比较方便。\n比如，你有一台机器里面安装了 Linux 的系统，但是并没有安装图形界面，只有命令行操作界面。此时你可以在命令行里面执行上述命令，然后在另外一台有图形环境的电脑上（比如 Windows）通过浏览器访问对应的页面进行操作。只要这两台电脑网络上是联通的即可。\n关闭方法 关闭浏览器窗口 在执行脚本的终端，根据提示按 Ctrl + c 键退出 验证与自查 执行 sudo bash VentoyWeb.sh 后终端打印出 http://127.0.0.1:24680 地址，浏览器可打开 Ventoy 界面 安装完成后 sudo fdisk -l 可看到 U 盘上有 Ventoy 相关分区（vfat 类型） 将 ISO 镜像拷入 U 盘数据分区，重启从 U 盘引导能出现 Ventoy 选择菜单 参考 Ventoy 官方文档 - Linux 命令行模式 Ventoy 官方文档 - Linux GTK/QT 模式 Ventoy 官方网站 ","permalink":"https://heihei0299.github.io/posts/linux/ventoy%E5%90%AF%E5%8A%A8%E7%9B%98%E5%88%B6%E4%BD%9C/","summary":"\u003ch1 id=\"ventoy-启动盘制作\"\u003eVentoy 启动盘制作\u003c/h1\u003e\n\u003cp\u003e适用场景：在 Linux 系统（尤其是没有图形界面的服务器/命令行环境）上制作可引导 U 盘。你会学到 Ventoy 的 WebUI 使用方法、指定监听 IP 与端口，以及如何安全退出。\u003c/p\u003e","title":"Ventoy 启动盘制作"},{"content":"安装 fcitx5 框架 适用场景：Linux Mint（或其他 Ubuntu 系发行版）需要中文拼音输入法。你会学到用 flatpak 安装 fcitx5 框架、通过 im-config 启用、配置环境变量与开机自启，并安装主题和词库。\n安装 fcitx5 sudo flatpak install fcitx5 fcitx5-qt fcitx5-gtk fcitx5-configtool 启用 fcitx5 im-config 在开启的图形化界面中启用 fcitx5，之后从应用中打开 fcitx5，就可以愉快地使用了（需要使用配置添加简体中文的拼音）。\n配置环境变量（通用办法） 参考 fcitx5 官方 Wiki - Setup Fcitx 5，编辑 /etc/profile：\nsudo nano -m /etc/profile 在文件末尾添加：\nexport XMODIFIERS=@im=fcitx export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx 重启 配置环境变量后需要重启系统（或重新登录）使其生效。\n设置开机自启 配置 fcitx5 安装自定义主题 Adwaita-dark：\ngit clone https://github.com/escape0707/fcitx5-adwaita-dark.git ~/.local/share/fcitx5/themes/adwaita-dark 之后在 fcitx5 设置界面中切换。\n安装词库 萌娘百科词库 维基百科词库 下载 *.dict 文件，放置到 ~/.local/share/fcitx5/pinyin/dictionaries。\n验证与自查 重启后 fcitx5 托盘图标出现，按 Ctrl+Space 可切换中英文 im-config 中首选输入法为 fcitx5 fcitx5-configtool 的输入法列表中存在「拼音」，且「键盘 - 英语」在首位 在任意输入框输入拼音能出候选词 参考 fcitx5 官方 Wiki fcitx5-adwaita-dark 主题 萌娘百科词库 维基百科中文拼音词库 ","permalink":"https://heihei0299.github.io/posts/linux/%E5%AE%89%E8%A3%85fctix5%E6%A1%86%E6%9E%B6/","summary":"\u003ch1 id=\"安装-fcitx5-框架\"\u003e安装 fcitx5 框架\u003c/h1\u003e\n\u003cp\u003e适用场景：Linux Mint（或其他 Ubuntu 系发行版）需要中文拼音输入法。你会学到用 flatpak 安装 fcitx5 框架、通过 im-config 启用、配置环境变量与开机自启，并安装主题和词库。\u003c/p\u003e","title":"安装 fcitx5 框架"},{"content":"Linux Mint 安装中文输入法 适用场景：Linux Mint（或其他 Ubuntu 系发行版）需要中文拼音输入法。你会学到 Fcitx 5 的最小安装（主程序 + 引擎 + 图形界面）、维基百科词库安装、im-config 与环境变量配置、开机自启设置，以及 JetBrains IDE 输入框错位、iBus 冲突两个已知问题的处理。\n安装 fcitx5 检查系统中文环境 在 Ubuntu 设置中打开「区域与语言」—— 「管理已安装的语言」，然后会自动检查已安装语言是否完整。若不完整，根据提示安装即可。\n检查可用的语言支持。\n最小安装 为使用 Fcitx 5，需要安装三部分基本内容：\nFcitx 5 主程序 中文输入法引擎 图形界面相关 按照这个思路，可以直接使用 apt 进行安装：\nsudo apt install fcitx5 \\ fcitx5-chinese-addons \\ fcitx5-frontend-gtk4 fcitx5-frontend-gtk3 fcitx5-frontend-gtk2 \\ fcitx5-frontend-qt5 安装中文词库 在 GitHub 打开维基百科中文拼音词库的 Releases 界面，下载最新版的 .dict 文件。按照 README 的指导，将其复制到 ~/.local/share/fcitx5/pinyin/dictionaries/ 文件夹下即可。\n# 下载词库文件 wget https://github.com/felixonmars/fcitx5-pinyin-zhwiki/releases/download/0.2.4/zhwiki-20220416.dict # 创建存储目录 mkdir -p ~/.local/share/fcitx5/pinyin/dictionaries/ # 移动词库文件至该目录 mv zhwiki-20220416.dict ~/.local/share/fcitx5/pinyin/dictionaries/ 配置 设置为默认输入法 使用 im-config 工具可以配置首选输入法，在任意命令行输入：\nim-config 根据弹出窗口的提示，将首选输入法设置为 Fcitx 5 即可。\n环境变量 需要为桌面会话设置环境变量，即将以下配置项写入某一配置文件中：\nexport XMODIFIERS=@im=fcitx export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx 如果使用 Bash 作为 shell，则建议写入至 ~/.bash_profile，这样只对当前用户生效，而不影响其他用户。\n另一个可以写入此配置的文件为系统级的 /etc/profile。\n将配置写入到 /etc/profile 文件末尾。\n开机自启动 安装 Fcitx 5 后并没有自动添加到开机自启动中，每次开机后需要手动在应用程序中找到并启动，非常繁琐。\n解决方案非常简单，在 Tweaks（sudo apt install gnome-tweaks）中将 Fcitx 5 添加到「开机启动程序」列表中即可。\n将 Fcitx5 添加到开机启动程序列表中。\nFcitx 配置 Fcitx 5 提供了一个基于 Qt 的强大易用的 GUI 配置工具，可以对输入法功能进行配置。有多种启动该配置工具的方法：\n在应用程序列表中打开「Fcitx 配置」 在 Fcitx 托盘上右键打开「设置」 命令行命令 fcitx5-configtool 根据个人偏好进行设置即可。需要注意的是「输入法」标签页下，应将「键盘 - 英语」放在首位，拼音（或其他中文输入法）放在后面的位置。\nFcitx5 configtool。\n自定义主题 Fcitx 5 默认的外观比较朴素，用户可以根据喜好使用自定义主题。\n第一种方式为使用经典用户界面，可以在 GitHub 搜索主题，然后在 Fcitx5 configtool —— 「附加组件」 —— 「经典用户界面」中设置即可。\n第二种方式为使用 Kim 面板，一种基于 DBus 接口的用户界面。此处安装了 Input Method Panel 这个 GNOME 扩展，黑色的风格与正在使用的 GNOME 主题 Orchis-dark 非常搭配。\nInput Method Panel 效果。\n已知问题 修复 JetBrains 系 IDE 显示问题 在 JetBrains 系 IDE（如 PyCharm）中，输入法选择框的位置始终固定于屏幕左下角，而非随输入光标移动，在中文输入很不方便。该问题为 IDE 的 JetBrainsRuntime 缺陷所致。可尝试使用 RikudouPatrickstar/JetBrainsRuntime-for-Linux-x64 这个仓库发布的 JBR 文件解决。\niBus 与 Fcitx 5 冲突：卸载后输入法失效 出于精简空间和减少冲突干扰之考虑，使用\nsudo apt remove ibus 卸载了 iBus，但重启（使生效）之后发现 Fcitx 5 受到了影响。具体表现为：除在终端中之外，其他输入场景无法切换至中文输入。使用 apt 装回 iBus，再次重启即又恢复正常。\n检查包依赖关系，卸载 ibus 包后会自动移除 ibus-data、ibus-gtk4、python3-ibus-1.0 三个包，似乎都只是与 iBus 紧密联系的。暂为不解之谜。\n验证与自查 fcitx5-configtool 中「键盘 - 英语」在首位、拼音在后 任意输入框按 Ctrl+Space 可切换中英文，候选词正常 重启后 fcitx5 托盘图标自动出现（开机自启生效） pgrep -a fcitx5 有进程输出 参考 官方 Wiki —— 安装 Fcitx5 官方 Wiki —— Setup Fcitx 5 官方 Wiki —— 定制主题 维基百科中文拼音词库 ","permalink":"https://heihei0299.github.io/posts/linux/mint%E5%AE%89%E8%A3%85%E4%B8%AD%E6%96%87%E8%BE%93%E5%85%A5%E6%B3%95/","summary":"\u003ch1 id=\"linux-mint-安装中文输入法\"\u003eLinux Mint 安装中文输入法\u003c/h1\u003e\n\u003cp\u003e适用场景：Linux Mint（或其他 Ubuntu 系发行版）需要中文拼音输入法。你会学到 Fcitx 5 的最小安装（主程序 + 引擎 + 图形界面）、维基百科词库安装、im-config 与环境变量配置、开机自启设置，以及 JetBrains IDE 输入框错位、iBus 冲突两个已知问题的处理。\u003c/p\u003e","title":"Linux Mint 安装中文输入法"},{"content":"双系统引导教程 适用场景：Windows 11 + Linux Mint 双系统，安装时引导器被装进错误的 EFI 分区，或 Windows 更新后 Mint 的 EFI 引导文件丢失。你会学到在 Mint 系统内重建引导（grub-install + 修 fstab）和在 LiveOS 下用 Boot-Repair 修复两种方案。\n问题背景 ubuntu 安装的时候有一个祖传 bug，安装的时候即使指定 efi 分区，ubuntu 也会把 efi 文件安装到硬盘的第一个 efi 分区。同样的 mint\u0026quot;完美\u0026quot;地继承了这个 bug。这样会导致一个问题：win11 更新系统后，可能会发生 mint 的 efi 引导文件丢失的事情。因此安装 mint 系统后，我们需要修复这个错误。我们可以手动指定 mint 系统的 efi 分区。\n进入 Mint 系统补救 进入 mint 系统 umount 现有的 /boot/efi 并重新挂载上正确的分区（应该是空的） 使用 grub-install，重建缺失的 EFI 文件 修改 /etc/fstab 更正挂载到挂载选项，否则即使会在正常启动操作系统后挂载上错误的 /boot/efi 目录 恢复第一个操作系统的 EFI 分区（因为默认的安装策略修改了它） 如果第一个系统是同版本 mint，那么需要修正旧的 EFI 分区下的 \\EFI\\UBUNTU\\grub.cfg 文件中的 uuid 即可，把它改回去指向第一个 efi 的根目录 如果是其他的 Linux 系统，可以重启回第一个操作系统后使用 grub-install 重构它的 EFI 分区 如果第一个系统是 Windows，那么应该删除 EFI 分区下的整 ubuntu 目录和 BOOT 目录下的所有子文件，并把 \\EFI\\Microsoft\\Boot\\bootmgfw.efi 复制到 \\EFI\\BOOT\\Bootx64.efi（EFI 文件系统不分文件名大小写），也可以使用 dism++ 修复引导 使用 efibootmgr 调整启动项目（就是修改 NVRAM 配置） 在 LiveOS 环境下补救（使用 Boot-Repair） 从 mint 官网下载的 mint 系统 iso 镜像自带 boot-repair，因此我们直接使用 mint 官方的 iso 镜像进入 liveos。\n打开 Boot-Repair 点击左下角 mint 的标志，在搜索框搜索 bootrepair 然后打开。\n选择高级选项 一定要选择高级选项不要选择推荐修复。\n指定系统所在的分区（硬盘）和单独的 efi 分区 单独的efi分区需要提前创建，或者在安装系统的时候创建。\n最后点击 apply。\n使用 efibootmgr 调整启动项目 调整启动项目就是修改 NVRAM 配置。先查看当前启动项：\nsudo efibootmgr -v 如果 Linux 的引导项缺失，可以参考使用 efibootmgr 管理 UEFI 启动项一文，用 -c -w -L 参数重建；如果只是顺序不对，用 -o 参数调整 BootOrder 即可。\n验证与自查 重启后出现 GRUB 菜单，包含 Linux Mint 与 Windows 两个引导项 sudo efibootmgr 输出中 Linux 引导项存在且位于 BootOrder 前列 cat /etc/fstab | grep efi 确认 /boot/efi 指向正确的 EFI 分区（UUID 与 lsblk -f 一致） 进入系统后运行 sudo update-grub 能扫描到 Windows Boot Manager 参考 Ubuntu Community - Boot-Repair ArchWiki - GRUB efibootmgr man page ","permalink":"https://heihei0299.github.io/posts/linux/%E5%8F%8C%E7%B3%BB%E7%BB%9F%E5%BC%95%E5%AF%BC%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"双系统引导教程\"\u003e双系统引导教程\u003c/h1\u003e\n\u003cp\u003e适用场景：Windows 11 + Linux Mint 双系统，安装时引导器被装进错误的 EFI 分区，或 Windows 更新后 Mint 的 EFI 引导文件丢失。你会学到在 Mint 系统内重建引导（grub-install + 修 fstab）和在 LiveOS 下用 Boot-Repair 修复两种方案。\u003c/p\u003e","title":"双系统引导教程"},{"content":"无 U 盘安装 Linux Mint 适用场景：手边没有 U 盘（或不想做启动盘），需要在已有 Windows 的电脑上安装 Linux Mint 双系统。你会学到用 DiskGenius 划分 FAT32 引导分区、把 ISO 解压进去、用 EasyUEFI 添加引导项，然后从本地硬盘直接进入 LiveOS 安装的完整流程。\n此教程仅适合 UEFI 引导的系统\n关闭安全启动（Secure Boot）和 BitLocker 关闭安全启动（Secure Boot） 要禁用 secure boot，首先需要关机再重新开机，在系统启动之前进入 bios，对于联想笔记本来说就是按 F2 键。不同厂商的 bios 外观不同，因此无法统一说明。但是只要找到 secure boot 相关的选项，并禁用该选项即可。应用修改后重新尝试进入 linux 系统。\n关闭 BitLocker 通过控制面板 打开控制面板：在 Windows 搜索栏输入\u0026quot;控制面板\u0026quot;，然后点击打开。 找到 BitLocker 驱动器加密：在控制面板中，将查看方式设置为\u0026quot;小图标\u0026quot;，然后找到\u0026quot;BitLocker 驱动器加密\u0026quot;。 关闭 BitLocker：选择要关闭的磁盘，点击\u0026quot;关闭 BitLocker\u0026quot;。 确认解密：系统会提示你确认是否要解密磁盘，点击\u0026quot;关闭 BitLocker\u0026quot;开始解密。 等待解密完成：解密过程可能需要一些时间，请耐心等待。 通过设置（Windows 10/11） 打开\u0026quot;设置\u0026quot;：点击\u0026quot;开始\u0026quot;按钮，然后点击\u0026quot;设置\u0026quot;图标(齿轮形状)。 进入\u0026quot;更新和安全\u0026quot; (Windows 10) 或\u0026quot;隐私和安全性\u0026quot; (Windows 11)：在\u0026quot;设置\u0026quot;窗口中，点击\u0026quot;更新和安全\u0026quot; (Windows 10) 或者\u0026quot;隐私和安全性\u0026quot; (Windows 11)。 选择\u0026quot;设备加密\u0026quot;或\u0026quot;BitLocker 驱动器加密\u0026quot;：在 Windows 10 中，点击\u0026quot;设备加密\u0026quot;；在 Windows 11 中，点击\u0026quot;BitLocker 驱动器加密\u0026quot;。 关闭 BitLocker：点击\u0026quot;关闭\u0026quot;按钮来关闭设备加密或 BitLocker。 确认解密：系统会提示你确认是否要解密，点击确认即可。 等待解密完成：解密过程可能需要一些时间，请耐心等待。 准备镜像 下载镜像 访问 linuxmint 镜像下载界面，选择合适的链接下载镜像。\n解压镜像 将下载好的 ISO 镜像解压，后续会复制到 FAT32 引导分区中。\n制作启动分区 下载分区精灵 在驱动精灵下载页面下载 DiskGenius（分区精灵）。\n创建 FAT32 分区 创建一个 7G 的 FAT32 分区用作引导盘。\n将镜像解压到 FAT32 分区 添加引导项 下载 EasyUEFI 企业版 下载地址：\nhttps://pan.xunlei.com/s/VOPdYMjUTn_rVktEMu6GBio5A1?pwd=rnr8# https://pan.quark.cn/s/e88d03ced8d0 已高速(如有密码：3519)http://ct.ghpym.com/d/7369060-42830434-e4acb0 修改类型和描述 类型改为 Linux 或者其他系统，描述改为 linuxmint。\n选择存放 ISO 镜像的 FAT32 分区 选择 /EFI/boot/grubx64.efi 文件 调整引导项顺序至第二位 安装系统 进入 LiveOS 系统 开机，并进入 boot menu。进入的方法可能因为笔记本的型号不同而有所差别，但对于联想笔记本来说，方法是在开机但还未启动的时候按 F12 键（为了避免错过可以不断按 F12 直到成功进入）。在 boot menu 中可以选择启动方式。这里需要选择带有 linuxmint 的启动选项（上下方向键选择、回车键确认）。之后就进入了一个 linux mint 的桌面环境。\n但是目前的这个 linux mint 依旧在 FAT32 分区上，需要双击桌面上的 Install Linux Mint 将系统安装到磁盘上。\n在安装界面，首先选择语言、键盘布局等，这些当然都选择 Chinese 即可。之后也需要安装多媒体编码译码器。\n随后选择安装类型。因为要安装双系统，所以不能选\u0026quot;清除整个磁盘并安装 Linux Mint\u0026quot;选项，而要选择\u0026quot;其它选项\u0026quot;。\n硬盘分区 安装程序会为您提供两个分区硬盘驱动器的选项：\nErase disk and install Linux Mint (擦除磁盘并安装) Something else (其它) 第一个选项擦除整个磁盘，因此所有的文件和程序都被删除。此外，该选项自动和智能分区您的硬盘驱动器。\n它还提供了高级功能，如使用 LUKS 加密对硬盘进行加密。这个选项主要推荐给在手动分区硬盘方面没有太多经验的初学者和新手。\n第二个选项 Something else 提供了对如何分区硬盘的更多控制。它允许您手动配置磁盘分区，因此提供了更大的自主权和灵活性。该选项主要由擅长手动分区的中级和高级 Linux 用户使用。\n当您想要在硬盘驱动器上安装另一个操作系统时，就会使用它，而这正是我们想要的，因为我们想要双引导设置。\n因此，点击 Something else。这将显示硬盘驱动器上的所有磁盘分区，包括 Windows NTFS 分区。\n继续进行磁盘分区，请找到在步骤 1 中创建的空闲空间。\n我们将在我们的硬盘驱动器上创建以下磁盘分区：\n/boot : 1GB /home : 100GB / : 100GB Swap : 2GB（可选，如果内存较大可以不设置） EFI : 500MB 创建 boot 分区 要开始创建分区，请单击 plus 选项卡 [+]。\n创建 /boot 分区，1GB，Ext4 文件格式。\n完成之后，单击 OK。\n这将带您回到分区列表，这一次，您可以看到已经创建了 /boot 分区。\n创建 home 分区 创建 /home 分区，100GB，Ext4 文件格式。\n创建 EFI 分区 如所示创建 EFI 分区。\n创建 swap 分区（可选） 最后，创建 /swap 分区，与内存一样大，在 Use as 标签中指定交换区域。\n您的分区表应该与我们所的类似。接下来，单击 Install Now。\n在弹出的窗口中，单击 Continue 将分区更改写入硬盘驱动器。\n配置地理位置 安装程序将自动检测您的地理位置，如果您连接到互联网。或者，您可以单击世界上的任何地方来设置首选位置。\n单击 Continue 继续。\n创建普通登录用户 下一步是创建一个登录用户，因此继续并选择您的首选名称、计算机名、用户名和用户密码。然后单击 Continue。\n继续安装 安装程序将从安装媒体复制所有文件到硬盘驱动器的 Linux 分区。这个过程大约需要 30 分钟左右。\n安装完成后，单击 Restart Now 重启系统。\n现在删除安装介质，并按 ENTER 重新启动系统。\n系统重新启动后，将显示 GRUB 菜单，如图所示。第一个选项允许您引导到新安装的 Linux Mint 21 操作系统，而最后一个选项将允许您引导到 Windows 11。\n验证与自查 重启后 GRUB 菜单出现 Linux Mint 与 Windows 两个引导项，两个系统都能进入 进入 Mint 后 lsblk 确认 /、/home、/boot、EFI 分区挂载正确 sudo efibootmgr 能看到 Linux Mint 引导项 安装完成后可删除 FAT32 引导分区并回收空间（确认引导正常后） 参考 Linux Mint 官方下载页 Linux Mint 安装指南（官方文档） DiskGenius 官网 EasyUEFI 官网 ","permalink":"https://heihei0299.github.io/posts/linux/%E6%97%A0u%E7%9B%98%E5%AE%89%E8%A3%85linuxmint/","summary":"\u003ch1 id=\"无-u-盘安装-linux-mint\"\u003e无 U 盘安装 Linux Mint\u003c/h1\u003e\n\u003cp\u003e适用场景：手边没有 U 盘（或不想做启动盘），需要在已有 Windows 的电脑上安装 Linux Mint 双系统。你会学到用 DiskGenius 划分 FAT32 引导分区、把 ISO 解压进去、用 EasyUEFI 添加引导项，然后从本地硬盘直接进入 LiveOS 安装的完整流程。\u003c/p\u003e","title":"无 U 盘安装 Linux Mint"},{"content":"r2图床防止被刷 最近刚白嫖了cf的r2存储桶作为图床，但是r2存储计费无上限，也就是说存在被刷爆的风险。所以采取以下措施，防止被刷爆。\n缓存 利用Cloudflare的页面规则和Cache Rules 功能，为R2域名添加缓存。\n接入worker 统计整个网站 1 秒内的总请求数（全部 IP 所有路径）\n如果总请求数超过 10，则返回错误页面或自定义响应\njs代码 本脚本实现了两个核心功能：\n请求限流：对所有进入的请求进行秒级限流，限制每秒最大请求数，防止过载。 静态资源读取及缓存：仅允许 GET 请求访问，优先从边缘缓存读取资源，缓存未命中时从 Cloudflare R2 存储桶获取资源，并写入边缘缓存，提升访问性能。 主要流程 请求限流 以当前时间秒级时间戳作为限流键（count:\u0026lt;timestamp\u0026gt;）存储在 KV 命名空间 GLOBAL_REQUEST_COUNT 中。 获取当前秒已处理请求数，超过设定的最大阈值（默认 50）时，返回 HTTP 429 限流响应。 否则，异步将当前秒请求计数加一，并设置 5 秒过期，避免 KV 积压。 请求方法校验 仅允许 GET 请求访问资源，其他请求返回 HTTP 405（Method Not Allowed）。 缓存与资源读取 构造忽略查询参数的缓存键 cacheKey，尝试从边缘缓存 caches.default 读取资源。 若缓存命中，则异步刷新缓存 TTL，直接返回缓存内容。 缓存未命中时，从 R2 存储桶 MY_BUCKET 读取对应路径资源。 如果资源不存在，返回 404。 若资源存在，构建响应，附带 ETag 和 Cache-Control 头，实现浏览器端长时间缓存（1 年，immutable）。 异步将响应写入边缘缓存，供后续请求使用。 环境变量与绑定 GLOBAL_REQUEST_COUNT KV 命名空间，用于存储秒级请求计数。请在 Worker 绑定此 KV。 MY_BUCKET Cloudflare R2 存储桶绑定，用于读取静态资源。 MAX_REQUESTS_PER_SECOND（可选） 限流阈值，默认 50。如果需要更改每秒最大请求数，可以在 Worker 环境变量中设置该值。 export default { async fetch(request, env, ctx) { // 1. 限流逻辑（秒级请求数） const now = Math.floor(Date.now() / 1000); const limitKey = `count:${now}`; let count = await env.GLOBAL_REQUEST_COUNT.get(limitKey); count = parseInt(count || \u0026#34;0\u0026#34;, 10); const MAX = parseInt(env.MAX_REQUESTS_PER_SECOND || \u0026#34;50\u0026#34;, 10); if (count \u0026gt;= MAX) { return new Response(\u0026#34;Site is temporarily rate limited. Please try again later.\u0026#34;, { status: 429, headers: { \u0026#34;Content-Type\u0026#34;: \u0026#34;text/plain\u0026#34; }, }); } // 异步递增计数，设置 5 秒过期避免积压 ctx.waitUntil( env.GLOBAL_REQUEST_COUNT.put(limitKey, (count + 1).toString(), { expirationTtl: 5, }) ); // 2. 只允许 GET 请求 if (request.method !== \u0026#34;GET\u0026#34;) { return new Response(\u0026#34;Method Not Allowed\u0026#34;, { status: 405, headers: { Allow: \u0026#34;GET\u0026#34; }, }); } // 3. 处理缓存和 R2 读取 const url = new URL(request.url); const key = url.pathname.slice(1); // 去除开头 \u0026#34;/\u0026#34; // 缓存键，忽略 query 参数 const cacheKey = new Request(`https://${url.hostname}${url.pathname}`, request); // 尝试边缘缓存命中 let response = await caches.default.match(cacheKey); if (response) { // 异步续命缓存 ctx.waitUntil(caches.default.put(cacheKey, response.clone())); return response; } // 缓存未命中，从 R2 读取 const object = await env.MY_BUCKET.get(key); if (object === null) { return new Response(\u0026#34;404 Not Found\u0026#34;, { status: 404 }); } // 设置响应头，包含 etag 和缓存控制 const headers = new Headers(); object.writeHttpMetadata(headers); headers.set(\u0026#34;etag\u0026#34;, object.httpEtag); headers.set(\u0026#34;Cache-Control\u0026#34;, \u0026#34;public, max-age=31536000, immutable\u0026#34;); response = new Response(object.body, { headers }); // 写入边缘缓存 ctx.waitUntil(caches.default.put(cacheKey, response.clone())); return response; } }; 添加环境变量 控制台设置方法（如果不用 wrangler)\n打开 Cloudflare → Workers → 你的脚本 选择“Settings” → “Environment Variables” 添加一个变量： Name: MAX_REQUESTS_PER_SECOND Value: 50 连接R2存储桶和KV空间 配置页面规则 因为我的资源都放在r2.****.***下，所以我配置的域名是r2.****.***/*，缓存基本都拉到最大值，经过测试开启后确实也是从本地读取资源了。\n配置Cache Rules 防盗链 配置防盗链，防止其他网站盗取博客图片。\n当不包含博客链接引用方去访问我的 https://imgurl.780789.xyz/ 时，会进行阻止。\n开启速率限制 针对单ip限速，每个ip限速到50次/10秒，如果超过这个频率就会被阻止10秒。\n​\nwaf规则配置 白名单 可以放行常用的代理ip，方便调试。\n(ip.src eq x.x.x.x) or (ip.src eq x.x.x.x) or (ip.src eq x.x.x.x) or (ip.src eq x.x.x.x) or (ip.src in $vps_ip) 黑名单 防盗链（限制指定域名请求必须来自特定 Referer） 屏蔽已知爬虫和漏洞扫描工具（通过 User-Agent、请求头特征识别） 拦截异常请求（无User-Agent、无Accept-Encoding、无Accept-Language、空Referer） 黑名单IP和ASN拦截 屏蔽恶意或异常的请求特征 (http.request.full_uri contains \u0026#34;https:/图床域名.xyz\u0026#34; and not http.referer contains \u0026#34;博客域名l\u0026#34;) or (http.host wildcard \u0026#34;图床域名.\u0026#34;) or (cf.client.bot) or (http.request.full_uri wildcard r\u0026#34;图床域名.\u0026#34; and not http.referer contains \u0026#34;博客域名l\u0026#34;) or (http.user_agent eq \u0026#34;\u0026#34;) or (http.user_agent contains \u0026#34;Acunetix\u0026#34;) or (not len(http.request.headers[\u0026#34;accept-encoding\u0026#34;]) \u0026gt; 0) or (not len(http.request.headers[\u0026#34;accept-language\u0026#34;]) \u0026gt; 0) or (http.user_agent contains \u0026#34;Acunetix\u0026#34;) or (http.user_agent contains \u0026#34;apache\u0026#34;) or (http.user_agent contains \u0026#34;attachment\u0026#34;) or (http.user_agent contains \u0026#34;BackDoorBot\u0026#34;) or (http.user_agent contains \u0026#34;cobion\u0026#34;) or (http.user_agent contains \u0026#34;fhscan\u0026#34;) or (http.user_agent contains \u0026#34;fimap\u0026#34;) or (http.user_agent contains \u0026#34;Gscan\u0026#34;) or (http.user_agent contains \u0026#34;hey/\u0026#34;) or (http.user_agent contains \u0026#34;Joomla\u0026#34;) or (http.user_agent contains \u0026#34;libweb\u0026#34;) or (http.user_agent contains \u0026#34;libwww\u0026#34;) or (http.user_agent contains \u0026#34;masscan\u0026#34;) or (http.user_agent contains \u0026#34;nmap\u0026#34;) or (http.user_agent contains \u0026#34;Page\u0026#34; and http.user_agent contains \u0026#34;Analyzer\u0026#34;) or (http.user_agent contains \u0026#34;PHPCrawl\u0026#34;) or (http.user_agent contains \u0026#34;PyCurl\u0026#34;) or (http.user_agent contains \u0026#34;python\u0026#34;) or (http.user_agent contains \u0026#34;RankActive\u0026#34;) or (http.user_agent contains \u0026#34;RankingBot\u0026#34;) or (http.user_agent contains \u0026#34;RankurBot\u0026#34;) or (http.user_agent contains \u0026#34;researchscan\u0026#34;) or (http.user_agent contains \u0026#34;ScanAlert\u0026#34;) or (http.user_agent contains \u0026#34;scanbot\u0026#34;) or (http.user_agent contains \u0026#34;virusdie\u0026#34;) or (http.user_agent contains \u0026#34;VoidEYE\u0026#34;) or (http.user_agent contains \u0026#34;WebShag\u0026#34;) or (http.user_agent contains \u0026#34;Wprecon\u0026#34;) or (http.user_agent contains \u0026#34;WPScan\u0026#34;) or (http.user_agent contains \u0026#34;wrk\u0026#34;) or (http.user_agent contains \u0026#34;ZmEu\u0026#34;) or (http.user_agent contains \u0026#34;ZumBot\u0026#34;) or (http.user_agent contains \u0026#34;Zyborg\u0026#34;) or (http.user_agent contains \u0026#34;Zeus\u0026#34;) or (http.user_agent contains \u0026#34;zgrab\u0026#34;) or (http.user_agent contains \u0026#34;zmap\u0026#34;) or (http.user_agent contains \u0026#34;lient\u0026#34; and http.user_agent contains \u0026#34;ttp\u0026#34;) or (http.user_agent contains \u0026#34;fuck\u0026#34;) or (http.user_agent eq \u0026#34;undefined\u0026#34;) or (http.user_agent eq \u0026#34;\u0026#34;) or (any(http.request.headers[\u0026#34;checkmode\u0026#34;][*] contains \u0026#34;fast\u0026#34;)) or (len(http.request.headers[\u0026#34;checkmode\u0026#34;]) \u0026gt; 0) or (ip.src.asnum in {8075 64267 63949 60068 54252 51167 47583 45102 44477 397630 396982 36352 36351 31898 30058 24940 213230 212329 203020 202561 200373 16509 16276 14061 135377 132203 212329}) or (http.user_agent contains \u0026#34;netcraft\u0026#34;) or (ip.src in {194.52.68.0/24 194.72.238.0/24 83.138.182.72/29 83.138.189.96/29 81.91.240.0/24 89.36.24.0/24 83.222.232.216/30 184.172.0.0/16}) or (http.referer eq \u0026#34;\u0026#34;) or (http.request.version in {\u0026#34;HTTP/1.0\u0026#34; \u0026#34;HTTP/1.1\u0026#34;}) js质询 屏蔽来自指定ASN和国家的IP访问（黑名单策略） 识别异常或伪造请求头，如头部内容异常、伪造代理信息等 区分正常浏览器和爬虫、机器人，通过 User-Agent 和请求头字段组合 限制非法请求方法，只允许常用安全的 HTTP 方法 监控CDN相关头部及循环转发情况，防止恶意流量绕过防护 (ip.geoip.asnum in {174 195 209 577 792 793 794 1215 1216 1217 2497 2914 3223 3255 3269 3326 3329 3457 3462 3598 4184 4190 4637 4694 4755 4785 4788 4816 4826 4835 5056 5610 5617 6471 6584 6830 6876 6877 6939 7029 7224 7303 7489 7552 7684 8068 8069 8070 8071 8074 8075 8100 8220 8560 8881 8987 9009 9299 9312 9370 9534 9678 9952 9984 10026 10453 11351 11426 11691 12076 12271 12334 12367 12874 12876 12989 14061 14117 14140 14576 14618 15169 16276 16509 16591 16629 17043 17428 17707 17788 17789 17790 17791 18013 18228 18403 18450 18599 18734 18978 19527 19740 20207 20473 20552 20554 20860 21704 21769 21859 21887 22773 22884 23468 23724 23885 23959 23969 24088 24192 24424 24429 24940 25429 25697 25820 25935 25961 26160 26496 26818 27715 28429 28431 28438 28725 29066 29286 29287 29802 30083 30823 31122 31235 31400 31898 32097 32098 32505 32613 34081 34248 34549 34947 35070 35212 35320 35540 35593 35804 35816 35908 35916 36351 36352 36384 36385 36444 36492 36806 37963 37969 38001 38197 38283 38365 38538 38587 38588 38627 39284 40065 40676 40788 41009 41096 41264 41378 42652 42905 43289 43624 43989 45011 45012 45062 45076 45085 45090 45102 45102 45102 45103 45104 45139 45458 45566 45576 45629 45753 45899 45932 46484 46844 47232 47285 47927 48024 48024 48337 48905 49327 49588 49981 50297 50340 50837 51852 52000 52228 52341 53089 54463 54538 54574 54600 54854 54994 55158 55330 55720 55799 55924 55933 55960 55967 55990 55992 56005 56011 56109 56222 57613 58073 58199 58461 58466 58519 58543 58563 58593 58772 58773 58774 58775 58776 58844 58854 58862 58879 59019 59028 59048 59050 59051 59052 59053 59054 59055 59067 59077 59374 60068 60592 60631 60798 61112 61154 61317 61348 61577 61853 62044 62240 62468 62785 62904 63018 63023 63075 63288 63314 63545 63612 63620 63631 63655 63677 63678 63679 63727 63728 63729 63835 63838 63888 63916 63949 64050 131090 131106 131138 131139 131140 131141 131293 131428 131444 131477 131486 131495 132196 132203 132509 132510 132513 132591 132839 133024 133199 133380 133478 133492 133746 133752 133774 133775 133776 133905 133929 134238 134327 134760 134761 134763 134764 134769 134770 134771 134835 134963 135061 135290 135300 135330 135377 135629 137693 137697 137699 137753 137784 137785 137787 137788 137876 137969 138366 138407 138607 138915 138949 138950 138952 138982 138994 139007 139018 139124 139144 139201 139203 139220 139316 139327 139726 139887 140096 140596 140701 140716 140717 140720 140723 140979 141157 141180 142570 146817 149167 177453 177549 197099 197540 198047 198651 199490 199506 199524 199883 200756 201094 201978 202053 202675 203087 204601 204720 206092 206204 206791 206798 207319 207400 207590 208425 208556 211914 212708 213251 213375 262187 263022 263196 263639 263693 264344 264509 265443 265537 266706 267784 269939 270110 328608 394699 395003 395936 395954 395973 398101}) or (ip.src.country in {\u0026#34;AR\u0026#34; \u0026#34;BD\u0026#34; \u0026#34;BR\u0026#34; \u0026#34;CO\u0026#34; \u0026#34;CZ\u0026#34; \u0026#34;IN\u0026#34; \u0026#34;ID\u0026#34; \u0026#34;IR\u0026#34; \u0026#34;LR\u0026#34; \u0026#34;LY\u0026#34; \u0026#34;NL\u0026#34; \u0026#34;PH\u0026#34; \u0026#34;RU\u0026#34; \u0026#34;TR\u0026#34; \u0026#34;UA\u0026#34; \u0026#34;VN\u0026#34; \u0026#34;T1\u0026#34; \u0026#34;PR\u0026#34;}) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;identity\u0026#34;)) or (any(http.request.headers[\u0026#34;x-requested-with\u0026#34;][*] eq \u0026#34;XMLHttpRequest\u0026#34;)) or (len(http.request.headers[\u0026#34;x-cache\u0026#34;]) \u0026gt; 0) or (len(http.request.headers[\u0026#34;cdn-loopcount\u0026#34;]) \u0026gt; 0) or (len(http.request.headers[\u0026#34;trailer\u0026#34;]) \u0026gt; 0 and not http.user_agent contains \u0026#34;Firefox\u0026#34;) or (len(http.request.headers[\u0026#34;x-frame-options\u0026#34;]) \u0026gt; 0) or (len(http.request.headers[\u0026#34;accept-charset\u0026#34;]) \u0026gt; 0) or (http.user_agent contains \u0026#34;Firefox\u0026#34; and len(http.request.headers[\u0026#34;sec-ch-ua\u0026#34;]) \u0026gt; 0) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;x-real-ip\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;x-forwarded-for\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;x-forwarded-host\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;cloudfront-viewer-address\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;ali-cdn-real-ip\u0026#34;)) or (http.request.version eq \u0026#34;HTTP/1.1\u0026#34;) or (not http.request.method in {\u0026#34;GET\u0026#34; \u0026#34;POST\u0026#34; \u0026#34;OPTIONS\u0026#34; \u0026#34;HEAD\u0026#34;}) 验证与自查 连续快速请求图片超过 MAX_REQUESTS_PER_SECOND（默认 50），返回 429 限流响应 非 GET 方法请求（如 POST）返回 405 浏览器访问图片时响应头带 Cache-Control: public, max-age=31536000, immutable，二次访问命中边缘缓存 在 Cloudflare 分析面板确认 WAF/速率限制规则有拦截记录，爬虫 UA（如 zgrab、masscan）被拦截 参考 Cloudflare Workers 文档 Cloudflare R2 文档 Cloudflare Cache Rules 文档 Cloudflare Rate Limiting 文档 Cloudflare WAF 文档 ","permalink":"https://heihei0299.github.io/posts/blog/r2%E5%9B%BE%E5%BA%8A%E9%98%B2%E6%AD%A2%E8%A2%AB%E5%88%B7/","summary":"\u003ch1 id=\"r2图床防止被刷\"\u003er2图床防止被刷\u003c/h1\u003e\n\u003cp\u003e最近刚白嫖了cf的r2存储桶作为图床，但是r2存储计费无上限，也就是说存在被刷爆的风险。所以采取以下措施，防止被刷爆。\u003c/p\u003e\n\u003ch2 id=\"缓存\"\u003e缓存\u003c/h2\u003e\n\u003cp\u003e利用Cloudflare的页面规则和Cache Rules 功能，为R2域名添加缓存。\u003c/p\u003e","title":"r2图床防止被刷"},{"content":"阿里云oss cloudflare 本文适用于想用阿里云 OSS 做免费图床的场景。你会学到：如何利用 Cloudflare 带宽联盟免除 OSS 外网流量费（前提是源站在海外地域）、如何配置私有 Bucket + Cloudflare CDN + 自定义域名，以及用 PicList 一键上传。\n前言 带宽联盟是 Cloudflare 推出的一项服务，由一群具有前瞻性思维的云服务和网络公司组成，致力于为共同客户降低或免除数据传输（带宽）费用。为什么能做到非常低甚至能免除成本呢？云厂商跟 Cloudflare 直接的数据传输，是通过专用网络接口 (PNI) 或专用互连的，中间不经过任何网络提供商（比如电信），既然是直连，那么就不存在 PNI 的增量成本了，自然也就便宜了。虽然阿里云不是第一批加入带宽联盟的，但是到现在为止也加入一年多了（我白嫖也一年多了），并推出了 Cloudflare+Alibaba Cloud OSS 的解决方案。这点还是非常良心的，虽然腾讯云后来也加入了，但是我觉得不够\u0026quot;清晰透明\u0026quot;，就一直没试过。\n存储费用 根据存储费用文档，我们可以了解到，在海外（部分）区域，标准存储（本地冗余）容量支持使用 5 GB/月的免费额度（即每月标准存储（本地冗余）容量≤5 GB 时，不收取标准存储（本地冗余）容量费用）。\n流量费用 根据流量费用文档，我们可以了解到，在海外（部分）区域，外网流出流量（oss_flow_out）支持使用 5 GB/月的免费额度（即每月外网流出流量≤5 GB 时，不收取外网流出流量费用）。\n请求费用 根据请求费用文档，算了没必要看了，请求费用是需要收费滴，不过好在便宜，几乎可以忽略不计。\n有一点需要特别注意：出口传输费优惠或豁免可能需要与托管服务提供商注册，不适用于源于中国大陆的数据传输。也就是说，OSS 源站必须是海外节点，所以大陆节点没有免费 5GB 存储，其实无所谓的啦！\n主要操作流程 创建 Bucket：开通 OSS 并创建 Bucket。创建 Bucket 时选择海外的地域（如果在别的地域没有服务器的话，建议使用香港），存储类型选择标准存储即可，读写权限一定要选择私有，其余的附加服务按需启用（有的得加钱）。 但为了让图床正常使用，需要设置让 Cloudflare 的 ip 能够访问。\nCloudflare 的 ip 官方已经给出了：\nIP Ranges\n简单处理一下，\n173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22 配置访问权限：进入权限控制 -\u0026gt; Bucket 授权策略面板，添加授权，配置如下图。 配置 CDN：在 CloudFlare 的 DNS 管理面板添加 CNAME 记录，目标设为 Bucket 的域名（可以在 Bucket 的概览界面找到），代理状态设为已代理，否则 CDN 不起作用。 绑定域名：在阿里云的 Bucket 配置 -\u0026gt; 域名管理界面绑定你刚设置的域名，这时阿里云需要验证域名的所有权，按照其所说的在 CloudFlare 的 DNS 管理处添加指定 TXT 记录即可。 创建并添加证书：在 CloudFlare 的配置面板的 SSL/TLS -\u0026gt; 源服务器处，选择创建证书。创建之后会告诉你源证书与私钥，这个界面暂时不要动。打开刚才在阿里云控制台绑定证书的界面，选择证书托管，并上传 SSL 证书，这时会打开 SSL 证书的界面，选择上传证书，并将 CloudFlare 给出的源证书和密钥复制到上传证书的对应字段处（证书名字随便设），然后确定。这时切换回上传 SSL 证书，应该就能在证书名称处看到刚刚设置的证书名字了（看不到的话重开一下这个界面试试），选中，然后点下方的上传即可。 然后我们查看域名绑定配置，发现已经绑定上了。然后我们去上传一张照片。 自有域名选择自己配置好的解析域名，我们就可以看到 URL 已经变成咱们自定义的啦！\n安全性配置 跨域设置：在阿里云 OSS 的数据安全 -\u0026gt; 跨域设置中创建跨域规则，来源设置为你的网站的地址。为了能让网站在本地测试时也能正常展示图片，建议同时添加 localhost:* 与 127.0.0.1:*。 防盗链设置：和跨域设置类似。不同之处在于 Referer 是包含请求协议的，所以类似于 example.org 或 localhost:* 等不包括协议的配置是无效的，需要改为 https://example.com 或 *://localhost:*；需要注意的是 *.example.org 虽然是有效的，但是没有指定 https 协议，安全起见最好改为 https://*.example.org。 CloudFlare 安全规则： 不需要设置错误页：因为 bucket 是私有的，所以 bucket 内存储的文件权限只能是私有的，不具备公开读或者公开写的权限。只有当 s3 的 bucket 公开的时候，才需要设置。因为 S3 协议的 bucket 在公开读的权限下会默认展示桶内文件列表和路径，特征明显而且并不友好。绝大多数的对象存储都支持设置静态网站 index.html 和错误页 404.html 的设置，比如阿里云 OSS 在【基础设置】-【静态页面】下。这里随手找了两个单页供选择和参考。\nPicList 配置 PicList 是一款快速上传图片到图床，并自动复制图片 URL 到剪贴板的工具，你可以在 Github 的 release 页获取该程序。\n权限设置：在阿里云的权限控制 -\u0026gt; Bucket 授权策略面板新增授权，配置如下： 如果当前没有 RAM 子帐号，请点击右上角头像 -\u0026gt; 访问控制，然后在左侧的身份管理 -\u0026gt; 用户处创建一个子帐号。创建完成后在子帐号的详情页创建 AccessKey，得到 AccessKey 的 KeyID 与 KeySecret，保留备用。\n上传配置：打开 PicList 主界面，在图床设置 -\u0026gt; 阿里云 OSS 内填写对应的表单项。\u0026ldquo;KeyID\u0026rdquo; 与 \u0026ldquo;KeySecret\u0026rdquo; 即刚才获取的子帐号 AccessKey 的 KeyID 与 KeySecret，\u0026ldquo;设定 Bucket\u0026rdquo; 为 Bucket 的名称，\u0026ldquo;存储区域\u0026rdquo; 为 Bucket 所在区域（与 Bucket 域名内的值统一，例如 oss-cn-hongkong），自定义域名填写你设置的域名，其余两项按需填写即可。配置完成后点击确定并设为默认图床。 验证与自查 在 PicList 上传一张图片，复制出的 URL 为自定义域名（如 https://img.example.com/xxx.png），浏览器可正常访问 图片响应头中出现 cf-cache-status，说明流量经过 Cloudflare CDN 而非直连 OSS 使用非白名单来源（如直接 curl 带错误 Referer）访问图片被拒绝（防盗链生效） 在阿里云 OSS 费用中心确认外网流出流量为 0 或极低（带宽联盟生效） 参考 Cloudflare 带宽联盟 Cloudflare+Alibaba Cloud OSS 解决方案 Cloudflare IP Ranges 阿里云 OSS 存储费用文档 阿里云 OSS 流量费用文档 PicList GitHub Releases ","permalink":"https://heihei0299.github.io/posts/blog/%E9%98%BF%E9%87%8C%E4%BA%91oss-cloudflare/","summary":"\u003ch1 id=\"阿里云oss-cloudflare\"\u003e阿里云oss cloudflare\u003c/h1\u003e\n\u003cp\u003e本文适用于想用阿里云 OSS 做免费图床的场景。你会学到：如何利用 Cloudflare 带宽联盟免除 OSS 外网流量费（前提是源站在海外地域）、如何配置私有 Bucket + Cloudflare CDN + 自定义域名，以及用 PicList 一键上传。\u003c/p\u003e","title":"阿里云oss cloudflare"},{"content":"搭建图床cloudflare r2 picgo 本文适用于需要免费、稳定图床的博客作者。你会学到：用 Cloudflare R2 创建存储桶并绑定自定义域名，配合 PicGo 的 S3 插件实现一键上传图片，同时禁用公开转发 URL 防止被恶意刷流量。\nR2 存储桶 购买 R2 计划 需要信用卡，支持银联卡 无需预先付费，验证通过就行 创建存储桶 两处完形填空：\n名字随便起，符合规范，简单点就行 若主要面向用户大陆，推荐亚太地区 给存储桶绑定域名 为了使用自己的域名来访问文件：\n注意，javaedge.cn 这个域名我已经在 CloudFlare 做过解析了。如果没有做过解析的话，需要先去做下！\n填入一个域名，这里 javaedge.cn 是我的顶级域名；\nmy-img.javaedge.cn 是我自己定义的二级域名，按自己喜好就行。\n需要手动刷新页面才会看到状态变化，大概一分钟变成 Active。\n添加自定义域名后，可以让 R2 存储桶的内容在互联网上公开访问。\n禁用公开转发 url 禁用 公开转发url 可以让未知请求（例如请求不存在的文件）不进入 R2 存储桶。这样可以避免恶意请求刷爆免费的 B 类次数。\n使用 PicGo 上传图片到 R2 创建 R2 API 令牌 准备以下信息 Access Key ID d9d04a555b70c12e09fd949d358d293e Secret Access Key 7d66bad4ce5fc88f07ef34be7352de5eb3da0d44913c9bc90fc3c931f8d21d59 Use jurisdiction-specific endpoints for S3 clients: https://xxxxxx.r2.cloudflarestorage.com PicGo 配置自动上传图床 安装 S3 插件 搜索时，可能网比较卡，半天才出来，多试几次：\n配置完成！本文图片都是该图床！验证完毕！\n验证与自查 上传一张图片后，用浏览器访问 https://my-img.javaedge.cn/\u0026lt;文件名\u0026gt; 能正常打开 PicGo 上传日志无报错，图片 URL 使用自定义域名而非 pub-*.r2.dev 域名 在 Cloudflare 控制台确认存储桶的 公开转发url 为禁用状态 请求一个不存在的文件路径，应直接返回错误而不计入 R2 请求 参考 Cloudflare R2 官方文档 PicGo 官方文档 R2 S3 API 兼容文档 ","permalink":"https://heihei0299.github.io/posts/blog/%E6%90%AD%E5%BB%BA%E5%9B%BE%E5%BA%8Acloudflare-r2-picgo/","summary":"\u003ch1 id=\"搭建图床cloudflare-r2-picgo\"\u003e搭建图床cloudflare r2 picgo\u003c/h1\u003e\n\u003cp\u003e本文适用于需要免费、稳定图床的博客作者。你会学到：用 Cloudflare R2 创建存储桶并绑定自定义域名，配合 PicGo 的 S3 插件实现一键上传图片，同时禁用公开转发 URL 防止被恶意刷流量。\u003c/p\u003e","title":"搭建图床cloudflare r2 picgo"},{"content":"防止阿里云oss被刷 本文适用于把图片放在对象存储（阿里云 OSS、Backblaze B2 等）并开启公开读的场景。你会学到：隐藏 bucket 特征（文件列表、响应标头）、配置错误页，以及通过 Cloudflare 缓存规则降低被刷成本。\nS3 协议的 bucket 在公开读的权限下会默认展示桶内文件列表和路径，特征明显而且并不友好。\n隐藏Bucket特征 设置错误页 绝大多数的对象存储都支持设置静态网站 index.html 和错误页 404.html 的设置，比如阿里云 OSS 在【基础设置】-【静态页面】下。这里随手找了两个单页供选择和参考（点击前往）。\n显然，像 B2 这种简单粗暴的又一次是没有这样的功能，可以如图使用 URL 重写规则将主页静态定向至 index.html。404 页面的功能没有一个比较好的办法，不过 B2 的友好访问链接不会展示目录列表和 bucket 信息，影响并不大。\n隐藏bucket标头 在对象存储的标头中会包含有一些 bucket 信息，可以通过控制台的 Network 选项卡暴露出来，也是对对象存储特征的一个暴露点。\n通过 CloudFlare 转换规则中的【修改响应头】，能够简单地实现响应头的去除，同时还可以加入跨域请求头等需要的标头。\n目前 B2 和 OSS 可以隐藏的标头大致如下，其他对象存储请根据实际情况去查看和配置，在此就不一一列出了。\n阿里云OSS Backblaze B2 x-oss-hash-crc64ecma x-bz-content-sha1 x-oss-object-type x-bz-file-id x-oss-request-id x-bz-file-name x-oss-server-time x-bz-info-src_last_modified_millis x-oss-storage-class x-bz-upload-timestamp 配置缓存规则 通过 Cloudflare 配置对象存储的缓存规则，命中缓存的请求不会回源，从而减少被刷时的源站费用：\n验证与自查 浏览器直接访问 bucket 域名根路径，显示的是 index.html 而非文件列表 访问一个不存在的路径，返回的是自定义 404.html 浏览器 DevTools → Network 检查响应头，x-oss-* / x-bz-* 标头已消失 同一图片二次访问时命中 Cloudflare 缓存（响应头出现 cf-cache-status: HIT） 参考 阿里云 OSS 静态页面设置文档 Cloudflare Transform Rules 文档 Cloudflare Cache Rules 文档 Backblaze B2 文档 ","permalink":"https://heihei0299.github.io/posts/blog/%E9%98%B2%E6%AD%A2%E9%98%BF%E9%87%8C%E4%BA%91oss%E8%A2%AB%E5%88%B7/","summary":"\u003ch1 id=\"防止阿里云oss被刷\"\u003e防止阿里云oss被刷\u003c/h1\u003e\n\u003cp\u003e本文适用于把图片放在对象存储（阿里云 OSS、Backblaze B2 等）并开启公开读的场景。你会学到：隐藏 bucket 特征（文件列表、响应标头）、配置错误页，以及通过 Cloudflare 缓存规则降低被刷成本。\u003c/p\u003e","title":"防止阿里云oss被刷"},{"content":"使用Cloudflare worker加速Cloudflare R2访问速度 cloudflare R2 提供了免费10G的对象存储并且兼容Amazon S3 api 操作, 与大多数对象存储提供商不一样的是R2的流出流量是免费的, 不用担心请求被恶意刷爆第二天银行来收房子的情况发生. 但是cloudflare给R2分配的ip都是xxx.xxx.xxx.1形式的ip, 这种ip节点在国内的访问体验很不好, 特别是在移动网络下, 基本无法访问. 然而计算机领域里有句叫\u0026rsquo;没有什么问题是加一个中间层不能解决的\u0026rsquo;, 而cloudflare里的worker刚好可以当这个\u0026rsquo;中间层\u0026rsquo;. worker是cloudflare提供的一个可以运行js/ts代码的serverless容器.\n设计原理 我们无法更改cloudflare为R2分配的ip, 但是worker的路由我们是可以配置. 由此我们可以通过优选好的路由连接到worker, 然后让worker作为中间人去帮我们访问R2的资源, worker和r2同属于cloudflare网络, 它们之间的通信会非常快. 这样一来就能实现R2的\u0026rsquo;加速\u0026rsquo;访问. 本篇文章演示中R2绑定的访问域名是static.merack.top, worker通过worker路由绑定的域名是cdn.merack.top. 其中cdn.merack.top是最终给用户看到的域名. 用户向cdn.merack.top发出请求调用worker, worker请求R2: static.merack.top, 将R2的数据返回给用户.\n创建worker 点击左侧workers和pages, 新建一个hello world 的worker, 不用选其他模板. 然后点击右上角的编辑代码按钮修改为如下代码\nconst R2_DOMAIN = \u0026#39;static.merack.top\u0026#39;; // 改成自己的R2公共访问域名 async function handleRequest(request) { try { const url = new URL(request.url); // 构建新的R2资源URL const targetUrl = new URL(`https://${R2_DOMAIN}`); targetUrl.pathname = url.pathname; targetUrl.search = url.search; // 复制并修改请求头 const headers = new Headers(request.headers); headers.set(\u0026#39;Host\u0026#39;, R2_DOMAIN); headers.delete(\u0026#39;Cookie\u0026#39;); // 移除不必要的cookie头 // 创建新请求 const newRequest = new Request(targetUrl, { method: request.method, headers: headers, redirect: \u0026#39;follow\u0026#39; }); let response = await fetch(newRequest); return response; } catch (err) { // 错误处理 return new Response(err.stack, { status: 500, headers: { \u0026#39;Content-Type\u0026#39;: \u0026#39;text/plain\u0026#39; } }); } } // 监听所有请求 addEventListener(\u0026#39;fetch\u0026#39;, event =\u0026gt; { event.respondWith(handleRequest(event.request)); }); R2_DOMAIN 常量改成自己的R2公共访问域名, 比如我这里是static.merack.top, 然后点击部署\n配置worker路由 点击设置-\u0026gt;域和路由, 点击右上方的添加按钮, 类型选择路由 接下来填入需要面向用户的那个域名, 路径填/*, 例如我这里填的就是cdn.merack.top/* 这样用户访问cdn.merack.top时就触发worker去代理请求到R2, 但是此时还并不是优选的路由, 我们还需要修改下DNS解析 添加DNS记录让cdn.merack.top指向优选好的cloudflare cdn节点ip, 关于cf如何优选ip网上有很多教程就不赘述了, 图方便的话可以直接添加一个cname记录到别人的优选域名, 这里我用的是 cloudflare.182682.xyz , 也可以填我的博客域名www.merack.top , 但是注意后面的小黄云记得关闭, 让其状态为 \u0026lsquo;仅DNS\u0026rsquo; 模式, 这样你的cname才有意义.点击保存后就可以将我们的访问域名换成worker路由的域名看看效果了.\n测试 经过一番折腾后效果对比以前有了很大提升, 但还是有些地区无法正常访问, 这取决于当地的网络状况和优选ip的质量. 如果要做到100%的可用性的话, 建议还是花点钱买个好点的服务, 比如Amazon S3 + Amazon Cloudfont, 如果你的域名有备案, 国内云服务商提供的对象存储也是不错的选择. 限制 这个方案的限制主要来自worker. worker的免费订阅最关键的两个限制是CPU时间和日请求额度. 每个请求的CPU时间是10毫秒, 超过这个时间请求会中断, 因此该方案不适用于大文件下载, 下到一半会中断. 10ms的时间最多能下到多大的文件没有测试过, 我用来请求存储本博客图片资源的R2的中值CPU时间不到1ms, 如果只是用作图片类型的文件加速, 10ms绰绰有余. worker的每日请求是十万, 因此该方案只能适用于日访问量在万级以下的小博客. 如果你的日访问量都到万级以上了, 应该不缺那点小钱去买个好点存储和cdn了吧. 为了防止脚本恶意刷请求, 可以配合cloudflare waf里的速率限制规格做一定的限制, 关于cloudflare waf我之前有写过文章介绍. 扩展 1.本文例子中的使用场景是主要图片访问加速, 那么就会涉及到缓存时间的问题. worker返回的静态资源响应是会带有缓存控制的响应头(Cache-Control)的 如果你想修改缓存的过期时间, 可以使用worker的cache api来控制, 具体可以参照官方文档: https://developers.cloudflare.com/workers/runtime-apis/cache/ https://developers.cloudflare.com/workers/reference/how-the-cache-works/ 2.虽说本文是以加速r2的访问为主题, 当也可用于加速其他的网站, 只要把代码中的 R2_DOMAIN 常量改改就可以做到. 但如果你代理的是某些版权意识比较看重的大公司的站点, 访问量大了他们可能会向cloudflare投诉, 认为你在进行\u0026rsquo;假冒官网\u0026rsquo;, \u0026lsquo;欺骗用户\u0026rsquo;等欺诈行为, 那么cloudflare可能会封禁你的账号.\n验证与自查 curl -I https://cdn.merack.top/\u0026lt;图片路径\u0026gt; 返回 200，内容与直连 static.merack.top 一致 对比直连与经 worker 访问同一图片的耗时，worker 路径明显更快 大文件下载测试：超过 10ms CPU 时间上限会中断，确认只用于图片等小文件 观察 worker 面板的请求数与 CPU 时间，确认未逼近免费额度（日 10 万请求、10ms CPU/请求） 参考 Cloudflare Workers Runtime Cache API Cloudflare How the Cache Works Cloudflare Workers 官方文档 ","permalink":"https://heihei0299.github.io/posts/blog/%E4%BD%BF%E7%94%A8cloudflare-worker%E5%8A%A0%E9%80%9Fcloudflare-r2%E8%AE%BF%E9%97%AE%E9%80%9F%E5%BA%A6/","summary":"\u003ch1 id=\"使用cloudflare-worker加速cloudflare-r2访问速度\"\u003e使用Cloudflare worker加速Cloudflare R2访问速度\u003c/h1\u003e\n\u003cp\u003ecloudflare R2 提供了免费10G的对象存储并且兼容Amazon S3 api 操作, 与大多数对象存储提供商不一样的是R2的流出流量是免费的, 不用担心请求被恶意刷爆第二天银行来收房子的情况发生. 但是cloudflare给R2分配的ip都是\u003cstrong\u003exxx.xxx.xxx.1\u003c/strong\u003e形式的ip, 这种ip节点在国内的访问体验很不好, 特别是在移动网络下, 基本无法访问. \u003cimg alt=\"img\" loading=\"lazy\" src=\"/images/20250615-4237f0dcd800a3748df9e15619752f34.png\"\u003e\u003c/p\u003e","title":"使用Cloudflare worker加速Cloudflare R2访问速度"},{"content":"Syncthing 同步方案：Windows 与 Debian 双向同步 本文适合想要把 Windows 重要数据备份/同步到 Linux 服务器的用户。你会学到：Windows 上用 nssm 把 Syncthing 注册为开机自启服务、Debian 12 上用 systemd 部署，以及 Windows 整盘同步时 System Volume Information 权限报错的解决办法。\n1. 背景 因为旧电脑突然故障，我的部分重要数据被丢失（没养成备份数据的习惯！！！）。因此我需要备份windows上的重要数据。选来选去，我最终选择syncthing 。\n2. 工具介绍 介绍 Syncthing 工具的主要特点和优势。\nSyncthing 是一个使用 Go 语言来发的开源文件同步工具，可以在多台计算机之间实时同步文件。它致力于保护用户数据，确保安全、易于使用，并且可以在各种常见的计算机上运行。\n安全性和隐私 Syncthing 基于 P2P 技术 实现设备间的文件同步，所以它的同步是 去中心化 的，即你并不需要一个服务器，故不需要担心这个中心的服务器给你带来的种种限制。 Syncthing 致力于保护用户数据，确保不会丢失或损坏。它使用 TLS 加密来保护通信，并采用完美前向保密以防止窥探者获取数据。每个设备都使用强加密证书进行身份验证，只有经过明确允许的设备才能连接到其他设备。 易用性和功能 Syncthing 旨在易于使用，可以在各种操作系统上运行，包括 macOS、Windows、Linux 等。它可以同步多个文件夹，支持局域网和互联网上的同步，无需复杂的 IP 地址或高级配置。用户只需将设备 ID 分享给其他设备，即可进行文件夹共享 3. win上部署syncthing 下载 syncthing 从syncthing 发布页下载下在适用于windows平台的syncthing，解压后会得到解压安装包，然后双击syncthing.exe打开。\n出现这个界面后，可以看见web界面访问地址。初次安装应该会直接在浏览器中打开该地址，然后有一个提示对话框，匿名报告相关的，可以选择否。\n使用nssm将syncthing注册成服务 首先以管理员身份运行powershell运行一下命令\nnssm.exe edit syncthing Application 选项卡的参数如下。\nPath:D:\\install\\syncthing\\syncthing.exe #syncthing.exe的路径 Startup directory:D:\\install\\syncthing #syncthing.exe所在的目录 Ardumens；serve --no-browser --no-restart --logflags=0 #syncthing启动参数 Details 选项卡中的Startup type 的参数选择 Automatic(即开机自启)\n在Log on选项卡下使用本地用户账户（非系统账户或者管理员账户）登录。 syncthing推荐不适用系统账户或者管理员账户运行sync thing\n在IO选项卡上设置日志目录\n使用浏览器打开syncthing syncthing默认监听 127.0.0.1:8384 ，在浏览输入http://127.0.0.1:8384/ 即可打开 syncthing gui界面。\n在debian12上部署syncthing 在syncthing发布页下载适用于debian12的二进制文件。 wget https://github.com/syncthing/syncthing/releases/download/v1.30.0-rc.1/syncthing-linux-amd64-v1.30.0-rc.1.tar.gz 解压后得到二进制文件 mkdir /opt/syncthing \u0026amp;\u0026amp; cd /opt/syncthing tar -zxf syncthing-linux-amd64-v1.30.0-rc.1.tar.gz cd syncthing-linux-amd64-v1.30.0-rc.1 \u0026amp;\u0026amp; chmod+x syncthing \u0026amp;\u0026amp; mv syncthing /opt/syncthing 添加 syncthing 用户用于运行syncthing adduser --system --no-create-home --group syncthing chown -R syncrhing:syncthing /opt/syncthing/ 注册systemd服务 nano /lib/systemd/system/syncthing.service root@nl-bwg:~/syncthing-linux-amd64-v1.30.0-rc.1# cat /lib/systemd/system/syncthing.service [Unit] Description=Syncthing - Open Source Continuous File Synchronization for %I Documentation=man:syncthing(1) After=network.target StartLimitIntervalSec=60 StartLimitBurst=4 [Service] User=syncthing Environment=HOME=/opt/syncthing ExecStart=/opt/syncthing/syncthing serve --no-browser --no-restart --logflags=0 --gui-address=[host:port] Restart=on-failure RestartSec=1 SuccessExitStatus=3 4 RestartForceExitStatus=3 4 # Hardening ProtectSystem=full PrivateTmp=true SystemCallArchitectures=native MemoryDenyWriteExecute=true NoNewPrivileges=true # Elevated permissions to sync ownership (disabled by default), # see https://docs.syncthing.net/advanced/folder-sync-ownership #AmbientCapabilities=CAP_CHOWN CAP_FOWNER [Install] WantedBy=multi-user.target 配置同步文件夹 添加设备 这里需要用到工具的ID，先去服务端查看，启动服务的时候就会显示，现在可以通过web界面进行查看。客户端同理。\n刚才复制的是服务端的ID，那么现在就打开客户端的web管理界面。\n如果只是局域网操作，这里直接保存即可。\n如果想公网访问，就可以内网穿透工具。在服务器搭建内网穿透工具，可以使用 tailscale\n配置Debian服务端。打开服务端的web界面。\n创建共享文件夹 在 Windows 创建共享文件夹\n文件夹共享给Debian。\n文件夹版本控制。官方文档的文件版本控制介绍。\n到这点Windows的就配置好了。\nDebian 服务端添加被分享的文件夹\n可以看见出现了一个新文件夹，这时点击添加即可。如果没有出现，可能的原因是服务端这边还没有与Windows进行连接。再次检查一下远程设备是否已经同步完成。\n这里选择共享给Windows，这样就可以把Debian变化后的文件也同步到Windows里面。\n这样就实现了简单的双向同步文件。\n踩坑 syncthing无法打开被分享的文件夹 问题分析 查看日志如下\n2025-06-13 21:33:41.614: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:41.794: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:41.966: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:42.131: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:42.289: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:42.777: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:43.331: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:43.498: [BETJW] INFO: Scanner (folder \u0026#34;电脑上的E盘\u0026#34; (home:E://), item \u0026#34;System Volume Information\u0026#34;): scan: open \\\\?\\E:\\System Volume Information: Access is denied. 2025-06-13 21:33:44.704 System Volume Information 是 Windows 系统自动创建的隐藏系统文件夹，用于：\n系统还原点（System Restore） 卷影复制（Shadow Copies） 索引服务 存储一些 NTFS 元数据 该目录默认只有 系统账户（SYSTEM） 有权限访问，普通用户（包括管理员）和程序 都无法直接访问这个文件夹，即使你是管理员。\nSyncthing 报错原因：\nSyncthing 在对整个 E: 盘进行扫描同步时，试图进入这个系统文件夹，结果因为权限不足而报错。\n解决方法 排除该目录\n你可以在 Syncthing 的文件夹设置中，添加 排除项（Ignore Patterns） 来避免扫描这个目录。\n步骤：\n打开 Syncthing 的 Web 界面； 找到同步的\u0026quot;电脑上的E盘\u0026quot;目录； 编辑该文件夹的\u0026quot;忽略列表\u0026quot;； 添加以下一行： System Volume Information/ 或者更保险地写：\n(?i)System Volume Information/ （(?i) 表示忽略大小写）\n验证与自查 Windows 与 Debian 的 WebGUI 远程设备列表中都显示对方\u0026quot;已连接\u0026quot; 在 Windows 的 E 盘新建测试文件，Debian 对应目录自动出现；反向修改也会同步回 Windows 同步日志中不再出现 System Volume Information: Access is denied 报错 重启 Windows 后 nssm 服务自动拉起 syncthing（任务管理器/服务列表确认），Debian 侧 systemctl is-enabled syncthing 返回 enabled 参考 Syncthing 官方文档 Syncthing 版本控制（File Versioning） Syncthing 忽略模式（Ignore Patterns） nssm（Non-Sucking Service Manager） Tailscale 官网 ","permalink":"https://heihei0299.github.io/posts/ops/syncthing-%E5%90%8C%E6%AD%A5%E6%96%B9%E6%A1%88/","summary":"\u003ch1 id=\"syncthing-同步方案windows-与-debian-双向同步\"\u003eSyncthing 同步方案：Windows 与 Debian 双向同步\u003c/h1\u003e\n\u003cp\u003e本文适合想要把 Windows 重要数据备份/同步到 Linux 服务器的用户。你会学到：Windows 上用 nssm 把 Syncthing 注册为开机自启服务、Debian 12 上用 systemd 部署，以及 Windows 整盘同步时 \u003ccode\u003eSystem Volume Information\u003c/code\u003e 权限报错的解决办法。\u003c/p\u003e","title":"Syncthing 同步方案：Windows 与 Debian 双向同步"},{"content":"开网站五年多了，分享下我的 Cloudflare WAF 规则 本文适合使用 Cloudflare 免费版或付费版的站长。你会学到：一套经过实战迭代的 WAF 分层防御体系——托管质询（Managed Challenge）、阻止（Block）、放行（Allow）三层规则，以及 ASN、地区、请求头、User-Agent 等维度的具体表达式。\n0x00 前情提要 我的 Cloudflare WAF 配置并非一蹴而就的完美方案，而是一套经过多次迭代、结合业务场景和实战经验形成的动态防御体系。核心逻辑始终围绕三个关键目标：主动防御、最小化误拦截、以及性能与成本的平衡。\n在开始之前，让我们了解一些关键概念：\nWeb 应用程序防火墙（Web Application Firewall, WAF）是一种防护网络攻击的技术，通过检查和过滤 HTTP 流量中的恶意请求和攻击，保护 Web 应用程序的安全。 Cloudflare WAF 通常有这样几种策略：Allow（允许）、Managed Challenge（托管质询）、Block（阻止） 对于 Managed Challenge 策略，访问网站标题通常为 Just a moment... 对于 Block 策略，访问网站标题通常为 Attention Required! Cloudflare 并且页面会有一个大红色的阻止图标 在 WAF 规则的管理页面，我们可以注意到 CSR (Challenge Score Rate)，它是一个百分比值，用于表示通过的质询请求和发出的质询请求的比例。CSR 的值越小越好，此指标可帮助我们评估 WAF 规则的有效性。 实现完美配置的基础是理解防御的优先级。我将规则分为三层，对应三个不同的策略，每一层解决不同层级的问题。\n0x01 质询一些看起来很奇怪的请求 IDC 验证 通常而言，我们的访客不会在数据中心上网，所以大部分来自数据中心的流量都是有问题的流量，我们可以使用 托管质询 来应对这部分流量：\n(ip.geoip.asnum in {174 195 209 577 792 793 794 1215 1216 1217 2497 2914 3223 3255 3269 3326 3329 3457 3462 3598 4184 4190 4637 4694 4755 4785 4788 4816 4826 4835 5056 5610 5617 6471 6584 6830 6876 6877 6939 7029 7224 7303 7489 7552 7684 8068 8069 8070 8071 8074 8075 8100 8220 8560 8881 8987 9009 9299 9312 9370 9534 9678 9952 9984 10026 10453 11351 11426 11691 12076 12271 12334 12367 12874 12876 12989 14061 14117 14140 14576 14618 15169 16276 16509 16591 16629 17043 17428 17707 17788 17789 17790 17791 18013 18228 18403 18450 18599 18734 18978 19527 19740 20207 20473 20552 20554 20860 21704 21769 21859 21887 22773 22884 23468 23724 23885 23959 23969 24088 24192 24424 24429 24940 25429 25697 25820 25935 25961 26160 26496 26818 27715 28429 28431 28438 28725 29066 29286 29287 29802 30083 30823 31122 31235 31400 31898 32097 32098 32505 32613 34081 34248 34549 34947 35070 35212 35320 35540 35593 35804 35816 35908 35916 36351 36352 36384 36385 36444 36492 36806 37963 37969 38001 38197 38283 38365 38538 38587 38588 38627 39284 40065 40676 40788 41009 41096 41264 41378 42652 42905 43289 43624 43989 45011 45012 45062 45076 45085 45090 45102 45102 45102 45103 45104 45139 45458 45566 45576 45629 45753 45899 45932 46484 46844 47232 47285 47927 48024 48024 48337 48905 49327 49588 49981 50297 50340 50837 51852 52000 52228 52341 53089 54463 54538 54574 54600 54854 54994 55158 55330 55720 55799 55924 55933 55960 55967 55990 55992 56005 56011 56109 56222 57613 58073 58199 58461 58466 58519 58543 58563 58593 58772 58773 58774 58775 58776 58844 58854 58862 58879 59019 59028 59048 59050 59051 59052 59053 59054 59055 59067 59077 59374 60068 60592 60631 60798 61112 61154 61317 61348 61577 61853 62044 62240 62468 62785 62904 63018 63023 63075 63288 63314 63545 63612 63620 63631 63655 63677 63678 63679 63727 63728 63729 63835 63838 63888 63916 63949 64050 131090 131106 131138 131139 131140 131141 131293 131428 131444 131477 131486 131495 132196 132203 132509 132510 132513 132591 132839 133024 133199 133380 133478 133492 133746 133752 133774 133775 133776 133905 133929 134238 134327 134760 134761 134763 134764 134769 134770 134771 134835 134963 135061 135290 135300 135330 135377 135629 137693 137697 137699 137753 137784 137785 137787 137788 137876 137969 138366 138407 138607 138915 138949 138950 138952 138982 138994 139007 139018 139124 139144 139201 139203 139220 139316 139327 139726 139887 140096 140596 140701 140716 140717 140720 140723 140979 141157 141180 142570 146817 149167 177453 177549 197099 197540 198047 198651 199490 199506 199524 199883 200756 201094 201978 202053 202675 203087 204601 204720 206092 206204 206791 206798 207319 207400 207590 208425 208556 211914 212708 213251 213375 262187 263022 263196 263639 263693 264344 264509 265443 265537 266706 267784 269939 270110 328608 394699 395003 395936 395954 395973 398101 }) 这个列表包括了各个国家大部分数据中心的出口 ASN，一般不会影响到普通家宽用户。另外请注意我这里质询了来自 Akile AS61112 的流量，如果您有需求请自行解开。\n对于高风险 ASN 进行质询 这部分 ASN 建议和上方一起使用，效果更佳。（可选，有一定的误报率）\n(ip.src.asnum in {965 6461 7195 7203 7713 7941 8075 8560 8987 9009 9123 11878 13349 13965 14061 14593 14618 15169 16276 18779 18978 19148 19527 19871 20853 21704 21769 22552 22612 22616 25369 26496 28753 30633 34081 36599 37148 37963 39686 43513 44144 44477 45090 45102 46261 46516 46606 47583 49367 49981 50304 50495 50835 51167 51290 51765 52393 52449 52485 53667 54252 54600 55081 55286 55960 55990 59253 60068 60781 62160 62240 62874 63023 64080 64267 64286 132203 132817 133499 133752 133944 134450 135377 137718 139659 141995 142002 149428 150436 152194 200373 201035 201341 202044 202496 202914 203020 203061 203098 203346 203999 204287 205544 205659 205964 206092 207990 209043 209709 210644 210906 212144 212329 212335 212384 215859 216071 263740 394380 394474 394814 39486 395954 396356 396982 397391 397630 398101 398823}) 对于高风险地区进行质询 这些地区常用来做攻击（基本上是小国家），建议质询。（可选，有一定的误报率）\n(ip.src.country in {\u0026#34;AR\u0026#34; \u0026#34;BD\u0026#34; \u0026#34;BR\u0026#34; \u0026#34;CO\u0026#34; \u0026#34;CZ\u0026#34; \u0026#34;IN\u0026#34; \u0026#34;ID\u0026#34; \u0026#34;IR\u0026#34; \u0026#34;LR\u0026#34; \u0026#34;LY\u0026#34; \u0026#34;NL\u0026#34; \u0026#34;PH\u0026#34; \u0026#34;RU\u0026#34; \u0026#34;TR\u0026#34; \u0026#34;UA\u0026#34; \u0026#34;VN\u0026#34; \u0026#34;T1\u0026#34; \u0026#34;PR\u0026#34;}) 对于奇怪的请求头进行质询 这里有一些与浏览器不匹配的请求头，通过这条规则可以很方便的将它们拦截下来。\n(any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;identity\u0026#34;)) or (any(http.request.headers[\u0026#34;x-requested-with\u0026#34;][*] eq \u0026#34;XMLHttpRequest\u0026#34;)) or (len(http.request.headers[\u0026#34;x-cache\u0026#34;]) \u0026gt; 0) or (len(http.request.headers[\u0026#34;cdn-loopcount\u0026#34;]) \u0026gt; 0) or (len(http.request.headers[\u0026#34;trailer\u0026#34;]) \u0026gt; 0 and not http.user_agent contains \u0026#34;Firefox\u0026#34;) or (len(http.request.headers[\u0026#34;x-frame-options\u0026#34;]) \u0026gt; 0) or (len(http.request.headers[\u0026#34;accept-charset\u0026#34;]) \u0026gt; 0) or (http.user_agent contains \u0026#34;Firefox\u0026#34; and len(http.request.headers[\u0026#34;sec-ch-ua\u0026#34;]) \u0026gt; 0) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;x-real-ip\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;x-forwarded-for\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;x-forwarded-host\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;cloudfront-viewer-address\u0026#34;)) or (any(http.request.headers[\u0026#34;accept-encoding\u0026#34;][*] eq \u0026#34;ali-cdn-real-ip\u0026#34;)) 慎用：质询可能有风险的请求 通常而言，大部分 CC 攻击都是 HTTP/1.1 的请求（少部分 HTTP/2 或 HTTP/3），所以我们可以针对这一点进行封堵：\n(http.request.version eq \u0026#34;HTTP/1.1\u0026#34;) 另外附上一条不常用 HTTP Method 的质询规则，这需要根据你的业务适当放行 API 站点：\n(not http.request.method in {\u0026#34;GET\u0026#34; \u0026#34;POST\u0026#34; \u0026#34;OPTIONS\u0026#34; \u0026#34;HEAD\u0026#34;}) 0x02 阻止 奇怪的 User-Agent 正常用户访问通常会携带一个看上去像话的 User-Agent，而部分恶意请求的 User-Agent 为空或者为请求库默认 User-Agent，这部分流量我们可以质询或者阻止。\n(http.user_agent eq \u0026#34;\u0026#34;) 另外还有一些各类工具的请求，我们可以通过这个表达式来阻止：\n(http.user_agent contains \u0026#34;Acunetix\u0026#34;) or (http.user_agent contains \u0026#34;apache\u0026#34;) or (http.user_agent contains \u0026#34;attachment\u0026#34;) or (http.user_agent contains \u0026#34;BackDoorBot\u0026#34;) or (http.user_agent contains \u0026#34;cobion\u0026#34;) or (http.user_agent contains \u0026#34;fhscan\u0026#34;) or (http.user_agent contains \u0026#34;fimap\u0026#34;) or (http.user_agent contains \u0026#34;Gscan\u0026#34;) or (http.user_agent contains \u0026#34;hey/\u0026#34;) or (http.user_agent contains \u0026#34;Joomla\u0026#34;) or (http.user_agent contains \u0026#34;libweb\u0026#34;) or (http.user_agent contains \u0026#34;libwww\u0026#34;) or (http.user_agent contains \u0026#34;masscan\u0026#34;) or (http.user_agent contains \u0026#34;nmap\u0026#34;) or (http.user_agent contains \u0026#34;Page\u0026#34; and http.user_agent contains \u0026#34;Analyzer\u0026#34;) or (http.user_agent contains \u0026#34;PHPCrawl\u0026#34;) or (http.user_agent contains \u0026#34;PyCurl\u0026#34;) or (http.user_agent contains \u0026#34;python\u0026#34;) or (http.user_agent contains \u0026#34;RankActive\u0026#34;) or (http.user_agent contains \u0026#34;RankingBot\u0026#34;) or (http.user_agent contains \u0026#34;RankurBot\u0026#34;) or (http.user_agent contains \u0026#34;researchscan\u0026#34;) or (http.user_agent contains \u0026#34;ScanAlert\u0026#34;) or (http.user_agent contains \u0026#34;scanbot\u0026#34;) or (http.user_agent contains \u0026#34;virusdie\u0026#34;) or (http.user_agent contains \u0026#34;VoidEYE\u0026#34;) or (http.user_agent contains \u0026#34;WebShag\u0026#34;) or (http.user_agent contains \u0026#34;Wprecon\u0026#34;) or (http.user_agent contains \u0026#34;WPScan\u0026#34;) or (http.user_agent contains \u0026#34;wrk\u0026#34;) or (http.user_agent contains \u0026#34;ZmEu\u0026#34;) or (http.user_agent contains \u0026#34;ZumBot\u0026#34;) or (http.user_agent contains \u0026#34;Zyborg\u0026#34;) or (http.user_agent contains \u0026#34;Zeus\u0026#34;) or (http.user_agent contains \u0026#34;zgrab\u0026#34;) or (http.user_agent contains \u0026#34;zmap\u0026#34;) or (http.user_agent contains \u0026#34;lient\u0026#34; and http.user_agent contains \u0026#34;ttp\u0026#34;) or (http.user_agent contains \u0026#34;fuck\u0026#34;) or (http.user_agent eq \u0026#34;undefined\u0026#34;) or (http.user_agent eq \u0026#34;\u0026#34;) 更进一步，有时候会有一些老旧的或者不符合 User-Agent 规范的 UA，我们可以继续添加如下内容来屏蔽：\n(not starts_with(http.user_agent, \u0026#34;Mozilla/5.0 (\u0026#34;)) or (http.user_agent eq \u0026#34;Mozilla/5.0\u0026#34;) or (http.user_agent eq \u0026#34;Mozilla/5.0 (compatible)\u0026#34;) ITDOG 有些网站源站性能较差，如果有人在 itdog 上进行了快速测速很可能导致源站 CPU 飙升，我们可以通过规则屏蔽这部分请求：\n(any(http.request.headers[\u0026#34;checkmode\u0026#34;][*] contains \u0026#34;fast\u0026#34;)) 如果你想要同时阻止快速和监测，请使用下面这条规则：\n(len(http.request.headers[\u0026#34;checkmode\u0026#34;]) \u0026gt; 0) 终极审判：Accept-Language 和 Accept-Encoding 正常的浏览器请求在请求头中必定携带 Accept-Language 和 Accept-Encoding 这两个请求头且 UA 都是以 Mozilla/5.0 开头，如果缺失这三个请求头，则表明请求为异常或恶意请求。\n请注意本条规则可能误伤各个写法不规范的友链检查请求，请自行放行\n(not len(http.request.headers[\u0026#34;accept-encoding\u0026#34;]) \u0026gt; 0) or (not len(http.request.headers[\u0026#34;accept-language\u0026#34;]) \u0026gt; 0) 对于部分高风险 ASN 进行阻止 这些 ASN 通常被表明用在各种攻击中，建议直接阻止（可选，有一定的误报率）\n(ip.src.asnum in {8075 64267 63949 60068 54252 51167 47583 45102 44477 397630 396982 36352 36351 31898 30058 24940 213230 212329 203020 202561 200373 16509 16276 14061 135377 132203}) Netcraft 单拎出来，懂的都懂。\n(ip.src.asnum in {212329}) or ((lower(http.user_agent) contains \u0026#34;netcraft\u0026#34;)) or (ip.src in {194.52.68.0/24 194.72.238.0/24 83.138.182.72/29 83.138.189.96/29 81.91.240.0/24 89.36.24.0/24 83.222.232.216/30 184.172.0.0/16}) 0x03 其他设置 Tor Services 使用 ASN 处置方案，不建议在生产里使用，纯粹用于内部黑名单系统炫技\n(ip.src.asnum in { 3 9 32 160 174 209 278 553 559 577 680 701 719 786 803 812 823 852 855 1101 1102 1103 1133 1136 1205 1213 1221 1257 1267 1299 1403 1653 1680 1759 1835 2027 2033 2108 2116 2119 2514 2516 2518 2527 2603 2611 2614 2856 2860 2900 2907 3061 3164 3209 3214 3215 3223 3236 3242 3243 3249 3257 3258 3269 3292 3301 3303 3320 3329 3352 3356 3462 3758 3790 3927 4181 4224 4319 4508 4658 4685 4713 4739 4764 4766 4771 4788 4804 4922 5089 5378 5404 5408 5410 5432 5483 5603 5607 5610 5617 5645 5650 5769 6128 6145 6167 6181 6315 6327 6661 6696 6698 6718 6724 6730 6799 6805 6810 6830 6848 6855 6871 6876 6893 6939 7018 7203 7377 7418 7459 7506 7545 7552 7684 7765 7922 7992 8075 8151 8218 8283 8339 8400 8412 8422 8437 8447 8455 8473 8495 8560 8648 8708 8717 8758 8767 8820 8849 8866 8873 8881 8916 8943 8972 9008 9009 9031 9044 9050 9063 9105 9123 9136 9141 9145 9269 9299 9318 9370 9378 9443 9506 9534 9790 10143 10242 10489 10796 10929 10961 10996 11114 11232 11351 11403 11404 11426 11427 11550 11572 11650 11721 11796 11847 11878 12083 12093 12119 12161 12312 12322 12338 12353 12355 12364 12392 12400 12479 12496 12508 12552 12586 12605 12731 12735 12767 12874 12876 13022 13030 13036 13037 13045 13046 13101 13110 13127 13170 13188 13213 13285 13692 13693 13868 13977 14007 14061 14315 14593 14618 14638 14670 14956 15081 15108 15435 15440 15502 15547 15557 15600 15614 15626 15657 15704 15895 15943 15962 16019 16075 16086 16097 16125 16276 16302 16509 16591 16652 17055 17356 17506 17547 17552 17676 17931 18106 18390 18460 18526 18530 19045 19148 19237 19318 19437 19504 19994 20001 20055 20115 20257 20454 20473 20676 20712 20766 20845 20853 20857 20860 20904 21100 21211 21409 21473 21499 21859 21949 22047 22296 22611 22612 22773 23310 23423 23470 23655 23884 24086 24233 24282 24685 24768 24800 24875 24940 24953 24961 24971 25198 25255 25353 25369 25472 25512 25795 26042 26068 26347 26383 26527 26615 26801 26832 27176 27411 27631 27651 27823 28258 28573 28753 28855 29066 29075 29107 29134 29169 29222 29259 29314 29447 29518 29551 29580 29691 29695 29802 30036 30058 30083 30600 30633 30722 30766 30823 30836 30860 30893 30983 31012 31034 31103 31148 31371 31863 31898 32097 32133 32181 32244 32338 32489 32613 33083 33387 33566 33705 33796 33837 33885 33911 33915 33943 33984 34222 34224 34244 34259 34288 34689 34694 34700 34702 34779 34878 34911 34927 35042 35100 35112 35179 35244 35366 35425 35426 35434 35470 35518 35530 35592 35661 35699 35758 35916 36149 36352 36375 36813 36849 36850 37611 38074 38195 39020 39287 39375 39378 39392 39405 39519 39587 39614 39622 39642 39788 39798 39819 39857 39900 39912 40021 40029 40138 40244 40676 40749 40924 40933 40994 41047 41075 41108 41111 41164 41281 41608 41634 41653 41745 42159 42237 42275 42532 42541 42611 42624 42652 42675 42708 42730 42831 42908 42927 43150 43180 43258 43289 43317 43341 43350 43513 43541 43641 43847 43902 43927 44103 44133 44176 44477 44486 44489 44592 44679 44709 44716 44820 44869 44901 44925 45012 45037 45045 45102 45382 45671 45731 45758 45839 45899 46015 46375 46475 46632 46690 46841 46844 46887 47066 47172 47263 47325 47447 47536 47570 47583 47610 47692 47890 47987 48090 48101 48314 48635 48753 48874 48900 48926 48927 49367 49392 49409 49453 49468 49505 49544 49581 49601 49635 49683 49770 49855 49981 50066 50266 50304 50338 50340 50360 50436 50613 50629 50673 50821 50840 50867 50979 51059 51167 51175 51177 51290 51395 51396 51659 51692 51744 51765 51815 51852 52000 52048 52092 52173 52423 53072 53486 53667 53914 54098 54290 54641 54702 54965 55081 55286 55293 55592 55688 55720 56030 56067 56153 56322 56380 56381 56478 56610 56611 56630 56655 56803 56971 57043 57169 57269 57494 57608 57717 57811 57860 58010 58061 58087 58212 58294 58329 58791 58915 59253 59444 59491 59702 59711 59720 59729 59741 59878 59891 60016 60068 60117 60131 60191 60404 60522 60602 60729 60781 61098 61106 61112 61125 61138 61157 61174 61272 61323 61335 61424 61493 62000 62005 62217 62240 62248 62336 62370 62425 62564 62744 62904 63018 63023 63473 63770 63806 63930 63949 63997 64289 64396 64425 131965 131970 132203 133159 133206 133210 133398 134351 134697 134835 135377 135887 135932 135959 136052 136258 136557 141995 142036 149440 151858 151873 197019 197071 197422 197448 197451 197524 197540 197566 197648 197695 197706 197902 197922 198071 198093 198161 198178 198620 198859 198930 198967 198985 199058 199118 199173 199246 199284 199524 199654 199950 199959 200019 200052 200081 200195 200223 200462 200490 200533 200651 200698 200713 200736 200950 201112 201146 201308 201670 201814 201838 202053 202246 202282 202302 202306 202401 202422 202425 202448 202520 202539 202585 202613 202618 202759 203003 203020 203363 203380 203424 203446 203476 203953 204410 204601 204615 204641 204911 204957 205007 205053 205090 205100 205235 205449 205714 205771 206075 206125 206216 206236 206238 206264 206642 206774 206804 207003 207083 207143 207252 207279 207459 207503 207560 207645 207656 207713 207799 207916 207957 207992 208046 208101 208208 208226 208323 208367 209243 209281 209605 209641 209696 209825 210079 210083 210152 210218 210226 210403 210558 210630 210644 210731 210874 210893 211301 211462 211507 211593 211619 211720 211742 211882 212027 212238 212271 212317 212396 212441 212477 212508 212531 212895 212913 213230 213250 213281 213354 213373 213438 213725 213850 213873 213877 213887 213959 214094 214196 214209 214285 214354 214366 214379 214481 214503 214677 214717 214790 214808 214902 214918 214996 215026 215120 215248 215296 215362 215381 215467 215481 215540 215659 215703 215730 215762 215904 216063 216071 216107 216127 216188 262287 262391 263608 263812 267546 328170 328364 329007 329184 329325 393238 393336 393408 393552 393577 393746 393886 394056 394380 395466 395510 395673 395853 395954 396190 396362 396527 397032 397122 397162 397373 397423 398101 398242 398721 399122 399334 399629 399646 399804 399820 400175 400226 400304 400509 400564 400771 400848 400861 401104 401332 401401 }) 0x04 放行一些请求 绝大部分情况下，使用这条规则放行就够了，特殊情况特殊对待：\n(cf.client.bot) or ((lower(http.user_agent) contains \u0026#34;uptime\u0026#34;)) 0x05 参考链接 Cloudflare 免费版 WAF 规则分享和详解 窥探 Cloudflare WAF 防护（关键词：打探敌情、5 秒盾穿盾、Bot score） WolfYangFan/cloudflare.ruleset sefinek/Cloudflare-WAF-Expressions Pysio 在群内分享的部分规则 阳帆极寒之地群 流量垃圾桶的每周赃流量统计 0x06 结语 本文分享了我经过实战验证的 Cloudflare WAF 分层防御配置方案，核心围绕主动防御、最小化误拦截、性能与成本平衡三大目标。通过分层策略（托管质询、阻止、放行）实现动态防御。如有任何问题欢迎留言反馈。\n验证与自查 在 WAF 规则管理页面查看各规则的 CSR（Challenge Score Rate），值越小说明质询拦截越有效 用手机流量（家宽 ASN）访问网站应直接放行，无质询页 用 curl -A \u0026quot;masscan\u0026quot; -I https://yourdomain.com 模拟恶意 UA，应被 Block（返回 403） 新规则上线后先在**日志（Security Events）**中观察误报，再逐步调整表达式 参考 Cloudflare WAF 规则官方文档 Cloudflare 规则表达式语言（Rules language） Cloudflare 托管质询（Managed Challenge） 原文：开网站五年多了，分享下我的 Cloudflare WAF 规则 ","permalink":"https://heihei0299.github.io/posts/ops/waf/","summary":"\u003ch1 id=\"开网站五年多了分享下我的-cloudflare-waf-规则\"\u003e开网站五年多了，分享下我的 Cloudflare WAF 规则\u003c/h1\u003e\n\u003cp\u003e本文适合使用 Cloudflare 免费版或付费版的站长。你会学到：一套经过实战迭代的 WAF 分层防御体系——托管质询（Managed Challenge）、阻止（Block）、放行（Allow）三层规则，以及 ASN、地区、请求头、User-Agent 等维度的具体表达式。\u003c/p\u003e","title":"开网站五年多了，分享下我的 Cloudflare WAF 规则"},{"content":"ssh方案之 winscp+putty 本文适合使用 WinSCP + PuTTY 管理多台 SSH 服务器的 Windows 用户。你会学到：为什么通过 WinSCP 调起的 PuTTY 会话会断连，以及如何设置保活参数 PingIntervalSecs 让连接长期保持。\n前言 在使用多个ssh客户端以后，我决定选择winscp+putty的方案。在这个方案里，winscp主要用于sftp和管理多个ssh节点。\n遇到的问题 通过winscp调起的putty无法与主机保持长时间通信。如果长时间不操作putty窗口，putty会自动断开与主机的连接。\n解决办法 向putty终端传递 PingIntervalSecs=1e 这个字段。以下是设置方法。\n验证与自查 保存设置后重新连接，保持 PuTTY 窗口空闲超过原断连时间（如 10 分钟），连接不被断开 观察 PuTTY 窗口标题或状态栏，确认连接状态仍为活跃 在远端执行命令正常返回，说明会话未过期 参考 PuTTY 官方网站 PuTTY 文档：keepalive 保活配置 WinSCP 官方网站 ","permalink":"https://heihei0299.github.io/posts/ops/ssh%E6%96%B9%E6%A1%88-winscp-putty/","summary":"\u003ch1 id=\"ssh方案之-winscpputty\"\u003essh方案之 winscp+putty\u003c/h1\u003e\n\u003cp\u003e本文适合使用 WinSCP + PuTTY 管理多台 SSH 服务器的 Windows 用户。你会学到：为什么通过 WinSCP 调起的 PuTTY 会话会断连，以及如何设置保活参数 \u003ccode\u003ePingIntervalSecs\u003c/code\u003e 让连接长期保持。\u003c/p\u003e\n\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e在使用多个ssh客户端以后，我决定选择winscp+putty的方案。在这个方案里，winscp主要用于sftp和管理多个ssh节点。\u003c/p\u003e","title":"ssh方案之 winscp+putty"},{"content":"Linux Mint 22 安装指南 适用场景：想从 Windows 或 macOS 转投 Linux 阵营的新手，或需要一套稳定 LTS 桌面系统的用户。你会学到 Linux Mint 22 从下载镜像、制作启动 U 盘到分区安装的完整流程，以及各安装选项（LVM、全盘加密、手动分区）的取舍。\nLinux Mint 是一款基于 Ubuntu 的流行发行版，凭借其现代、精致、易于使用的特性，深受小伙伴们所喜爱。对于想从 Windows 或 macOS 转投 Linux 阵营的小白用户来说，它也是一个「新手上路」的绝佳选择。\n目前，最新版本的 Linux Mint 22.1「Xia」提供了三个主要版本：Cinnamon、Xfce 和 MATE。作为一个 LTS（长期支持）版本，Linux Mint 22 将支持直到 2029 年，非常适合注重稳定性和长期使用的用户。\n如果你还不知道什么是 Linux Mint，一定不要错过这篇文章。\nLinux Mint 22 硬件要求 内存：至少需要 2GB，但建议 4GB 或更大，运行更流畅。 处理器：双核处理器起步，性能足够应对日常使用。 磁盘空间：最低需要 20GB 可用空间，但建议预留 100GB 或以上，方便后续安装软件和存储文件。 显示器：分辨率至少 1024×768，确保显示效果清晰。 互联网连接：安装过程推荐连网，以便获取最新更新和驱动。 U 盘：准备一个 4GB 或更大容量的 U 盘，用于制作启动盘。 第 1 步：下载 Linux Mint 22.1 安装镜像 根据你的需求和喜好，从以下链接下载对应的 ISO 镜像： Linux Mint 22.1 Cinnamon（强烈推荐） Linux Mint 22.1 Xfce Linux Mint 22.1 MATE Cinnamon 版本界面现代、功能丰富，适合大多数用户；Xfce 版本轻量高效，适合老旧设备；MATE 版本则介于两者之间，兼顾性能和美观。\n将 .iso 文件保存到本地，接下来我们会用它来制作 Linux Mint 22 的启动 U 盘。 第 2 步：制作 Linux Mint 启动 U 盘 制作启动 U 盘是安装 Linux Mint 22 的关键步骤。你可以参考这篇文章中的详细教程，操作步骤十分简单。唯一的区别是：将教程中提到的 Ubuntu ISO 替换成你刚刚下载好的 Linux Mint ISO 文件。\n使用 Rufus 制作 Linux Mint 启动 U 盘\n第 3 步：从 U 盘引导 Linux Mint 将电脑完全关机，然后断开所有不必要的 USB 设备和外部存储设备（比如移动硬盘、读卡器等），只保留键盘、鼠标。这样可以避免引导过程中出现干扰。\n插入 Linux Mint 启动 U 盘并开机，进入 BIOS/UEFI 设置界面，然后选择 U 盘引导。\n第 4 步：安装 Linux Mint 22 系统启动后，在 Grub 菜单中选择「Start Linux Mint」并按回车。 选择「Start Linux Mint」。\n桌面加载成功后，双击「Install Linux Mint」启动安装向导。 双击「Install Linux Mint」\n在欢迎界面选择「中文 (简体)」，然后点击「继续」。 选择系统语言\n4.1 选择「键盘布局」 在这里，系统已经自动检测出我使用的是「Chinese」键盘布局。如果没有检测出来或不正确，可以手动「探测键盘布局」或干脆手动指定。设置好后点击「继续」。\n选择「键盘布局」\n4.2 安装「多媒体编解码器」 在接下来的步骤中，推荐勾选「安装多媒体编码译码器」，然后单击「继续」。\n勾选「多媒体编解码器」\n4.3 选择「安装类型」 要进行全新安装，请选择「清除整个磁盘并安装 Linux Mint」，再点击「现在安装」。 对于有特殊需求的用户，可以通过「高级特性」： 在 Linux Mint 新安装中使用 LVM：LVM（逻辑卷管理）是一种工具，可以让你灵活管理磁盘空间，方便进行调整和扩展。 加密 Linux Mint 新安装以提高安全性：这个选项会对硬盘内容进行加密，你需要在下一步设置一个安全密钥来解密和使用磁盘。 选择「安装类型」\n如果要手动分区，请选择「其它选项」再单击「继续」。此时会打开一个新对话框，让你创建分区表和逻辑驱动器。下面会介绍如何对磁盘进行分区。 4.4 创建磁盘分区（可选） 双击设备列表中的磁盘，例如 dev/sda，或者在选中后点击「新建分区表」\u0026gt;「继续」。 新建分区表\n创建「EFI 系统分区」：这是 UEFI 系统的必选分区。它将保存由 UEFI 固件启动的 EFI 引导加载程序和驱动程序。选择「空闲」空间，然后点击添加「+」按钮，创建一个新的分区，并设置 EFI 分区属性： 大小：100 ~ 500 MB 新分区类型：主分区 新分区位置：空间起始位置 用于：EFI 系统分区 创建「EFI 系统分区」\n创建「SWAP 交换空间」：SWAP 在内存用尽还需要额外空间时才激活，它也会被用于休眠模式，允许你保存并恢复系统状态。选择「空闲」空间，然后点击添加「+」按钮，创建一个新的分区，并设置以下属性： 大小：500 MB 或更大，推荐与内存大小相同。 新分区类型：主分区 新分区位置：空间起始位置 用于：交换空间 创建「交换空间」\n创建「保留 BIOS 启动区域」：用于存储引导加载程序代码。选择「空闲」空间，然后点击添加「+」按钮，创建一个新的分区，并设置以下属性： 大小：1 MB 新分区类型：主分区 新分区位置：空间起始位置 用于：保留 BIOS 启动区域 创建「保留 BIOS 启动区域」\n创建「根分区」：选择「空闲」空间，然后点击添加「+」按钮，创建一个新的分区，并设置以下属性： 大小：所有剩余的空闲空间，最小应为 20 GB，但建议大于 100 GB。 新分区类型：主分区 新分区位置：空间起始位置 用于：Ext4 日志文件系统 挂载点：/ 创建「根分区」\n创建好所有分区后，单击「现在安装」。在弹出窗口中，单击「继续」查看并确认新的分区表设置。 点击「现在安装」\n确认分区并继续\n4.5 选择「当前时区」 接下来，就来到了时区设置，国内通常选择「上海」即可。选择好之后点击「继续」，你也可以在以后随时更改时区。\n选择「系统时区」\n4.6 创建用户账户 这一步想必不用解释，按你的实际情况填写好要创建的用户，然后点击「继续」。\n创建用户账户\n第 5 步：完成 Linux Mint 安装 接下来，Linux Mint 会正式开始复制文件并进行安装。 开始安装并复制文件\n安装完成后，点击「现在重启」。 安装好后点击「现在重启」\n重启完成后，就可以登录到 Linux Mint 22 桌面了。 Linux Mint 22 Cinnamon 桌面\n验证与自查 重启后能进入 Linux Mint 桌面，中文显示正常 lsblk 确认根分区（/）与 EFI 分区挂载正确 更新系统：sudo apt update \u0026amp;\u0026amp; sudo apt upgrade 无报错 检查驱动与网络：Wi-Fi、显卡、声音均正常 总结 Linux Mint 22 提供了强大的功能和友好的用户界面，无论是用于日常办公、娱乐，还是编程开发，都是一个不错的选择。通过以上步骤，只要按照指引一步步操作，小白用户也能轻松装好 Linux Mint 22 操作系统。\n安装完成后，建议进行这 10 项目基础优化，能让你的系统跑得更顺溜，用得更舒心。\n参考 Linux Mint 官方下载页 Linux Mint 安装指南（官方文档） Linux Mint 22 发布公告 ","permalink":"https://heihei0299.github.io/posts/linux/mint%E5%AE%89%E8%A3%85/","summary":"\u003ch1 id=\"linux-mint-22-安装指南\"\u003eLinux Mint 22 安装指南\u003c/h1\u003e\n\u003cp\u003e适用场景：想从 Windows 或 macOS 转投 Linux 阵营的新手，或需要一套稳定 LTS 桌面系统的用户。你会学到 Linux Mint 22 从下载镜像、制作启动 U 盘到分区安装的完整流程，以及各安装选项（LVM、全盘加密、手动分区）的取舍。\u003c/p\u003e","title":"Linux Mint 22 安装指南"},{"content":"Arch Linux KVM 镜像离线初始化指南 挂载 Arch 镜像 使用 qemu-nbd 连接镜像 加载模块：\nsudo modprobe nbd max_part=8 连接：\nsudo qemu-nbd \\ --connect=/dev/nbd0 \\ /var/lib/libvirt/images/arch 查看磁盘分区 lsblk /dev/nbd0 示例：\nNAME SIZE TYPE nbd0 2G disk ├─nbd0p1 1M part ├─nbd0p2 300M part └─nbd0p3 1.7G part 确认根分区 查看：\nblkid /dev/nbd0p* 通常：\n分区 用途 nbd0p1 GPT 保留 nbd0p2 EFI nbd0p3 Linux 根分区 挂载：\nsudo mount /dev/nbd0p3 /mnt 如果有 EFI：\nsudo mkdir -p /mnt/boot sudo mount /dev/nbd0p2 /mnt/boot 设置 root 密码 推荐使用 virt-customize，无需进入系统。\nsudo virt-customize \\ -a /var/lib/libvirt/images/arch \\ --root-password password:12 执行完成后，root 登录密码为：12。\n配置 systemd-networkd DHCP 创建 networkd 配置文件 文件路径：\n/etc/systemd/network/20-wired.network 内容：\n[Match] Type=ether [Network] DHCP=yes 不绑定网卡名称，自动匹配所有以太网设备，适合 KVM/libvirt 虚拟机。\n写入镜像 创建临时文件：\ncat \u0026gt;/tmp/20-wired.network \u0026lt;\u0026lt;EOF [Match] Type=ether [Network] DHCP=yes EOF 写入：\nsudo virt-customize \\ -a /var/lib/libvirt/images/arch \\ --write \\ /tmp/20-wired.network:/etc/systemd/network/20-wired.network 启用 systemd-networkd sudo virt-customize \\ -a /var/lib/libvirt/images/arch \\ --run-command \\ \u0026#39;systemctl enable systemd-networkd\u0026#39; 启用 DNS：\nsudo virt-customize \\ -a /var/lib/libvirt/images/arch \\ --run-command \\ \u0026#39;systemctl enable systemd-resolved\u0026#39; 修复 resolv.conf sudo virt-customize \\ -a /var/lib/libvirt/images/arch \\ --run-command \\ \u0026#39;rm -f /etc/resolv.conf\u0026#39; \\ --run-command \\ \u0026#39;ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf\u0026#39; 进入系统环境 sudo arch-chroot /mnt 修复 pacman keyring 遇到的问题 安装软件：\npacman -S fish 出现：\nwarning: Public keyring not found; have you run \u0026#39;pacman-key --init\u0026#39;? error: keyring is not writable error: required key missing from keyring 原因 镜像未初始化 pacman keyring keyring 文件损坏 镜像过旧 修复 初始化：\npacman-key --init 导入官方密钥：\npacman-key --populate archlinux 更新 keyring：\npacman -Sy archlinux-keyring 升级系统并安装软件 更新系统：\npacman -Syu 安装软件，例如 fish：\npacman -S fish locale 警告处理 进入 chroot 时可能出现：\nbash: warning: setlocale: LC_ALL: cannot change locale (zh_CN.UTF-8) 原因：locale 未生成。\n修复：编辑 /etc/locale.gen，取消注释：\n#zh_CN.UTF-8 UTF-8 改为：\nzh_CN.UTF-8 UTF-8 生成 locale：\nlocale-gen 卸载镜像 退出 chroot：\nexit 卸载文件系统：\nsudo umount -R /mnt 如果 busy：\nsudo umount -l /mnt 断开 nbd：\nsudo qemu-nbd \\ --disconnect \\ /dev/nbd0 卸载 nbd 模块（可选）：\nsudo modprobe -r nbd 检查确认 nbd0 已消失：\nlsblk 遇到的问题总结 问题 原因 解决 guestmount 下 pacman 下载失败 FUSE 写入异常 改用 qemu-nbd mount /dev/nbd0pX 失败 X 是占位符 使用真实分区 pacman keyring 不可写 keyring 未初始化 pacman-key \u0026ndash;init locale 警告 locale 未生成 locale-gen 虚拟机无网络 没有 networkd 配置 systemd-networkd DHCP root 无密码 镜像未初始化 virt-customize 设置密码 推荐初始化顺序 qemu-nbd 挂载镜像 virt-customize：设置 root 密码、写入 systemd-networkd 配置、enable networkd/resolved arch-chroot pacman-key 初始化 pacman -Syu 安装软件 卸载镜像 该流程适合制作 KVM/libvirt Arch Linux 基础镜像。\n","permalink":"https://heihei0299.github.io/posts/arch/arch-kvm-image-offline-init-guide/","summary":"\u003ch1 id=\"arch-linux-kvm-镜像离线初始化指南\"\u003eArch Linux KVM 镜像离线初始化指南\u003c/h1\u003e\n\u003ch2 id=\"挂载-arch-镜像\"\u003e挂载 Arch 镜像\u003c/h2\u003e\n\u003ch3 id=\"使用-qemu-nbd-连接镜像\"\u003e使用 qemu-nbd 连接镜像\u003c/h3\u003e\n\u003cp\u003e加载模块：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo modprobe nbd \u003cspan class=\"nv\"\u003emax_part\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"m\"\u003e8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e连接：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo qemu-nbd \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  --connect\u003cspan class=\"o\"\u003e=\u003c/span\u003e/dev/nbd0 \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  /var/lib/libvirt/images/arch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"查看磁盘分区\"\u003e查看磁盘分区\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003elsblk /dev/nbd0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e示例：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eNAME      SIZE TYPE\nnbd0       2G disk\n├─nbd0p1   1M part\n├─nbd0p2 300M part\n└─nbd0p3 1.7G part\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"确认根分区\"\u003e确认根分区\u003c/h3\u003e\n\u003cp\u003e查看：\u003c/p\u003e","title":"Arch Linux KVM 镜像离线初始化指南"},{"content":"SSH 登录方案 —— 使用 systemd user unit 管理 ssh-agent 背景 传统方式下，ssh-agent 通常由桌面环境或 shell 启动，依赖 X 会话。离开图形环境（如纯 TTY、SSH 跳板机）时，agent 不会自动运行，需要手动处理。\n从 OpenSSH 9.4p1-3 开始，openssh 包提供了原生的 ssh-agent.service 用户单元，由 systemd 接管生命周期，随用户登录启动，与 X 无关。\n配置步骤 1. 启用 ssh-agent 用户服务 systemctl --user enable --now ssh-agent.service 2. 设置环境变量 将 SSH_AUTH_SOCK 指向 systemd 管理的 socket。\nShell 配置（添加到 ~/.bashrc 或 ~/.zshrc）：\nexport SSH_AUTH_SOCK=\u0026#34;$XDG_RUNTIME_DIR/ssh-agent.socket\u0026#34; systemd 环境（确保 SSH_AUTH_SOCK 对 systemd user units 也可见）：\nmkdir -p ~/.config/environment.d 创建 ~/.config/environment.d/ssh-agent.conf：\nSSH_AUTH_SOCK=${XDG_RUNTIME_DIR}/ssh-agent.socket ~/.config/environment.d/*.conf 中的变量由 systemd 在用户登录时自动导入，所有 systemd user 服务都能继承该环境变量，无需额外操作。\n3. 自动添加密钥到 agent 编辑 ~/.ssh/config，全局启用自动添加：\nHost * AddKeysToAgent yes 这样，首次使用 SSH 连接时，私钥会自动加载到 agent，后续连接无需重复输入密码。\n验证 # 检查 agent 是否运行 systemctl --user status ssh-agent.service # 查看已加载的密钥 ssh-add -l 进阶方案 —— KeePassXC 代理 SSH Agent KeePassXC 是一款跨平台开源密码管理器，原生支持与 SSH Agent 集成。私钥以附件形式加密存储在数据库中，解锁数据库时自动加载到 agent，锁定或关闭时自动移除，实现按需使用。\n为什么用密码管理器管理 SSH 密钥 密钥文件默认存在 ~/.ssh/ 目录，一旦系统被入侵，私钥可能被窃取。即使设置了密码，每次使用都要手动输入，体验繁琐。\nKeePassXC 将密钥对加密存储在 KDBX 数据库中，使用时通过主密码解锁，用完后锁定即卸载密钥。既免去了密钥文件管理、重复输入密码的麻烦，又提高了安全性。\n保存私钥到 KeePassXC 创建 SSH 专用群组后，新建条目：\n条目标签：标题/用户名可任意填写（仅用于备忘），密码填入创建密钥时设置的密码 高级标签：附件区域点击「添加文件-从磁盘加载」，将公钥和私钥都添加上 SSH 代理标签： 勾选「在打开或解锁数据库时，向代理添加密钥」 勾选「数据库关闭或锁定时，从代理中删除密钥」 私钥区域选择附件中对应的私钥文件 Windows 不要勾选「使用此密钥时请求用户确认」（不支持该特性） 保存后，密钥便安全地托管在 KeePassXC 中。\n集成 SSH Agent 打开 KeePassXC 工具 → 设置 → SSH 代理：\n勾选「启用 SSH 代理集成」 代理类型选择「使用 OpenSSH」（推荐，Pageant 仅 PuTTY 生态可用） 保存后返回该页面，若显示 SSH 代理连接工作正常 则表示集成成功。Windows 下若提示 代理未在运行，需先启动 ssh-agent 服务。\n验证 锁定 KeePassXC，打开终端执行：\nssh-add -l # 输出: The agent has no identities. 解锁 KeePassXC 后再次执行，将显示已加载的密钥指纹。\n日常使用：SSH Config 配合公钥匹配 由于私钥不再保存在文件系统中，IdentityFile 需指向对应的公钥文件。SSH 会通过公钥指纹从 agent 中找到已加载的私钥。\nHost github.com HostName github.com User git IdentitiesOnly yes IdentityFile ~/.ssh/id_ed25519_github.pub Host myserver HostName 192.168.1.100 User ubuntu IdentitiesOnly yes IdentityFile ~/.ssh/id_ed25519_server.pub 参考 Arch Wiki: SSH keys KeePassXC SSH Agent FAQ ","permalink":"https://heihei0299.github.io/posts/arch/ssh%E7%99%BB%E5%BD%95%E6%96%B9%E6%A1%88/","summary":"\u003ch1 id=\"ssh-登录方案--使用-systemd-user-unit-管理-ssh-agent\"\u003eSSH 登录方案 —— 使用 systemd user unit 管理 ssh-agent\u003c/h1\u003e\n\u003ch2 id=\"背景\"\u003e背景\u003c/h2\u003e\n\u003cp\u003e传统方式下，\u003ccode\u003essh-agent\u003c/code\u003e 通常由桌面环境或 shell 启动，依赖 X 会话。离开图形环境（如纯 TTY、SSH 跳板机）时，agent 不会自动运行，需要手动处理。\u003c/p\u003e","title":"SSH 登录方案 —— 使用 systemd user unit 管理 ssh-agent"},{"content":"字体最终修复方案（Arch） 适用场景：Arch Linux + Niri + DMS 环境下，VSCode、Kitty、opencode 等终端出现黑块、断字、中文异常。你会学到\u0026quot;四层字体架构\u0026quot;的最终方案：系统层安装、fontconfig 全局 fallback、VSCode 与 Kitty 对齐配置，并掌握最终验证方法。\n下面给你一套\u0026quot;字体最终修复方案（Arch + Niri + DMS + VSCode + Kitty + opencode 全一致无黑块版）\u0026quot;。\n这套方案的目标只有一个：所有终端 / GUI / TUI / 编辑器字体完全统一，不再出现黑块、断字、中文异常。\n最终架构 字体不再是\u0026quot;一个字体解决全部\u0026quot;，而是 4 层：\n1. 英文/代码 -\u0026gt; JetBrainsMono Nerd Font Mono 2. 图标 -\u0026gt; Nerd Fonts Symbols Mono 3. 中文 -\u0026gt; Noto Sans CJK SC 4. Emoji -\u0026gt; Noto Color Emoji 系统级安装（必须） sudo pacman -S \\ ttf-jetbrains-mono-nerd \\ noto-fonts \\ noto-fonts-cjk \\ noto-fonts-emoji \\ ttf-nerd-fonts-symbols-mono fontconfig 全局统一（核心修复点） 编辑 ~/.config/fontconfig/fonts.conf，写入以下内容：\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE fontconfig SYSTEM \u0026#34;fonts.dtd\u0026#34;\u0026gt; \u0026lt;fontconfig\u0026gt; \u0026lt;!-- ========== monospace 主字体链 ========== --\u0026gt; \u0026lt;match target=\u0026#34;pattern\u0026#34;\u0026gt; \u0026lt;test name=\u0026#34;family\u0026#34;\u0026gt; \u0026lt;string\u0026gt;monospace\u0026lt;/string\u0026gt; \u0026lt;/test\u0026gt; \u0026lt;edit name=\u0026#34;family\u0026#34; mode=\u0026#34;prepend\u0026#34;\u0026gt; \u0026lt;string\u0026gt;JetBrainsMono Nerd Font Mono\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;Noto Sans Mono CJK SC\u0026lt;/string\u0026gt; \u0026lt;/edit\u0026gt; \u0026lt;/match\u0026gt; \u0026lt;!-- ========== sans 中文 fallback ========== --\u0026gt; \u0026lt;match target=\u0026#34;pattern\u0026#34;\u0026gt; \u0026lt;test name=\u0026#34;lang\u0026#34; compare=\u0026#34;contains\u0026#34;\u0026gt; \u0026lt;string\u0026gt;zh\u0026lt;/string\u0026gt; \u0026lt;/test\u0026gt; \u0026lt;edit name=\u0026#34;family\u0026#34; mode=\u0026#34;prepend\u0026#34;\u0026gt; \u0026lt;string\u0026gt;Noto Sans CJK SC\u0026lt;/string\u0026gt; \u0026lt;/edit\u0026gt; \u0026lt;/match\u0026gt; \u0026lt;!-- ========== emoji ========== --\u0026gt; \u0026lt;alias\u0026gt; \u0026lt;family\u0026gt;emoji\u0026lt;/family\u0026gt; \u0026lt;prefer\u0026gt; \u0026lt;family\u0026gt;Noto Color Emoji\u0026lt;/family\u0026gt; \u0026lt;/prefer\u0026gt; \u0026lt;/alias\u0026gt; \u0026lt;/fontconfig\u0026gt; VSCode（最容易出问题的地方） 打开 settings.json：\n\u0026#34;terminal.integrated.fontFamily\u0026#34;: \u0026#34;JetBrainsMono Nerd Font Mono, Symbols Nerd Font Mono, Noto Sans CJK SC, Noto Color Emoji\u0026#34; 关键额外设置（必须） \u0026#34;terminal.integrated.gpuAcceleration\u0026#34;: \u0026#34;off\u0026#34;, \u0026#34;terminal.integrated.fontLigatures\u0026#34;: true Kitty（对齐 VSCode） font_family JetBrainsMono Nerd Font Mono bold_font auto italic_font auto symbol_map U+4E00-U+9FFF Noto Sans CJK SC emoji_font Noto Color Emoji opencode / TUI 环境（避免断字符） 确保 locale 设置：\nexport LANG=zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8 export TERM=xterm-256color 已在 DMS environment.d 中配置，这里只做确认。\nDMS / Niri（你已经做对了） ~/.config/environment.d/90-dms.conf 内容：\nLANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 TERM=xterm-256color 这一步已经 OK，非常关键。\n最终验证方法 在所有环境运行：\necho \u0026#34;中文 test ABC    🌏 ▓▒░\u0026#34; 正常结果 中文：正常不方块 英文：等宽 图标：正常（无黑块） emoji：彩色 排版：对齐 如果仍然有黑块（最后兜底） fc-match monospace fc-cache -fv 并检查：\nfc-list | grep \u0026#34;JetBrainsMono\u0026#34; fc-list | grep \u0026#34;Noto Sans CJK\u0026#34; 最终总结 这套系统最终正确状态：\nDMS / systemd env -\u0026gt; 控制 LANG / TERM fontconfig -\u0026gt; 控制中文 fallback VSCode -\u0026gt; 控制 terminal glyph Kitty -\u0026gt; 对齐渲染 Noto CJK -\u0026gt; 中文兜底 Nerd Font -\u0026gt; 图标核心 一句话终极结论 字体问题的本质不是\u0026quot;缺字体\u0026quot;，而是 fontconfig + terminal + Nerd Font 没有统一 fallback 链。\n参考 ArchWiki - Font configuration Nerd Fonts 官方仓库 VSCode 终端外观文档 Kitty 官方文档 ","permalink":"https://heihei0299.github.io/posts/linux/%E5%AD%97%E4%BD%93%E6%9C%80%E7%BB%88%E4%BF%AE%E5%A4%8D%E6%96%B9%E6%A1%88arch/","summary":"\u003ch1 id=\"字体最终修复方案arch\"\u003e字体最终修复方案（Arch）\u003c/h1\u003e\n\u003cp\u003e适用场景：Arch Linux + Niri + DMS 环境下，VSCode、Kitty、opencode 等终端出现黑块、断字、中文异常。你会学到\u0026quot;四层字体架构\u0026quot;的最终方案：系统层安装、fontconfig 全局 fallback、VSCode 与 Kitty 对齐配置，并掌握最终验证方法。\u003c/p\u003e","title":"字体最终修复方案（Arch）"},{"content":"使用 sing-box 搭建 Shadowsocks over ShadowTLS 适用场景：需要抗主动探测的代理传输方式，用 sing-box 同时提供 ShadowTLS 伪装层与 Shadowsocks 入站。你会学到原理、安装、服务端配置与客户端接入。\n原理说明 这种方案本质上是通过 shadow-tls 传输 Shadowsocks 流量。\n客户端先与服务端建立 shadow-tls 连接。 然后在这条连接中继续传输 Shadowsocks 数据。 服务端解开 shadow-tls 后，再把数据交给本地的 Shadowsocks 入站处理。 安装 sing-box 官方安装文档： sing-box 安装说明\nDebian / Ubuntu bash \u0026lt;(curl -fsSL https://sing-box.app/deb-install.sh) CentOS bash \u0026lt;(curl -fsSL https://sing-box.app/rpm-install.sh) 服务端配置示例 { \u0026#34;log\u0026#34;: { \u0026#34;disabled\u0026#34;: false, \u0026#34;level\u0026#34;: \u0026#34;error\u0026#34;, \u0026#34;timestamp\u0026#34;: true }, \u0026#34;inbounds\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;shadowtls\u0026#34;, \u0026#34;tag\u0026#34;: \u0026#34;st-in\u0026#34;, \u0026#34;version\u0026#34;: 3, \u0026#34;listen\u0026#34;: \u0026#34;xx.xx.xx.xx\u0026#34;, \u0026#34;listen_port\u0026#34;: 50000, \u0026#34;detour\u0026#34;: \u0026#34;ss-in\u0026#34;, \u0026#34;users\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;sekai\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;111111\u0026#34; } ], \u0026#34;handshake\u0026#34;: { \u0026#34;server\u0026#34;: \u0026#34;www.tesla.com\u0026#34;, \u0026#34;server_port\u0026#34;: 443 }, \u0026#34;handshake_for_server_name\u0026#34;: { \u0026#34;example.com\u0026#34;: { \u0026#34;server\u0026#34;: \u0026#34;www.tesla.com\u0026#34;, \u0026#34;server_port\u0026#34;: 443 } }, \u0026#34;strict_mode\u0026#34;: false }, { \u0026#34;type\u0026#34;: \u0026#34;shadowsocks\u0026#34;, \u0026#34;tag\u0026#34;: \u0026#34;ss-in\u0026#34;, \u0026#34;listen\u0026#34;: \u0026#34;127.0.0.1\u0026#34;, \u0026#34;listen_port\u0026#34;: 50051, \u0026#34;method\u0026#34;: \u0026#34;aes-128-gcm\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;111111\u0026#34;, \u0026#34;multiplex\u0026#34;: {} } ], \u0026#34;outbounds\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;direct\u0026#34;, \u0026#34;tag\u0026#34;: \u0026#34;direct\u0026#34; } ] } 配置要点：\nshadowtls 入站的 detour 指向 ss-in，即把解包后的流量交给 Shadowsocks 入站处理。 handshake 的 server / server_port 是 TLS 握手伪装目标，请换成可达的合法站点。 strict_mode 为 false 时兼容性更好，true 时更严格但可能影响部分客户端。 Shadowsocks 入站只监听 127.0.0.1，不直接对外暴露。 Surge 配置参考 配置截图 1 配置截图 2 验证与自查 systemctl status sing-box 显示 active（running） ss -lntup | grep 50000 确认 ShadowTLS 入站端口监听正常 客户端（sing-box / Surge）用 ShadowTLS + Shadowsocks 配置连接成功，curl ip.sb 出口为服务端 IP journalctl -u sing-box -e 无 error 日志 参考 sing-box 安装说明 sing-box shadowtls 入站文档 shadow-tls（ihciah，GitHub） ","permalink":"https://heihei0299.github.io/posts/network/%E4%BD%BF%E7%94%A8sing-box%E6%90%AD%E5%BB%BAss-via-shadowtls/","summary":"\u003ch1 id=\"使用-sing-box-搭建-shadowsocks-over-shadowtls\"\u003e使用 sing-box 搭建 Shadowsocks over ShadowTLS\u003c/h1\u003e\n\u003cp\u003e适用场景：需要抗主动探测的代理传输方式，用 sing-box 同时提供 ShadowTLS 伪装层与 Shadowsocks 入站。你会学到原理、安装、服务端配置与客户端接入。\u003c/p\u003e\n\u003ch2 id=\"原理说明\"\u003e原理说明\u003c/h2\u003e\n\u003cp\u003e这种方案本质上是通过 \u003ccode\u003eshadow-tls\u003c/code\u003e 传输 \u003ccode\u003eShadowsocks\u003c/code\u003e 流量。\u003c/p\u003e","title":"使用 sing-box 搭建 Shadowsocks over ShadowTLS"},{"content":"Debian 11 Bullseye 升级 Debian 12 Bookworm 本文适合想把 Debian 11 VPS/服务器大版本升级到 Debian 12 的用户。你会学到：升级前的备份注意事项、替换软件源、处理 non-free-firmware 新组件，以及常见报错（dbus machine-id）的解决方法。\n本文将指导如何升级 Debian 11 Bullseye 到 Debian 12 Bookworm。\n相关教程：Debian 10 Buster 升级 Debian 11 Bullseye。\n准备工作 除非你是物理服务器，以及没有用过奇奇怪怪定制或修改的内核的 KVM 构架的 VPS 和云主机，否则升级大版本更新内核是有一定机率导致 Grub 加载失败的，切记备份重要数据！\nOpenVZ 6 和 LXC 构架的 VPS 是无法升级的，因为他们没有自己独立的内核\n再强调一遍，一定要备份重要数据！\n以下操作需要在 root 用户下完成，请使用 sudo -i 或 su root 切换到 root 用户进行操作\n更新系统 首先需要更新你当前的系统\napt update apt upgrade -y apt dist-upgrade -y apt autoclean apt autoremove -y 如果内核更新了，可以重启让最新的内核生效，也可以直接进行升级。\n升级系统 首先更新 apt 源，替换 bullseye 为 bookworm：\nsed -i \u0026#39;s/bullseye/bookworm/g\u0026#39; /etc/apt/sources.list sed -i \u0026#39;s/bullseye/bookworm/g\u0026#39; /etc/apt/sources.list.d/*.list 对于 Debian 12 以后的版本，所有 Debian 可以分发的打包的非自由固件二进制文件 (non-free)，比如某些驱动，都被转移到 Debian Archive 中的一个新组件，称为非自由固件 (non-free-firmware)。如果您从旧版的 Debian 升级，并且需要这些固件二进制文件，您应该更新您系统上的 /etc/apt/sources.list，以使用这个新组件 (来源)：\nsed -i \u0026#39;s/non-free/non-free non-free-firmware/g\u0026#39; /etc/apt/sources.list 默认的系统 apt 源文件 /etc/apt/sources.list 应该是类似这样的：\ndeb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware 大部分 Debian 的软件源配置文件使用传统的 One-Line-Style，路径为 /etc/apt/sources.list；但是对于容器镜像，从 Debian 12 开始，其软件源配置文件变更为 DEB822 格式，路径为 /etc/apt/sources.list.d/debian.sources:（参考）\nTypes: deb URIs: https://deb.debian.org/debian Suites: bookworm bookworm-updates bookworm-backports Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://security.debian.org/debian-security Suites: bookworm-security Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg 国内服务器可以替换 deb.debian.org 和 security.debian.org 为 mirrors.tuna.tsinghua.edu.cn\n然后我们再次执行更新系统：\napt update apt upgrade -y apt dist-upgrade -y 更新过程种会提示一些软件是否需要自动重启，选 Yes 即可，以及一些软件的配置文件是否需要更新，按照自己的情况选择即可，默认回车即视为使用旧的配置文件，一般会出现在 OpenSSH 等软件的更新上。\n在 apt-listchanges: News 界面可以按 q 退出：\n提示是否自动重启服务：\n提示是否更新 OpenSSH 配置文件：\n注意某些软件更新后可能会更新 systemd 服务配置，此时我们可以执行 systemctl daemon-reload 重新加载配置。\n如果升级的时候遇到了如下错误：\nSetting up dbus-daemon (1.14.6-1) ... UUID file \u0026#39;/var/lib/dbus/machine-id\u0026#39; should contain a hex string of length 32, not length 0, with no other text dpkg: error processing package dbus-daemon (--configure): installed dbus-daemon package post-installation script subprocess returned error exit status 1 dpkg: dependency problems prevent configuration of dbus: dbus depends on dbus-daemon (= 1.14.6-1); however: Package dbus-daemon is not configured yet. dpkg: error processing package dbus (--configure): dependency problems - leaving unconfigured Processing triggers for libc-bin (2.36-9) ... Errors were encountered while processing: dbus-daemon dbus E: Sub-process /usr/bin/dpkg returned an error code (1) 删除 /var/lib/dbus/machine-id 这个空文件后重新执行命令即可，升级 dbus 时会自动生成这个文件。\nrm -rf /var/lib/dbus/machine-id 更新后删除不必要的软件和依赖：\napt autoclean apt autoremove -y 然后我们使用 reboot 命令重启系统，耐心等待后，查看最新的系统版本：\nroot@debian ~ # cat /etc/debian_version 12.5 root@debian ~ # lsb_release -a No LSB modules are available. Distributor ID:\tDebian Description:\tDebian GNU/Linux 12 (bookworm) Release:\t12 Codename:\tbookworm root@debian ~ # uname -a Linux debian 6.1.0-21-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.90-1 (2024-05-03) x86_64 GNU/Linux 这时我们就已经更新到了最新的 Debian 12 Bookworm 和内核了。\n验证与自查 cat /etc/debian_version 输出为 12.x（如 12.5） lsb_release -a 的 Description 为 Debian GNU/Linux 12 (bookworm) uname -a 显示内核版本为 6.1.x 系列 执行 apt update 无报错，且 apt policy 可正常检索 bookworm 软件包 参考 Debian 官方升级指南 (DebianUpgrade) Debian 12 Bookworm Release Notes Debian 12 固件组件说明 软件源说明 mirrors.help ","permalink":"https://heihei0299.github.io/posts/ops/debian11%E5%8D%87%E7%BA%A7debian12/","summary":"\u003ch1 id=\"debian-11-bullseye-升级-debian-12-bookworm\"\u003eDebian 11 Bullseye 升级 Debian 12 Bookworm\u003c/h1\u003e\n\u003cp\u003e本文适合想把 Debian 11 VPS/服务器大版本升级到 Debian 12 的用户。你会学到：升级前的备份注意事项、替换软件源、处理 \u003ccode\u003enon-free-firmware\u003c/code\u003e 新组件，以及常见报错（dbus machine-id）的解决方法。\u003c/p\u003e","title":"Debian 11 Bullseye 升级 Debian 12 Bookworm"},{"content":"Loon 进阶配置示例 适用场景：把 Loon 从\u0026quot;能上网\u0026quot;升级为\u0026quot;按地区自动选路、故障自动转移\u0026quot;的进阶配置。这个文件保存了一份 Loon 进阶配置示例，适合作为分地区策略、故障转移与远程规则的参考模板。\n配置结构说明 这份配置按 Loon 的配置节组织，各节作用如下：\n[General]：基础参数。dns-server 指定国内 DNS（223.5.5.5、119.29.29.29）；resource-parser 使用 Sub-Store 的 Loon 解析器；geoip-url 使用国内 GeoIP 数据；skip-proxy / bypass-tun 声明不走代理的网段。 [Remote Filter]：节点过滤正则。通过节点名称正则把订阅节点分成 游戏/全球/香港/台湾/日本/韩国/新国/美国 八组，供策略组引用。 [Proxy Group]：策略组。分四层： SSID 场景策略（香港场景 等）：按 Wi-Fi SSID 或蜂窝网络选择策略，示例中 \u0026quot;PHICOMM_5G\u0026quot; = DIRECT 表示在家连该 Wi-Fi 时直连； 手动选择（香港策略 等）：select 类型，可在三种自动策略间手动切换； 自动策略：url-test 时延优选、fallback 故障转移、load-balance 负载均衡（pcc 算法）； 兜底切换：fallback 组合所有场景策略，作为 FINAL 兜底。 [Rule] / [Remote Rule]：分流规则。FINAL, 兜底切换 兜底；远程规则按服务（Telegram、Netflix、YouTube 等）指定策略，enabled 控制是否启用。 [Host]：域名映射，示例把 ssid:PHICOMM_5G 的网关映射为 10.0.0.1。 [Rewrite] / [Script] / [Plugin]：重写、脚本与插件（示例加载了 LoonGallery 与多个去广告插件）。 完整配置示例 # Loon 进阶配置 # Date: 2024-04-18 21:47:43 # Author: https://t.me/iKeLee [General] ipv6 = false dns-server = system, 223.5.5.5, 119.29.29.29 wifi-access-http-port = 7222 wifi-access-socks5-port = 7221 allow-wifi-access = false test-timeout = 2 interface-mode = auto sni-sniffing = true disable-stun = false fast-switch = false disconnect-on-policy-change = false switch-node-after-failure-times = 3 internet-test-url = http://223.6.6.6 proxy-test-url = http://1.0.0.1 resource-parser = https://gitlab.com/sub-store/Sub-Store/-/releases/permalink/latest/downloads/sub-store-parser.loon.min.js geoip-url = https://gitlab.com/Masaiki/GeoIP2-CN/-/raw/release/Country.mmdb skip-proxy = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, localhost, *.local, e.crashlynatics.com bypass-tun = 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.88.99.0/24, 192.168.0.0/16, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 255.255.255.255/32 [Proxy] [Remote Proxy] [Remote Filter] 游戏节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)游戏|🎮|(\\b(GAME)\\b)))(?!.*((?i)回国|校园)).*$\u0026#34; 全球节点 = NameRegex, FilterKey = \u0026#34;^(?=.*(.))(?!.*((?i)群|邀请|返利|循环|官网|客服|网站|网址|获取|订阅|流量|到期|机场|下次|版本|官址|备用|过期|已用|联系|邮箱|工单|贩卖|通知|倒卖|防止|国内|地址|频道|无法|说明|使用|提示|特别|访问|支持|(\\b(USE|USED|TOTAL|EXPIRE|EMAIL|Panel)\\b|(\\d{4}-\\d{2}-\\d{2}|\\dG)))).*$\u0026#34; 香港节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)🇭🇰|香港|(\\b(HK|Hong)\\b)))(?!.*((?i)回国|校园|游戏|🎮|(\\b(GAME)\\b))).*$\u0026#34; 台湾节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)🇹🇼|台湾|(\\b(TW|Tai|Taiwan)\\b)))(?!.*((?i)回国|校园|游戏|🎮|(\\b(GAME)\\b))).*$\u0026#34; 日本节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)🇯🇵|日本|川日|东京|大阪|泉日|埼玉|(\\b(JP|Japan)\\b)))(?!.*((?i)回国|校园|游戏|🎮|(\\b(GAME)\\b))).*$\u0026#34; 韩国节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)🇰🇷|韩国|韓|首尔|(\\b(KR|Korea)\\b)))(?!.*((?i)回国|校园|游戏|🎮|(\\b(GAME)\\b))).*$\u0026#34; 新国节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)🇸🇬|新加坡|狮|(\\b(SG|Singapore)\\b)))(?!.*((?i)回国|校园|游戏|🎮|(\\b(GAME)\\b))).*$\u0026#34; 美国节点 = NameRegex, FilterKey = \u0026#34;^(?=.*((?i)🇺🇸|美国|波特兰|达拉斯|俄勒冈|凤凰城|费利蒙|硅谷|拉斯维加斯|洛杉矶|圣何塞|圣克拉拉|西雅图|芝加哥|(\\b(US|United States)\\b)))(?!.*((?i)回国|校园|游戏|🎮|(\\b(GAME)\\b))).*$\u0026#34; [Proxy Group] # -------------------------------- # SSID地区场景策略 香港场景 = ssid, default = 香港策略, cellular = 香港策略, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 台湾场景 = ssid, default = 台湾策略, cellular = 台湾策略, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 日本场景 = ssid, default = 日本策略, cellular = 日本策略, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 韩国场景 = ssid, default = 韩国策略, cellular = 韩国策略, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 新国场景 = ssid, default = 新国策略, cellular = 新国策略, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 美国场景 = ssid, default = 美国策略, cellular = 美国策略, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 游戏场景 = ssid, default = 游戏手动, cellular = 游戏手动, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png 手动场景 = ssid, default = 全球手动, cellular = 全球手动, \u0026#34;PHICOMM_5G\u0026#34; = DIRECT, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SSID.png # -------------------------------- # 手动选择地区策略 香港策略 = select, 香港时延优选, 香港故障转移, 香港负载均衡, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/HK.png 台湾策略 = select, 台湾时延优选, 台湾故障转移, 台湾负载均衡, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/TW.png 日本策略 = select, 日本时延优选, 日本故障转移, 日本负载均衡, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/JP.png 韩国策略 = select, 韩国时延优选, 韩国故障转移, 韩国负载均衡, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/KR.png 新国策略 = select, 新国时延优选, 新国故障转移, 新国负载均衡, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SG.png 美国策略 = select, 美国时延优选, 美国故障转移, 美国负载均衡, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/US.png # -------------------------------- # 自动 # 时延优选 香港时延优选 = url-test, 香港节点, url = http://1.0.0.1, interval = 60, tolerance = 50, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/HK.png 台湾时延优选 = url-test, 台湾节点, url = http://1.0.0.1, interval = 60, tolerance = 50, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/TW.png 日本时延优选 = url-test, 日本节点, url = http://1.0.0.1, interval = 60, tolerance = 50, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/JP.png 韩国时延优选 = url-test, 韩国节点, url = http://1.0.0.1, interval = 60, tolerance = 50, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/KR.png 新国时延优选 = url-test, 新国节点, url = http://1.0.0.1, interval = 60, tolerance = 50, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SG.png 美国时延优选 = url-test, 美国节点, url = http://1.0.0.1, interval = 60, tolerance = 50, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/US.png # -------------------------------- # 故障转移 香港故障转移 = fallback, 香港节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/HK.png 台湾故障转移 = fallback, 台湾节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/TW.png 日本故障转移 = fallback, 日本节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/JP.png 韩国故障转移 = fallback, 韩国节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/KR.png 新国故障转移 = fallback, 新国节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SG.png 美国故障转移 = fallback, 美国节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/US.png # -------------------------------- # 负载均衡 香港负载均衡 = load-balance, 香港节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, algorithm = pcc, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/HK.png 台湾负载均衡 = load-balance, 台湾节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, algorithm = pcc, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/TW.png 日本负载均衡 = load-balance, 日本节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, algorithm = pcc, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/JP.png 韩国负载均衡 = load-balance, 韩国节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, algorithm = pcc, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/KR.png 新国负载均衡 = load-balance, 新国节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, algorithm = pcc, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/SG.png 美国负载均衡 = load-balance, 美国节点, url = http://1.0.0.1, interval = 60, max-timeout = 2000, algorithm = pcc, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/US.png # -------------------------------- # 兜底策略故障转移 兜底切换 = fallback, 香港场景, 台湾场景, 日本场景, 韩国场景, 新国场景, 美国场景, url = http://1.0.0.1, interval = 60, max-timeout = 2000, img-url = https://github.com/shindgewongxj/WHATSINStash/raw/main/icon/fallback.png # -------------------------------- # 手动 游戏手动 = select, 游戏节点, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/GAME.png 全球手动 = select, 全球节点, img-url = https://raw.githubusercontent.com/Orz-3/mini/master/Color/Global.png [Rule] FINAL, 兜底切换 [Remote Rule] https://adrules.top/adrules.list, policy = REJECT, tag = AdRules, enabled = false https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Telegram/Telegram.list, policy = 兜底切换, tag = Telegram, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/TikTok.list, policy = 全球手动, tag = TikTok, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/AI.list, policy = 全球手动, tag = AI, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/ApplePushNotificationService.list, policy = DIRECT, tag = Apple Push Notification Service, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/AppleID.list, policy = DIRECT, tag = Apple ID, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/AppStore.list, policy = DIRECT, tag = App Store, enabled = false https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/GitHub/GitHub.list, policy = 兜底切换, tag = GitHub, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Netflix/Netflix.list, policy = 新国场景, tag = Netflix, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/YouTube/YouTube.list, policy = 兜底切换, tag = Youtube, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Disney/Disney.list, policy = 兜底切换, tag = Disney, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Twitter/Twitter.list, policy = 兜底切换, tag = Twitter, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Facebook/Facebook.list, policy = 兜底切换, tag = Facebook, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Instagram/Instagram.list, policy = 美国场景, tag = Instagram, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Spotify/Spotify.list, policy = 美国场景, tag = Spotify, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/Google/Google.list, policy = 兜底切换, tag = Google, enabled = true https://gitlab.com/lodepuly/proxy_tool_resources/-/raw/master/rule/Loon/OneDrive/OneDrive.list, policy = 兜底切换, tag = OneDrive, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/LAN, policy = DIRECT, tag = LAN, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Rule/CN_REGION, policy = DIRECT, tag = CN REGION, enabled = true [Host] ssid:PHICOMM_5G = server:10.0.0.1 [Rewrite] [Script] [Plugin] https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/LoonGallery.plugin, policy = 兜底切换, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/Cainiao_remove_ads.plugin, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/CaixinMedia_remove_ads.plugin, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/CoolApk_remove_ads.plugin, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/Taobao_remove_ads.plugin, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/Tieba_remove_ads.plugin, enabled = true https://gitlab.com/lodepuly/vpn_tool/-/raw/master/Tool/Loon/Plugin/JD_remove_ads.plugin, enabled = true 使用要点 各节点的 NameRegex 正则需与你的订阅节点命名匹配，不匹配的分组会为空，对应策略组无节点可选。 远程规则中的 enabled = false（如 AdRules、App Store）表示默认不启用，需要时改为 true。 SSID 场景策略中的 \u0026quot;PHICOMM_5G\u0026quot; = DIRECT 是作者家庭 Wi-Fi 示例，按你自己的场景增删。 resource-parser 依赖 Sub-Store 服务，未使用 Sub-Store 时可移除该行。 插件（Plugin）按需保留，不需要的去广告插件可整行删除。 验证与自查 在 Loon 中导入配置后无语法报错，各策略组能显示节点数量 每个地区策略组执行一次测速（延迟测试），url-test 组能自动选出最低延迟节点 访问 Netflix / YouTube，在 Loon 的日志中确认命中对应规则且走预期策略（新国场景 / 兜底切换） 断开当前 Wi-Fi 或切换蜂窝网络，确认 SSID 场景策略按 default / cellular 生效 参考 Loon 官方文档 Sub-Store（GitHub） lodepuly/vpn_tool（规则与插件资源，GitLab） ","permalink":"https://heihei0299.github.io/posts/network/loon%E8%BF%9B%E9%98%B6%E9%85%8D%E7%BD%AE%E7%A4%BA%E4%BE%8B/","summary":"\u003ch1 id=\"loon-进阶配置示例\"\u003eLoon 进阶配置示例\u003c/h1\u003e\n\u003cp\u003e适用场景：把 Loon 从\u0026quot;能上网\u0026quot;升级为\u0026quot;按地区自动选路、故障自动转移\u0026quot;的进阶配置。这个文件保存了一份 Loon 进阶配置示例，适合作为分地区策略、故障转移与远程规则的参考模板。\u003c/p\u003e","title":"Loon 进阶配置示例"},{"content":"Linux Mint 安装 fcitx5 适用场景：Linux Mint（或其他 Ubuntu 系发行版）需要中文拼音输入法。你会学到 Fcitx 5 的最小安装（主程序 + 引擎 + 图形界面）、词库安装、im-config 设置默认输入法、环境变量与开机自启配置，以及 JetBrains IDE 输入框错位、iBus 冲突两个已知问题的处理。\n安装 fcitx5（配置中文输入法） 检查系统中文环境 在 Ubuntu 设置中打开「区域与语言」—— 「管理已安装的语言」，然后会自动检查已安装语言是否完整。若不完整，根据提示安装即可。\n检查可用的语言支持。\n最小安装 为使用 Fcitx 5，需要安装三部分基本内容：\nFcitx 5 主程序 中文输入法引擎 图形界面相关 按照这个思路，可以直接使用 apt 进行安装：\nsudo apt install fcitx5 \\ fcitx5-chinese-addons \\ fcitx5-frontend-gtk4 fcitx5-frontend-gtk3 fcitx5-frontend-gtk2 \\ fcitx5-frontend-qt5 安装中文词库 在 GitHub 打开维基百科中文拼音词库的 Releases 界面，下载最新版的 .dict 文件。按照 README 的指导，将其复制到 ~/.local/share/fcitx5/pinyin/dictionaries/ 文件夹下即可。\n# 下载词库文件 wget https://github.com/felixonmars/fcitx5-pinyin-zhwiki/releases/download/0.2.4/zhwiki-20220416.dict # 创建存储目录 mkdir -p ~/.local/share/fcitx5/pinyin/dictionaries/ # 移动词库文件至该目录 mv zhwiki-20220416.dict ~/.local/share/fcitx5/pinyin/dictionaries/ 配置 设置为默认输入法 使用 im-config 工具可以配置首选输入法，在任意命令行输入：\nim-config 根据弹出窗口的提示，将首选输入法设置为 Fcitx 5 即可。\n环境变量 需要为桌面会话设置环境变量，即将以下配置项写入某一配置文件中：\nexport XMODIFIERS=@im=fcitx export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx 如果使用 Bash 作为 shell，则建议写入至 ~/.bash_profile，这样只对当前用户生效，而不影响其他用户。\n另一个可以写入此配置的文件为系统级的 /etc/profile。\n将配置写入到 /etc/profile 文件末尾。\n开机自启动 安装 Fcitx 5 后并没有自动添加到开机自启动中，每次开机后需要手动在应用程序中找到并启动，非常繁琐。\n解决方案非常简单，在 Tweaks（sudo apt install gnome-tweaks）中将 Fcitx 5 添加到「开机启动程序」列表中即可。\n将 Fcitx5 添加到开机启动程序列表中。\nFcitx 配置 Fcitx 5 提供了一个基于 Qt 的强大易用的 GUI 配置工具，可以对输入法功能进行配置。有多种启动该配置工具的方法：\n在应用程序列表中打开「Fcitx 配置」 在 Fcitx 托盘上右键打开「设置」 命令行命令 fcitx5-configtool 根据个人偏好进行设置即可。需要注意的是「输入法」标签页下，应将「键盘 - 英语」放在首位，拼音（或其他中文输入法）放在后面的位置。\nFcitx5 configtool。\n自定义主题 Fcitx 5 默认的外观比较朴素，用户可以根据喜好使用自定义主题。\n第一种方式为使用经典用户界面，可以在 GitHub 搜索主题，然后在 Fcitx5 configtool —— 「附加组件」 —— 「经典用户界面」中设置即可。\n第二种方式为使用 Kim 面板，一种基于 DBus 接口的用户界面。此处安装了 Input Method Panel 这个 GNOME 扩展，黑色的风格与正在使用的 GNOME 主题 Orchis-dark 非常搭配。\nInput Method Panel 效果。\n已知问题 修复 JetBrains 系 IDE 显示问题 在 JetBrains 系 IDE（如 PyCharm）中，输入法选择框的位置始终固定于屏幕左下角，而非随输入光标移动，在中文输入很不方便。该问题为 IDE 的 JetBrainsRuntime 缺陷所致。可尝试使用 RikudouPatrickstar/JetBrainsRuntime-for-Linux-x64 这个仓库发布的 JBR 文件解决。\niBus 与 Fcitx 5 冲突：卸载后输入法失效 出于精简空间和减少冲突干扰之考虑，使用\nsudo apt remove ibus 卸载了 iBus，但重启（使生效）之后发现 Fcitx 5 受到了影响。具体表现为：除在终端中之外，其他输入场景无法切换至中文输入。使用 apt 装回 iBus，再次重启即又恢复正常。\n检查包依赖关系，卸载 ibus 包后会自动移除 ibus-data、ibus-gtk4、python3-ibus-1.0 三个包，似乎都只是与 iBus 紧密联系的。暂为不解之谜。\n验证与自查 fcitx5-configtool 中「键盘 - 英语」在首位、拼音在后 任意输入框按 Ctrl+Space 可切换中英文，候选词正常 重启后 fcitx5 托盘图标自动出现（开机自启生效） pgrep -a fcitx5 有进程输出 经验总结 本次折腾中的几点感慨：\n谨慎缓慢更新：像主力机操作系统这样重要的东西，应至少等待数月再跟进 善用官方文档，中文博客仅供参考：每台机器的环境都有所不同，每个用户的喜好也有差异，其他人的安装记录帖可以参考（包括本文），但还是要以官方文档（大概率是英文，阅读稍累一些）为准 参考 官方 Wiki —— 安装 Fcitx5 官方 Wiki —— Setup Fcitx 5 官方 Wiki —— 定制主题 Ubuntu20.04 安装 fcitx5 输入法 ","permalink":"https://heihei0299.github.io/posts/linux/linux-mint%E5%AE%89%E8%A3%85fictx5/","summary":"\u003ch1 id=\"linux-mint-安装-fcitx5\"\u003eLinux Mint 安装 fcitx5\u003c/h1\u003e\n\u003cp\u003e适用场景：Linux Mint（或其他 Ubuntu 系发行版）需要中文拼音输入法。你会学到 Fcitx 5 的最小安装（主程序 + 引擎 + 图形界面）、词库安装、im-config 设置默认输入法、环境变量与开机自启配置，以及 JetBrains IDE 输入框错位、iBus 冲突两个已知问题的处理。\u003c/p\u003e","title":"Linux Mint 安装 fcitx5"},{"content":"Syncthing：Linux 文件同步神器 本文适合有多台 NAS/电脑、希望实时同步文件并做异地备份的用户。你会学到：Syncthing 在 Windows/macOS/Linux/Android 的安装方式，单向备份与双向同步两种使用场景的配置步骤，以及常见问题（inotify 限制、群晖权限）的解决方法。\n自从在学校和家里都放上了NAS之后，由于网络的问题，从学校远程访问家中/从家中远程访问学校总会遇到速度的限制。迫于无奈，使用文件同步来实时同步两台NAS上的文件，不仅起到加速访问的功能，还能够保证数据的异地备份。\n下载和安装 Syncthing在开发之际确实超前地考虑到全平台的通用性，因此采用了浏览器/服务器（B/S）架构以适应绝大多数操作系统和体系结构。但是，这样设计的代价是易用性的部分缺失，你将会看到下面介绍的部分非Base Syncthing的\u0026quot;修改\u0026quot;版本（SyncTrayzor、syncthing-macos等）都是为了解决例如_开机自启动_、后台运行、_避免使用WebGUI_等问题而开发。因此在下一步开始时，你可以根据自身的情况下选择合适的版本。\nSyncthing支持的操作系统和硬件架构很多，这里只介绍两个常用的平台：Windows、MacOS、Linux和Android。\nBase Syncthing安装完毕后默认的Web访问端口为8384，默认_仅_可以使用127.0.0.1:8384访问WebGUI。而自带桌面GUI的版本则不需要使用浏览器操作。\nWindows Windows端直接在官方网站下载Base Syncthing安装即可，安装完后点击启动。\nWindows平台还有SyncTrayzor可以使用，他是自带图形界面而非基本包的WebGUI。\nmacOS macOS推荐安装**syncthing-macos**。和上述的Windows一样，这个版本支持桌面GUI而非基本包的WebGUI。\nLinux 根据你的发行版，如果发行版为Debian系，那么可以使用添加apt源的方式安装。当然你也可以使用源码自行编译，不过这不属于本教程的内容。:-(\n目前Debian的发行版官方apt仓库中有Syncthing，但是版本较低，不建议使用。\n安装发布密钥\nsudo curl -o /usr/share/keyrings/syncthing-archive-keyring.gpg https://syncthing.net/release-key.gpg 添加apt：稳定发布渠道\necho \u0026#34;deb [signed-by=/usr/share/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable\u0026#34; | sudo tee /etc/apt/sources.list.d/syncthing.list *[可选]*添加apt：优先发布渠道\necho \u0026#34;deb [signed-by=/usr/share/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing candidate\u0026#34; | sudo tee /etc/apt/sources.list.d/syncthing.list 更新apt源\nsudo apt-get update -y sudo apt-get install syncthing -y 启动Syncthing\n可以直接使用命令启动。注意，Syncthing不需要以root权限运行，也不建议使用root权限运行。\nsyncthing 如此启动后，Syncthing会在该控制台输出日志信息。\n也可以按照以下说明加入系统服务以开机自启动。\nsudo systemctl enable syncthing-resume.service Android 该平台直接从Google Play上搜索下载即可，国内用户可以从F-Droid平台下载apk安装包。\n软件界面 0. WebGUI地址设置 在首次启动Base Syncthing时会发现，非本机无法访问Syncthing。因此需要访问位于服务器上的Syncthing（无法直接使用127.0.0.1本机访问）时，需要对Syncthing进行IP和端口的配置。在配置之前，需要使用当前用户预先运行Syncthing一次，以生成默认配置文件。\n打开~/.config/syncthing/config.xml，找到约第32行\u0026lt;address\u0026gt;127.0.0.1:8384\u0026lt;/address\u0026gt;，将其中的127.0.0.1:8384修改为0.0.0.0:端口，保存后重启Syncthing生效。这样就可以使WebGUI监听来自任何地址的访问了。\n1. 匿名使用报告 首次进入，会弹出匿名使用的许可，根据自己的意愿选择即可。\n2. 主界面 主界面十分简洁。\n左边为所有参与同步的文件夹。刚刚安装完成的Syncthing中，其已经默认添加了一个文件夹。\n右边为当前设备的信息，包括：\n上传、下载速度 本地链路状况 资源使用率 侦听和设备发现 当前版本 右下角为远程设备列表，展示了所有已经和本机同步的设备。\n使用场景 1. 单向备份 假设现在在公司有1台主机，其中有多个工作文件夹比较重要，想要备份在家里的NAS服务器上以备不时之需（当然实际情况下公司的文件不能带出公司，要好好遵守公司保密规章）。\n要求：A主机上的文件夹备份到B主机上，同时修改A上的文件会影响到B主机上的文件，但是B主机上修改文件不会影响到A主机。\n查找两台主机的ID\n在任一角色的主机上，点击右上角的操作-显示ID，显示一串类似XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX的主机唯一ID和一个二维码。如果使用手机端的Syncthing则可以直接扫描二维码，否则将上述的ID复制保存备用。\n将ID输入到另一台主机\n点击右下角添加远程设备，将上述的主机ID填入，即可完成设备的添加。\n添加共享文件夹\n在文件当前存储的主机上，点击文件夹下的添加文件夹。输入文件夹标签和文件夹路径。在高级选项卡，将文件夹类型改为仅发送（在其它设备中对该文件夹内文件的修改并不会被同步到本机，但是在本机上对其的修改，则会被同步到集群中的其它设备）。\n接受文件夹共享\n在需要接收文件夹主机的操作界面上会自动提示请求共享的文件夹，许可并设置接收文件夹位置，并在高级选项卡，将文件夹类型改为仅接收（文件将从集群同步，但本地所作的任何更改都不会被发送到其他设备）。\n2. 双向同步 有那么一段时间，OneDrive、DropBox等网盘在国内使用会有一些速度上的问题，甚至是连接性问题。那么我们就设想使用Syncthing的同步功能来打造离线同步\u0026quot;网盘\u0026quot;。虽然没有云端功能，但是同步可以借助Syncthing轻松做到。\n要求：多台电脑上的同一文件夹需要在多台主机上进行同步，同时对任意一台主机上的修改需要同步到另外的所有主机上，亦即，保持各设备某个/多个文件夹内容的同一性。\n添加多台主机ID\n同上一个场景，对每台主机添加其他设备的ID。但是如果设备过多，配置网状结构的时间开销将会很大。因此，我们可以开启Syncthing的作为中介，以通过某一主机来共享具有同一文件夹的主机ID，这样只要具有相同的共享文件夹，主机之间就可以通过某一主机发现并自动添加到主机列表中，从而自动地形成网状同步网络。\n进入添加远程设备，输入想要作为中介的主机ID（此主机在初始配置时需要能够被发现），并在共享选项卡中，勾选作为中介（将此新设备上拥有的\u0026quot;远程设备\u0026quot;都自动添加到您这边的\u0026quot;远程设备\u0026quot;列表中（如果它们跟您存在相同的文件夹的话）），以开启远程设备共享。\n选择需要共享的文件夹\n在拥有需要同步的文件夹的主机上，配置共享文件夹，方法上同。在高级选项卡，将文件夹类型改为发送与接收。\n在其余的主机上同意同步文件夹\n方法上同。在高级选项卡，将文件夹类型改为发送与接收。\n常见问题 1. inotify 限制报错 我在Linux系统上使用，总是提示我failed to setup inotify handler. Please increase inotify limits.\n使用crontab，添加如下命令\nsh -c \u0026#39;(sleep 90 \u0026amp;\u0026amp; echo 204800 \u0026gt; /proc/sys/fs/inotify/max_user_watches)\u0026amp;\u0026#39; 在每次开机时执行即可。\n2. 群晖没有权限读取文件夹 我在群晖系统上安装了Syncthing，但是没有权限读取我的文件夹怎么办？\n群晖的应用账户需要手动设置读写权限。在群晖的共享文件夹中修改内置用户的权限即可。\n验证与自查 两台设备互加 ID 后，WebGUI 的远程设备列表中对方状态变为\u0026quot;已连接\u0026quot; 在 A 主机文件夹中新建/修改文件，B 主机对应目录自动出现该文件 单向备份场景中，在 B（仅接收）主机修改文件不会回传到 A 双向同步场景中，任意主机修改文件，其余主机都能收到更新 参考 Syncthing 官方文档 Syncthing GitHub Releases Syncthing apt 仓库 syncthing-android (F-Droid) ","permalink":"https://heihei0299.github.io/posts/ops/syncthing-linux%E6%AD%A5%E7%A5%9E%E5%99%A8/","summary":"\u003ch1 id=\"syncthinglinux-文件同步神器\"\u003eSyncthing：Linux 文件同步神器\u003c/h1\u003e\n\u003cp\u003e本文适合有多台 NAS/电脑、希望实时同步文件并做异地备份的用户。你会学到：Syncthing 在 Windows/macOS/Linux/Android 的安装方式，单向备份与双向同步两种使用场景的配置步骤，以及常见问题（inotify 限制、群晖权限）的解决方法。\u003c/p\u003e","title":"Syncthing：Linux 文件同步神器"},{"content":"使用 acme.sh 申请 Google 公共证书 适用场景：需要免费 HTTPS 证书且希望由 Google 签发（不依赖 Let\u0026rsquo;s Encrypt）。本文将带你走完从开通 Google Public CA API、获取 EAB 密钥，到用 acme.sh 签发并安装 RSA/ECC 证书的全部流程。\n首发时间：2022-04-19，更新日期：2023-12-16（Google Public CA 已于 2023 年 5 月结束内测）。\n证书特点 Google Public CA 支持通过 ACME 协议自动签发证书，主要特点如下：\n支持多域名与通配符证书。 支持 ECC 和 RSA 证书。 支持 IP 证书，但需要对应 IP Block 的所有者完成验证。 证书最长有效期为 90 天。 不支持 IDN 域名。 开通 Google Public CA API 首先需要拥有一个 Google Cloud Platform 账号。\n申请内测功能 Google Public CA 已于 2023 年 5 月结束内测，普通 GCP 账号即可直接使用 GTS ACME API，这一步现在通常可以跳过。\n早期如果未开通，执行相关命令可能会出现如下报错：\n(gcloud.alpha.publicca.external-account-keys.create) NOT_FOUND: Method not found. 获取 EAB 密钥 ID 和 HMAC 启用 Public Certificate Authority API： gcloud services enable publicca.googleapis.com 选择当前项目： gcloud config set project [project ID] 创建外部账户密钥： gcloud publicca external-account-keys create 成功后会返回如下格式的信息，后续注册 ACME 账户时会用到：\nCreated an external account key [b64MacKey: xxxxxxxxxxxxxxxxxxxxxxx keyId: xxxxxxxxxxxxxxx] 注意：EAB 密钥通常有有效期，建议申请后尽快使用。\n申请证书 默认以 root 用户演示。\n安装 acme.sh curl https://get.acme.sh | sh 如果后续提示 command not found，可以执行：\nsource ~/.bashrc 配置 Cloudflare API 如果你使用 Cloudflare 做 DNS 验证，可先导出对应环境变量：\nexport CF_Token=\u0026#34;xxxxxxxxxxxxxxxxxxxxxxxx\u0026#34; export CF_Account_ID=\u0026#34;xxxxxxxxxxxxxxxxxxxxxxxx\u0026#34; export CF_Zone_ID=\u0026#34;xxxxxxxxxxxxxxxxxxxxxxxx\u0026#34; 注册 Google ACME 账户 acme.sh --register-account -m [邮箱] --server google \\ --eab-kid [申请到的 keyId] \\ --eab-hmac-key [申请到的 b64MacKey] 如果出现 DNS 或连接错误，可先升级 acme.sh：\nacme.sh --upgrade 也可以手动指定服务端地址：\nacme.sh --register-account -m [邮箱] \\ --server https://dv.acme-v02.api.pki.goog/directory \\ --eab-kid [申请到的 keyId] \\ --eab-hmac-key [申请到的 b64MacKey] 设置默认 CA acme.sh --set-default-ca --server google 签发证书 签发 RSA 证书：\nacme.sh --issue --dns dns_cf -d example.com -d *.example.com --keylength 2048 签发 ECC 证书：\nacme.sh --issue --dns dns_cf -d example.com -d *.example.com --keylength ec-256 安装到 Nginx 安装 RSA 证书：\nacme.sh --install-cert -d example.com \\ --key-file /path/to/ssl/private.key \\ --fullchain-file /path/to/ssl/fullchain.pem \\ --capath /path/to/ssl/ca.pem \\ --reloadcmd \u0026#34;service nginx reload\u0026#34; 安装 ECC 证书：\nacme.sh --install-cert -d example.com --ecc \\ --key-file /path/to/ssl/ecc_private.key \\ --fullchain-file /path/to/ssl/ecc_fullchain.pem \\ --capath /path/to/ssl/ecc_ca.pem \\ --reloadcmd \u0026#34;service nginx reload\u0026#34; 验证与自查 acme.sh --list 能看到已签发的证书记录 openssl x509 -in /path/to/ssl/fullchain.pem -noout -issuer -dates 检查签发者（应为 Google Trust Services）与有效期 curl -I https://example.com 返回 200，浏览器地址栏证书有效 参考 acme.sh 官方文档（GitHub） Google Public CA 官方文档 Let\u0026rsquo;s Encrypt：ACME 协议说明 ","permalink":"https://heihei0299.github.io/posts/network/acme-sh%E7%94%B3%E8%AF%B7google%E5%85%AC%E5%85%B1%E8%AF%81%E4%B9%A6/","summary":"\u003ch1 id=\"使用-acmesh-申请-google-公共证书\"\u003e使用 acme.sh 申请 Google 公共证书\u003c/h1\u003e\n\u003cp\u003e适用场景：需要免费 HTTPS 证书且希望由 Google 签发（不依赖 Let\u0026rsquo;s Encrypt）。本文将带你走完从开通 Google Public CA API、获取 EAB 密钥，到用 acme.sh 签发并安装 RSA/ECC 证书的全部流程。\u003c/p\u003e","title":"使用 acme.sh 申请 Google 公共证书"},{"content":"使用 acme.sh 自动续签 SSL 证书 本文适合不想手动申请证书、希望免费证书自动签发与续期的站长。你会学到：acme.sh 的安装、CA 选择，以及 HTTP 和 DNS 两种验证方式下签发并安装证书到 Nginx/Apache 的完整流程。\n本文将介绍使用 acme.sh 配置自动续签的 SSL 证书。\n之前介绍了 Nginx 和 Apache 手工配置 SSL 证书的方法，美中不足的是，基本上大多数商业 SSL 证书都需要手工申请和签发，能支持 ACME 自动签发的并不多，有也略贵，比如 ZeroSSL 高级版和 Digicert 等，那么对于大多数懒人来说，免费的 Let\u0026rsquo;s Encrypt、Buypass 和 ZeroSSL 免费版就是不错的选择。\n自动签发和手工签发证书的对比 功能 自动签发 手工签发 有效期 3 个月至 6 个月 30 天到 1 年 难度 不容易 容易 友好度 不友好 友好 适合懒人 是 否 系统集成 方便 不方便 后台管理 大多数没有 大多数都有 所以我们建议如果您对服务器有完全控制权，那么自动签发的证书比较适合懒人运维，如果是长期运营的网站和项目，手工签发的证书对新手更友好，请自行选择。\n安装 acme.sh acme.sh 是一个集成了 ACME 客户端协议的 Bash 脚本，作者是 @neilpangxa，按照官方文档说明，我们直接在 Linux 下安装。\ncurl \u0026lt;https://get.acme.sh\u0026gt; | sh -s email=username@example.com 如果是国内的机器，可以使用拖回源码直接安装：\ngit clone --depth 1 \u0026lt;https://github.com/acmesh-official/acme.sh.git\u0026gt; cd acme.sh ./acme.sh --install -m username@example.com 请注意替换 username@example.com 为你自己的邮箱，避免无法收到上游证书的邮件通知，比如 Let\u0026rsquo;s Encrypt 偶尔会错发证书，然后就会邮件通知你，这时候就需要重新签发一次证书了。\n安装完成后重新加载 Bash：\nsource ~/.bashrc 然后也可以开启自动更新：\nacme.sh --upgrade --auto-upgrade 选择默认 CA 目前 acme.sh 支持 5 个正式环境 CA，分别是 Let\u0026rsquo;s Encrypt、Buypass、ZeroSSL、SSL.com 和 Google Public CA，默认使用 ZeroSSL，如果需要更换可以使用如下命令：\n切换 Let\u0026rsquo;s Encrypt\nacme.sh --set-default-ca --server letsencrypt 切换 Buypass\nacme.sh --set-default-ca --server buypass 切换 ZeroSSL\nacme.sh --set-default-ca --server zerossl 切换 SSL.com\nacme.sh --set-default-ca --server ssl.com 切换 Google Public CA\nacme.sh --set-default-ca --server google 如果已有 ZeroSSL 帐号，可以在后台控制面板拿到 API Key，然后执行如下命令\napt install jq curl -s -X POST \u0026#34;\u0026lt;https://api.zerossl.com/acme/eab-credentials?access_key=你的API_Key\u0026gt;\u0026#34; | jq 终端会输出如下内容\n{ \u0026#34;success\u0026#34;: true, \u0026#34;eab_kid\u0026#34;: \u0026#34;kid字符串\u0026#34;, \u0026#34;eab_hmac_key\u0026#34;: \u0026#34;hmac_key字符串\u0026#34;, } 然后手工添加帐号\nacme.sh --register-account --server zerossl \\\\ --eab-kid kid字符串 \\\\ --eab-hmac-key hmac_key字符串 Google Public CA 需要按照官方博客申请内测，然后获取 Key。\n几个 CA 的简单对比\n功能 LE Buypass ZeroSSL SSL.com Google Public CA 有效期 90 天 180 天 90 天 90 天 90 天 多域名 支持 支持，最多 5 个 支持 收费支持 支持 泛域名 支持 不支持 支持 收费支持 支持 Rate Limit 有 有 收费无 未知 有 GUI 管理 否 否 有 有 无 ECC 证书链 否 否 有 未知 无 客户支持 社区 收费 收费 收费 收费 简单来说，如果没有特殊需求，可以选择 Let\u0026rsquo;s Encrypt，如果服务器在国内，可以选择 ZeroSSL 或 Buypass，如果愿意付费得到更好的服务和保障，可以选择 ZeroSSL 和 SSL.com，如果面向欧盟用户，可以选择 Buypass 和 ZeroSSL。\n注意：经过测试 Google Public CA 的 ACME 验证域名在国内是无法访问的，只有国外服务器才可以申请，申请完成后的证书并无影响。\n使用 HTTP 验证签发证书 首先我们要做一下准备工作，假设你域名是 example.com，解析到你的服务器让其生效后，我们建立一个目录：\nmkdir -p /var/www/letsencrypt 我们的目的是绑定 http://example.com/.well-known/acme-challenge 到这个目录。\n如果您用的 Nginx，那么新建一个配置文件：\nserver { listen 80; listen [::]:80; server_name example.com; location /.well-known/acme-challenge { root /var/www/letsencrypt; } location / { rewrite ^/(.*)$ \u0026lt;https://$host/$1\u0026gt; permanent; } } 如果您使用的 Apache，那么新建一个配置文件：\n\u0026lt;VirtualHost *:80\u0026gt; ServerName example.com DocumentRoot /var/www/letsencrypt RewriteEngine On RewriteCond %{REQUEST_URI} !^/\\\\.well\\\\-known/acme\\\\-challenge/ RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]\u0026lt;/VirtualHost\u0026gt; 我们以 Let\u0026rsquo;s Encrypt 为例，直接在终端运行\nacme.sh --issue -d example.com -w /var/www/letsencrypt 如果希望签发 ECC 证书，则运行\nacme.sh --issue -d example.com --keylength ec-256 -w /var/www/letsencrypt 如果需要多个域名，则运行\nacme.sh --issue -d example.com -d example.org -w /var/www/letsencrypt 然后就等他执行完，直到出现 Cert success 的提示\nimage.png\n然后我们可以安装证书\nNginx\nacme.sh --install-cert -d example.com \\\\ --key-file /etc/nginx/ssl/example.com.key \\\\ --fullchain-file /etc/nginx/ssl/example.com.crt \\\\ --ca-file /etc/nginx/ssl/example.com.ca.crt \\\\ 对应的 Nginx 配置指定证书文件\nssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_trusted_certificate /etc/nginx/ssl/example.com.ca.crt; Apache\nacme.sh --install-cert -d example.com \\\\--key-file /etc/apache2/ssl/example.com.key \\\\--fullchain-file /etc/apache2/ssl/example.com.crt \\\\--ca-file /etc/apache2/ssl/example.com.ca.crt \\\\--reloadcmd \u0026#34;curl \u0026lt;https://ssl-config.mozilla.org/ffdhe2048.txt\u0026gt; \u0026gt;\u0026gt; /etc/apache2/ssl/example.com.crt \u0026amp;\u0026amp; systemctl restart apache2\u0026#34; 对应的 Apache 配置指定证书文件\nSSLCertificateFile /etc/apache2/ssl/example.com.crtSSLCertificateKeyFile /etc/apache2/ssl/example.com.key 如果是 ECC 证书，则安装的时候需要带上 --ecc 参数，比如\nacme.sh --install-cert --ecc -d example.com \\\\--key-file /etc/nginx/ssl/example.com.key \\\\--fullchain-file /etc/nginx/ssl/example.com.crt \\\\--ca-file /etc/nginx/ssl/example.com.ca.crt \\\\--reloadcmd \u0026#34;systemctl restart nginx\u0026#34; 注意如果是多个域名，也仅需要在 -d 参数后面指定第一个域名即可。\n使用 DNS 验证签发证书 有时候因为不想暴露一些二级域名，或者希望在多台机器上部署同一个域名的证书，这时候就需要用到 DNS 插件了，acme.sh 支持几十种 DNS 插件。\n这里以 Cloudflare 为例，登录 Cloudflare Dash 后在 API Token 菜单里添加一个 API Token：\nimage.png\n然后选择 Edit Zone DNS 的模板\nimage.png\n选择你要编辑的域名，也可以加入你服务器的 IP 作为白名单\nimage.png\n完成后会给你一串字符，把他复制下来，需要填入下方的 CF_Token 参数\nimage.png\n然后进入域名的管理页面，在右侧 API 列找到 Account ID 和 Zone ID 并复制\nimage.png\n接着在终端运行\nexport CF_Token=\u0026#34;HNrt84HzPnAiO69la1xVLPrunXVAur4j3ht5m_V2\u0026#34; export CF_Account_ID=\u0026#34;复制下来的 Account ID\u0026#34; export CF_Zone_ID=\u0026#34;复制下来的 Zone ID\u0026#34; 然后开启 acme.sh 的 DNS API 模式申请证书\nacme.sh --issue --dns dns_cf -d example.com -d *.example.com 安装证书方法同上，另外吐槽下，很多教程会让你用 Cloudflare 的全局 Global API Key，真的是，风险太大了，最后怎么被黑的都不知道 = =\n如果不想使用第三方的 DNS 服务完全可以自建 acme-dns 或者 PowerDNS，篇幅有限，我们之后再介绍。\n验证与自查 查看已签发的证书列表：acme.sh --list 检查证书文件已生成：ls -l /etc/nginx/ssl/example.com.crt（或对应 Apache 路径） 用 openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -dates 查看证书有效期，确认到期日会自动续期 查看定时任务：crontab -l | grep acme，确认 acme.sh 的自动续期任务存在 参考 acme.sh 官方仓库 acme.sh DNS API 插件列表 Let\u0026rsquo;s Encrypt 官网 ZeroSSL 官网 ","permalink":"https://heihei0299.github.io/posts/ops/acme/","summary":"\u003ch1 id=\"使用-acmesh-自动续签-ssl-证书\"\u003e使用 acme.sh 自动续签 SSL 证书\u003c/h1\u003e\n\u003cp\u003e本文适合不想手动申请证书、希望免费证书自动签发与续期的站长。你会学到：acme.sh 的安装、CA 选择，以及 HTTP 和 DNS 两种验证方式下签发并安装证书到 Nginx/Apache 的完整流程。\u003c/p\u003e","title":"使用 acme.sh 自动续签 SSL 证书"},{"content":"Cloudflare WARP 教程：给 VPS 额外添加\u0026rsquo;原生\u0026rsquo; IPv4/IPv6 双栈网络出口 本文适合只有单栈网络的 VPS（IPv6-only 或 IPv4-only）用户。你会学到：用 wgcf 注册 WARP 账户、生成 WireGuard 配置，并按需路由 IPv4/IPv6 流量，以及验证与故障排查方法。\n概述 本教程介绍如何使用 Cloudflare WARP 为 VPS 服务器添加额外的 IPv4 或 IPv6 网络支持，实现双栈网络出口。\n主要应用场景 1. IPv6-only VPS 获取 IPv4 访问能力 无需依赖 NAT64/DNS64 突破仅 IPv6 环境的限制 2. IPv4-only VPS 获取 IPv6 连接能力 访问 IPv6 网络资源 实现双栈网络支持 3. 访问具有原生 IP 检测的服务 Netflix Google Scholar ChatGPT 其他地理位置敏感的服务 重要限制说明 NAT 访问限制 仅支持出站连接（outbound-only） 无法接收入站连接 基于 NAT 的访问方式 IP 共享问题 多个用户共享相同的 IP 地址 IP 信誉度可能受到影响 部分服务可能已将 WARP IP 列入黑名单 性能考虑 高峰期可能出现性能下降 网络延迟可能增加 技术实现步骤 第一步：安装 WireGuard Debian/Ubuntu 系统\n# 更新软件包列表 apt update # 安装 WireGuard apt install -y wireguard CentOS/RHEL 系统\n# CentOS 8 / RHEL 8 dnf install -y wireguard-tools # CentOS 7 / RHEL 7 yum install -y epel-release elrepo-release yum install -y kmod-wireguard wireguard-tools Alpine Linux\napk add wireguard-tools Arch Linux\npacman -S wireguard-tools 第二步：下载并安装 wgcf 工具 下载 wgcf\n# 下载最新版本（Linux amd64） wget -O /usr/local/bin/wgcf https://github.com/ViRb3/wgcf/releases/latest/download/wgcf_2.2.20_linux_amd64 # 或者使用 curl curl -fsSL https://github.com/ViRb3/wgcf/releases/latest/download/wgcf_2.2.20_linux_amd64 -o /usr/local/bin/wgcf # 添加执行权限 chmod +x /usr/local/bin/wgcf 其他架构下载链接\nLinux ARM64: wgcf_2.2.20_linux_arm64 Linux ARM: wgcf_2.2.20_linux_armv7 Linux 386: wgcf_2.2.20_linux_386 使用 wgcf 生成配置\n# 注册 WARP 账户 wgcf register # 生成配置文件 wgcf generate 生成的配置文件默认名称为 wgcf-profile.conf。\n第三步：修改配置文件 复制配置文件到 WireGuard 目录\n# 复制配置文件 cp wgcf-profile.conf /etc/wireguard/wgcf.conf # 或者直接编辑 vim /etc/wireguard/wgcf.conf IPv4-only 服务器配置（获取 IPv6 访问）\n修改 /etc/wireguard/wgcf.conf：\n[Interface] PrivateKey = \u0026lt;your-private-key\u0026gt; Address = 172.16.0.2/32, fd01:5ca1:ab1e:xxxx:xxxx:xxxx:xxxx:xxxx/128 DNS = 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001 MTU = 1280 [Peer] PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= AllowedIPs = ::/0 Endpoint = engage.cloudflareclient.com:2408 关键修改点：\nAllowedIPs = ::/0 - 仅路由 IPv6 流量 IPv6-only 服务器配置（获取 IPv4 访问）\n修改 /etc/wireguard/wgcf.conf：\n[Interface] PrivateKey = \u0026lt;your-private-key\u0026gt; Address = 172.16.0.2/32, fd01:5ca1:ab1e:xxxx:xxxx:xxxx:xxxx:xxxx/128 DNS = 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001 MTU = 1280 [Peer] PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= AllowedIPs = 0.0.0.0/0 Endpoint = [2606:4700:d0::a29f:c001]:2408 关键修改点：\nAllowedIPs = 0.0.0.0/0 - 仅路由 IPv4 流量 Endpoint = [2606:4700:d0::a29f:c001]:2408 - 使用 IPv6 端点地址 双栈服务器配置（同时路由 IPv4 和 IPv6）\n[Peer] PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = engage.cloudflareclient.com:2408 第四步：启用 WireGuard 接口 手动启动和停止\n# 启动 WireGuard 接口 wg-quick up wgcf # 停止 WireGuard 接口 wg-quick down wgcf # 查看接口状态 wg show 设置开机自启\n# 启用开机自启 systemctl enable wg-quick@wgcf # 启动服务 systemctl start wg-quick@wgcf # 查看服务状态 systemctl status wg-quick@wgcf # 重启服务 systemctl restart wg-quick@wgcf # 停止服务 systemctl stop wg-quick@wgcf # 禁用开机自启 systemctl disable wg-quick@wgcf 第五步：DNS 优化（可选） 修改 DNS 配置\n在 /etc/wireguard/wgcf.conf 的 [Interface] 部分修改 DNS：\n# 使用 Cloudflare DNS DNS = 1.1.1.1, 1.0.0.1 # 或使用 Google DNS DNS = 8.8.8.8, 8.8.4.4 # IPv6 DNS DNS = 2606:4700:4700::1111, 2606:4700:4700::1001 调整 IPv4/IPv6 优先级\n编辑 /etc/gai.conf：\n# 优先使用 IPv4 precedence ::ffff:0:0/96 100 # 优先使用 IPv6（默认） # precedence ::/0 40 第六步：验证连接 测试 IPv4 连接\n# 检查 IPv4 地址 curl -4 ip.p3terx.com curl -4 ifconfig.me # 测试 IPv4 连通性 ping -4 1.1.1.1 测试 IPv6 连接\n# 检查 IPv6 地址 curl -6 ip.p3terx.com curl -6 ifconfig.me # 测试 IPv6 连通性 ping -6 2606:4700:4700::1111 查看路由信息\n# 查看路由表 ip route show ip -6 route show # 查看 WireGuard 接口信息 ip addr show wgcf 故障排查 常见问题 1. 无法连接到 WARP\n# 检查 WireGuard 接口状态 wg show # 查看系统日志 journalctl -u wg-quick@wgcf -f # 检查防火墙规则 iptables -L -n -v ip6tables -L -n -v 2. DNS 解析问题\n# 测试 DNS 解析 nslookup google.com 1.1.1.1 dig @1.1.1.1 google.com # 检查 DNS 配置 cat /etc/resolv.conf 3. 重新生成配置\n# 删除旧配置 rm -f wgcf-account.toml wgcf-profile.conf # 重新注册和生成 wgcf register wgcf generate 第七步：与代理工具集成（可选） 可以将 WARP 与其他代理工具（如 V2Ray、Xray、Clash）结合使用，实现更灵活的流量路由。\n高级配置 分流配置（仅特定流量走 WARP） 仅特定 IP 段走 WARP\n[Peer] PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= # 仅 Netflix IP 段 AllowedIPs = 23.246.0.0/18, 37.77.184.0/21, 45.57.0.0/17 Endpoint = engage.cloudflareclient.com:2408 使用路由表实现策略路由\n# 创建自定义路由表 echo \u0026#34;200 warp\u0026#34; \u0026gt;\u0026gt; /etc/iproute2/rt_tables # 添加路由规则 ip rule add from 172.16.0.2 table warp ip route add default dev wgcf table warp # 标记特定流量 iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 200 ip rule add fwmark 200 table warp MTU 优化 [Interface] MTU = 1420 # 默认值 # 或 MTU = 1280 # 保守值，适用于复杂网络环境 测试最佳 MTU 值：\n# 测试 MTU（从 1500 开始递减） ping -M do -s 1472 1.1.1.1 # 如果失败，减小数值重试 ping -M do -s 1400 1.1.1.1 持久化 iptables 规则 # Debian/Ubuntu apt install -y iptables-persistent netfilter-persistent save # CentOS/RHEL yum install -y iptables-services service iptables save 注意事项 合理使用: 请遵守 Cloudflare 的服务条款，避免滥用 账户限制: 不当使用可能导致账户被限制 IP 黑名单: 由于滥用问题，越来越多的服务将 WARP IP 列入黑名单 性能影响: 使用 WARP 可能会影响网络性能 仅出站访问: 无法用于需要入站连接的服务 相关资源 官方文档 WireGuard 官方网站 Cloudflare WARP 官方文档 工具项目 wgcf GitHub 仓库 WireGuard 工具 自动化脚本 P3TERX 的 WARP 一键脚本 其他社区维护的安装脚本 端点地址参考 IPv4 端点\nengage.cloudflareclient.com:2408 162.159.192.1:2408 162.159.193.1:2408 IPv6 端点\n[2606:4700:d0::a29f:c001]:2408 [2606:4700:d0::a29f:c101]:2408 原文 P3TERX 原创教程：使用 Cloudflare WARP 为 VPS 添加 IPv4/IPv6 网络支持 本文档整理自 P3TERX 的原创教程，仅供学习和参考使用。\n","permalink":"https://heihei0299.github.io/posts/ops/cloudflare-warp-%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"cloudflare-warp-教程给-vps-额外添加原生-ipv4ipv6-双栈网络出口\"\u003eCloudflare WARP 教程：给 VPS 额外添加\u0026rsquo;原生\u0026rsquo; IPv4/IPv6 双栈网络出口\u003c/h1\u003e\n\u003cp\u003e本文适合只有单栈网络的 VPS（IPv6-only 或 IPv4-only）用户。你会学到：用 wgcf 注册 WARP 账户、生成 WireGuard 配置，并按需路由 IPv4/IPv6 流量，以及验证与故障排查方法。\u003c/p\u003e","title":"Cloudflare WARP 教程：给 VPS 额外添加'原生' IPv4/IPv6 双栈网络出口"},{"content":"UEFI 启动背后的原理 适用场景：在双系统、移动硬盘装系统时遇到启动项丢失、引导装错分区等「玄学」问题。你会学到 UEFI 启动的真实逻辑（NVRAM 启动项 + EFI 文件）、SecureBoot 的真相，以及系统安装时引导器无视选择装进第一个 EFI 分区的天坑与补救方法。\n现在的电脑基本上都是采取 UEFI 启动了，之前看过一些资料，大抵都讲道\u0026quot;现在 UEFI 不需要制作引导分区了，系统会自动从 EFI 分区下的 \\EFI\\Boot\\Bootx64.efi 文件启动\u0026quot;，这种说法其实是以偏概全了。导致很多情况下出现了一些奇怪的问题（比如当我准备把操作系统安装到一个移动硬盘上的时候）。\n究其错误的原因，从根本上来说是忽略了主板上 NVRAM（非易失性随机访问存储器，主板上自带的一小块配置存储器，不在硬盘上，你把整块硬盘都擦了它还在）中启动配置的存在。\n真正的 UEFI 启动逻辑 def 从EFI文件启动(文件路径): 根据文件路径加载EFI文件 if 开启了SecureBoot功能: if not SecureBoot校验通过: 启动失败报错退出 # 一般可以在屏幕上看到错误信息 执行EFI # 一般来说也就从这里进入了操作系统或者Grub，所以不会返回 def 从存储设备启动(存储设备): # 可以是是GPT分区，也可以是MBR分区 for 分区 in 存储设备: # 分区类型在MBR/GPT的分区表中标注，其本质是个普通FAT文件系统分区 if 分区类型 == EFI系统分区: # 注意，FAT文件系统路径不区分大小写，且由于出自Windows，所以其路径习惯用反斜杠\\而非斜杠/ # 对于非x86_64类型的硬件，默认路径也有差异 if 分区根目录下存在\u0026#34;\\EFI\\Boot\\Bootx64.efi\u0026#34;文件: 从EFI文件启动(分区索引 + 该文件在分区下路径) def 从EFI启动项启动(启动项): if 该启动项指向的是一个EFI文件路径: 从EFI文件启动(启动项所指文件路径) elif 启动项指向的是一个存储设备: 从存储设备启动(启动项所指设备) def 启动(): 根据主板NVRAM中启动项配置，以及扫描的硬件（比如刚插入装机U盘等），生成启动顺序表 if 用户无操控: for 启动项 in 启动顺序表: 从EFI启动项启动(启动项) # 成功则返回，失败可能会返回，也可能是报错退出 else: 根据启动顺序表选择界面 if 用户选择了一个启动项: # 这对应了手动从列表中选择一个启动设备 从EFI启动项启动(启动项) elif 用户手动选择了一个具体的EFI文件: # 这对应了手动浏览EFI分区并选择某一个具体的文件 从EFI文件启动(文件路径) 这次启动失败了，死机？退出主界面？或者重新尝试？结果因机器而异 硬件扫描和准备 启动() 所以，总结说来，UEFI 并不是单纯地找到启动设备上的 EFI 分区里的某固定文件路径就启动了，而是会结合 NVRAM 中的配置来完成整个过程。NVRAM 中的启动配置包括一系列启动项和响应的预设优先顺序，Linux 下可以用 efibootmgr -v 命令查看。它会在以下情况下发生更改：\n当安装操作系统完成后，操作系统会自动添加一条启动项，如 ubuntu 会生成一条名为 ubuntu 的项目并指向 EFI 分区的 \\EFI\\ubuntu\\shimx64.efi 文件，而 Windows 会生成一条名为 Windows Boot Manager 的项目指向 EFI 分区的 \\EFI\\Microsoft\\Boot\\bootmgfw.efi 文件。 在操作系统中通过 efibootmgr 等工具手动修改，使用 grub-install 命令时如果没有指定 --no-nvram 也会添加一个启动项 在 BIOS（UEFI 不是 BIOS，但是习惯上还是管机器的 UEFI 配置界面叫 BIOS）中修改 由于 Windows 用户实在太多了，有的主板生产商会自动为 Windows 添加启动项（避免小白搞坏配置），甚至强制将其列为第一优先级（这就很恶心了） 手动从某个设备成功启动后，硬件可能会自动地将其对应的 EFI 文件加入启动项（因硬件而异） 硬件检测到不存在的设备/文件，自动删除启动项（因硬件而异） …… 总之这个 NVRAM 中的启动项配置的编辑策略各不相同，甚至不排除可能有的主板出于安全（恰烂钱）考虑拒绝你对它进行编辑，强制你从 Windows 启动，所以需要根据自己的电脑自行实验。一般来说，台式机自由度比较大，绝大多数都是允许你自行编辑的，且不会强制更改你自定义优先级；而高端或者新的笔记本也和台式机差不多，但是又老又旧的笔记本的坑就比较多了。\n因此，到这里就可以解释如下现象了：\n为啥删除了 EFI 分区下的 \\EFI\\BOOT 目录也还能正常启动？\n因为 NVRAM 中的启动项配置直接指向 \\EFI\\ubuntu 或者 \\EFI\\microsoft 目录，不依赖于 \\EFI\\BOOT，\\EFI\\BOOT 下的东西只在直接从该设备启动时生效，相当于作为一个后备。\n为啥换了主板后就只能从 Windows 启动了？\nNVRAM 在主板上，换了后自然全没了，剩下 Windows 是因为前面说的有的主板为了方便小白使用自动探测 Windows 启动项的存在并添加之，Linux 就没福气了，这需要可以通过引导盘进入 Live 环境后用 efibootmgr 修复。\n为什么安装 Linux 后安装 Windows，无法进入 Linux（Grub）？\n还是能进入的，只不过 Windows 把自己设置成了第一优先级，默认直接进 Windows；所以一般先装 Windows 再装 Linux，这样 Linux 把自己设置成第一优先级，可以在 Grub 界面二选一。不过其实这都是小问题，改一下 NVRAM 配置就行，并不是很多教程说的必须先装 Windows，没那么死板。\n关于安全启动（SecureBoot） 所有教你安装 Ubuntu 之前关闭 SecureBoot 的教程可以一律拉黑\n2020 年了，Ubuntu 它早就向微软购买了安全启动认证密钥，所以它的 \\EFI\\ubuntu\\shimx64.efi 文件是可以通过机器安全启动校验的。这个 EFI 文件的作用就是作为一个入口跳板，通过 SecureBoot 后再去加载后续 EFI 文件完成启动操作。\nGrub 是如何被加载的 \\EFI\\ubuntu\\shimx64.efi 同目录下有一个 \\EFI\\ubuntu\\grub.cfg。内容如下：\nsearch.fs_uuid 62c89a8b-9df9-4acd-bc18-8622a8d501d0 root hd0,gpt2 set prefix=($root)\u0026#39;/boot/grub\u0026#39; configfile $prefix/grub.cfg 第一行的 uuid 是我 Ubuntu 系统所在分区的 uuid，（似乎 root 后的 hd0,gpt2 可以省略，删掉也没影响）。找到这个分区后，根据第二第三行的配置，Grub 的 EFI 部件会从中读取 /boot/grub/grub.cfg 文件作为 grub 启动配置（该文件其实就是执行 grub-update 的输出）。\n然后，就是展示你熟悉的 Grub 窗口，再然后根据选择的 Grub 启动项进入操作系统，不过这已经不是 UEFI 的范畴了。\n系统安装时候的一个天坑 想象中，最理想的形式应该是系统安装时可以让用户自主选择安装到哪个 EFI 分区，这对双硬盘/移动硬盘用户绝对是好东西，可以保证每个硬盘上的系统互相独立，拔掉硬盘到别的电脑上也能继续用。\n但是！\nWindows 就不必说了，向来是帮用户做决定，直接自作主张安装到它找到的第一个 EFI 分区中去，不让你选择。\nUbuntu 倒是有一个界面让你选择安装启动引导器的设备，不过这好像是一个陈年老 bug，一直没有修复，它也和 Windows 一样，也是不理会选择结果，直接奔着扫描到的第一个 EFI 分区去了。参见 ubuntuforums。\n所以，如果真的想要做到双硬盘独立的双系统，你可以这么事后补救：\n进入第二个系统 umount 现有的 /boot/efi 并重新挂载上正确的分区（应该是空的） 使用 grub-install，重建缺失的 EFI 文件 修改 /etc/fstab 更正挂载到挂载选项，否则即使会在正常启动操作系统后挂载上错误的 /boot/efi 目录 恢复第一个操作系统的 EFI 分区（因为默认的安装策略修改了它） 如果第一个系统是同版本 ubuntu，那么需要修正旧的 EFI 分区下的 \\EFI\\UBUNTU\\grub.cfg 文件中的 uuid 即可，把它改回去指向第一个系统的根目录 如果是其他的 Linux 系统，可以重启回第一个操作系统后使用 grub-install 重构它的 EFI 分区 如果第一个系统是 Windows，那么应该删除 EFI 分区下的整 ubuntu 目录和 BOOT 目录下的所有子文件，并把 \\EFI\\Microsoft\\Boot\\bootmgfw.efi 复制到 \\EFI\\BOOT\\Bootx64.efi（EFI 文件系统不分文件名大小写） 使用 efibootmgr 调整启动项目（就是修改 NVRAM 配置） 上面的操作都挺麻烦的，其实最简单的办法还是防止这种事情发生，原理很简单，把无关硬盘在本次装机运行周期内移除。可以在 Try Ubuntu 环境下执行如下操作。\n对于 SATA 类型的硬盘（设备号形如 /dev/sda），方法很简单\necho 0 | sudo tee /sys/block/\u0026lt;如sda\u0026gt;/device/delete 而对于 NVME 类型的硬盘（设备号形如 /dev/nvme0n1），就有些麻烦了\n# 查看设备的PCI路径 realpath /sys/block/\u0026lt;如nvme0n1\u0026gt; # 输出结果应该大致为 /sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1 # 然后根据输出结果，将对应的NVME设备移除 echo 1 | sudo tee /sys/devices/pci0000:00/0000:00:0e.0/remove 或者直接一行命令\necho 1 | sudo tee $(realpath /sys/block/nvme0n1)/../../../remove 当然，如果是台式机，那还可以使用物理学方法，没那么多花里胡哨的，关机拔线就完事了\n此命令会在将整个磁盘设备彻底隐藏（重启后恢复），因此当然不需要担心安装到了错误的 EFI 分区了。\n验证与自查 ls /sys/firmware/efi 存在内容 → 当前是 UEFI 启动（而非 Legacy BIOS） sudo efibootmgr -v 可查看 NVRAM 中的启动项、指向的 EFI 文件路径与启动顺序 删除 EFI 分区的 \\EFI\\BOOT 目录后仍能正常启动（理解 NVRAM 直接指向 \\EFI\\ubuntu 等目录，\\EFI\\BOOT 只是后备） 双系统出现「只能进 Windows」时，用 efibootmgr 重建/调整 Linux 启动项 参考 ArchWiki - Unified Extensible Firmware Interface efibootmgr man page Ubuntu Forums - 安装引导器设备选择失效讨论 ","permalink":"https://heihei0299.github.io/posts/linux/mint%E5%AE%89%E8%A3%85%E7%9A%84%E5%9D%91/","summary":"\u003ch1 id=\"uefi-启动背后的原理\"\u003eUEFI 启动背后的原理\u003c/h1\u003e\n\u003cp\u003e适用场景：在双系统、移动硬盘装系统时遇到启动项丢失、引导装错分区等「玄学」问题。你会学到 UEFI 启动的真实逻辑（NVRAM 启动项 + EFI 文件）、SecureBoot 的真相，以及系统安装时引导器无视选择装进第一个 EFI 分区的天坑与补救方法。\u003c/p\u003e","title":"UEFI 启动背后的原理"},{"content":"Debian 11 开机基础配置：bash 补全、SSH 安全与 nftables 本文适合刚装好 Debian 11 系统的用户。你会学到：启用 bash 命令补全、上传 SSH 公钥并加固 sshd（修改端口、禁止密码登录）、用 nftables 配置入站防火墙并设置开机自启。\n1. 安装 bash-completion apt-get install bash-completion 2. 编辑 ~/.bashrc 文件，添加如下内容 vim ~/.bashrc if [ -f /etc/bash_completion ]; then . /etc/bash_completion fi 3. 重启 ~/.bashrc source ~/.bashrc 2. ssh 安全设置 1. 上传 ssh 公钥 mkdir -p /root/.ssh vim authorized_keys ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFFCbTPyuQWsnmEjNn+W7om1ag8vldFLqi4bF7dWvcaJ 2. 修改 ssh 默认端口，启用密钥登录，禁止密码登录 vim /etc/ssh/sshd_config systemctl restart ssh journalctl -u ssh.service -f 修改后的 /etc/ssh/sshd_config 关键项如下（完整文件）：\n#\t$OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options override the # default value. Include /etc/ssh/sshd_config.d/*.conf Port 50030 #AddressFamily any #ListenAddress 0.0.0.0 #ListenAddress :: #HostKey /etc/ssh/ssh_host_rsa_key #HostKey /etc/ssh/ssh_host_ecdsa_key #HostKey /etc/ssh/ssh_host_ed25519_key # Ciphers and keying #RekeyLimit default none # Logging #SyslogFacility AUTH #LogLevel INFO # Authentication: #LoginGraceTime 2m PermitRootLogin yes #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 #PubkeyAuthentication yes # Expect .ssh/authorized_keys2 to be disregarded by default in future. #AuthorizedKeysFile\t.ssh/authorized_keys .ssh/authorized_keys2 #AuthorizedPrincipalsFile none #AuthorizedKeysCommand none #AuthorizedKeysCommandUser nobody # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #HostbasedAuthentication no # Change to yes if you don\u0026#39;t trust ~/.ssh/known_hosts for # HostbasedAuthentication #IgnoreUserKnownHosts no # Don\u0026#39;t read the user\u0026#39;s ~/.rhosts and ~/.shosts files #IgnoreRhosts yes # To disable tunneled clear text passwords, change to no here! PasswordAuthentication no #PermitEmptyPasswords no # Change to yes to enable challenge-response passwords (beware issues with # some PAM modules and threads) ChallengeResponseAuthentication no # Kerberos options #KerberosAuthentication no #KerberosOrLocalPasswd yes #KerberosTicketCleanup yes #KerberosGetAFSToken no # GSSAPI options #GSSAPIAuthentication no #GSSAPICleanupCredentials yes #GSSAPIStrictAcceptorCheck yes #GSSAPIKeyExchange no # Set this to \u0026#39;yes\u0026#39; to enable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will # be allowed through the ChallengeResponseAuthentication and # PasswordAuthentication. Depending on your PAM configuration, # PAM authentication via ChallengeResponseAuthentication may bypass # the setting of \u0026#34;PermitRootLogin without-password\u0026#34;. # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and ChallengeResponseAuthentication to \u0026#39;no\u0026#39;. UsePAM yes #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no X11Forwarding yes #X11DisplayOffset 10 #X11UseLocalhost yes #PermitTTY yes PrintMotd no #PrintLastLog yes #TCPKeepAlive yes #PermitUserEnvironment no #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 #UseDNS no #PidFile /var/run/sshd.pid #MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none #VersionAddendum none # no default banner path #Banner none # Allow client to pass locale environment variables AcceptEnv LANG LC_* # override default of no subsystems Subsystem\tsftp\t/usr/lib/openssh/sftp-server # Example of overriding settings on a per-user basis #Match User anoncvs #\tX11Forwarding no #\tAllowTcpForwarding no #\tPermitTTY no #\tForceCommand cvs server 3. 配置 nftables 1. 修改配置文件 vim /etc/nftables.conf #!/usr/sbin/nft -f flush ruleset #!/usr/sbin/nft -f table inet filter { chain input { type filter hook input priority 0; # 允许回环接口 iifname lo accept # 允许已建立和相关联的连接 ct state established,related accept # 允许 ICMP ip protocol icmp accept ip6 nexthdr icmpv6 accept tcp dport 50000-50100 accept udp dport 50000-50100 accept # 拒绝其他流量并记录 reject } chain forward { type filter hook forward priority -100; policy accept; } chain output { type filter hook output priority 0; policy accept; } } 2. 启动 nftables，并设置开机自启 ## 启动nftables systemctl restart nftables.service ## 开机自启动nftables systemctl enable nftables.service 验证与自查 输入命令首字母后按 Tab 能补全（bash-completion 生效） ssh -p 50030 root@服务器IP 使用密钥免密登录成功，密码登录被拒绝 nft list ruleset 输出规则表，且 systemctl is-enabled nftables 返回 enabled 从其他机器 telnet 服务器IP 50030 能通（或 nmap -p 50030 显示 open），其它端口被拒 参考 OpenSSH 官方手册 sshd_config(5) ArchWiki: OpenSSH nftables 官方 wiki Debian 官方文档：nftables ","permalink":"https://heihei0299.github.io/posts/ops/debian11%E5%BC%80%E6%9C%BA%E6%93%8D%E4%BD%9C/","summary":"\u003ch1 id=\"debian-11-开机基础配置bash-补全ssh-安全与-nftables\"\u003eDebian 11 开机基础配置：bash 补全、SSH 安全与 nftables\u003c/h1\u003e\n\u003cp\u003e本文适合刚装好 Debian 11 系统的用户。你会学到：启用 bash 命令补全、上传 SSH 公钥并加固 sshd（修改端口、禁止密码登录）、用 nftables 配置入站防火墙并设置开机自启。\u003c/p\u003e","title":"Debian 11 开机基础配置：bash 补全、SSH 安全与 nftables"},{"content":"","permalink":"https://heihei0299.github.io/about/","summary":"about","title":"关于"}]