External PanDA production monitoring frontend for the ePIC experiment at the Electron Ion Collider. Provides open-internet access to PanDA monitoring services that live behind BNL's firewall.
Live at https://epic-devcloud.org/
Browser → epic-devcloud.org (Django/Apache)
↓ proxy (full rendered HTML)
SSH tunnel (autossh, persistent)
↓
swf-monitor (BNL) → PanDA database
- Web pages — the hub, all PanDA views, and all PCS views — are
proxied as full rendered HTML from swf-monitor through the SSH tunnel
(
remote_app/monitor_client.proxy), which rewrites swf-monitor URLs to local/prod/paths and swaps in local auth controls. Same URL structure. Only swf-remote's own pages render locally (see Local vs proxied pages). - MCP relay at
/prod/mcp/forwards swf-monitor's MCP endpoint, its full tool set, through the tunnel as the signed-in user. Headless clients authenticate with a per-user token (see Live-data access). - No local PanDA data — all data comes from swf-monitor in real time.
- TeamComms — HTTP, MCP and streaming under
/prod/teamcomms/, using existing devcloud accounts and tokens. The integration contract describes authentication, AI identity, introspection and deployment.
- swf-monitor — Django web service at BNL with PanDA DB access, REST API, MCP server
- swf-testbed — Streaming workflow testbed orchestration and agents
- swf-common-lib — Shared utilities for SWF agents
| Path | Description |
|---|---|
/ |
PanDA Hub — links to all monitoring views |
/panda/activity/ |
Activity overview — job/task counts by status, user, site |
/panda/jobs/ |
Job list with DataTables filtering and search |
/panda/jobs/<pandaid>/ |
Job detail — full record, files, errors, log URLs |
/panda/tasks/ |
JEDI task list with DataTables filtering |
/panda/tasks/<taskid>/ |
Task detail with constituent jobs |
/panda/errors/ |
Error summary — top patterns ranked by frequency |
/panda/diagnostics/ |
Failed jobs with full error details |
/snapper/<scope>/report/ |
Coherent operational state snaps and history |
/snapper/<scope>/system/ |
Snapper capture policy, scheduler, and component registry |
/system/ |
Aggregate SWF system health |
/mcp/ |
MCP relay to swf-monitor (POST JSON-RPC, token only), limited to the external tool set in remote_app/mcp_policy.py; GET serves the self-contained setup page |
/account/tokens/ |
Create and revoke API tokens |
# Clone both repos side by side
git clone https://github.com/BNLNPPS/swf-remote.git
git clone https://github.com/BNLNPPS/swf-monitor.git
# Set up dev environment (venv, symlinks to swf-monitor templates)
cd swf-remote
bash setup-dev.sh
# Set up database and .env
bash setup-server.sh
# Run dev server
cd src && ../.venv/bin/python manage.py runserver# Full deploy: rsync, venv, deps, symlinks, migrations, Apache, SSL
bash deploy/setup-apache.sh
# SSL (after DNS is pointing to the server)
sudo certbot --apache -d epic-devcloud.orgThe tunnel is managed by systemd via autossh. See
deploy/swf-remote-tunnel.service. Requires SSH key access from
the hosting server to swf-monitor's host via an SSH gateway.
sudo cp deploy/swf-remote-tunnel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now swf-remote-tunnel
sudo systemctl status swf-remote-tunnel- Django 5.2 (Python 3.11) — upgrading to Django 6.0 / Python 3.12 after OS upgrade
- PostgreSQL (local, for Django internals only)
- Apache + mod_wsgi
- httpx for upstream REST calls
- autossh + systemd for persistent SSH tunnel
- Let's Encrypt for HTTPS
Environment variables prefixed SWF_REMOTE_ to avoid collisions with
other apps on the same server. See .env.example.
Every proxied page is built by swf-monitor and fetched over the tunnel, so
viewing one requires an account. Anonymous visitors reach a locally rendered
landing page, the login path, and static assets; everything else redirects to
sign-in. An account is established either by a local username and password or
by signing in with GitHub, which creates it on first use. Signing in is all
that reading requires. Acting on the production system requires authority,
held as two attributes on the account in swf-monitor and enforced there:
observed eic organization membership, and granted rights. A command-line client authenticates with a
per-user token created on /prod/account/tokens/ and sent as
Authorization: Bearer <token>; it reaches swf-monitor as that user. The
cross-component policy is defined in
Live-data access policy.
Most pages — the hub, all PanDA views, Alarms, and all PCS views — are served as
full rendered HTML proxied from swf-monitor (remote_app/views.py), so
they carry swf-monitor's own templates, nav, and styling. swf-remote
renders a smaller set itself using its own base.html: the MCP setup page,
the tokens page, and the login / password-change pages. Their nav is
swf-monitor's own, taken from the production hub through the tunnel
(monitor_client.nav_html), rewritten to local paths with local auth
controls like every proxied page, and cached for a minute; the last fetched
copy stands in during a tunnel outage. swf-remote holds no nav markup, so
the monitor's nav is the only one. Alarm code from the old local
implementation remains in the tree for rollback/reference, but live
/prod/alarms/... pages proxy to swf-monitor.
The proxy (monitor_client.proxy) preserves swf-monitor's page body and
<style>, rewriting only URLs and the auth controls — so swf-monitor's
own nav and styling reach the browser intact on proxied pages.
swf-remote keeps a symlink to swf-monitor's templates (created by
setup-dev.sh) for the shared assets the local pages pull in. URL names
use app_name = 'monitor_app' so {% url %} tags resolve to swf-remote's
own routes.