# Architecture
PlayMolecule's design separates **where apps come from** (manifest discovery) from **where jobs run** (execution). Both are pluggable. Understanding the split is the key to picking the right environment variables and predicting what a given call will do.
## Two orthogonal backend axes
```{mermaid}
graph LR
subgraph Registries [Manifest backend]
D[docker://<registry>
Docker registry]
H[http://<url>
PlayMolecule HTTP backend]
L[local:<path>
filesystem]
end
subgraph Executors [Execution backend]
EL[Local
docker run / apptainer run
--
direct or via SLURM sbatch]
EH[HTTP
POST to backend]
end
D --> EL
H --> EH
L --> EL
```
*Left column: where apps are discovered (`PM_REGISTRIES`). Right column: where jobs run (`PM_EXECUTOR`).*
- **Manifest backend** is chosen by `PM_REGISTRIES`. It answers "what apps exist and what are their parameters?". One of: `docker://`, `http://`, or `local:`.
- **Execution backend** is chosen by `PM_EXECUTOR`. It answers "when I call `ed.run()`, where does the container actually start?". One of `local` (default) or an `http://` URL.
The two are independent. A common production setup mixes them — e.g., discover apps from a `docker://` registry but execute remotely through an `http://` backend; or browse an `http://` backend's catalogue locally without ever submitting through it.
## What each backend does
### Manifest backends
| `PM_REGISTRIES` prefix | When you'd use it | How it discovers apps |
|------------------------|------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| `docker://` | Default. Pulls Acellera's released apps from a container registry. | Lists images in the configured Docker registry. Reads each image's manifest label. |
| `http://` | When jobs are dispatched to a remote PlayMolecule backend. | Hits the backend's catalogue endpoint and decodes the JSON. |
| `local:` | **Developer use only** — when you're writing your own app and want to iterate on its manifest without publishing. | Scans `/apps///` for `*.json` manifests on disk. |
The output of all three is the same shape — a `{appname: {version: {manifest, files, run.sh}}}` dict — so the rest of the system doesn't care which one ran.
### Execution backends
| `PM_EXECUTOR` value | How it runs jobs |
|------------------------------|-------------------------------------------------------------------------------|
| `local` (default) | `docker run` or `apptainer run`, mounting the run directory into the container. |
| `http://` | POSTs the prepared input JSON to the backend, polls for status, downloads outputs. |
| (SLURM is a *mode* of local) | `ed.run(queue="slurm", ...)` wraps the local execution in an `sbatch` script. |
SLURM isn't a separate execution backend — it's an `sbatch` wrapper around the local execution path. SLURM workers still use Docker or Apptainer to run the container; PlayMolecule just generates the submission script.
## The flow of a single call
```{mermaid}
sequenceDiagram
actor User
participant App as proteinprepare(...)
participant ED as ExecutableDirectory
participant Exec as execution backend
participant Container
User->>App: proteinprepare(outdir, pdbid='3ptb')
App->>App: validate args against manifest signature
App->>ED: set up run_/ on disk, write input JSON
App-->>User: return ed (nothing has executed yet)
User->>ED: ed.run()
ED->>Exec: dispatch
Exec->>Container: docker run / apptainer run / HTTP POST
Container-->>Exec: outputs to outdir, exit code
Exec-->>ED: status
ED-->>User: control returns
```
The two-phase split — *setup* then *run* — is deliberate. It lets you:
- Inspect or tweak inputs in `outdir/run_/` before launching.
- Save an `ed` reference, submit it to SLURM, and check status hours later from a fresh process.
- Batch many `ed`s into a single SLURM submission with {py:func}`~playmolecule.slurm_mps`.
## Where configuration lives
| Source | Reads / writes |
|-----------------------------------|-------------------------------------------------------------|
| `PM_*` environment variables | Single source of truth at import time. Listed in [Environment variables](../reference/environment-variables.md). |
| App manifest | Per-app parameters, default resources, expected outputs. |
| `outdir/run_/inputs.json` | The exact inputs sent to a specific run. |
| `~/.cache/playmolecule/cookies/` | HTTP backend session. |
| `~/.cache/playmolecule/apptainer/`| SIF cache (Docker images converted on first use). |
Everything user-tunable is in env vars; everything app-tunable is in the manifest; everything specific to a run is in `run_/`. There is no global mutable state inside PlayMolecule itself.
## See also
- [Apps and manifests](apps-and-manifests.md)
- [Executable directory](executable-directory.md)
- [Job lifecycle](job-lifecycle.md)
- [Environment variables](../reference/environment-variables.md)