A local full-stack control plane for managing Netlify sites, local repo clones, preview containers, media tooling, and reviewer email workflows.
This repo contains two applications:
backend/(FastAPI): wraps Netlify CLI, Podman, SMTP, and utility tools behind HTTP APIs.netlify-dashboard/(React + MUI + Vite): cinematic dashboard UI for site operations, tooling, and email workflows.
The app is designed for local operator use on Linux (Parrot/Debian-family), not for direct internet exposure.
The frontend is a single-screen, viewport-locked operations UI with three primary views:
Sites: card-by-card Netlify site navigation, clone status, live site metadata snapshot, and preview controls.Workspace: per-site deep operations (live metadata fetch, Codex launcher, media asset preview, SMTP/contact/email studio).Tools: power tab foryt-dlp,ffmpeg, repo creation, WHOIS, and DNS lookup.
Notable design/system characteristics implemented in code:
- Dark neon visual system with MUI theme overrides in
netlify-dashboard/src/theme.js. - "Fixed-screen" UX: content sections scroll internally, while the app frame stays pinned to viewport.
- Layered ambient visuals and animated ghost motifs in
netlify-dashboard/src/App.jsx. - Function-first component structure:
- Site card ops:
SiteCard.jsx - Pod lifecycle/logs:
GhostControl.jsx - Per-site detail workspace:
SiteWorkspace.jsx - SMTP/contact/email builder:
EmailStudio.jsx - Tools surface:
ToolsPage.jsx
- Site card ops:
The backend mounts static file roots and composes API domains via routers:
- App factory:
backend/backend_api/application.py - State/config roots:
backend/backend_api/state.py - Routers:
Core backend behavior:
- Site inventory comes from
netlify sites:list --json. - Repo clone state is inferred from local git directories under
sentinel_clones. - Preview apps run in Podman containers mapped to dynamic local ports.
- Live metadata parser fetches title/meta/OG/canonical/favicon with short cache (
15m). /downloadsand/reposare exposed as static mounts for media previews.
React UI (5173)
-> FastAPI (8000)
-> netlify CLI (sites inventory)
-> git / gh (clone + repo workflows)
-> podman (preview containers)
-> smtp (email delivery)
-> yt-dlp / ffmpeg / whois / dig|nslookup (tooling)
Static mounts:
- /downloads -> backend/downloads
- /repos -> backend/sentinel_clones
.
├── backend/
│ ├── main.py
│ ├── backend_api/
│ │ ├── application.py
│ │ ├── state.py
│ │ ├── utils.py
│ │ └── routers/
│ │ ├── sites.py
│ │ ├── ghost.py
│ │ ├── email.py
│ │ └── tools.py
│ ├── sentinel_clones/ # local cloned/generated repos
│ ├── downloads/ # generated media + uploads
│ └── sentinel_config.json # local SMTP + contact config
└── netlify-dashboard/
├── src/
│ ├── App.jsx
│ ├── theme.js
│ └── components/
└── package.json
sudo apt update
sudo apt install -y python3 python3-venv python3-pip git curl podman whois dnsutils ffmpeg yt-dlpcurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
npm install -g netlify-cliOptional GitHub CLI support for repo creation flow:
sudo apt install -y ghRun backend from backend/ so relative paths resolve correctly (sentinel_clones, downloads, sentinel_config.json).
cd backend
python3 -m venv venv
source venv/bin/activate
pip install fastapi "uvicorn[standard]" python-multipart
uvicorn main:app --reload --host 0.0.0.0 --port 8000cd netlify-dashboard
npm install
npm run dev -- --host 0.0.0.0 --port 5173netlify login
netlify sites:listGET /api/sitesPOST /api/clone?repo_url=...GET /api/sites/{site_id}/metadata?force_refresh=falseGET /api/sites/{site_id}/assetsPOST /api/codex/open
POST /api/ghost/start/{repo_name}POST /api/ghost/stop/{repo_name}GET /api/ghost/logs/{repo_name}
GET /api/config/smtpPOST /api/config/smtpGET /api/sites/{site_id}/contactsPUT /api/sites/{site_id}/contactsPOST /api/sites/{site_id}/contactsDELETE /api/sites/{site_id}/contacts?email=...POST /api/email/send
GET /api/tools/statusPOST /api/tools/yt-dlpPOST /api/tools/yt-dlp/updatePOST /api/tools/uploadsGET /api/tools/ffmpeg/inboxPOST /api/tools/ffmpeg/convertGET /api/tools/downloads/mediaPOST /api/tools/repo/createPOST /api/tools/native/whoisPOST /api/tools/native/dnsPOST /api/tools/native/pingPOST /api/tools/native/traceroutePOST /api/tools/native/ipPOST /api/tools/native/ss
curl -X POST "http://localhost:8000/api/clone" \
--get --data-urlencode "repo_url=https://github.com/your-org/your-site.git"curl -X POST "http://localhost:8000/api/ghost/start/your-site"curl "http://localhost:8000/api/sites/<site_id>/metadata?force_refresh=true"curl -X POST "http://localhost:8000/api/config/smtp" \
-H "Content-Type: application/json" \
-d '{
"server": "smtp.example.com",
"port": 465,
"user": "ops@example.com",
"password": "***",
"from_name": "Ops Team",
"from_email": "ops@example.com",
"use_ssl": true,
"use_tls": false
}'curl -X POST "http://localhost:8000/api/email/send" \
-H "Content-Type: application/json" \
-d '{
"site_id": "<site_id>",
"recipient_mode": "selected_site_emails",
"selected_emails": ["reviewer@example.com"],
"subject": "Preview Ready",
"html_body": "<h1>Preview</h1><p>Your build is ready.</p>",
"text_body": "Preview ready."
}'# Put/import files into:
# backend/downloads/ffmpeg/inbox
curl -X POST "http://localhost:8000/api/tools/ffmpeg/convert" \
-H "Content-Type: application/json" \
-d '{
"input_path": "/absolute/path/to/backend/downloads/ffmpeg/inbox/input.mp4",
"output_name": "clip_720p",
"output_format": "mp4",
"scale": "1280:720",
"crf": "23",
"preset": "medium",
"overwrite": true
}'- Backend currently allows all CORS origins (
*) and should remain local/private. - SMTP credentials and site contacts are persisted to
backend/sentinel_config.json. - Tool and clone operations execute host commands via subprocess; host tool availability directly controls feature availability.
- Podman preview assumes repo apps serve from container port
3000.
- Run
netlify login. - Verify CLI returns JSON:
netlify sites:list --json.
- Ensure Podman service is active:
systemctl --user start podman.socket- Validate
podman infoworks for your user.
- Check tool presence from UI status chips or shell:
which ffmpeg
which yt-dlp- Site must be cloned locally.
- Asset scanner only indexes known media extensions and skips dirs like
node_modules,.git,dist,build.
- Confirm SMTP server/user/password are saved.
- Verify SSL/TLS/port combo matches provider requirements.
- Do not expose backend port
8000publicly. - Keep
backend/sentinel_config.jsonlocal only. - Rotate SMTP credentials if they were ever committed or shared.
- Consider origin restrictions and auth if used beyond local machine.
- Add
requirements.txt/pyproject.tomlfor reproducible backend installs. - Add API auth + scoped CORS for non-local use.
- Add test coverage (router-level + integration smoke tests).
- Add structured logging and command error telemetry.