# Phase 2 — Compass login + fetch notes Status: **blocked on Cloudflare.** The credential is available and the login page loads, but Cloudflare's managed challenge blocks the login **POST** under the headless harness. See AUD-104. ## Harness used - `pi-browser-harness` **v0.11.0** (`/home/sam/.agents/extensions/pi-browser-harness/package.json`). - Entrypoint: the pi extension `src/index.ts` plus its long-lived daemon `src/daemon/index.ts`. The daemon bridges `pi → Unix socket (/tmp/pi-browser-daemon.sock) → Chrome DevTools Protocol`. - Chrome: systemd user service `chrome-pi.service` (`/home/sam/.config/systemd/user/chrome-pi.service`), `--headless=new`, `--remote-debugging-port=9222`, `--user-data-dir=/home/sam/.chrome-pi`. - The Phase 2 fetcher (`scripts/compass-fetch.mjs`) talks to the harness daemon socket and drives the harness's Chrome over CDP. ## Compass - URL: `https://nhs-vic.compass.education/login.aspx` (Northcote High School). - Login form: `#username`, `#password`, hidden `browserFingerprint`, `__RequestVerificationToken`, submit `#button1` (`formaction="/login?handler=Login"`). Also present: a SAML button and **"Parent sign in with one-time code"** (`formaction="/login?handler=OtpSignin"`). ## Cloudflare finding (important) Compass is behind a Cloudflare managed challenge. With the default `HeadlessChrome` user agent, the challenge loops forever (`title="Just a moment..."`, body "Compass is verifying your connection..."). The fix that works: 1. Override the user agent to a normal desktop Chrome UA (`Network.setUserAgentOverride`), and 2. run the page in an **isolated browser context** (`Target.createBrowserContext`) so stale `cf_clearance` / `cf_chl_rc_ni` cookies from earlier attempts do not loop the challenge. With both, the challenge clears in ~5 s and the real login page loads. Both are implemented in `scripts/compass-fetch.mjs`. ### The login POST is the hard part When the form is submitted (`POST /login?handler=Login`) Cloudflare returns a **403 managed challenge** and serves `Just a moment...`. Under headless Chrome that challenge does not clear (`iframes=0`, body "Compass is verifying your connection..."), so Compass never sees the login. Captured response headers on the blocked POST show `server: cloudflare`; the GET of the same page passes. Tested and did **not** fix it: a normal desktop-Chrome UA override, isolated browser contexts, `Sec-CH-UA` user-agent metadata, focus/visibility emulation, client hints, and long waits (up to 4 minutes). The POST passes only intermittently. It was observed returning `200` twice, but five further attempts (including after a 75 s cooldown, each in a fresh context) were all challenged. `scripts/compass-fetch.mjs` retries the login on a fresh browser context up to `COMPASS_LOGIN_ATTEMPTS` (default 4) and fails loudly with `cloudflare_login_post_challenge` (exit 2) if none get through. This is the Phase 2 risk: the harness can read Compass but cannot log in from headless Chrome. ## Proof stored (2026-10-15) ``` var/raw/harry//login.html var/raw/harry//login.meta.json var/raw/harry//manifest.json ``` - `var/` is git-ignored. Raw pages stay local, never committed. - `login` — `https://nhs-vic.compass.education/login.aspx`, title `Login | Compass`, **125 741 bytes**, sha256 `4d3b731c34d64b6978d4e1bd7f4a7772ae2b5d8687241db3d7163c2beb325f01`. ## Blocked on A reliable way past Cloudflare's managed challenge for the login POST. The credential itself is no longer missing: it is in `~/.config/environment.d/10-secrets.conf` as `COMPASS_USERNAME` / `COMPASS_PASSWORD`. Options (owner: Sam, because they are host/infra or browser decisions): 1. Give the harness a **headful** Chrome (e.g. add `xvfb` to the `.13` NixOS config and run Chrome non-headless). Managed challenges normally pass there. 2. Supply a `cf_clearance` cookie from a real browser session on the same public IP and user agent, as a secret to inject. 3. Confirm a non-CF login path (SAML/SSO or the Compass mobile API) to pursue. ## Smallest useful scope for Phase 3 Until a login succeeds we cannot enumerate the per-child page URLs, so the smallest useful scope cannot be fixed yet. It is expected to be the "today/planner" page plus the messages/notices list, which is what answers "what is overdue".