Skip to content

Commit ff5f32d

Browse files
committed
Add auto-updater to all install channels and simplify dashboard
- Add pilot-updater to release workflow, install script, Python SDK, Node SDK, and documentation - Install script sets up systemd/launchd services for auto-updater with auto-restart (Restart=always / KeepAlive=true) - Write .pilot-version file during install for updater version tracking - Replace dashboard tags/task-executor cards with client version distribution chart - Uninstall cleans up both daemon and updater services
1 parent 4102371 commit ff5f32d

17 files changed

Lines changed: 318 additions & 175 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
run: |
6666
VERSION=${GITHUB_REF_NAME}
6767
LDFLAGS="-s -w -X main.version=${VERSION}"
68-
BINS="daemon pilotctl gateway registry beacon rendezvous nameserver"
68+
BINS="daemon pilotctl gateway registry beacon rendezvous nameserver updater"
6969
mkdir -p dist
7070
for bin in $BINS; do
7171
echo "Building $bin for ${{ matrix.goos }}/${{ matrix.goarch }}..."

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,11 @@ curl -fsSL https://pilotprotocol.network/install.sh | PILOT_EMAIL=user@example.c
272272

273273
- Detects your platform (linux/darwin, amd64/arm64)
274274
- Downloads pre-built binaries from the latest release (falls back to building from source if Go is available)
275-
- Installs `pilot-daemon`, `pilotctl`, and `pilot-gateway` to `~/.pilot/bin`
275+
- Installs `pilot-daemon`, `pilotctl`, `pilot-gateway`, and `pilot-updater` to `~/.pilot/bin`
276276
- Adds `~/.pilot/bin` to your PATH
277277
- Writes `~/.pilot/config.json` with the public rendezvous server pre-configured
278-
- Sets up a system service (**Linux**: systemd, **macOS**: launchd)
278+
- Sets up system services (**Linux**: systemd, **macOS**: launchd) for daemon and auto-updater
279+
- The auto-updater runs in the background, checking for new releases every hour and applying updates automatically
279280

280281
**Uninstall:** `curl -fsSL https://pilotprotocol.network/install.sh | sh -s uninstall`
281282

cmd/pilotctl/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,9 @@ func runDaemonInternal(args []string) {
15651565
listenAddr := flagString(flags, "listen", ":0")
15661566
socketPath := flagString(flags, "socket", defaultSocket)
15671567
identityPath := flagString(flags, "identity", "")
1568+
if identityPath == "" {
1569+
identityPath = configDir() + "/identity.json"
1570+
}
15681571
email := flagString(flags, "email", "")
15691572
owner := flagString(flags, "owner", "")
15701573
if email == "" && owner != "" {
@@ -1626,6 +1629,7 @@ func runDaemonForeground(configFile, registryAddr, beaconAddr, listenAddr,
16261629
AdminToken: adminToken,
16271630
Networks: pilotctlParseNetworkIDs(networks),
16281631
TrustAutoApprove: trustAutoApprove,
1632+
Version: version,
16291633
})
16301634

16311635
if err := d.Start(); err != nil {

install.sh

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,31 @@ if [ "${1}" = "uninstall" ]; then
3131
pilotctl gateway stop 2>/dev/null || true
3232
fi
3333

34-
# Remove system service
35-
if [ "$OS" = "linux" ] && [ -f /etc/systemd/system/pilot-daemon.service ]; then
34+
# Remove system services (daemon + updater)
35+
if [ "$OS" = "linux" ]; then
3636
if [ "$(id -u)" = "0" ] || sudo -n true 2>/dev/null; then
37-
sudo systemctl stop pilot-daemon 2>/dev/null || true
38-
sudo systemctl disable pilot-daemon 2>/dev/null || true
39-
sudo rm -f /etc/systemd/system/pilot-daemon.service
37+
for svc in pilot-daemon pilot-updater; do
38+
if [ -f "/etc/systemd/system/${svc}.service" ]; then
39+
sudo systemctl stop "$svc" 2>/dev/null || true
40+
sudo systemctl disable "$svc" 2>/dev/null || true
41+
sudo rm -f "/etc/systemd/system/${svc}.service"
42+
fi
43+
done
4044
sudo systemctl daemon-reload
41-
echo " Removed systemd service"
45+
echo " Removed systemd services"
4246
else
4347
echo " Skipped systemd removal (run with sudo to remove)"
4448
fi
4549
fi
4650
if [ "$OS" = "darwin" ]; then
47-
PLIST="$HOME/Library/LaunchAgents/com.vulturelabs.pilot-daemon.plist"
48-
if [ -f "$PLIST" ]; then
49-
launchctl unload "$PLIST" 2>/dev/null || true
50-
rm -f "$PLIST"
51-
echo " Removed LaunchAgent"
52-
fi
51+
for label in com.vulturelabs.pilot-daemon com.vulturelabs.pilot-updater; do
52+
PLIST="$HOME/Library/LaunchAgents/${label}.plist"
53+
if [ -f "$PLIST" ]; then
54+
launchctl unload "$PLIST" 2>/dev/null || true
55+
rm -f "$PLIST"
56+
fi
57+
done
58+
echo " Removed LaunchAgents"
5359
fi
5460

5561
# Remove pilot directory (binaries, config, identity, received files)
@@ -165,6 +171,8 @@ if [ -z "$TAG" ]; then
165171
CGO_ENABLED=0 go build -o "$TMPDIR/pilotctl" "$TMPDIR/src/cmd/pilotctl"
166172
echo "Building gateway..."
167173
CGO_ENABLED=0 go build -o "$TMPDIR/pilot-gateway" "$TMPDIR/src/cmd/gateway"
174+
echo "Building updater..."
175+
CGO_ENABLED=0 go build -o "$TMPDIR/pilot-updater" "$TMPDIR/src/cmd/updater"
168176
fi
169177

170178
# --- Install binaries to ~/.pilot/bin ---
@@ -184,7 +192,13 @@ if [ -f "$TMPDIR/gateway" ]; then
184192
else
185193
cp "$TMPDIR/pilot-gateway" "$BIN_DIR/pilot-gateway"
186194
fi
195+
if [ -f "$TMPDIR/updater" ]; then
196+
cp "$TMPDIR/updater" "$BIN_DIR/pilot-updater"
197+
elif [ -f "$TMPDIR/pilot-updater" ]; then
198+
cp "$TMPDIR/pilot-updater" "$BIN_DIR/pilot-updater"
199+
fi
187200
chmod 755 "$BIN_DIR/pilot-daemon" "$BIN_DIR/pilotctl" "$BIN_DIR/pilot-gateway"
201+
[ -f "$BIN_DIR/pilot-updater" ] && chmod 755 "$BIN_DIR/pilot-updater"
188202

189203
# --- Symlink to /usr/local/bin if writable, otherwise skip ---
190204

@@ -193,17 +207,21 @@ if [ -d "$LINK_DIR" ] && [ -w "$LINK_DIR" ]; then
193207
ln -sf "$BIN_DIR/pilot-daemon" "$LINK_DIR/pilot-daemon"
194208
ln -sf "$BIN_DIR/pilotctl" "$LINK_DIR/pilotctl"
195209
ln -sf "$BIN_DIR/pilot-gateway" "$LINK_DIR/pilot-gateway"
210+
[ -f "$BIN_DIR/pilot-updater" ] && ln -sf "$BIN_DIR/pilot-updater" "$LINK_DIR/pilot-updater"
196211
echo " Symlinked to ${LINK_DIR}"
197212
fi
198213

199214
# --- Update: stop here, skip config/service/PATH setup ---
200215

201216
if [ "$UPDATING" = true ]; then
217+
# Write version file for the auto-updater
218+
[ -n "$TAG" ] && echo "$TAG" > "$BIN_DIR/.pilot-version"
202219
echo ""
203220
echo "Updated to ${TAG:-source}:"
204-
echo " pilot-daemon ${BIN_DIR}/pilot-daemon"
205-
echo " pilotctl ${BIN_DIR}/pilotctl"
206-
echo " pilot-gateway ${BIN_DIR}/pilot-gateway"
221+
echo " pilot-daemon ${BIN_DIR}/pilot-daemon"
222+
echo " pilotctl ${BIN_DIR}/pilotctl"
223+
echo " pilot-gateway ${BIN_DIR}/pilot-gateway"
224+
echo " pilot-updater ${BIN_DIR}/pilot-updater"
207225
echo ""
208226
echo "Restart the daemon to use the new version:"
209227
echo " pilotctl daemon stop && pilotctl daemon start"
@@ -266,10 +284,32 @@ RestartSec=5
266284
[Install]
267285
WantedBy=multi-user.target
268286
SVC
287+
# Auto-updater service
288+
if [ -f "$BIN_DIR/pilot-updater" ]; then
289+
sudo tee /etc/systemd/system/pilot-updater.service >/dev/null <<USVC
290+
[Unit]
291+
Description=Pilot Protocol Auto-Updater
292+
After=network-online.target
293+
Wants=network-online.target
294+
295+
[Service]
296+
Type=simple
297+
User=$(whoami)
298+
ExecStart=${BIN_DIR}/pilot-updater \\
299+
-install-dir ${BIN_DIR}
300+
Restart=always
301+
RestartSec=30
302+
303+
[Install]
304+
WantedBy=multi-user.target
305+
USVC
306+
fi
307+
269308
sudo systemctl daemon-reload
270309
echo " Service: pilot-daemon.service"
271-
echo " Start: sudo systemctl start pilot-daemon"
272-
echo " Enable: sudo systemctl enable pilot-daemon"
310+
echo " Service: pilot-updater.service (auto-updates)"
311+
echo " Start: sudo systemctl start pilot-daemon pilot-updater"
312+
echo " Enable: sudo systemctl enable pilot-daemon pilot-updater"
273313
else
274314
echo " Skipped systemd setup (run as root or with passwordless sudo to enable)"
275315
fi
@@ -327,7 +367,37 @@ ${EXTRA_ARGS} </array>
327367
</dict>
328368
</plist>
329369
PLIST
370+
# Auto-updater LaunchAgent
371+
if [ -f "$BIN_DIR/pilot-updater" ]; then
372+
UPLIST="$PLIST_DIR/com.vulturelabs.pilot-updater.plist"
373+
cat > "$UPLIST" <<UPLIST
374+
<?xml version="1.0" encoding="UTF-8"?>
375+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
376+
<plist version="1.0">
377+
<dict>
378+
<key>Label</key>
379+
<string>com.vulturelabs.pilot-updater</string>
380+
<key>ProgramArguments</key>
381+
<array>
382+
<string>${BIN_DIR}/pilot-updater</string>
383+
<string>-install-dir</string>
384+
<string>${BIN_DIR}</string>
385+
</array>
386+
<key>RunAtLoad</key>
387+
<true/>
388+
<key>KeepAlive</key>
389+
<true/>
390+
<key>StandardOutPath</key>
391+
<string>${PILOT_DIR}/updater.log</string>
392+
<key>StandardErrorPath</key>
393+
<string>${PILOT_DIR}/updater.log</string>
394+
</dict>
395+
</plist>
396+
UPLIST
397+
fi
398+
330399
echo " Service: com.vulturelabs.pilot-daemon"
400+
echo " Service: com.vulturelabs.pilot-updater (auto-updates)"
331401
echo " Start: launchctl load $PLIST"
332402
echo " Stop: launchctl unload $PLIST"
333403
fi
@@ -358,11 +428,15 @@ fi
358428

359429
# --- Verify ---
360430

431+
# Write version file for the auto-updater
432+
[ -n "$TAG" ] && echo "$TAG" > "$BIN_DIR/.pilot-version"
433+
361434
echo ""
362435
echo "Installed:"
363-
echo " pilot-daemon ${BIN_DIR}/pilot-daemon"
364-
echo " pilotctl ${BIN_DIR}/pilotctl"
365-
echo " pilot-gateway ${BIN_DIR}/pilot-gateway"
436+
echo " pilot-daemon ${BIN_DIR}/pilot-daemon"
437+
echo " pilotctl ${BIN_DIR}/pilotctl"
438+
echo " pilot-gateway ${BIN_DIR}/pilot-gateway"
439+
echo " pilot-updater ${BIN_DIR}/pilot-updater (auto-updates in background)"
366440
echo ""
367441
echo "Config: ${PILOT_DIR}/config.json"
368442
echo " Registry: ${REGISTRY}"

pkg/registry/dashboard.go

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -137,23 +137,6 @@ func (s *Server) ServeDashboard(addr string) error {
137137
serveBadge(w, "requests", fmtCount(int(stats.TotalRequests)), "#a855f7")
138138
})
139139

140-
mux.HandleFunc("/api/badge/tags", func(w http.ResponseWriter, r *http.Request) {
141-
stats := s.GetDashboardStats()
142-
c := "#f59e0b"
143-
if stats.UniqueTags == 0 {
144-
c = "#9f9f9f"
145-
}
146-
serveBadge(w, "tags", fmtCount(stats.UniqueTags), c)
147-
})
148-
149-
mux.HandleFunc("/api/badge/task-executors", func(w http.ResponseWriter, r *http.Request) {
150-
stats := s.GetDashboardStats()
151-
c := "#4c1"
152-
if stats.TaskExecutors == 0 {
153-
c = "#9f9f9f"
154-
}
155-
serveBadge(w, "task executors", fmtCount(stats.TaskExecutors), c)
156-
})
157140

158141
// Snapshot trigger endpoint (POST only, localhost only)
159142
mux.HandleFunc("/api/snapshot", func(w http.ResponseWriter, r *http.Request) {
@@ -242,16 +225,23 @@ a:hover{text-decoration:underline}
242225
243226
.container{max-width:960px;margin:0 auto;padding:24px 16px}
244227
245-
header{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-bottom:1px solid #21262d;margin-bottom:32px}
228+
header{padding:16px 0;border-bottom:1px solid #21262d;margin-bottom:32px}
246229
header h1{font-size:20px;font-weight:600;color:#e6edf3}
247-
header .links{display:flex;gap:16px;font-size:13px}
248230
.uptime{font-size:12px;color:#8b949e;margin-top:4px}
249231
250-
.stats-row{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:32px}
232+
.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:32px}
251233
.stat-card{background:#161b22;border:1px solid #21262d;border-radius:8px;padding:20px;text-align:center}
252234
.stat-card .value{font-size:32px;font-weight:700;color:#e6edf3;display:block}
253235
.stat-card .label{font-size:12px;color:#8b949e;text-transform:uppercase;letter-spacing:0.5px;margin-top:4px}
254236
237+
.versions{background:#161b22;border:1px solid #21262d;border-radius:8px;padding:20px}
238+
.versions h2{font-size:14px;font-weight:600;color:#8b949e;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:12px}
239+
.ver-row{display:flex;align-items:center;gap:12px;margin-bottom:8px}
240+
.ver-label{min-width:120px;font-size:13px;color:#c9d1d9}
241+
.ver-bar-bg{flex:1;height:20px;background:#0d1117;border-radius:4px;overflow:hidden}
242+
.ver-bar{height:100%;border-radius:4px;transition:width 0.3s}
243+
.ver-count{min-width:60px;text-align:right;font-size:13px;color:#8b949e}
244+
255245
footer{text-align:center;padding:24px 0;border-top:1px solid #21262d;margin-top:32px;font-size:12px;color:#484f58}
256246
footer a{color:#484f58}
257247
footer a:hover{color:#58a6ff}
@@ -269,10 +259,6 @@ footer a:hover{color:#58a6ff}
269259
<h1>Pilot Protocol</h1>
270260
<div class="uptime">Uptime: <span id="uptime">—</span></div>
271261
</div>
272-
<div class="links">
273-
<a href="https://github.com/TeoSlayer/pilotprotocol">GitHub</a>
274-
<a href="https://pilotprotocol.network">pilotprotocol.network</a>
275-
</div>
276262
</header>
277263
278264
<div class="stats-row">
@@ -292,16 +278,10 @@ footer a:hover{color:#58a6ff}
292278
<span class="value" id="trust-links">—</span>
293279
<span class="label">Trust Links</span>
294280
</div>
295-
<div class="stat-card">
296-
<span class="value" id="unique-tags">—</span>
297-
<span class="label">Unique Tags</span>
298-
</div>
299-
<div class="stat-card">
300-
<span class="value" id="task-executors">—</span>
301-
<span class="label">Task Executors</span>
302-
</div>
303281
</div>
304282
283+
<div class="versions" id="versions"></div>
284+
305285
<footer>
306286
Pilot Protocol &middot;
307287
<a href="https://pilotprotocol.network">pilotprotocol.network</a> &middot;
@@ -312,15 +292,28 @@ footer a:hover{color:#58a6ff}
312292
<script>
313293
function fmt(n){if(n>=1e9)return(n/1e9).toFixed(1)+'B';if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'K';return n.toString()}
314294
function uptimeStr(s){var d=Math.floor(s/86400),h=Math.floor(s%86400/3600),m=Math.floor(s%3600/60);var p=[];if(d)p.push(d+'d');if(h)p.push(h+'h');p.push(m+'m');return p.join(' ')}
295+
function renderVersions(versions){
296+
var el=document.getElementById('versions');
297+
if(!versions||!Object.keys(versions).length){el.innerHTML='';return}
298+
var sorted=Object.entries(versions).sort(function(a,b){return b[1]-a[1]});
299+
var max=sorted[0][1];
300+
var colors=['#58a6ff','#3fb950','#a855f7','#f59e0b','#f97316','#ef4444','#8b949e'];
301+
var html='<h2>Client Versions</h2>';
302+
sorted.forEach(function(e,i){
303+
var pct=Math.max(2,Math.round(e[1]/max*100));
304+
var c=colors[i%colors.length];
305+
html+='<div class="ver-row"><span class="ver-label">'+e[0]+'</span><div class="ver-bar-bg"><div class="ver-bar" style="width:'+pct+'%;background:'+c+'"></div></div><span class="ver-count">'+fmt(e[1])+'</span></div>';
306+
});
307+
el.innerHTML=html;
308+
}
315309
function update(){
316310
fetch('/api/stats').then(function(r){return r.json()}).then(function(d){
317311
document.getElementById('total-requests').textContent=fmt(d.total_requests);
318312
document.getElementById('total-nodes').textContent=fmt(d.total_nodes||0);
319313
document.getElementById('active-nodes').textContent=fmt(d.active_nodes||0);
320314
document.getElementById('trust-links').textContent=fmt(d.total_trust_links||0);
321-
document.getElementById('unique-tags').textContent=fmt(d.unique_tags||0);
322-
document.getElementById('task-executors').textContent=fmt(d.task_executors||0);
323315
document.getElementById('uptime').textContent=uptimeStr(d.uptime_secs);
316+
renderVersions(d.versions);
324317
}).catch(function(){})
325318
}
326319
update();setInterval(update,30000);

0 commit comments

Comments
 (0)