A self-updating application written in Go 1.25.
Nametag uses a two-binary architecture for reliable self-updates:
┌─────────────────┐ HTTPS ┌─────────────────┐
│ Update Server │◄─────────────────│ nametag │
│ (serves bins) │ │ (main app) │
└─────────────────┘ └────────┬────────┘
▲ │ spawn
│ HTTPS ▼
└────────────────────────────┌────────────────┐
│ nametag-up │
│ (updater) │
└────────────────┘
Update Flow:
nametagchecks the server for a new version- Downloads new binary to a temp location
- Verifies SHA256 checksum
- Spawns
nametag-upwith update instructions nametagexitsnametag-upwaits for parent process to exit- Replaces the old binary with the new one (atomic)
- Launches the updated
nametag
Requires Go 1.25 or later and just.
# Build for current platform
just build
# Build for all platforms (darwin, linux, windows)
just build-all
# Run tests
just test
# Create release structure for server
just version=1.0.0 release# Show version
./bin/nametag version
# Check for updates
./bin/nametag check -server http://localhost:8080
# Download and apply updates
./bin/nametag update -server http://localhost:8080# Start the server (serves from ./releases directory)
./bin/server -addr :8080 -assets ./releases
# Or use just
just serverThe server expects binaries organized as:
releases/
├── nametag/
│ └── 1.0.0/
│ ├── nametag-darwin-amd64
│ ├── nametag-darwin-arm64
│ ├── nametag-linux-amd64
│ ├── nametag-linux-arm64
│ └── nametag-windows-amd64.exe
└── nametag-up/
└── 1.0.0/
├── nametag-up-darwin-amd64
└── ...
| Endpoint | Description |
|---|---|
GET /v1/manifest.json |
Version manifest with checksums |
GET /v1/download/{component}/{platform}/{version} |
Download binary |
GET /health |
Health check |
{
"schema_version": 1,
"generated": "2025-01-15T10:30:00Z",
"components": {
"nametag": {
"name": "nametag",
"version": "1.1.0",
"release_date": "2025-01-15T10:00:00Z",
"assets": {
"linux-amd64": {
"url": "/v1/download/nametag/linux-amd64/1.1.0",
"size": 5242880,
"sha256": "abc123..."
}
}
}
}
}| Platform | Status |
|---|---|
| Linux (amd64, arm64) | Supported |
| macOS (amd64, arm64) | Supported |
| Windows (amd64) | Supported |
Windows:
- Running executables can be renamed but not overwritten
- Old binaries are cleaned up on next startup
macOS:
- Quarantine extended attribute is automatically removed from downloaded binaries
-
Build version 1.0.0:
just version=1.0.0 release
-
Copy binaries to a test location:
cp bin/nametag /tmp/test/nametag cp bin/nametag-up /tmp/test/nametag-up
-
Build version 1.1.0:
just version=1.1.0 release
-
Start the server:
just server
-
Run update from the test location:
cd /tmp/test ./nametag update -server http://localhost:8080 -
Verify the update:
./nametag version
-
Two-Binary Architecture: Separates update logic from application logic. The updater is minimal and stable, while the main app can change frequently.
-
SHA256 Verification: Each download is verified against the checksum in the manifest.
-
Atomic Replacement: Uses rename operations for atomic updates. On failure, the old binary is restored.
-
IPC via File: Update commands are passed between processes via a JSON file, which is more reliable than command-line arguments for complex data.
-
Structured Logging: Uses Go's
log/slogpackage for consistent, queryable logs.
nametag/
├── cmd/
│ ├── nametag/ # Main application
│ ├── nametag-up/ # Updater binary
│ └── server/ # Update server
├── internal/
│ ├── update/ # Update logic (checker, downloader, replacer)
│ ├── platform/ # Platform-specific code
│ └── ipc/ # Inter-process communication
├── go.mod
├── justfile
└── README.md
- Go 1.25+
- For Windows builds:
golang.org/x/sys/windowspackage
MIT