This is a replacement browser for the Santa Monica city-run activities reservation website: https://anc.apm.activecommunities.com/santamonicarecreation. The primary reason for building this site is that one must click into an activity's detail page to see what days of the week it is on. This is especially annoying when trying to sign up for a child's swim lessons because there will be 10-12 instances of the same lesson on different days and times and each one must individually be clicked into to see when it is! So this app provides a card browser that contains that information up front and also a calendar view.
This project is also my first time using an AI for anything. I built this with the opencode terminal and Claude Opus 4.5. So far I'm super impressed with the capability of the AI, but the code (although functional) is still quite a bit of a mess. Functionality isn't cleanly separated between modules, URL's and other settings-type stuff is hardcoded everywhere despite the existince of a settings.py file, and Claude seems to treat Python's typing system like a suggestion rather than a requirement (but hey, maybe that's the Pythonic thing to do with type hints anyhow...).
Another observation while working this small project is that Claude always jumps straight to a solution that is code based, even if the right thing to do is ask the user to run some tests first. Once I realized this and started asking things like "what test can I do to inform the next steps" the development process suddenly started going a lot faster.
Overall I got to a working MVP of the activity browser in about 4-5 20 minute sessions with Claude (using Opus 4.5 burns through my 5 hour quota pretty darn fast!).
The application supports multi-user sessions. Each visitor gets their own isolated connection to the upstream ActiveNet API.
When a new visitor arrives, the server automatically bootstraps an anonymous ActiveNet session on their behalf:
- The server makes a
GETrequest to the ActiveNet sign-in page. This page goes through a redirect chain and ultimately returns HTML that embeds a CSRF token (window.__csrfToken = "...") in an inline<script>tag. - The CSRF token and all cookies set during the redirect chain are captured
and stored in a new
ActiveNetClientinstance. - A cryptographically random session ID (generated via
secrets.token_urlsafe) is created and sent to the visitor's browser as anhttponlycookie (samo_session). - The visitor can now browse and search activities without logging in — the bootstrapped session provides the cookies and CSRF token needed for read-only API calls.
When a visitor clicks "Log In", they are presented with a form that asks for their ActiveNet email and password. On submission:
- The browser sends the email and password to
POST /loginon this server. - The server's route handler receives the credentials as local function parameters.
- The handler calls
ActiveNetClient.login(username, password), which builds a JSON request body and sends it to the upstream ActiveNet API atPOST /rest/user/signinover HTTPS. - The upstream API validates the credentials and, on success, returns new session cookies, a JWT access token, a JWT refresh token, and basic customer info (name, email, customer ID).
- The server updates the visitor's
ActiveNetClientinstance with the new session cookies and tokens. It also stores the non-sensitive customer info (name, email, customer ID) for display in the UI header. - The visitor is redirected to the homepage. Their session is now authenticated and the header shows their name with a "Log Out" link.
When a visitor clicks "Log Out":
- The server looks up their session by the
samo_sessioncookie. - The
ActiveNetClientinstance is cleared (all cookies, tokens, and user info are wiped) and removed from the session store. - The
samo_sessioncookie is deleted from the browser. - The visitor is redirected to the homepage. On the next request, a new anonymous session is bootstrapped automatically.
Each visitor's ActiveNetClient instance is stored in an in-memory dictionary
on the server, keyed by their random session ID. Different visitors never
share a client instance. Sessions do not survive a server restart.
The plaintext password is handled with strict discipline to ensure it is never retained after use:
- Never stored as an attribute: The password is only ever present as a
function parameter — first in the
POST /loginroute handler, then in theActiveNetClient.login()method. It is never assigned toself, a class attribute, a global, or any persistent data structure. - Never written to disk: The password is not saved to any file, database, session store, cookie, or cache.
- Never logged: The application does not log request bodies. The
login()method logs the username on success or failure for diagnostics, but never the password. - Single use: The password appears in exactly one place — the JSON body of
the HTTPS POST to the upstream ActiveNet API (
/rest/user/signin). - Immediate disposal: After the upstream API call returns, the
passwordlocal variable goes out of scope and becomes eligible for garbage collection. No reference to it survives. - What IS stored: Only the upstream API's session tokens (
JSESSIONID,access_token,refresh_token) and non-sensitive customer info (first_name,last_name,email,customer_id) are retained in memory for the duration of the session.