All method pages

MethodDevelopment

verified checked against the code · what the statuses mean

Getting started — run it locally

In simple terms

The app is a local tool: a Python backend and a browser frontend, both on your machine, talking over localhost. There’s no cloud and no deployment. The first time you open it you create your account (name, e-mail, password); from then on you sign in with it — and a developer’s own server can skip that with PTX_DEV_PRINCIPAL (see Environment variables). This page gets you from a fresh clone to a running app and tells you how to check your work. The app also runs as one Docker image — on a server behind a proxy, or on your own computer with your data in a folder of yours: see Run with Docker.

Prerequisites (verified on the dev box)

  • Python 3.12 (arm64 on Apple Silicon) in a virtual environment. .python-version names the minor; the pinned numpy needs 3.12 or newer, and 3.9 (end of life) no longer works. The API line is FastAPI on Starlette 1.x with pydantic 2; the exact pins are in requirements.txt (what the code imports, one reason per group) and requirements.lock (every transitive package, generated).
  • uv (recommended) — it installs the Python interpreter itself and the locked set in seconds. Plain venv + pip works too.
  • Node 20 (via nvm: v20.20.2) for the frontend.
  • No database server, no secrets — SQLite is a file and Parquet is on disk.

Backend

# from the repo root — first time only
uv python install 3.12
uv venv venv --python 3.12
uv pip install --python venv/bin/python -r requirements.lock
#   pip route instead: python3.12 -m venv venv && venv/bin/pip install -r requirements.txt

# every time
source venv/bin/activate
python backend/scripts/serve.py --reload
#   = uvicorn backend.api.app:app --reload --reload-dir backend --port 8000 (127.0.0.1)

The API is then on http://localhost:8000. Quick check: GET /health. The SQLite database and the data/ tree are created/read under backend/data/ (and are gitignored — this folder is the only copy; back it up — including backend/data/dab_workspaces/, where each workspace’s own default books live).

The API answers only to this Mac’s own names (localhost, 127.0.0.1) — a request for any other Host is refused (400), however the server was started — and accepts changes only from the configured dev frontend pages: http://localhost:5173 and http://127.0.0.1:5173 unless PTX_DEV_ORIGINS says otherwise (environment variables).

Changing a dependency: edit the pin in requirements.txt, regenerate the lock (every transitive package pinned, with hashes, for every platform), install it, and run the full suite:

uv pip compile requirements.txt -o requirements.lock --universal --python-version 3.12 --generate-hashes
uv pip sync --python venv/bin/python requirements.lock
python -m pytest backend/tests -q

The solver stack (pypsa, linopy, highspy, numpy, pandas) is also checked with the identical-solve gate on a database copy (backend/scripts/verify_db_copy.py --lp), because the rule-engine figures never touch PyPSA and a solver change is invisible without the LP re-solve.

First run, and adding colleagues

  • The first time the app opens it asks you to set up your account (name, e-mail, password). You become the admin and the owner of the workspace, and the app’s super admin (see Managing workspaces below); every project already on this computer stays yours.
  • A colleague — account menu → Workspace settings → Add a member (e-mail, name, role: viewer, editor or admin). Someone who already has an account here joins at once. Anyone else gets a one-time link, shown once with a Copy button: send it to them privately. It works once, for 7 days, and lets whoever opens it choose the password — they land in the app with the role you chose. Pending links are listed there and can be withdrawn.
  • Several workspaces — the account menu lists every workspace you belong to, with your role in each; choosing one moves you there. An admin can create a new one (New workspace…); its creator owns it. The owner renames a workspace, hands it on (Transfer ownership) or deletes it — and the super admin can do all three for any workspace (Managing workspaces, below).
  • Your password — account menu → Change password… (this signs out your other sessions). Forgotten: backend/scripts/set_password.py <e-mail> on the machine.
  • A lost or wrong link — in Links not used yet, New link makes a new one-time link for that person (shown once, valid 7 days from now); the previous link stops working at once. Use it for a lost link, an expired one, or a link made for the wrong address.

Managing workspaces

  • The super admin is the account that set the app up. It sees every workspace — account menu → All workspaces: each one’s name, owner, how many members and projects it has, when it was made and its last activity. Not what it holds: to open a workspace’s projects, choose Join… and a role. Its members see that you joined (in their members list, and in “Since your last visit” on their projects home); Leave… is shown to them too. From the same list the super admin renames any workspace, transfers its ownership to one of its admins, or deletes it.

  • Another super admin is named — or removed — only on this Mac, from the repository root:

    venv312/bin/python backend/scripts/super_admin.py --list
    venv312/bin/python backend/scripts/super_admin.py --add ana@example.com
    venv312/bin/python backend/scripts/super_admin.py --remove ana@example.com
    

    The account must already be able to sign in (someone still holding an invitation link uses it first). The change counts from the next page they open.

  • Deleting a workspace — its owner: Workspace settings → Delete this workspace…; the super admin: All workspaces → ⋯ → Delete…. The dialog first offers Download every project (one zip, a bundle per project — import one back with Projects → Import a project), says where the backup goes, lists the members who would be left with no workspace (they could no longer sign in until someone adds them to another), and asks you to type the workspace’s name. Before removing anything the app saves a copy of the whole database in ~/Desktop/PtX_backups/ (ptx_projects-<date>-<time>-before-delete-workspace-<id>.db, with the workspace’s own default books beside it in …-default-books/). Everything in the workspace then goes: its projects and their results, its members’ access, its links, its default books. It cannot be undone from the app — only by restoring that copy: stop the app, copy the .db file over backend/data/ptx_projects.db (and the …-default-books/ folder back to backend/data/dab_workspaces/<id>/), start the app again.

Share with colleagues on your Wi-Fi

By default only this Mac can open the app. To let colleagues on the office Wi-Fi open it from their own computers, start the server like this — from the repository root, on the Mac that holds the data:

cd frontend && npm run build && cd ..                     # once, and after each update
venv312/bin/python backend/scripts/serve.py --share
  • It serves the API on http://127.0.0.1:8000 exactly as before (so npm run dev keeps working), and the built app over HTTPS on this Mac’s Wi-Fi address only, port 8443: colleagues open https://<your-mac-name>.local:8443 (the exact address is printed, and shown in Workspace settings → Sharing on this network). Stop it with Ctrl+C; without --share nothing is shared (serve.py alone = the API on this Mac).
  • Before it listens, it refuses — with the reason — when no account exists yet, when PTX_DEV_PRINCIPAL is set, when the Wi-Fi has no password, or when the Mac has no private office address on its Wi-Fi (never every network at once, never a VPN). The first time, it makes this Mac’s certificate: each colleague installs it once (below).
  • If the Mac joins another network (or its Wi-Fi address changes), sharing stops by itself within seconds and the settings page says why: start it again on the office network. --reload is refused with --share.
  • The macOS firewall is off on many Macs: turn it on (System Settings → Network → Firewall) and choose Allow when macOS asks whether Python may accept incoming connections.
  • Inviting — Workspace settings → Add a member → Who will open this link? → A colleague on your network: send them that link (and the one beneath it, by the Mac’s address, for a computer that cannot find .local names) together with the certificate file. While nothing is shared, that choice shows the command instead of a link that would not open.
  • Never share the certificate authority’s key (backend/data/private/tls/ca.key): whoever has it can pass for this Mac on every computer that installed the certificate. Only the certificate itself (PtX-Model-local-CA.crt, downloaded from the settings page) is meant for colleagues. The key, like the database, never leaves this Mac.

Install this Mac’s certificate

Each colleague does this once per computer or phone, so their browser trusts this Mac’s shared address. Compare the fingerprint first: the admin reads it from Workspace settings → Sharing on this network (by phone or in person, not in the same message as the file); the colleague’s system shows it while installing. If it differs, do not install the file.

  • macOS — double-click the file: Keychain Access adds it to the login keychain. Double-click PtX Model local CA (…) → Trust → When using this certificate: Always Trust → close the window and confirm with your password. The fingerprint is under Details (SHA-256).
  • Windows — double-click the file → check the Thumbprint on the Details tab (the settings page shows it too) → Install Certificate… → Current User → Place all certificates in the following store → Browse → Trusted Root Certification Authorities → Finish → Yes.
  • iPhone, iPad — send the file by AirDrop or e-mail and open it → Settings → Profile Downloaded → Install. Then Settings → General → About → Certificate Trust Settings → switch on PtX Model local CA.
  • Android — Settings → Security & privacy → More security settings → Encryption & credentials → Install a certificate → CA certificate → Install anyway → choose the file (menu names vary by maker; search Settings for “CA certificate”).
  • Firefox keeps its own list on every system: Settings → Privacy & Security → Certificates → View Certificates → Authorities → Import… → tick Trust this CA to identify websites.

Then open the link. The certificate can vouch only for .local names and private network addresses — never for a public website. To remove it, delete PtX Model local CA (…) from the same place.

When a colleague cannot connect

  • Test the network from their device: https://<your-mac-name>.local:8443/api/health should show {"status":"ok"}. If the name is not found, try the address form (https://<wifi-address>:8443/api/health).
  • It cannot work on mobile data or from home (the Mac’s address exists only inside the office network), on guest or public Wi-Fi (they keep devices apart on purpose), through the colleague’s VPN, or while this Mac sleeps. A browser warning about the certificate means it is not installed yet on that device (above).

Frontend

export PATH="$HOME/.nvm/versions/node/v20.20.2/bin:$PATH"   # Node 20 on PATH
cd frontend
npm install                              # first time only
npm run dev                              # Vite dev server, http://localhost:5173

Open http://localhost:5173. The frontend calls the backend at VITE_API_BASE (default http://localhost:8000) — start the backend first, or the rooms show their “start the backend” error state. The dev page server listens on this Mac only (server.host: 'localhost'), on port 5173 exactly (strictPort: when the port is taken it stops and says so, rather than moving to a port the API would refuse), and may read only the frontend/ folder, its node_modules and docs/ (server.fs.allow) — never backend/ or the data. The built app (npm run build, what serve.py --share serves) calls /api on its own address instead. The data font (JetBrains Mono) ships in frontend/public/fonts/ with its licence: no font is fetched from an outside service.

A second copy on other ports (a worktree’s verification servers, say an API on 8037 and a dev page on 5200): start the API with PTX_DEV_ORIGINS=http://localhost:5200 (and its own PTX_DB_PATH / data folders), and the page with VITE_API_BASE=http://localhost:8037 npx vite --port 5200 — the API accepts that page and nothing else, and the user’s own servers are not touched.

The quality gates (run before you commit)

The frontend’s unit tests run on Node’s own test runner (npm test: the files in frontend/tests/ load a pure module — a sentence or a verdict — through Vite; no browser, no extra dependency). The gates are:

# frontend (from frontend/)
npm run build      # tsc -b && vite build — must be clean
npm run lint       # eslint — 0 errors
npm test           # node --test tests/ — must pass

# backend (from repo root, venv active)
python -m pytest backend/tests -q      # must stay green — read the exit code

npm run build runs the TypeScript project build and the Vite production build, so it catches type errors the dev server tolerates. Treat a red build/lint/pytest as a blocker.

Handy scripts

Command What
npm run dev Vite dev server (HMR).
npm run build tsc -b && vite build (the type + prod gate).
npm run lint ESLint.
npm run preview Serve the production build locally.
npm run docs:check The docs gate: generated numbers current + front-matter and links valid (uses ../venv, or PTX_PYTHON).
python -m pytest backend/tests -q Backend test suite.
python -m pytest backend/tests/test_api.py -k solve A focused subset.

Gotchas

  • French OS locale: the dev machine formats numbers/dates in French. Numeric inputs use parseDecimal and dates use fmtWhen (en-GB) so both . and , work — keep it that way (see frontend/src/format.ts).
  • New docs .md file? The in-app /docs explorer resolves its Markdown glob at dev-server start-up — restart npm run dev for a new file to appear (editing an existing one hot-reloads). The production build always picks up new files.
  • Adding a new .md and a Markdown glob in a JS comment: never put a **/ pattern inside a /* … */ block comment — the */ closes it early. The docs loader uses // line comments for that reason.

How to modify this safely

  • Backend behaviour → core/ + a pytest. Orchestration → controllers.py. New HTTP surface → a thin route in app.py.
  • A response-shape change is a three-place change: core/ producer → api.ts type → client.ts/room. Run tsc to catch every break.

From the app’s documentation, docs/05-development/getting-started.md · Hyprisma 1.0.5