Repo-root utility scripts: trust kit lifecycle (scaffold, register, sync, distribute, onboard),
local environment/status checks, cross-service drift guards run by make targets or CI, and secret
scanning. Most are invoked through make targets rather than run directly — see each section
below for the wrapping target.
These scaffold, write, and keep in sync the trust/.env.<CODE>.<env> kit files that carry a
trust's identity, credentials, and Hub-shared configuration. trust_kit_lib.py is the shared
in-place-upsert implementation behind the others, so a kit file's operator edits (host-local
ports, bind dirs) always survive a re-run.
new_trust.py(make new-trust TRUST_CODE=<CODE> TRUST_NAME="..." HUB=<stag|prod>; leaveHUBunset for development) — scaffoldstrust/.env.<CODE>.<env>from the base template (trust/.env.example), prepending the trust's identity (TRUST_NAME/TRUST_CODE/TRUST_REGION). Refuses to overwrite an existing kit (the rootnew-trusttarget also refuses beside a kit still under its pre-FLIP#1479 name). The result is ready for registration to fill in credentials (make -C deploy/providers/AWS register-trusts KIT=<CODE> HUB=<hub>on a deployed hub,make register-trust KIT=<CODE>in development).trust_kit_lib.py— the single kit-file writer behinddistribute_trust_kits.py,sync_trust_kit.py, and the AWS registration path. Merges a kit dict into a kit file while preserving operator edits: credentials (TRUST_API_KEY/TRUST_INTERNAL_SERVICE_KEY) are written only on a new registration and never clobbered on an idempotent re-run; metadata (EXPECTED_TRUST_ID/FL_KIT_SLOT/FL_KIT_SLOT_NUMBER) and the Hub-shared block are upserted unconditionally, the latter under a sentinel header added once on first write. Writes every kit file0600.distribute_trust_kits.py— writes a trust kit file fromregister_trust's JSON output: one kit (object or one-element array) on stdin to the required--target PATH— the devregister-trustMakefile pipe (--target trust/.env.<CODE>.<env>) anddeploy/providers/AWS/scripts/register-trusts.shfor stag/prod alike.sync_trust_kit.py(make sync-trust-kit KIT=<CODE> HUB=<env>) — refreshes only the Hub-shared block intrust/.env.<CODE>.<env>from the caller's environment (the root Makefileincludes the right.env.<env>and exports it first). Credentials and operator edits are left untouched. Portable across dev/stag/prod — no docker compose exec, no ECS round-trip.onboard_onprem_trust.py(make onboard-onprem-trust KIT=<CODE>) — a readiness checklist for an on-prem trust host, run as a precheck byup-onprem-trust. Diagnoses what's missing beforemake up-onprem-trustwill succeed: public IP, docker swarm state, kit file presence, Hub-shared block, kit credentials,FL_KIT_DIRcontents, OMOP/Orthanc data dirs, and GPU capacity (NUM_AVAILABLE_GPUSvs. what the host actually exposes). Each check renders ✅ / ❌ / ⏳ (pending on an earlier check) and exits non-zero if anything fails, so an operator gets concrete diagnostics instead of a cryptic compose or pydantic failure deeper in the stack.
site_upgrade.py(invoked bymake -C trust upgrade-trust, and by the AWS/Helm twins — seeAGENTS.md"Site release upgrades") — the plan-and-guard-and-pin phase of a site release upgrade (FLIP#1204). Resolves the target tag against what the hub reports on/api/health(defaults to that;--tag vX.Y.Zor--tag sha-<short7>overrides), says when GitHub lists a newer release (offering to stop and move to it only when the hub already runs it), runs the guards (image tag shape, downgrade →--force, release-tag checkout match →--allow-checkout-drift, every image actually built at the tag viadocker manifest inspect, operator confirmation unless--yes), and — if everything passes — rewritesDOCKER_TAG/DOCKER_FL_TAGin the kit's Hub-shared block. The script stops before any container is touched; the Makefile then re-includes the updated kit and does the pull / recreate (_upgrade-trust-applyon trust; the EC2 twin andmake -C trust/deploy/helm upgrade-trust-k8sdo the same for the other shapes). The operator entry points wrap both phases together —make upgrade-onprem-trust KIT=<CODE>also runsonboard-onprem-trust --gate(the readiness checklist implemented byonboard_onprem_trust.py, above) before the plan phase. Exit codes (the Makefile's contract): 0 pinned, 2 no usable target, 3 refused downgrade, 4 not confirmed, 5 an image is missing at the target tag, 6 the checkout is not at the target release. Running the script directly rewrites the kit but leaves the containers at the old tag — use the Make targets. Runbook:docs/source/sys-admin/admin-upgrading-sites.rst.
check_local_status.py(run directly:python3 scripts/check_local_status.py) — verifies the local Docker Compose stack is functioning: Docker daemon, containers, networks, application endpoints (UI, API, FL API), trust endpoints (trust-api, imaging-api, data-access-api), database connectivity, and system resources. Exits 0 when all checks pass (warnings are acceptable), 1 otherwise. Options include--skip-endpoints,--skip-docker,--project-dir, and--env-file. Resolves container and network names through compose project labels (honouringFLIP_INSTANCE/MAIN_ENV_FILE) so it reports correctly against a prefixed second stack, not just the default one.check_env_vars.py(pre-commit hookcheck-env-vars) — verifies every variable declared in.env.development.exampleis also present in.env.development, so the example file stays up to date and a new required variable can't be missed silently.
None of these is a pre-commit hook. The first two are make-time guards that run in no CI
workflow; the other two are CI-only:
check-fl-provisioned.sh(themake up/make up-no-trustguard, via the root Makefile's_check-fl-provisioned) — fails fast when a backend's per-net FL credentials are missing, instead of letting the FL containers crash-loop. Neither backend's credentials are created bymake up— they come frommake -C fl-services/<backend> provision....check-uv-version.sh(themake lockguard) — fails fast whenuvis too old to understand the dependency cooldown (exclude-newer = "3 days", which needs uv >= 0.10.0). An older uv silently discards the whole[tool.uv]table and resolves with no cooldown at all, rewritinguv.lockwith no warning worth noticing.check_fl_api_validation_sync.sh(CI:fl-api-validation-sync.yml) — verifies thatsafe_joinandvalidate_bundle_url(the SSRF and path-traversal guards in front of the server-side bundle fetch) stay byte-identical between the FLARE and Flower fl-api services. They're intentionally separate copies (different Docker build contexts, uv projects, and images), so a fix applied to one and not the other would silently leave the second vulnerable.check_tutorial_sync.sh(CI:fl-apps-check-tutorial-sync.yml) — verifies that tutorial files kept as byte-identical copies of another file have not drifted. It holds two families of hand-listed pairs: the Ark+ FLARE pair (data_utils.pyandarkplus_flat_models.py, shared between the two evaluation apps) and the EHR risk-prediction cross-backend pair (feature_engineering.py,models.py,query.sql, with the FLARE copy as the reference). The Flower tutorial-vs-fl-apps/flower/template pairs moved tofl-tutorials/tests/test_flower_platform_parity.py, which derives them from the tree. These can't be symlinks —flwr buildexcludes symlinks from the FAB — so each keeps a real copy that must be resynced by hand when its reference changes.pr_paths_changed.py(CI:pr_paths_changed.yml, called by every gated service-test workflow) — implements the PR path gate: reads a service workflow'spaths:filter, fetches the PR's changed files, and outputsrun=truewhen the filter matches so the workflow's jobs gate on it. A PR intomainalways runs everything.scripts/tests/test_pr_paths_changed.pypins the gate against drift.utils.sh— shared shell helpers (colour-codedlog_info/log_success/ etc.) sourced only by the two secret-scanning scripts (scan-secrets.sh,setup-secret-scanning.sh); the drift guards above are self-contained. Not run directly.
Tooling for extracting and comparing per-round FL timings (not model metrics) from either a live
platform run (CloudWatch logs) or a local FLARE simulator workspace. See
fl_round_metrics/README.md for the tools and the
make round-metrics / make reproduce-overhead wrapper in the arkplus fine-tuning tutorial.
Stdlib-only tests for the scripts in this directory (scripts/tests/test_*.py), each following
the same if __name__ == "__main__": main() contract — no pytest, no external services beyond
uv itself (needed because test_sync_trust_kit.py drives sync_trust_kit.py through
uv run --no-config). Run the whole directory:
for test_file in scripts/tests/test_*.py; do
echo "== $test_file"
python3 "$test_file" || exit 1
doneCI runs the same loop in .github/workflows/test_trust_kit_scripts.yml,
triggered on changes under scripts/** and on the hub/trust compose files that
test_container_identity.py asserts against (so an edit to those triggers the guard even though
it touches nothing under scripts/).
This directory also contains scripts for detecting secrets and sensitive information in the FLIP monorepo.
This monorepo uses uv for Python package management. If you haven't installed it yet:
curl -LsSf https://astral.sh/uv/install.sh | shRun the setup script once to install and configure all secret scanning tools:
./scripts/setup-secret-scanning.shThis will:
- Install
pre-commit,detect-secrets, andtrufflehog - Set up pre-commit hooks
- Create a baseline for detect-secrets
- Update .gitignore to exclude security reports
To scan the entire monorepo for secrets:
./scripts/scan-secrets.shCheck staged files before committing:
pre-commit runCheck all files:
pre-commit run --all-filesAvailable Pre-commit Hooks (see .pre-commit-config.yaml for
the authoritative list):
- check-env-vars: Verifies that all variables in
.env.development.exampleexist in.env.development. This ensures the example file stays up to date and developers are aware of new required environment variables. - fl-apps-required-files: Regenerates each backend's
fl-apps/<backend>/required_files.jsonfrom its per-templaterequired_files.jsonarrays. Rewrites-and-fails on drift (likeprettier), so re-stage and commit again. - xnat-dcm2niix-pin-sync: Checks that the dcm2niix Container Service image pin (
ARG DCM2NIIX_VERSIONin its Dockerfile) agrees across the three deploy configs that reference it as a literal tag string. Report-only — the sites are hand-written, not generated. - trufflehog: Scans for high-entropy strings and verified secrets
- detect-secrets: Pattern-based secret detection with baseline support
- check-added-large-files: Prevents committing files larger than 1000 KiB (
--maxkb=1000— the hook compares againstgetsize // 1024, so the cap is ~1.024 MB) - check-merge-conflict: Detects merge conflict markers
- check-yaml: Validates YAML syntax
- prettier: Formats YAML under
deploy/providers/AWS/ - end-of-file-fixer: Ensures files end with a newline
- detect-private-key: Detects private SSH/SSL keys
- uv-lock (one entry per uv project — root, flip-api, docs, trust-api, imaging-api, data-access-api, omop-db, xnat-tests, aws-deploy, flip-utils, fl-api-flower, fl-api-base): verifies each project's
uv.lockis in sync with itspyproject.toml(--check, so it fails on drift instead of silently regenerating). Usemake lockto regenerate when apyproject.tomllegitimately changes.
To scan specific components (flip-api, trust, etc.):
cd flip-api
trufflehog git file://. --only-verifiedIf secrets were accidentally committed, follow the GitHub guide on removing sensitive data using tools such as git filter-repo or BFG Repo-Cleaner.
High-entropy string and secret scanner with built-in detector patterns.
Direct usage:
trufflehog git file://. --only-verified --jsonPattern-based scanner with baseline support.
Direct usage:
detect-secrets scan --all-filesSecret scanning runs automatically on:
- Push to
mainordevelopbranches - Pull requests
- Weekly schedule (Sundays at 2 AM UTC)
See .github/workflows/secret-scanning.yml for details.
To update the baseline with new false positives:
detect-secrets scan --baseline .secrets.baseline --updateTo audit the baseline:
detect-secrets audit .secrets.baselineThis project uses uv for Python package management. Install it with:
curl -LsSf https://astral.sh/uv/install.sh | shAfter installation, ensure ~/.local/bin is in your PATH:
# Add to your ~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish
export PATH="$HOME/.local/bin:$PATH"If pre-commit or detect-secrets aren't found after running the setup script, the uv tool directory may not be in your PATH. Add it:
export PATH="$HOME/.local/bin:$PATH"Or reinstall the tools globally via Homebrew:
brew install pre-commit
pip install detect-secrets # Only if using pipx or virtual environmentInstall via Homebrew:
brew install trufflesecurity/trufflehog/trufflehogOr use Docker:
docker pull trufflesecurity/trufflehog:latestIf pre-commit hooks fail due to missing tools:
- Ensure tools are installed:
which trufflehog - Reinstall pre-commit hooks:
pre-commit install - Update pre-commit:
pre-commit autoupdate
- Run scans before committing: Use
pre-commit runto catch secrets early - Review scan results carefully: Not all findings are real secrets
- Never commit real secrets: Use environment variables or secret management
- Update baselines regularly: Keep false positive baselines current
- Scan the entire history: Use full scans periodically to find historical secrets
Scan reports are saved to .security-reports/ (gitignored). Review these carefully before sharing or opening issues.
For issues or questions about secret scanning:
- Check tool documentation: TruffleHog, detect-secrets
- Review CONTRIBUTING.md for security guidelines