Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
feba857
chore(deps): bump actions/upload-artifact from 4.6.2 to 7.0.1 (#14)
dependabot[bot] May 18, 2026
9f7b560
chore(deps): bump actions/download-artifact from 4.3.0 to 8.0.1 (#15)
dependabot[bot] May 18, 2026
8bf2c99
chore(deps): bump sha2 from 0.10.9 to 0.11.0 in /lynx (#16)
dependabot[bot] May 18, 2026
c9f801a
chore(deps): bump x509-parser from 0.16.0 to 0.18.1 in /lynx (#17)
dependabot[bot] May 18, 2026
6bcc0dd
chore(deps): bump tower-http from 0.6.10 to 0.6.11 in /lynx (#18)
dependabot[bot] May 18, 2026
3449261
chore(deps): bump notify from 6.1.1 to 8.2.0 in /lynx (#19)
dependabot[bot] May 18, 2026
dc49870
chore(deps): bump nix from 0.29.0 to 0.31.3 in /lynx (#21)
dependabot[bot] May 18, 2026
4326251
chore(deps): bump redis from 0.27.6 to 1.2.1 in /lynx (#20)
dependabot[bot] May 18, 2026
99942c8
chore(deps): bump tokio-tungstenite from 0.24.0 to 0.29.0 in /lynx (#22)
dependabot[bot] May 18, 2026
47e25c8
chore(deps): bump rand from 0.8.6 to 0.10.1 in /lynx (#25)
dependabot[bot] May 18, 2026
47dedb5
chore(deps): bump rcgen from 0.13.2 to 0.14.8 in /lynx (#24)
dependabot[bot] May 18, 2026
517e510
chore(deps): bump bollard from 0.17.1 to 0.21.0 in /lynx (#23)
dependabot[bot] May 18, 2026
d381ce2
chore(deps): update Cargo.lock for bollard 0.21.0
Jaro-c May 18, 2026
6cd1f73
chore: update dependencies in Cargo.lock
Jaro-c May 18, 2026
16d877e
ci: add CI gates to release workflows
Jaro-c May 18, 2026
0d6e153
fix: update code for rand 0.10, rcgen 0.14, and tungstenite 0.29 API …
Jaro-c May 18, 2026
aa4f457
style: apply rustfmt to config.rs and pki.rs
Jaro-c May 18, 2026
ce92b25
Merge remote-tracking branch 'origin/develop' into develop
Jaro-c May 19, 2026
346e10d
fix: sync release-dashboard.yml with main (remove duplicate jobs from…
Jaro-c May 19, 2026
5275c36
fix(dashboard): resolve installation failures for 1.4.1 release
Jaro-c May 19, 2026
0720ed5
fix(lint): pass RELEASE_VERIFY_KEY to Python as argv instead of hardc…
Jaro-c May 19, 2026
b57419b
feat(dashboard): add nginx TLS termination on port 19443
Jaro-c May 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/release-dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,24 @@ jobs:
cp -r .next/static .next/standalone/.next/static
cp -r public .next/standalone/public

- name: Patch server.js for portable __dirname
working-directory: lynx/dashboard/ui
run: |
# bun --compile embeds the compile-time __dirname (CI path) into the binary.
# At runtime on the deployment server that path does not exist, causing chdir to fail.
# Fix: shadow __dirname with a version that falls back to the binary's own directory
# when the compile-time path is absent from disk.
node -e "
const fs = require('fs');
let content = fs.readFileSync('.next/standalone/server.js', 'utf8');
content = content.replace(
\"const path = require('path')\",
\"const path = require('path')\\nconst __dirname = require('fs').existsSync(__dirname) ? __dirname : path.dirname(process.execPath)\"
);
fs.writeFileSync('.next/standalone/server.js', content);
process.stdout.write('server.js patched: __dirname resolves to binary directory on deployment\\n');
"

- name: Compile standalone binary
working-directory: lynx/dashboard/ui
run: |
Expand Down
28 changes: 24 additions & 4 deletions lynx/dashboard/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
services:
nginx:
container_name: lynx-dashboard-nginx
image: docker.io/library/nginx@sha256:65645c7bb6a0661892a8b03b89d0743208a18dd2f3f17a54ef4b76fb8e2f2a10
ports:
- "19443:19443"
volumes:
- /etc/lynx/tls:/etc/lynx/tls:ro
- /etc/lynx/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- /etc/lynx/nginx/updating.html:/etc/lynx/nginx/updating.html:ro
depends_on:
frontend:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "pgrep nginx > /dev/null"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
networks:
- lynx-dashboard-app

frontend:
container_name: lynx-dashboard-frontend
image: docker.io/library/alpine@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
working_dir: /etc/lynx/frontend
command: ["/etc/lynx/frontend/lynx-dashboard-frontend"]
ports:
- "19443:3000"
command: ["/bin/sh", "-c", "apk add --no-cache libgcc libstdc++ && exec /etc/lynx/frontend/lynx-dashboard-frontend"]
environment:
- NODE_ENV=production
- PORT=3000
Expand Down Expand Up @@ -90,7 +110,7 @@ services:
- lynx-dashboard-pg-root
- lynx-dashboard-pg-pass
volumes:
- postgres_data:/var/lib/postgresql/data
- postgres_data:/var/lib/postgresql
- ./server/db/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d lynx_dashboard"]
Expand Down
30 changes: 30 additions & 0 deletions lynx/dashboard/nginx/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
server {
listen 19443 ssl;

ssl_certificate /etc/lynx/tls/dashboard.crt;
ssl_certificate_key /etc/lynx/tls/dashboard.key;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;

add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy no-referrer always;

location / {
proxy_pass http://lynx-dashboard-frontend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 3600s;
}

error_page 502 503 /updating.html;
location = /updating.html {
root /etc/lynx/nginx;
internal;
}
}
45 changes: 45 additions & 0 deletions lynx/dashboard/nginx/updating.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="5">
<title>Lynx — Updating</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #0f1117;
color: #e2e8f0;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.card {
text-align: center;
max-width: 400px;
padding: 2.5rem;
}
.spinner {
width: 48px;
height: 48px;
border: 3px solid #334155;
border-top-color: #6366f1;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin: 0 auto 1.5rem;
}
@keyframes spin { to { transform: rotate(360deg); } }
h1 { font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem; }
p { font-size: 0.875rem; color: #94a3b8; }
</style>
</head>
<body>
<div class="card">
<div class="spinner"></div>
<h1>Updating Lynx Dashboard</h1>
<p>The system is applying an update. This page will reload automatically.</p>
</div>
</body>
</html>
2 changes: 1 addition & 1 deletion lynx/dashboard/server/db/init/01-init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ GRANT CONNECT ON DATABASE lynx_dashboard TO lynx_dashboard_app;

\connect lynx_dashboard

GRANT USAGE ON SCHEMA public TO lynx_dashboard_app;
GRANT USAGE, CREATE ON SCHEMA public TO lynx_dashboard_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO lynx_dashboard_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
Expand Down
4 changes: 3 additions & 1 deletion lynx/dashboard/server/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ async fn check_releases(state: &AppState) {
}

if let Some(ref ver) = latest_dashboard {
let current = env!("CARGO_PKG_VERSION");
let current = std::fs::read_to_string(crate::update::VERSION_FILE)
.map(|s| s.trim().to_string())
.unwrap_or_else(|_| env!("CARGO_PKG_VERSION").to_string());
if ver.as_str() != current {
tracing::info!(latest = %ver, current, "scheduler: dashboard update available");
trigger_dashboard_update(state, ver).await;
Expand Down
10 changes: 10 additions & 0 deletions lynx/dashboard/server/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const FRONTEND_BINARY: &str = "/etc/lynx/frontend/lynx-dashboard-frontend";
const FRONTEND_DIR: &str = "/etc/lynx/frontend";
const FRONTEND_CONTAINER: &str = "lynx-dashboard-frontend";
const PODMAN_SOCKET: &str = "/run/podman/podman.sock";
pub const VERSION_FILE: &str = "/etc/lynx/bin/dashboard-version";
const MAX_DOWNLOAD_BYTES: usize = 200 * 1024 * 1024;

pub struct DashboardUpdateParams {
Expand Down Expand Up @@ -52,6 +53,9 @@ pub async fn perform_dashboard_update(p: DashboardUpdateParams) {
.await;

if result.is_ok() {
if let Err(e) = write_version_file(&p.version) {
tracing::warn!("could not write version file: {e}");
}
tracing::info!(
version = p.version,
"dashboard update complete — exiting for Podman restart"
Expand Down Expand Up @@ -166,6 +170,12 @@ fn swap_backend_binary(binary: &[u8]) -> Result<()> {
Ok(())
}

/// Write `version` to the on-disk version file so the scheduler can read it
/// on the next startup and avoid re-triggering the same update.
pub(crate) fn write_version_file(version: &str) -> Result<()> {
std::fs::write(VERSION_FILE, version).context("write version file")
}

fn extract_assets(data: &[u8], dest: &str) -> Result<()> {
use std::io::Write;
use std::process::{Command, Stdio};
Expand Down
Loading
Loading