66 lines
2.9 KiB
Markdown
66 lines
2.9 KiB
Markdown
# 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/`.
|