From 24417bc7cf992ca846f3ed69c3ed3c4c1593ec3f Mon Sep 17 00:00:00 2001 From: Rene Luria Date: Wed, 8 Jul 2026 14:11:18 +0200 Subject: [PATCH] doc: add AGENTS.md --- AGENTS.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..4487875 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,65 @@ +# AGENTS.md + +Compact operational guide for OpenCode sessions. See `CLAUDE.md` for the prose +overview; this file captures what an agent would otherwise get wrong. + +## Commands + +```bash +# First-time test env (creates venv/, installs requirements.txt + requirements-test.txt) +./setup_test_env.sh + +# Full suite with coverage (activates venv, erases old coverage, builds htmlcov/) +./run_tests.sh + +# Manual, from an active venv +python -m pytest tests/ --cov=. --cov-config=.coveragerc -v + +# Single test +python -m pytest tests/test_main.py::test_health_check -v + +# Dev server (must run from repo root — see Gotchas) +uvicorn main:app --host 0.0.0.0 --port 8000 --reload +``` + +There is no lint, typecheck, formatter, or CI config in this repo. Do not +claim to run `ruff`, `mypy`, `flake8`, or `black` — none are installed or +configured. The only verification step is the pytest suite above. + +## Gotchas + +- **Run from repo root.** `read_root()` and `favicon()` open + `templates/index.html` / `templates/favicon.ico` via relative paths + (`main.py:329`, `main.py:371`). Invoking uvicorn from another cwd 404s the + frontend. +- **`@lru_cache` on OIDC helpers.** `get_well_known_config`, `get_jwks`, + `get_userinfo_endpoint`, `get_token_endpoint` are all `lru_cache(maxsize=1)` + (`main.py:68-129`). When a test patches `requests` with a different + response, call `func.cache_clear()` first or you get the previous mock's + cached value. Existing tests do this; new tests must too. +- **Python 3.14, not 3.7.** `.python-version` and the Dockerfile + (`python:3.14-alpine`) target 3.14. The README's "3.7+" claim is stale. +- **`.envrc` is gitignored and ships real credentials.** It exports a live + `CLIENT_ID` / `CLIENT_SECRET` for direnv. Never commit it, never paste its + values into code or commits. +- **`CLIENT_ID` has no default.** `main.py:28` reads it from env with no + fallback; the app and several tests need it set. `OIDC_ISSUER` defaults to + `https://login.infomaniak.com`; `CLIENT_SECRET` defaults to `""` (breaks + `/refresh-token` with "Client secret not configured"). + +## Architecture essentials + +- Single-file backend `main.py` (FastAPI) + single-file frontend + `templates/index.html`. No package layout, no `pyproject.toml`, no + `setup.cfg`. +- `AUTHORIZED_USERS` (`main.py:33`) is a hardcoded `{email: secret_phrase}` + dict — the "protected resource". Not a database. +- Token validation: `verify_token(id_token, expected_nonce=None)` does JWKS + signature verification + audience/issuer checks. The `expected_nonce` + param binds the id_token to the login flow (anti-replay); `/validate-token` + forwards the nonce from the request body (`main.py:278`). Tests in + `test_token_validation.py` and `test_main.py` cover the nonce paths — keep + them green when touching auth. +- Tests use `fastapi.testclient.TestClient` (sync, backed by `httpx`) and + `unittest.mock.patch` against `main.*`. Fixtures live in `tests/conftest.py`. +- Coverage config (`.coveragerc`) omits `venv/`, `tests/`, `.venv/`.