REvoCompute Server Developer Guide¶
REvoCompute is a multi-user scientific compute service. Nginx is the only public service, Flask/Gunicorn handles authenticated API and pages, Celery workers dispatch isolated jobs, Redis carries the queue, and a maintenance process owns scheduled cleanup, backups, digests, and log rotation.
This guide explains server behavior and developer validation. Operators should use the production operations and task adapter guide; the registry schema is described in task types and runtime families.
Architecture¶
browser / API client
|
v
nginx gateway (public port)
| |
| `---- authorized result streaming (read-only mount)
v
Flask / Gunicorn ---- authentication + task/result/admin APIs
|
v
Redis <---- maintenance scheduler
|
v
Celery worker
|
| global job_executor
+--------------------+
| |
v v
Docker runner srun -> SLURM -> Apptainer SIF
| |
+----------+---------+
v
isolated task snapshot and result tree
The five long-lived Compose services are gateway, web, worker,
maintenance, and redis. Scientific runtime images are not profile-disabled
Compose services; the manifest build/pull loops prepare them and workers launch
them on demand. Only the worker receives the Docker socket. Web is internal and
the gateway mounts results read-only.
Source layout¶
server/
├── config/
│ ├── task_types.yaml portable registry
│ └── runners/ one machine config per runtime family
├── docker/runners/ Dockerfile, run.sh, and .def per family
├── revocompute/
│ ├── app.py app factory and shared setup
│ ├── routes.py page, task, result, and admin routes
│ ├── task_runtime.py workspace/result safety and manifests
│ ├── task_types/ registry loader and dataclasses
│ ├── job/runners/ Docker and SLURM implementations
│ ├── maintenance/ scheduled background operations
│ └── static/ dynamic forms and result preview plugins
├── run/restart.sh build, activation, password reset, preflight
├── tests/ unit, static-contract, and integration tests
└── OPERATIONS_AND_TASK_ADAPTER_GUIDE.md
Configuration boundaries¶
The production environment file is selected explicitly through
REVODESIGN_SERVER_ENV. It contains Compose identity, host paths, service
configuration, enabled tasks, and credentials. Keep it Git-ignored and mode
0600; never dump it in logs.
CONFIG_DIR points to the active registry and runner directory. In production
this is normally an external host directory mounted read-only into web, worker,
and maintenance. Missing task_types.yaml is fatal—there is no embedded
GREMLIN fallback that can hide deployment drift.
The registry has three ownership levels:
- global
job_executorandcontainer_runtime; - runtime-family image, entrypoint, Dockerfile, definition, and versioned SIF;
- task input schema, family selection, GPU flag, fixed arguments, stages, and typed parameters.
One runner YAML per runtime family contains only mounts, environment,
max_runtime_seconds, and deployment defaults. Per-task enabled state and
SLURM resources live in manage.sqlite and are edited through the admin
configuration page.
ENABLED_TASKRUNNERS limits advertised/accepted tasks; GREMLIN is always
enabled. The create-task form is generated from GET /compute/api/types/<name>
and must not duplicate parameter definitions in JavaScript.
Task submission and execution¶
The server accepts constrained single- or multi-file uploads. It preserves safe relative paths and rejects absolute paths, traversal, unsupported types, and symlink escape. Each submission creates a task-specific host snapshot:
${SERVER_DIR}/workspaces/<username>/<task-id>/
├── inputs/
└── outputs/
The selected Docker or Apptainer runtime sees:
/mnt/revocompute/<username>/
├── inputs/ read-only
└── outputs/ writable only for this task
This stable virtual username path is not a shared host home. Tasks belonging to the same user receive separate host snapshots and cannot inspect or mutate each other.
The launcher provides TASK_TYPE, verified TASK_PARAMS, the full
TASK_INPUTS manifest, a primary -i path, and an -o directory. Optional
empty form values are omitted. Runner scripts must validate required files,
write generated data only under outputs or /tmp, propagate scientific
failures, and create task_finished only after real success.
Docker and SLURM runners share this contract. SLURM starts a job in a valid
existing working directory, stores the actual SLURM job ID, polls queue/account
state, and maps cancellation to scancel. A task changes from queued to running
when SLURM reports execution or a stage marker arrives; it is not inferred only
from local srun process state.
GPU task types require user GPU permission and configured GPU resources.
Apptainer receives --nv only for GPU tasks. CPU task families must not inherit
GPU passthrough.
Task and result states¶
The active task lifecycle includes pending, queued, running, finished,
failed, and cancelled, plus cleanup/deletion audit states. The legacy
deleted:finshed spelling remains for database compatibility.
A process exit code of zero is insufficient for finished: publication also
requires at least one non-empty scientific artifact. The server atomically
writes manifest.json after inventorying the uncompressed output tree.
The dedicated result page treats the manifest as the source of truth. It lists
metadata and supports authenticated individual download, suitable HTTP ranges,
and bounded text, table, image, and structure previews. Structure preview uses
the pinned Mol bundle inside a sandboxed, opaque-origin iframe. The shell's
isolated CSP permits the bundle's required dynamic evaluation without weakening
the parent page, and the shell script runs only after its viewer DOM exists. The
parent owns a dedicated sun/moon preference in the
revodesign-molstar-theme cookie (light by default) and sends it across the
message boundary, so the opaque shell can use the matching pinned Mol theme
without gaining storage or same-origin access.
The preview falls back safely when the asset, WebGL, or file size is unsuitable.
A ZIP is requested explicitly and built asynchronously from the manifest-approved
set plus manifest.json; it is not required for display or task completion.
Cleanup independently targets the selected task's result tree, optional ZIP, and workspace snapshot. Another task from the same user must remain untouched.
Important API routes¶
| Method | Route | Purpose |
|---|---|---|
GET |
/compute/api/types |
Enabled task and runtime-family schemas |
GET |
/compute/api/types/<name> |
Dynamic submission schema |
POST |
/compute/api/post |
Validated task submission |
GET |
/compute/api/running/<task-id> |
Task state and trace |
POST |
/compute/api/cancel/<task-id> |
Cancel owned task |
DELETE |
/compute/api/delete/<task-id> |
Delete one owned task |
GET |
/compute/api/results/<task-id> |
Manifest and archive state |
GET |
/compute/api/results/<task-id>/artifacts/<path> |
Authorized artifact/range response |
POST |
/compute/api/results/<task-id>/archive |
Request optional ZIP |
GET |
/compute/api/download/<task-id> |
Download an already-created ZIP |
Page routes include /compute/login, /compute/dashboard,
/compute/create_task, /compute/results/<task-id>, and admin-only user,
runtime-configuration, and log views. Logged-out protected pages return 401;
logout clears the HttpOnly cookie server-side.
Authentication and authorization¶
Browser navigation uses an HttpOnly, SameSite=Lax cookie. API clients use a
Bearer token or limited API key. Cookie-only state-changing requests are
rejected. API keys cannot perform profile or admin operations. Task reads and
writes are restricted to the owner or an administrator.
Signing keys are ephemeral per preloaded web launch, so restart invalidates active login, verification, and password-reset tokens. Login and registration are rate-limited. Administrators cannot remove their own last administrative authority, and banned users cannot authenticate.
Fresh deployments bootstrap configured admins through transient generated passwords; secrets are never persisted in the environment file. To rotate a specific administrator or user credential, run as the non-root deployment account:
REVODESIGN_SERVER_ENV=server/.env.production \
bash server/run/restart.sh reset-passwd <username>
The plaintext generated password is shown once to the interactive operator and must not be copied into logs, commits, or reports.
Build and activation semantics¶
# Build all declared runtime families and the server; does not stop the stack.
REVODESIGN_SERVER_ENV=server/.env.production \
bash server/run/restart.sh build
# Same build with the proxy configured as REVODESIGN_BUILD_PROXY in the env.
REVODESIGN_SERVER_ENV=server/.env.production \
bash server/run/restart.sh build --use-proxy
# Activate existing local images/SIFs after complete preflight; no build/pull.
REVODESIGN_SERVER_ENV=server/.env.production \
bash server/run/restart.sh restart --mode=prepared
A bare restart defaults to --mode=dev, so it performs down, builds every
runtime family and server image, and starts again. This explains why it rebuilds
an unrelated family such as EasIFA. --mode=prod pulls after stopping and is
safe only when every configured tag is genuinely published and pullable.
For an existing SLURM deployment with valid SIF paths, there is no reason to
rebuild runner Docker images during activation. Docker rebuilds prepare new
SIFs or validate Docker execution. Production SIFs are built manually to new
.partial/versioned paths while the healthy stack remains up, validated, then
atomically promoted before prepared activation.
Never run the restart helper with sudo or as root. It intentionally does not
recursively chmod/chown application data. Build proxies are passed as build
arguments and cleared in final runtime stages; do not hard-code them in
Dockerfiles.
Model data and caches¶
Large or required model data belongs on operator-managed shared storage,
mounted read-only through the family runner YAML. Do not rely on
/home/<runner>/.cache: it may be small, node-local, or unavailable on another
compute node.
Provision downloads in staging, verify checksums, validate archive paths, and then promote. ThermoMPNN-D requires both its ensemble weights and the vanilla ProteinMPNN weights. BioEmu checkpoints, ESM checkpoints, EasIFA metadata, and other runtime data follow the same offline/shared-storage principle. Runner preflight should fail clearly before inference when required data is missing.
Development and tests¶
python -m pip install -e 'server/[test]'
cd server
python -m py_compile tests/full_stack_smoke.py
python -m pytest -q tests/test_tasks.py
python -m pytest -q \
--ignore=tests/test_docker.py \
--ignore=tests/test_runner_docker_compat.py
cd ..
bash -n server/run/restart.sh
git diff --check
Docker integration tests are opt-in because they build/run images. The full
stack shell helper creates and destroys an isolated stack; do not point it at
production. Production smoke testing uses tests/full_stack_smoke.py against
the deployed Nginx gateway with credentials supplied through environment
variables, after confirming that submitting a real SLURM job is acceptable.
New task adapters need focused tests for schema validation, upstream CLI flag compatibility, optional-empty argument omission, nested paths, read-only input, failure propagation, output validation, and CPU/GPU gating. The final evidence must include a minimum real server → worker → SLURM → Apptainer smoke.
Security validation¶
Before release, retain automated coverage for these boundaries:
- web and maintenance containers do not receive
/var/run/docker.sock; - only the gateway publishes the public port and its result mount is read-only;
- task IDs and artifact paths reject traversal and symlink escape;
- logged-out protected pages and admin APIs reject access;
- cookie-only writes, banned users, and unauthorized task access fail closed;
- admins cannot self-lockout the deployment;
- multi-file task snapshots do not cross task boundaries;
- deleting one task does not remove another user's or same-user task data;
- optional archives contain only manifest-approved artifacts;
- runner output cannot turn an internal failure into a finished task.
Docker socket access gives the worker daemon-level authority even when its process UID is non-root. Keep the worker trusted, never expose the socket over a network, and consider a restricted socket proxy for less-trusted environments. Redis remains internal and currently has no authentication; setting an unused password environment variable alone does not secure it.
REvoDesign integration¶
The desktop/plugin client should authenticate through the gateway and consume the task-type and manifest APIs rather than assuming GREMLIN-only forms or a ZIP-first result. Individual artifacts and their metadata are the stable integration contract; archive generation is an optional user action.