架构
单进程、单二进制
Section titled “单进程、单二进制” ┌──────────────────────────────────┐ │ Pier (single binary) │ │ │ Browser ───────▶ │ Axum ──▶ API routes (100+) │ │ │ │ │ ├──▶ MiniJinja ──▶ HTML (HTMX) │ │ ├──▶ Bollard ──▶ Docker Engine │ │ ├──▶ rusqlite ──▶ SQLite │ │ └──▶ reqwest ──▶ Remote Agents │ └──────────────────────────────────┘ │ ┌───────────────┴────────────────┐ │ Traefik (reverse proxy) │ │ Let's Encrypt · Auto-routing │ └────────────────────────────────┘| 层级 | Crate | 职责 |
|---|---|---|
| HTTP + WebSocket | axum | API 端点,以及用于日志和指标的 SSE/WebSocket 数据流 |
| Docker | bollard | 所有容器、卷和网络操作 |
| 数据库 | rusqlite | 嵌入式 SQLite,采用 WAL 模式以支持并发读取 |
| 模板 | minijinja | 为仪表盘服务端渲染 HTML |
| 认证 | axum-login + totp-rs | 不透明 cookie 会话;带恢复码的 TOTP 双因素认证 |
| SSH | russh | 远程服务器配置 |
| Git | gix | 用于 Git 部署的克隆和拉取 |
| 指标 | sysinfo | 系统和容器指标 |
| 缓存 | moka | 用于 Docker 统计数据的内存缓存 |
| 运行时 | tokio | 异步 I/O |
UI 通过 MiniJinja 在服务端渲染。HTMX 负责局部更新;Alpine.js 处理小型交互(模态框、开关)的客户端状态。客户端 JS 总量约为 30 KB。所有资源均内嵌于二进制文件中——没有 CDN,没有外部字体,运行时也无需打包器。
/opt/pier/├── bin/pier # the binary├── bin/pier.old # previous version (for rollback)├── .env # PIER_SECRET — AES-256 key for env vars└── data/ ├── pier.db # SQLite main database ├── pier.db-wal # WAL file ├── backups/ # automated DB + .env backups └── traefik/ # dynamic config files per domain静态数据安全
Section titled “静态数据安全”存储在数据库中的每一个环境变量都使用 AES-256-GCM 加密。密钥来自 PIER_SECRET(32 个随机字节,经 base64 编码)——systemd 从 /opt/pier/.env 读取它;如果该变量未设置,Pier 会改为在数据目录中持久化一个 .pier-secret 文件。仅有数据库而没有密钥是毫无用处的——这种拆分是有意为之,以确保泄露的 SQL 转储不会泄露密钥。
加密是向后兼容的:在该功能上线之前写入的行可以作为纯文本读取;新写入的数据带有 ENC: 前缀,并始终加密。