Skip to content

Web portal sections, Let's Encrypt, Cloudflare Tunnel, and no redirect loop behind IIS - #19

Merged
epell-development merged 6 commits into
mainfrom
claude/project-thread-ogfpo1
Oct 26, 2026
Merged

epell-development merged 6 commits into
mainfrom
claude/project-thread-ogfpo1

Conversation

@epell-development

Copy link
Copy Markdown
Owner

Requested by epell · project thread

The Webportal and Server parts of the v0.2.9 list.

Server

  • IIS URL_TOO_MANY_REDIRECTS fixed. With HTTPS on, every plain-HTTP request got a 301 to https://, and a proxy that handles HTTPS itself (IIS/ARR, nginx, Cloudflare) sent it straight back. Plain requests from a proxy that says the browser used HTTPS (X-Forwarded-Proto: https, IIS's X-ARR-SSL, CF-Visitor, Forwarded: proto=https) are now passed through instead of redirected (tls.rs, behind_https_proxy, pass_plain).
  • Let's Encrypt (new src/acme.rs, RFC 8555 with ring + rcgen + ureq, no new async stack). HTTP-01 on port 80 (also answered on every port, so a proxy can pass /.well-known/acme-challenge/ on). Renews with 30 days left. The new certificate is swapped in without a restart: the TLS config now uses a swappable cert resolver (tls::CertSwap). Port 80 also redirects browsers to the portal. CLI: --acme, --acme-email, --acme-staging, --acme-http-port, --no-acme. Setup asks for a domain.
  • Certificates given with --cert/--key are reloaded when the files change (certbot, win-acme), checked every 5 minutes.
  • Cloudflare Tunnel (optional) (new src/tunnel.rs). The server runs cloudflared, restarts it if it dies and stops it on exit. With a token it uses your own domain; without one you get a quick trycloudflare.com address (shown in the log and Server settings). The token goes in through the environment, not argv. Switching it on or off in Server settings takes effect at once. CLI: --tunnel, --tunnel-token / CLOUDFLARE_TUNNEL_TOKEN, --cloudflared.
  • Update check on the server (every 6 h). ServerInfo gains latest, release_page, cert_expires, acme_* and tunnel_*. updater::check/download now take a repaint closure instead of an egui Context, so the server can use it.
  • ServerUpdate's new fields are Options, so an older app that saves Server settings doesn't switch Let's Encrypt or the tunnel off.
  • The app's File → Server settings gets the Let's Encrypt and Cloudflare Tunnel options, their status, and the certificate's expiry date.
  • New API: POST /api/v1/azuracast (proxies only /station/{st}…, /nowplaying…, /status and /stations, using the AzuraCast settings), plus Cmd::Insert and Cmd::RemoveRows for portal playlist edits.

Web portal

  • Sections: Studio, Library (search, add to any playlist at the end or to play next, rescan), Carts (cart wall, edit pads from the library), Generator (generate, auto-fill, edit templates and categories with server folders), Adverts (rules: on/off, play now, add/edit/delete), AzuraCast (now playing, listeners, skip/restart, start/stop broadcasting and the AutoDJ, toggle AzuraCast playlists, connection settings), Log.
  • Settings edits replay on the server's latest copy if someone else changed it first (409), so they don't overwrite each other.
  • Mobile: one column, the section bar fixed along the bottom, bigger tap targets, safe areas, no sideways scroll (checked at 390 px).
  • Black flash before login fixed. #desk {display:grid} was overriding the hidden attribute. [hidden] now always wins, and nothing shows until the script has picked the sign-in box or the desk.
  • RadioMaster favicon (/favicon.png, /favicon.ico, apple-touch-icon).
  • Banner when the server is behind a newer release.

Testing

  • cargo build has no warnings, cargo fmt was run, and cargo test passes. New tests:
    • proxy pass-through with IIS, X-Forwarded-Proto and Cloudflare headers, and the redirect still happening for X-Forwarded-Proto: http
    • favicon
    • the ACME challenge path
    • certificate swap
    • not_after parsing for UTCTime and GeneralizedTime
    • JWS/JWK signing
    • tunnel log parsing and a missing cloudflared
  • Let's Encrypt end to end against Pebble (Let's Encrypt's test server): account, http-01 check, finalize, download, a second order on the same account, and loading the result into the server. This test runs when PEBBLE/PEBBLE_DIR are set and is skipped otherwise, so CI skips it.
  • The portal was driven in headless Chromium against a real server at desktop and iPhone sizes: sign in, rescan, add tracks, edit a category and template, generate, set and play a cart pad, add a timed rule, AzuraCast view, update banner. No console or CSP errors.
  • Not tested for real: a real Let's Encrypt certificate (needs a public domain with port 80), a real cloudflared, IIS itself, and AzuraCast control calls against a real station.

Changelog lines (for v0.2.9)

New

  • Web portal: library, cart wall, playlist generator, advert and jingle rules, and AzuraCast station management; works on phones; says when the server is behind an update.
  • RadioMaster Server: free HTTPS certificates from Let's Encrypt, renewed on their own.
  • RadioMaster Server: optional Cloudflare Tunnel for the web portal.
  • Certificates given to the server are picked up again when they're renewed.

Fixed

  • ERR_TOO_MANY_REDIRECTS with HTTPS on behind IIS or another reverse proxy.
  • The web portal showed its desk for a moment before signing in.
  • The web portal now has the RadioMaster icon.

Docs: docs/SERVER.md covers Let's Encrypt, reverse proxies (IIS, nginx) and Cloudflare Tunnel. The website's /server/ page is being updated in PR #14.


Generated by Claude Code

…and no redirect loop behind an HTTPS proxy

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8hDUgJ5YkhjxzEwQXHEps
… layout, update notice, icon; no flash before sign-in

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8hDUgJ5YkhjxzEwQXHEps
@epell-development epell-development self-assigned this Oct 25, 2026

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MergeGuard review
Risk: 🔴 60/100 — risky
Findings: 🔴 1 high · 🟠 1 medium

Recommended fixes

  • src/tls.rs — Restrict HTTPS-bypass logic to trusted proxy sources by verifying the peer address is loopback (or later extend to a configurable allowlist). Continue to allow ACME challenge paths. (confidence 86%)
Security review — 🔴 1 high · 🟠 1 medium (2 findings)

Dependency, filesystem & container image checks (OSV + Trivy + container scan) for this pull request.
Lockfile and manifest advisories are listed here only — bump affected versions in package.json (or your package manager manifest) and regenerate lockfiles locally. MergeGuard does not post these on Files changed or auto-fix lockfiles.

Cargo.lock — 🟠 1 medium (1 finding)
  • 🟠 medium · security — Trivy (GHSA-wrw7-89jp-8q8g): Unsoundness in Iterator and DoubleEndedIterator impls for glib::VariantStrIter — glib@0.18.5 in Cargo.lock

    Review GHSA-wrw7-89jp-8q8g and upgrade or remove the affected dependency. Reference: GHSA-wrw7-89jp-8q8g

__Open **Files changed** for 1 inline review thread on the diff.__

💡 Use the Fix All MergeGuard Findings checkbox comment on this PR to apply auto-fixes in batch, or reply @mergeguards fix on any inline finding.

⚠️ This PR may benefit from a deep scan — consider triggering @mergeguards deep-scan.

MergeGuard scope: 1 dependency manifest or lockfile (package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, etc.) was not sent to the AI reviewer — MergeGuard runs OSV/Trivy security scans on those paths instead. Upgrade vulnerable packages in your manifest and regenerate lockfiles locally.


Reviewed by MergeGuard AI

Comment thread src/tls.rs Outdated
@mergeguards

mergeguards Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

MergeGuard agent actions

Fix all findings

All issues (1)
  • medium · src/server.rs:642 — The /api/v1/server endpoint is exposed without authentication, leaking potentially sensitive server configuration (e.g., Let's Encrypt contact email, certificate expiry, tunnel status/URL, devices)…

Apply auto-fixes for the findings above on this branch:

  • Fix All MergeGuard Findings

Or reply with @mergeguards fix-all on this pull request.

epell-development pushed a commit that referenced this pull request Oct 25, 2026
…ctions

Documents the 0.2.9 server features from PR #19: --acme (automatic Let's
Encrypt with renewal, port 80, staging), --tunnel / --tunnel-token
(Cloudflare Tunnel via cloudflared), certificate files reloaded without a
restart, reverse proxies that send X-Forwarded-Proto / X-ARR-SSL /
CF-Visitor / Forwarded no longer loop, and the portal's Library, Carts,
Generator, Adverts and AzuraCast sections with the update banner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014PpT9xjPyRV4m8YCwhW7if
…e or the local network

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8hDUgJ5YkhjxzEwQXHEps

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ MergeGuard — new commits are on this pull request.

Re-scan the latest diff: paste this line by itself (copy/paste is fine) — either on the Conversation tab (Add a comment) or on Files changed as a comment on the diff:

@mergeguard-followup

Copy link
Copy Markdown
Owner Author

MergeGuard's two findings:

  • High, src/tls.rs (spoofable proxy headers): fixed in 965bd85. The headers are now only believed from loopback or private/link-local/ULA addresses.
  • Medium, glib@0.18.5 (GHSA-wrw7-89jp-8q8g): this isn't from this PR. glib comes in through rfd's GTK backend and is already in Cargo.lock on main; this PR only adds ring, which was already in the lock through rustls. Fixing it means moving rfd to a version without GTK 0.18 (or onto its xdg-portal backend), which belongs in its own change.

Generated by Claude Code

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MergeGuard review
Risk: 🟢 23/100 — low risk
Findings: 🟠 1 medium

Security review — 🟠 1 medium (1 finding)

Dependency, filesystem & container image checks (OSV + Trivy + container scan) for this pull request.
Lockfile and manifest advisories are listed here only — bump affected versions in package.json (or your package manager manifest) and regenerate lockfiles locally. MergeGuard does not post these on Files changed or auto-fix lockfiles.

Cargo.lock — 🟠 1 medium (1 finding)
  • 🟠 medium · security — Trivy (GHSA-wrw7-89jp-8q8g): Unsoundness in Iterator and DoubleEndedIterator impls for glib::VariantStrIter — glib@0.18.5 in Cargo.lock

    Review GHSA-wrw7-89jp-8q8g and upgrade or remove the affected dependency. Reference: GHSA-wrw7-89jp-8q8g

⚠️ This PR may benefit from a deep scan — consider triggering @mergeguards deep-scan.

MergeGuard scope: 1 dependency manifest or lockfile (package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, etc.) was not sent to the AI reviewer — MergeGuard runs OSV/Trivy security scans on those paths instead. Upgrade vulnerable packages in your manifest and regenerate lockfiles locally.


Reviewed by MergeGuard AI

The data folder is set once per test process, so a playlist one test saved
leaked into the next and made https_and_portal / app_and_server_stay_in_step
time out depending on test order (CI test-linux).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8hDUgJ5YkhjxzEwQXHEps

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ MergeGuard — new commits are on this pull request.

Re-scan the latest diff: paste this line by itself (copy/paste is fine) — either on the Conversation tab (Add a comment) or on Files changed as a comment on the diff:

@mergeguard-followup

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MergeGuard review
Risk: 🟠 35/100 — moderate
Findings: 🟠 3 medium

Recommended fixes

  • src/server.rs — Do not persist the Cloudflare Tunnel token to server.json; keep it in-memory only while still allowing updates from the UI. This prevents plaintext secrets at rest. (confidence 78%)
  • src/server_main.rs — Avoid persisting the Cloudflare Tunnel token from CLI flags to the saved config to reduce leakage via server.json. Encourage use of the environment variable instead. (confidence 70%)
Security review — 🟠 3 medium (3 findings)

Dependency, filesystem & container image checks (OSV + Trivy + container scan) for this pull request.
Lockfile and manifest advisories are listed here only — bump affected versions in package.json (or your package manager manifest) and regenerate lockfiles locally. MergeGuard does not post these on Files changed or auto-fix lockfiles.

Cargo.lock — 🟠 1 medium (1 finding)
  • 🟠 medium · security — Trivy (GHSA-wrw7-89jp-8q8g): Unsoundness in Iterator and DoubleEndedIterator impls for glib::VariantStrIter — glib@0.18.5 in Cargo.lock

    Review GHSA-wrw7-89jp-8q8g and upgrade or remove the affected dependency. Reference: GHSA-wrw7-89jp-8q8g

__Open **Files changed** for 2 inline review threads on the diff.__

💡 Use the Fix All MergeGuard Findings checkbox comment on this PR to apply auto-fixes in batch, or reply @mergeguards fix on any inline finding.

⚠️ This PR may benefit from a deep scan — consider triggering @mergeguards deep-scan.

MergeGuard scope: 1 dependency manifest or lockfile (package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, etc.) was not sent to the AI reviewer — MergeGuard runs OSV/Trivy security scans on those paths instead. Upgrade vulnerable packages in your manifest and regenerate lockfiles locally.


Reviewed by MergeGuard AI

Comment thread src/server_main.rs
Comment thread src/server.rs
… file

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8hDUgJ5YkhjxzEwQXHEps

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ MergeGuard — new commits are on this pull request.

Re-scan the latest diff: paste this line by itself (copy/paste is fine) — either on the Conversation tab (Add a comment) or on Files changed as a comment on the diff:

@mergeguard-followup

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MergeGuard review
Risk: 🔴 60/100 — risky
Findings: 🔴 1 high · 🟠 1 medium

Recommended fixes

  • src/tls.rs — Restrict trust of reverse-proxy HTTPS headers to loopback connections only to prevent LAN clients from downgrading HTTPS by forging headers. (confidence 90%)
Security review — 🔴 1 high · 🟠 1 medium (2 findings)

Dependency, filesystem & container image checks (OSV + Trivy + container scan) for this pull request.
Lockfile and manifest advisories are listed here only — bump affected versions in package.json (or your package manager manifest) and regenerate lockfiles locally. MergeGuard does not post these on Files changed or auto-fix lockfiles.

Cargo.lock — 🟠 1 medium (1 finding)
  • 🟠 medium · security — Trivy (GHSA-wrw7-89jp-8q8g): Unsoundness in Iterator and DoubleEndedIterator impls for glib::VariantStrIter — glib@0.18.5 in Cargo.lock

    Review GHSA-wrw7-89jp-8q8g and upgrade or remove the affected dependency. Reference: GHSA-wrw7-89jp-8q8g

__Open **Files changed** for 1 inline review thread on the diff.__

💡 Use the Fix All MergeGuard Findings checkbox comment on this PR to apply auto-fixes in batch, or reply @mergeguards fix on any inline finding.

⚠️ This PR may benefit from a deep scan — consider triggering @mergeguards deep-scan.

MergeGuard scope: 1 dependency manifest or lockfile (package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, etc.) was not sent to the AI reviewer — MergeGuard runs OSV/Trivy security scans on those paths instead. Upgrade vulnerable packages in your manifest and regenerate lockfiles locally.


Reviewed by MergeGuard AI

Comment thread src/tls.rs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8hDUgJ5YkhjxzEwQXHEps

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ MergeGuard — new commits are on this pull request.

Re-scan the latest diff: paste this line by itself (copy/paste is fine) — either on the Conversation tab (Add a comment) or on Files changed as a comment on the diff:

@mergeguard-followup

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MergeGuard review
Risk: 🟠 35/100 — moderate
Findings: 🟠 2 medium

Recommended fixes

  • src/server.rs — Require authentication for the /api/v1/server endpoint to prevent unauthenticated disclosure of server configuration details. (confidence 92%)
Security review — 🟠 2 medium (2 findings)

Dependency, filesystem & container image checks (OSV + Trivy + container scan) for this pull request.
Lockfile and manifest advisories are listed here only — bump affected versions in package.json (or your package manager manifest) and regenerate lockfiles locally. MergeGuard does not post these on Files changed or auto-fix lockfiles.

Cargo.lock — 🟠 1 medium (1 finding)
  • 🟠 medium · security — Trivy (GHSA-wrw7-89jp-8q8g): Unsoundness in Iterator and DoubleEndedIterator impls for glib::VariantStrIter — glib@0.18.5 in Cargo.lock

    Review GHSA-wrw7-89jp-8q8g and upgrade or remove the affected dependency. Reference: GHSA-wrw7-89jp-8q8g

__Open **Files changed** for 1 inline review thread on the diff.__

💡 Use the Fix All MergeGuard Findings checkbox comment on this PR to apply auto-fixes in batch, or reply @mergeguards fix on any inline finding.

MergeGuard scope: 1 dependency manifest or lockfile (package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, etc.) was not sent to the AI reviewer — MergeGuard runs OSV/Trivy security scans on those paths instead. Upgrade vulnerable packages in your manifest and regenerate lockfiles locally.


Reviewed by MergeGuard AI

Comment thread src/server.rs
@epell-development
epell-development merged commit df1a292 into main Oct 26, 2026
7 checks passed
epell-development pushed a commit that referenced this pull request Oct 26, 2026
Keeps this branch's server update check (selfupdate, ServerCfg.check_updates)
as the one source of ServerInfo.latest and drops #19's second update thread;
the release page now comes from the same release. AzuraCast calls from the
portal need the AzuraCast permission. Playlist Insert/RemoveRows commands need
edit_playlists. The generator's category folder picker (moved to genui.rs)
goes through App::pick.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5uW8ddhUDidYMFx9SLghq
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants