Latest Learning

Stuff I learned recently.

docker-compose.ymltty: truestdin_open: true 这两个配置项分别对应 docker run-t-i 参数。

tty: true

分配虚拟终端(Pseudo-TTY),让程序输出像在普通命令行中运行,保留颜色和交互式输出。

stdin_open: true

保持标准输入开启,即使没有 attach 到容器也能接收指令。

适用场景

Minecraft 服务器等需要交互式控制台的服务。

为什么 Minecraft 必须用它们

Minecraft 服务器有交互式控制台,可以:

  • 手动输入命令:/op yourname 给自己管理员权限,或 /stop 安全关闭服务器
  • 使用 docker attach 进入游戏控制台直接输入指令

如果没开这两个选项,docker attach 只能看输出,无法输入命令。

services:
  mc:
    image: itzg/minecraft-server
    tty: true
    stdin_open: true

总结:tty 负责输出,stdin_open 负责输入。

公司网络能审计 HTTPS 访问记录,并非”破解了加密”,而是利用了 TLS 握手阶段明文传输的 SNI(Server Name Indication)

原理

在 HTTPS(即 TLS)连接中,代理/防火墙无需解密流量,也无需建立 CONNECT 隧道,只需旁路观察 TLS 握手的第一个 ClientHello 报文。

TLS 握手流程(简化)

  1. TCP 三次握手
  2. 客户端发送 ClientHello(明文)
  3. 服务器响应 ServerHello
  4. 后续加密通信

ClientHello 中的 SNI

ClientHello未加密的明文数据,其中包含扩展字段。SNI 格式如下:

Extension: server_name
Hostname: example.com

代理/防火墙只需解析这个明文握手包,即可获取目标域名,无需:

  • 解密 TLS 流量
  • 建立了 CONNECT 隧道(传统 HTTP 代理模式)

SNI 是 TLS 扩展,用于解决单 IP 多域名场景。客户端必须在 ClientHello明文告诉服务器想访问的域名,否则服务器无法选择正确证书。

审计边界

  • ✅ 能看到:目标域名(SNI)、目标 IP、连接时间、流量大小、连接时长
  • ❌ 不能看到:URL 路径、查询参数、HTTP Header、页面内容、POST 数据

HTTPS(TLS)保护的只是 HTTP 请求内容(路径、Header、Body),但不会隐藏目标域名和 IP 等元数据。

防御方式

  • ECH(Encrypted Client Hello):TLS 1.3 扩展,使用 DNS 公钥加密 SNI
  • DoH/DoT:防止 DNS 查询泄露,但无法阻止 SNI 泄露
更彻底的审计:HTTPS 中间人解密(MITM)

企业可在终端安装根证书,代理动态生成假证书解密流量。但已不属于纯 CONNECT 透传。

根据 2026 年最新实测数据,简单有效的 email 地址防爬虫方法:

纯文本 email

方法阻止率
无保护0%
HTML Entities95%
HTML 注释98%
HTML SVG100%
CSS display: none100%
JS 拼接100%
JS 转换 (自定义函数)100%
JS AES 加密100%

可点击 mailto 链接

方法阻止率
无保护0%
HTML Entities100%
URL 编码96%
HTTP 重定向100%
HTML SVG100%
JS 拼接100%
JS 转换100%
JS AES 加密100%

推荐方案

最简单且有效:CSS display: none + 变化诱饵标签

<div class="email">ad@<span>email.</span>spencermortensen.<span>example.</span>com</div>
div.email > span:nth-child(2) { display: none; }

无 JS 依赖,完全无障碍可用。

推荐方案:JS 转换(自定义函数)

<span id="email">zibby example com</span>
const map = { zibby: 'hello', example: 'gmail', com: 'com' };
// 自定义转换逻辑

原理:HTML 源码只有乱码,需要在浏览器端用 JS 转换才能得到真实 email。

最强方案:JS AES 加密(需 HTTPS)

<span class="email">Kreuz2xa6xB8Fpjaa0lFgACNLO6n_Auu1CGjcG8z_Ec</span>

使用浏览器内置 SubtleCrypto 进行 AES-256 加密。

不推荐(破坏可用性)

  • 符号替换 (ag AT email DOT com) - 用户需手动还原
  • 图片 - 无法复制、屏幕阅读器无法读取
  • CSS content - 文本不可选中
  • CSS 文字方向反转 - 复制后是反的

关键发现:大多数爬虫很简单,即使最基础的混淆技术也能阻止 95% 以上的爬虫。建议组合使用多种技术。

来源: Email obfuscation: What works in 2026?

Docker Compose 默认会给卷名加上项目前缀(目录名),使用 name 属性可自定义卷名。

services:
  db:
    image: postgres
    volumes:
      - my_custom_volume:/var/lib/postgresql/data

volumes:
  my_custom_volume:
    name: "production_db_volume"

运行 docker compose up 后,docker volume ls 会显示 production_db_volume,而非 my-app_my_custom_volume

适用场景:

  • 多项目共享卷
  • 固定名字便于脚本编写和迁移

Docker Compose 中的 healthcheck 配置会覆盖 Dockerfile 中的 HEALTHCHECK 指令。

# docker-compose.yml
services:
  web:
    image: myapp
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

优先级:Dockerfile < Docker Compose。

为什么 Compose 优先?

遵循”运行时配置优于镜像构建配置”原则。Compose 覆盖 Dockerfile 的优势:

  • 灵活性:不同环境(开发/测试/生产)可配置不同的检查频率或超时时间
  • 依赖管理:配合 depends_oncondition: service_healthy 控制容器启动顺序
  • 集中管理:在 Compose 文件中一目了然,无需翻阅各镜像源码

AI 时代 LLM 经常需要读取 git diff,默认行为不要用 delta 修改。

Delta 安装时可能会自动添加配置:

[core]
    pager = delta

需要先移除这个配置,然后在 .gitconfig 中添加:

[alias]
    ds = -c pager.diff=delta diff
  • git diff 保持原生格式,便于 LLM 读取
  • git ds 查看带 delta 的漂亮 diff 效果

Bitwarden SSH Agent 默认会暴露所有可用密钥给 SSH 客户端,可能导致 Too many authentication failures 错误。通过 SSH Config 指定特定密钥解决。

# ~/.ssh/config

Host vps1
  HostName domain.com
  StrictHostKeyChecking no
  User root
  Port 26456
  IdentityFile ~/.ssh/bw_keys/vps-auth.pub
  IdentitiesOnly yes

Host vps2
  HostName 1.1.1.1
  StrictHostKeyChecking no
  User root
  Port 27644
  IdentityFile ~/.ssh/bw_keys/vps-auth.pub
  IdentitiesOnly yes

IdentitiesOnly yes 强制 SSH 只使用指定的密钥,跳过 Agent 中的其他密钥。

/usr/bin/env 是 Unix/Linux 系统中用于查找并运行程序的工具,主要用于 Shebang 中以提高脚本可移植性。

核心作用与用法

核心作用:提高脚本的可移植性

1. 硬编码路径(不推荐)

#!/usr/bin/python3 - 直接指定路径,跨系统兼容性差。

2. 使用 env(推荐)

#!/usr/bin/env python3 - 在 $PATH 中搜索,兼容性极佳。

工作流程

  1. 内核读取 Shebang,运行 env
  2. env 查看 $PATH 环境变量
  3. 按顺序搜索 python3
  4. 找到后启动该程序

对比表

特性直接路径使用 env
可移植性极佳
虚拟环境支持不支持完美支持
灵活性

进阶技巧:临时设置环境变量

# 临时设置语言
env LANG=en_US.UTF-8 check_system_status

# 查看所有环境变量
env

注意事项

env 不支持传递多个参数,如 #!/usr/bin/env python3 -u 在旧系统上可能失效。

使用 brew install 命令安装某些 cask 软件时,可以省略 cask 关键字,直接 brew install yaak 也能安装成功。

但在使用 brew bundle 命令时,必须在 Brewfile 中明确指定 cask 关键字:

# 正确写法
cask "yaak"

# 错误写法,会报错
brew "yaak"

原因:brew bundle 解析 Brewfile 时需要明确区分安装类型,而命令行 brew install 有自动检测能力。

将 Git 仓库的默认分支从 master 修改为 main。

# 重命名本地分支
git branch -m master main

# 获取远程更新
git fetch origin

# 设置本地 main 分支跟踪远程 main 分支
git branch -u origin/main main

# 设置远程仓库的默认分支
git remote set-head origin -a

Formula 用于从源码编译,Cask 用于分发预编译二进制。

Homebrew 团队希望统一 CLI 和 GUI 的安装体验,推动 brew install 作为唯一入口。因此 GoReleaser 官方推荐使用 Cask:

  • 安装更快(无编译过程)
  • 自动化程度更高
  • 符合 Homebrew 官方发展方向

何时用 Cask:Go CLI 工具官方通常提供预编译二进制。

何时用 Formula:上游只提供源码、需要编译定制、或依赖本地库的软件。

自定义 tap 可同时包含两者:

# Formula
brew install zhaochunqi/homebrew-tap/<tool>

# Cask
brew install --cask zhaochunqi/homebrew-tap/<tool>

参考:GoReleaser v2.10 Blog Post

通过 homebrew-tap,可以让自己的项目通过 brew install 直接安装。

安装

brew install zhaochunqi/tap/git-open

创建自己的 tap

  1. 创建 tap 仓库,命名为 homebrew-<tap-name>,例如 homebrew-tap
  2. 在仓库根目录创建 Formula/<formula-name>.rb 文件

⚠️ 注意: Homebrew 官方不提倡在 Formula 中分发预编译二进制,这被认为是不好的实践。对于 Go CLI 工具等提供预编译二进制的项目,推荐使用 Homebrew Cask 进行分发。参考 Go CLI 工具更适合通过 Homebrew Cask 分发预编译二进制

class GitOpen < Formula
  desc "Open your git repo in browser using one command"
  homepage "https://github.com/zhaochunqi/git-open"
  version "2.2.1"

  on_macos do
    on_arm do
      url "https://github.com/zhaochunqi/git-open/releases/download/v2.2.1/git-open_Darwin_arm64.tar.gz"
      sha256 "9b653ba97f5095e8764f43eeb9ab0e46d01f4bbd14eadd51760b636512812a8c"
    end
    on_intel do
      url "https://github.com/zhaochunqi/git-open/releases/download/v2.2.1/git-open_Darwin_x86_64.tar.gz"
      sha256 "ab24e6fe8a49b6f526a785a94a10b56a139430f795346d30f8a5a5db1387223d"
    end
  end

  on_linux do
    on_arm64 do
      url "https://github.com/zhaochunqi/git-open/releases/download/v2.2.1/git-open_Linux_arm64.tar.gz"
      sha256 "8776da29b63a21f0949cda1814e30cc2c926bb6dee4199a9f7f0684486671c70"
    end
    on_x86_64 do
      url "https://github.com/zhaochunqi/git-open/releases/download/v2.2.1/git-open_Linux_x86_64.tar.gz"
      sha256 "cf91815149c341d718ea7b26d5d671e261bb8a5701e2de2da803d9a5a6c278c4"
    end
  end

  def install
    bin.install "git-open"
  end

  test do
    system "#{bin}/git-open", "--version"
  end
end

发布流程

  1. 项目打标签并发布到 GitHub Releases,上传预编译的二进制文件
  2. 更新 Formula 文件中的版本号和 sha256
  3. 提交到 homebrew-tap 仓库

相关链接

zsh 绑定键位

查看绑定键位

查看 ctrl + r绑定的键位::

 bindkey '^r'
"^R" history-incremental-search-backward

绑定新的键位

比如绑定 fzf-ghq-widget方法:

zle -N fzf-ghq-widget

# 1. 绑定默认 (Emacs) 模式
bindkey '^g' fzf-ghq-widget

# 2. 绑定 Vi 插入模式 (防止使用 Vi 模式时失效)
bindkey -M viins '^g' fzf-ghq-widget

解析:

  1. zle (Zsh Line Editor): 这是 Zsh 的核心模块,专门负责处理你在终端里打字、移动光标、删除文字这些交互操作。
  2. -N (New): 告诉 Zsh:“我要创建一个新的 Widget”。
  3. fzf-ghq-widget: 这是你要注册的名字。通常我们会让 Widget 的名字 和 函数的某些名字 保持一致,这样省事。

配置方法

在所有的 git 全局配置中,配置:

# Configure Git to ensure line endings in files you checkout are correct for macOS
git config --global core.autocrlf input

或者在对应的 ~/.gitconfig 中配置:

[core]
	autocrlf = input

但是 github 推荐在 windows 中将这个选项设置为 true,我查了下觉得在三端均采用 input 是非常合理的。

配置说明

配置值含义
true双向转换:
 • 输入(commit)时:CRLF → LF
 • 输出(checkout)时:LF → CRLF
input仅输入时转换:
 • 输入(commit)时:CRLF → LF
 • 输出(checkout)时:不做转换(保留 LF)
false完全不转换

参考链接:https://docs.github.com/en/get-started/git-basics/configuring-git-to-handle-line-endings?platform=linux

CR

“CR” 在电池型号中是标准命名:

  1. C = 锂 - 二氧化锰(Lithium-Manganese Dioxide)
  2. R 代表形状:圆形

后面的数字代表尺寸:

  1. 前两位(20):直径,单位是毫米(mm)→ 直径 20mm
  2. 后两位(32):厚度,单位是 0.1mm → 3.2mm 厚

结论

  1. CR2032 = 锂锰电池,圆形,20mm 直径,3.2mm 厚
  2. CR2025 = 锂锰电池,圆形,20mm 直径,2.5mm 厚