A single walkthrough for getting PARTHA running locally, running its test suites, and resolving the failures a new contributor is most likely to hit. It consolidates and cross-links the root README, CONTRIBUTING.md, and the backend and frontend guides rather than duplicating them — follow those links for anything not covered here.
| Tool | Version | Needed for |
|---|---|---|
| Python | 3.12 or 3.13 | Backend |
| Node.js | 22 | Frontend and workflow scripts |
| Git | Recent version | Checkout and public GitHub import |
Day-to-day development runs the backend and frontend as two separate local
processes, described below. To use PARTHA rather than work on it, the root
docker-compose.yml runs the single-service
Dockerfile (the built frontend served by the same FastAPI
process) with SQLite and storage in a volume: docker compose up --build, then
http://localhost:8000. It runs with APP_ENV=production, so the first
account registered becomes the owner and later ones need
docker compose exec partha python scripts/approve_email.py --email ....
git clone https://github.com/Second-Origin/PARTHA.git
cd PARTHA
cd apps/backend
python3.13 -m venv .venv
source .venv/bin/activate
pip install -e .
cd ../..
npm run dev:backendThe API starts at http://localhost:8000. Confirm it is up with curl http://localhost:8000/ready
(expects {"status":"ready", ...}) or open http://localhost:8000/docs for the OpenAPI UI.
No .env file is required — every setting has a working default. Local
development defaults to SQLite at apps/backend/.local/partha.db and local
filesystem storage at apps/backend/.local/storage, so nothing else needs to
be installed or running. Copy apps/backend/.env.example to .env only if
you need to change one specific value.
In a second terminal, from the repository root:
npm ci --prefix apps/frontend
npm run dev:frontendOpen http://localhost:5173. There is no seeded account or sample
repository (and no pre-approved email address) — register a new local account through the UI
(in the default APP_ENV=development any address is accepted; anywhere else the first account
on an empty instance becomes its owner and every later one must be added with
apps/backend/scripts/approve_email.py), then add a
repository (upload an archive or import a public GitHub repository over
HTTPS) and start analysis.
VITE_API_URL sets the backend origin the frontend calls; leave it unset to
use the default (http://localhost:8000). It is inlined at build time, so
changing it requires restarting the dev server.
# Backend (from apps/backend, with its venv active)
python -m pytest
# Backend static analysis (needs apps/backend/requirements-dev.txt installed first)
ruff check app scripts tests
ruff format --check app scripts tests
mypy app scripts
# Frontend, from the repository root
npm --prefix apps/frontend run test # vitest
npm run lint:frontend # eslint
npm run build:frontend # tsc -b && vite build -- type errors surface here, not in lint
# API contract: regenerate the frontend's DTOs from the live FastAPI schema
npm run generate:api-contract
npm --prefix apps/frontend run generate:api-contract -- --check # fails if the checked-in file is stale
# Disposable fixtures + Playwright browser journeys
npm run test:e2eBackend tests default to a per-test SQLite database and an in-memory rate limiter, so nothing external is required to run the full suite. Two specific tests opt into real services and skip cleanly without them:
- the PostgreSQL migration round-trip and refresh-token concurrency test skip
with
set PARTHA_TEST_PG_URL to run the Postgres concurrency testunlessPARTHA_TEST_PG_URLis set to a real Postgres connection string; - the Redis-backed rate-limiter tests skip with
set PARTHA_TEST_REDIS_URL to run the Redis backend testunlessPARTHA_TEST_REDIS_URLis set.
CI provides both services and runs everything; neither is required for local development.
Backend won't start: ERROR: [Errno 48] error while attempting to bind on address ('127.0.0.1', 8000): address already in use
Something else (often a previous dev:backend you forgot to stop) is already
listening on 8000. Stop it, or start on a different port:
python -m uvicorn app.main:app --reload --reload-dir app --port 8001 (and
point the frontend at it with VITE_API_URL=http://localhost:8001).
Frontend silently starts on a different port than 5173
Vite does not fail on a port conflict — it logs Port 5173 is in use, trying another one... and starts on the next free port (typically 5174) instead.
Check the terminal output for the actual Local: URL it printed rather than
assuming 5173.
You are logged out on every page reload; POST /auth/refresh returns 401
The refresh cookie is SameSite=Lax, so the browser only sends it back on a
request to the same site the page is on. If the page origin and VITE_API_URL
disagree on host — most commonly one is localhost and the other is
127.0.0.1 — the cookie is withheld from /auth/refresh and the session never
re-establishes. Serve the frontend and point VITE_API_URL at the same
host (both localhost or both 127.0.0.1). The documented defaults already
match (localhost:5173 + http://localhost:8000); this only bites if you
change one of them. The same-site rule applies to real deployments too — see
System Overview § Authentication and session flow.
Backend refuses to start with a message naming a table that "already exists"
This is local database drift: AUTO_CREATE_TABLES (on by default in
development/test) built a table directly from the models without ever
recording it as an applied Alembic migration, so a later real migration for
that same table fails when it tries to create it again. The startup error
names the conflicting table(s) and the exact recovery — see
backend README § Local database schema drift
for the full explanation and the alembic stamp / alembic upgrade head
commands to run.
ruff: command not found / mypy: command not found when running static analysis
The runtime install (pip install -e .) deliberately excludes development
tooling. Install it first: pip install -r apps/backend/requirements-dev.txt
(see backend README § Static analysis).
**npm --prefix apps/frontend run generate:api-contract -- --check fails with Generated API contract is stale at character N. Run \npm run generate:api-contract`.** A backend schema change (a new/changed Pydantic model or route) was made without regenerating the frontend's DTOs. Run npm run generate:api-contractfrom the repository root, review the diff togenerated.ts`, and commit it
alongside the backend change that caused it.
A test needing PostgreSQL or Redis is skipped instead of failing That's expected locally — see the skip reasons in §3 above. It is not a sign of a broken local setup; CI runs those tests against real services.
Search open issues first to avoid a duplicate. For a bug, use the Bug
Report template in .github/ISSUE_TEMPLATE/
and include exact reproduction steps (route, endpoint, input repository,
commands run), expected versus actual behaviour, and any relevant log output
— see CONTRIBUTING.md § Choosing a template.
Never file a security vulnerability as a public issue — report it
privately through SECURITY.md instead. For a question that
isn't yet a confirmed bug, ask on Discord
first.