12 KiB
Đóng gói & Auto-Update với Velopack
Tài liệu này mô tả cách build, phát hành và cập nhật tự động cho Tailscale-Custom trên Windows bằng Velopack.
Phạm vi: chỉ phần tray app (
tailscale-tray.exe) — đây là main exe Velopack quản lý. Servicetailscaled.exechạy ởC:\Program Files (x86)\Tailscale-Customvà được tray copy/đăng ký lại qua một worker UAC (--ts-apply-service-update).
1. Tổng quan kiến trúc
┌─────────────────────────────────────────────────────────────┐
│ %LocalAppData%\TailscaleCustom\ (per-user, Velopack) │
│ ├── Update.exe (Velopack updater) │
│ ├── tailscale-tray.exe (launcher stub) │
│ ├── packages\ (downloaded nupkgs) │
│ └── current\ (active version) │
│ ├── sq.version │
│ ├── tailscale-tray.exe (main app) │
│ ├── tailscaled.exe │
│ ├── tailscale.exe │
│ └── wintun.dll │
└─────────────────────────────────────────────────────────────┘
│
│ on update -> elevated worker
▼
┌─────────────────────────────────────────────────────────────┐
│ C:\Program Files (x86)\Tailscale-Custom\ (machine-wide) │
│ ├── tailscaled.exe (Windows service: Tailscale-Custom) │
│ ├── tailscale.exe │
│ └── wintun.dll │
└─────────────────────────────────────────────────────────────┘
Vì sao tách 2 thư mục?
- Velopack swap thư mục
current\không cần admin → cập nhật tray mượt. - Service phải chạy
LocalSystemtừ thư mục không ghi được bởi user để tránh privilege escalation.
2. Layout package (nuspec)
Metadata trong lib/app/sq.version / TailscaleCustom.nuspec:
<package>
<metadata>
<id>TailscaleCustom</id>
<title>Tailscale Custom</title>
<authors>SoftsBusiness</authors>
<version>1.0.2</version>
<channel>win</channel>
<mainExe>tailscale-tray.exe</mainExe>
<os>win</os>
<rid>win-x64</rid>
<shortcutLocations>Desktop,StartMenuRoot</shortcutLocations>
<shortcutAumid>velopack.TailscaleCustom</shortcutAumid>
<machineArchitecture>x64</machineArchitecture>
</metadata>
</package>
Các trường bắt buộc giữ ổn định giữa các bản: id, channel, mainExe, rid.
Đổi version mỗi lần release.
3. Quy trình build
3.1. Yêu cầu
- Windows 10/11 x64
- Go toolchain (repo đã có sẵn ở
tool\go.exe, Go 1.26.x) - .NET 8 SDK +
dotnet tool install -g vpk(Velopack CLI) - Code signing cert (tùy chọn) — repo dùng cert self-signed
CN=Tailscale-Custom, O=SoftsBusiness, C=VN
3.2. Build các binary
Tất cả binary đầu ra để ở dist/:
$env:GOOS='windows'; $env:GOARCH='amd64'; $env:CGO_ENABLED='0'
.\tool\go.exe build -ldflags "-H=windowsgui -s -w" `
-o .\dist\tailscale-tray.exe ./cmd/tailscale-tray
.\tool\go.exe build -ldflags "-s -w" -o .\dist\tailscaled.exe ./cmd/tailscaled
.\tool\go.exe build -ldflags "-s -w" -o .\dist\tailscale.exe ./cmd/tailscale
# wintun.dll: copy bản x64 chính thức vào dist\
Lưu ý -H=windowsgui cho tray để không bật console window.
3.3. Pack bằng vpk
$ver = '1.0.3' # bump mỗi lần release
$pkg = 'TailscaleCustom'
$src = '.\dist'
$out = '.\velopack-releases'
vpk pack `
--packId $pkg `
--packVersion $ver `
--packDir $src `
--mainExe tailscale-tray.exe `
--packAuthors SoftsBusiness `
--packTitle 'Tailscale Custom' `
--channel win `
--shortcuts 'Desktop,StartMenuRoot' `
--outputDir $out
Đầu ra trong velopack-releases\:
| File | Mô tả |
|---|---|
TailscaleCustom-<ver>-full.nupkg |
Full package (dùng để cập nhật) |
TailscaleCustom-win-Setup.exe |
Installer cho user mới |
TailscaleCustom-win-Portable.zip |
Bản portable (không cần cài) |
releases.win.json |
Feed metadata (Update.exe dùng) |
RELEASES |
Manifest legacy (Squirrel format) |
assets.win.json |
Liệt kê asset của channel |
3.4. Ký số (tùy chọn nhưng khuyến nghị)
$cert = Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.Subject -match 'Tailscale-Custom' -and $_.HasPrivateKey } |
Sort-Object NotAfter -Descending | Select-Object -First 1
Set-AuthenticodeSignature -FilePath .\dist\tailscale-tray.exe `
-Certificate $cert -TimestampServer 'http://timestamp.digicert.com' `
-HashAlgorithm SHA256
Ký TRƯỚC khi vpk pack để chữ ký nằm trong nupkg.
4. Phát hành (release feed)
Tray sẽ poll feed tại URL cấu hình trong cmd/tailscale-tray/update.go:
const (
updateFeedURL = "https://vpn.softs.business/updates/"
updateChannel = "win"
)
4.1. Cấu trúc feed trên server
https://vpn.softs.business/updates/
├── releases.win.json
├── RELEASES
├── TailscaleCustom-1.0.2-full.nupkg
├── TailscaleCustom-1.0.3-full.nupkg
└── ...
Upload bằng SCP / SFTP / S3 sync — ví dụ:
scp .\velopack-releases\* user@vpn.softs.business:/var/www/updates/
4.2. Reverse proxy
Bất kỳ HTTP server tĩnh nào cũng được. Yêu cầu:
- HTTPS (tray dùng
http.Clientmặc định) - Content-Type
application/jsonchoreleases.win.json application/octet-streamcho.nupkg- KHÔNG cache
releases.win.json(hoặc TTL ngắn) để client thấy bản mới ngay
5. Luồng auto-update (client side)
Logic đầy đủ ở cmd/tailscale-tray/update.go.
sequenceDiagram
autonumber
participant Tray as tailscale-tray
participant Feed as Update server
participant Upd as Update.exe
participant UAC as Elevated worker
participant Svc as Tailscale-Custom service
Tray->>Tray: startAutoUpdater (delay 90s, loop 6h)
Tray->>Feed: GET releases.win.json
Feed-->>Tray: latest version + filename + SHA256
alt latest > installed
Tray->>Feed: GET *-full.nupkg
Tray->>Upd: Update.exe apply --silent --package ...
Tray-->>Tray: os.Exit(0) (Update.exe waits then swaps current\)
Upd->>Tray: relaunch new tailscale-tray.exe
Tray->>Tray: --veloapp-updated hook
Tray->>UAC: ShellExecuteEx runas --ts-apply-service-update
UAC->>Svc: sc stop
UAC->>UAC: copy service bins to Program Files (x86)
UAC->>Svc: sc start
else up to date
Tray->>Tray: log "up to date"
end
5.1. Lifecycle hooks Velopack invoke
Tray xử lý trong handleStartupArgs():
| Arg | Khi nào | Hành động |
|---|---|---|
--veloapp-install |
Lần đầu cài | syncServiceBinaries(allowElevate=true) |
--veloapp-updated |
Sau update | syncServiceBinaries(allowElevate=true) |
--veloapp-obsolete |
Bản cũ bị retire | no-op, exit |
--veloapp-uninstall |
Gỡ cài | stop + xoá service |
--ts-apply-service-update |
Worker elevated nội bộ | copy bins + (re)register service |
5.2. So sánh hash trước khi swap service
serviceBinsUpToDate() so SHA-256 của
%LocalAppData%\TailscaleCustom\current\<bin> với
C:\Program Files (x86)\Tailscale-Custom\<bin>.
Nếu trùng hết → bỏ qua UAC prompt.
5.3. Phiên bản hiện tại
Đọc từ current\sq.version:
<version>1.0.2</version>
So sánh dạng dotted-numeric (compareVersions) — không hỗ trợ pre-release tag.
6. Cài đặt lần đầu
6.1. Bằng Setup.exe (khuyến nghị)
.\TailscaleCustom-win-Setup.exe --silent
Kết quả:
- App cài vào
%LocalAppData%\TailscaleCustom\ - Shortcut tạo ở Desktop + Start Menu (theo
shortcutLocations) - Hook
--veloapp-installchạy → elevate → tạo service Windows
6.2. Portable
Bung TailscaleCustom-win-Portable.zip ra bất kỳ thư mục nào, chạy
Tailscale Custom.exe. Service sẽ KHÔNG được cài tự động.
7. Phát hành bản mới — checklist
- Bump version (Go) + ghi changelog
- Build binary (
tool\go.exe build) - Ký số
dist\*.exe vpk pack --packVersion <new>- (Tùy chọn) test local bằng:
$upd = "$env:LOCALAPPDATA\TailscaleCustom\Update.exe" & $upd apply --silent --package .\velopack-releases\TailscaleCustom-<new>-full.nupkg - Upload
velopack-releases\*lên feed URL - Tray các máy sẽ tự update trong vòng
updateInitialDelay + updateCheckInterval(90s + 6h)
8. Hành vi tray (close vs quit)
- Đóng menu / Esc → tray vẫn chạy ở khay hệ thống.
- Right-click → Quit → dialog xác nhận, default = No.
- Chỉ chọn Yes mới thực sự exit; service vẫn chạy nền và giữ login.
Auto-start sau login: shortcut ở
%AppData%\Microsoft\Windows\Start Menu\Programs\Startup\.
9. Troubleshooting
| Triệu chứng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Tray không thấy update | releases.win.json bị cache CDN |
Tắt cache hoặc PURGE URL |
| Update apply xong nhưng version cũ | SHA256 trong feed lệch nupkg | Re-pack & re-upload |
| UAC prompt mỗi lần khởi động | Service bins không match bundle | Chạy thủ công --ts-apply-service-update rồi kiểm tra hash |
Update.exe apply exit 0 nhưng không đổi |
Cùng version, swap bị skip | Tăng <version> rồi pack lại |
| Tray mất icon sau update | Crash sớm trước systray.Run |
Xem %ProgramData%\Tailscale-Custom\Logs\tray.log |
9.1. Vị trí log
- Tray:
%ProgramData%\Tailscale-Custom\Logs\tray.log(fallback%LocalAppData%\Tailscale-Custom\Logs\) - Velopack:
%LocalAppData%\TailscaleCustom\SquirrelClowdTemp\*.log
9.2. Force re-check ngay
Stop-Process -Name tailscale-tray -Force
Start-Process "$env:LOCALAPPDATA\TailscaleCustom\tailscale-tray.exe"
# tray sẽ check feed sau ~90s
9.3. Rollback
Velopack giữ bản cũ trong packages\. Apply package cũ:
$upd = "$env:LOCALAPPDATA\TailscaleCustom\Update.exe"
& $upd apply --silent --package "$env:LOCALAPPDATA\TailscaleCustom\packages\TailscaleCustom-1.0.1-full.nupkg"
10. Bảo mật
- Luôn HTTPS cho feed (tránh MITM nhét nupkg độc).
- Ký số mọi
.exetrước khi pack. - Velopack tự verify SHA256 của nupkg khớp
releases.win.jsontrước khi apply. - Tray KHÔNG nâng quyền nếu hash service bins đã trùng → giảm UAC prompt thừa.
- Service chạy ở
Program Files (x86)\Tailscale-Custom, ACL mặc định (SYSTEM + Administrators full control, Users read-only) là đủ an toàn.
11. Tham khảo file trong repo
| File | Vai trò |
|---|---|
| cmd/tailscale-tray/update.go | Auto-update + lifecycle hooks |
| cmd/tailscale-tray/main.go | Entry point + Quit confirm |
| velopack-releases/ | Output vpk pack |
| sign-release.ps1 | Tạo/ký bằng self-signed cert |
| Setup.wxs | MSI cũ (không dùng với Velopack) |