The backend now lives in one Python package, app/. No behaviour, API or data changes: every URL, environment variable and response is the same, and openapi.json is byte-identical.
Self-hosters: the start commands changed. If you run spoo.me with your own command or compose file, switch to:
uv run uvicorn app.main:app ...
uv run uvicorn --factory app.workers.click_worker:create_app ...
The old main:app and workers.click_worker:create_app targets no longer exist. The bundled dockerfile, docker-compose.yml, deploy/ compose files, railway.json, render.yaml and vercel.json are already updated.
What moved. main.py, config.py and errors.py moved into app/, and app.py became app/factory.py. routes/, services/, repositories/, schemas/, infrastructure/, middleware/, dependencies/, shared/, workers/, data/, static/ and templates/ moved under app/ with their names unchanged, so imports gain an app. prefix. config/apps.yaml is now app/data/apps.yaml. Data files now resolve relative to the package instead of the working directory, and GEOIP_COUNTRY_DB and GEOIP_CITY_DB still override the GeoIP defaults.
Rebasing a branch. Run git rebase origin/main, then python scripts/move_to_package.py, which moves anything still under an old folder and rewrites its imports. Running it twice changes nothing.