This tree holds the base FL application templates — the FLIP-owned server/orchestration code that every uploaded model is bundled with — one subtree per FL backend:
fl-apps/
├── nvflare/
│ ├── standard/ # JOB_TYPE=standard — FedAvg (Client API)
│ ├── evaluation/ # JOB_TYPE=evaluation — cross-site model evaluation
│ ├── fed_opt/ # JOB_TYPE=fed_opt — Adaptive Federated Optimization
│ └── required_files.json
├── flower/
│ ├── standard/ # job_type=standard — FedAvg
│ ├── evaluation/ # job_type=evaluation — federated evaluation
│ └── required_files.json
└── check_required_files.sh
Each <backend>/<template>/ directory is a job type: the server-side orchestration code, plus
a declaration of which files the researcher must upload alongside it. See each template's own
README for what it does and what its required_files.json requires:
nvflare/standard, nvflare/evaluation,
nvflare/fed_opt,
flower/standard, flower/evaluation.
All FLARE templates are Client API apps (ClientAPIExecutor, in-process mode) — the legacy
Executor-based templates are retired, though the pre-rename *_client_api job-type names still
resolve as aliases for models created before the rename (flip_api/fl_services/services/fl_service.py).
Each template directory carries its own required_files.json — a flat JSON array of the filenames
a researcher must upload for that job type (e.g. ["trainer.py", "config.json", "models.py"]).
Never hand-edit fl-apps/<backend>/required_files.json (the aggregate, one directory up): it is
generated from the per-template files by check_required_files.sh,
keyed by template name, with sorted keys for a byte-stable diff. Edit a template's own
required_files.json instead and regenerate:
bash fl-apps/check_required_files.shThis is enforced two ways, matching the pattern in .pre-commit-config.yaml:
- The
fl-apps-required-filespre-commit hook runs the script and fails the commit if it changed anything — re-stage and commit again, no need to remember to run it by hand. - The
fl-apps-check-required-files.ymlCI workflow re-runs the same script andgit diff --exit-codes the committed manifests, as the backstop for a commit made without pre-commit installed.
There is deliberately no cross-backend combined file — each backend owns its own job-type
keyspace, so template names never collide across nvflare/ and flower/.
flip-api reads templates straight from FL_APP_BASE_DIR (default /app/fl-apps, this tree baked
into the image) — no S3 involvement (FLIP#724). Bundling a template is a positive allowlist, not
a mirror (FLIP#1008, flip_api/fl_services/services/fl_service.py::list_local_base_files): only
two things ever ship —
- The app folder(s):
app/on both backends, plus FLARE's per-siteapp_*/variants (named inmeta.json'sdeploy_map). Inside an app folder, dot-prefixed entries,__pycache__,*.pyc/*.pyo,uv.lock, and symlinks are dropped too. - The backend's single root file:
meta.jsonfor FLARE (the job definition FLARE deploys),pyproject.tomlfor Flower (the FAB definition[tool.flwr.app.components]resolves against).
Everything else in the template directory — recipe.py, README.md, the per-template
required_files.json, dev tool caches — is excluded and logged at debug level, never shipped to a
trust. The allowlist fails closed: for an operator-provided FL_APP_BASE_DIR, an unrecognised
file is silently omitted rather than shipped, so a hotfix to a base template ships by rebuilding
(and, in prod, redeploying) the flip-api image, not by editing files in place at a trust.
Some tutorial files are kept as byte-identical copies of another file, and two guards police them — one derived from the tree, one hand-listed:
-
Flower tutorials vs. the
fl-apps/flower/templates. Everyfl-tutorials/flower/*/*/appships its own copy of the platform-ownedapp/*.py(server_app.py,strategy.py) of the template itsconfig.jsonjob type selects:flwr buildexcludes symlinks from a FAB, so these have to be real copies rather than links, and since the bundler discards an uploaded copy in favour of the base file (see above), the tutorial must carry what the platform will actually run.fl-tutorials/tests/test_flower_platform_parity.pyderives the pairs from the tree — every tutorial app against its template'sapp/*.py— and fails if a copy differs (a module that is inert in both trees, such as a docstring-only__init__.py, may). It runs undermake -C fl-tutorials testand thefl-tutorials-tests.ymlCI workflow (pathsfl-apps/flower/**,fl-tutorials/**), so a new Flower tutorial is covered the moment it exists — there is no list and no path filter to extend. Resync by copying the template file over the drifted copy. -
Tutorial-to-tutorial pairs still hand-listed. Two families remain in
scripts/check_tutorial_sync.sh(PAIRS), run by thefl-apps-check-tutorial-sync.ymlCI workflow on any push/PR touching the paths on either side:- The Ark+ FLARE pair. The two Ark+ evaluation tutorials share
data_utils.pyandarkplus_flat_models.pywith each other; nothing in the tree says so, so the pairing is hand-listed. Watched paths includefl-tutorials/nvflare/image_evaluation/**. Resync by copying the baseline app's file over the multimodel copy. - The EHR risk-prediction cross-backend pair. The EHR risk-prediction tutorial exists on
both backends and shares its
feature_engineering.py,models.pyandquery.sqlas byte-identical copies (same preprocessing/architecture must train on either backend). The FLARE copy is the reference; resync by copying it over the Flower copy. Watched paths includefl-tutorials/nvflare/tabular_classification/**andfl-tutorials/flower/tabular_classification/ehr_risk_prediction/**.
Adding another hand-listed pair means extending that workflow's path filters too, or drift on the new path goes uncaught.
- The Ark+ FLARE pair. The two Ark+ evaluation tutorials share
pyproject.toml files are deliberately not paired — the template's [tool.uv] tables pin
flip-utils to the in-image /opt/flip-utils source and torch to the cu130 index, neither of which
apply to a tutorial's workstation venv.
fl-tutorials/— runnable tutorials built on these templates (per-backend, with datasets and Makefile targets).docs/source/working-with-flip-apps.rst— the guides for adapting or building an app to upload to FLIP.flip-api/AGENTS.mdand the rootAGENTS.md(FL_APP_BASE_DIR) — how flip-api discovers, validates and bundles these templates at runtime.