One dashboard for every render queue on every machine.
After Effects and DaVinci Resolve both refuse to tell you when a render finishes. AE's post-render actions can only Import / Import & Replace Usage / Set Proxy — there is no "run a command". Resolve has no hook at all. So every shop ends up with a per-app, per-platform commercial tool, or nothing.
renderdeck is the missing layer: small companion apps on each machine that speak one job model to one server.
┌─ Mac: Resolve ──┐
┌─ Mac: AE panel ─┤ HTTPS POST ┌──────────────┐
┌─ PC: AE seq ───┼──────────────────▶│ renderdeck │──▶ dashboard
└─ PC: Resolve ──┘ common model │ server │──▶ history
└──────────────┘
No agents, no daemons you have to babysit, no accounts. A companion app is a single Python file with a JSON config next to it. If a machine can run Python and reach the server, it can report.
The server never reaches into your machines. Render boxes sleep, roam between networks, and sit behind NAT — a polling collector is wrong half the time and needs credentials for every host. Companion apps push, so the server only needs to be reachable, and a machine that stops reporting shows as offline rather than quietly vanishing.
That distinction is the whole design principle. See below.
A render's status comes from its OUTPUT, never from its process.
"The app is still running" and "nothing has been written recently" cannot distinguish a finished render from a hung one — a finished render looks exactly like a stalled one from the outside. renderdeck's watchers only ever report from evidence: a frame count against an expected total, or the app's own job status. Where a watcher genuinely cannot know, it says so instead of guessing.
server/renderdeck-server |
collector + dashboard. stdlib only, SQLite. |
watchers/renderdeck-resolve |
DaVinci Resolve render queue, per job |
watchers/renderdeck-ae-sequence |
AE image-sequence renders: progress, completion, stalls, missing frames |
watchers/renderdeck-ae-panel.jsx |
AE ScriptUI panel — publishes the render queue for the AE watcher to report |
install/setup.py |
writes the config |
install/bootstrap.sh |
one-command install (macOS/Linux) |
install/migrate-snapshot-pk.py |
one-time DB migration for pre-v0.2 servers |
AE's Post-Render Action can only Import / Import & Replace Usage / Set Proxy —
there is no "run a command" — so a resident panel is the only supported way to
observe a GUI render. But ExtendScript has no HTTP client worth trusting and
system.callSystem() blocks the AE UI, so a panel that phoned home on a
timer would stutter the app you're trying to watch.
So the work is split:
AE panel ──writes──▶ ~/.local/share/renderdeck/ae-queue.json ──read──▶ renderdeck-ae ──POST──▶ server
(reads the queue) (already running)
The panel does the one thing only it can do. The watcher, already running as a service, does the network. Panel closed or AE shut and the watcher falls back to sequence-watching or presence on its own.
Progress on After Effects, by output type. AE exposes no per-item progress to scripting, so renderdeck derives it from evidence instead:
| Output | Source of truth |
|---|---|
| Image sequence (TIFF/PNG/EXR/DPX) | frames on disk vs expected — exact |
| Movie file (ProRes, H.264, …) | AE's per-frame log, parsed for the last frame written |
| Neither available | null — an empty bar, never a guessed one |
A .mov is one growing file with no frames to count, so the movie path relies
on logType = ERRORS_AND_PER_FRAME_INFO. The panel sets that automatically
on every queued item, so you don't have to remember to enable it — it just has
to be set before the item starts, since AE rejects settings changes on an
in-flight render.
Every watcher emits exactly this, so notification, history and UI are written once:
{"id": "...", "name": "...", "state": "rendering",
"percent": 42.5, "elapsed_s": 900, "output": "/path", "error": null}state ∈ rendering | complete | failed | cancelled | stalled.
percent may be null when an app cannot honestly report it.
# server (anywhere the machines can reach)
RENDERDECK_TOKEN=$(python3 -c 'import secrets;print(secrets.token_urlsafe(32))') \
server/renderdeck-server --bind 0.0.0.0 --port 8090
# each render machine — macOS / Linux
curl -fsSL https://raw.githubusercontent.com/williamjvest/renderdeck/main/install/bootstrap.sh \
| bash -s -- --collector http://SERVER:8090 --token TOKEN
# each render machine — Windows (PowerShell)
.\install\bootstrap.ps1 -Collector http://SERVER:8090 -Token TOKEN
python3 watchers/renderdeck-resolve # Resolve
python3 watchers/renderdeck-ae-sequence --dir /out/seq --expect 13404 --fps 24Resolve additionally needs Preferences → System → General → External scripting using = Local.
POST /api/report |
a watcher publishes its jobs (Bearer token) |
POST /api/forget/<machine> |
drop a machine that will never report again (Bearer token) |
GET /api/state |
combined JSON |
GET /healthz |
liveness |
GET / |
dashboard |
Optional. Configure an ntfy topic in the config and watchers push completion, failure and stall alerts to your phone. Leave it out and you still get the dashboard.
v0.3.0 — early, but in daily use across three machines and two programs.
Software limitations, worst first. None of these are hidden behind an optimistic README.
| Gap | Detail |
|---|---|
| Movie-file progress unvalidated | Image-sequence progress is proven frame-for-frame against a real 13,404-frame render. The ProRes/H.264 path parses AE's per-frame log and is written to the documented format, but has never parsed a real one — only a synthetic fixture. First live movie render either confirms it or needs a regex tweak. |
| Not packaged | "Companion app" means a Python file plus a JSON config. The installer vendors its own checksum-verified interpreter, so it is one command — but it is not a signed, double-clickable .app/.exe. |
| One shared bearer token, plain HTTP | Fine on a tailnet. Do not expose the server publicly. No TLS, no per-machine tokens, no rotation mechanism beyond editing the config. |
| AE reports no percent from the panel alone | Adobe exposes no per-item progress to scripting. Percent comes from counting frames (sequences) or parsing the log (movies); with neither, the bar is honestly empty. |
| Resolve requires Studio | The free version has no scripting API. The watcher degrades to a heartbeat rather than failing, but it cannot see jobs. |
| Windows installer is less travelled | bootstrap.ps1 parses clean against the real Windows PowerShell parser and the Task Scheduler path is in daily use, but the full script has had fewer end-to-end runs than the shell one. |
So the above reads as caution, not doubt about the rest:
- Sequence completion, gap detection and stall detection — against a real 13,404-frame render and a synthetic harness.
- Resolve queue transitions — 6-case harness (baseline, no-change, single fire, no duplicate, failure, cancel).
- Multi-program-per-machine, offline detection, history dedupe.
- Checksum rejection of a tampered Python tarball.
- Auth: 200 with a token, 401 without, on every write route.
This repo is the software. For the Toldwell estate — which host runs what, how to push a change, and what is outstanding — see DEPLOYMENT.md.
Stall detection by percentage plateau, and the dashboard layout, are adapted from Fobdor/ResolveRenderMonitor (MIT).
MIT