# 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/`.