# Session summary: 2026-10-15 (v2.1.0) A Claude Code session that added per-user settings, closed a sign-in gap, and wrote the security document. Background is in `CLAUDE.md`, `docs/ROADMAP.md`, `docs/NOTES.md` and `docs/SECURITY.md`. ## Where things stand - v2.0.0 (per-user Zoho sign-in, Docker) is running in production. - Branch `v2.1.0` is pushed with four commits on top of v2.0.0, and tagged **`v2.1.0-prod-hotfix`** (`2f137af`). The tag starts the Gitea image build. **The build and the production deploy haven't been confirmed.** - Since that tag, these changes are **not committed**: `SECURITY.md` and `SESSION.md` moved into `docs/`, the open items section in `docs/SECURITY.md`, and the doc links in `CLAUDE.md` and the README. ### Before deploying v2.1.0 on the VPS 1. Add `ALLOWED_EMAIL_DOMAINS=mainecc.edu` (plus any campus domains staff use) to the production `.env`. **The server won't start without it.** 2. Add the `sdp-data:/app/data` volume to the compose file (on the service, plus the top-level `volumes:`), as in the repo's `docker-compose.yml`. Without it, saved settings are lost each time the container restarts. 3. `docker compose pull && docker compose up -d`, then check: - an MCCS account signs in and gets the setup prompt - a personal Zoho account is sent back with "not allowed" ## What this session did ### Per-user settings (commits `9479b12`, `53c519e`, `817fa47`) - **⚙ Settings** button, and a setup prompt on someone's first visit (Save, or Use the defaults). - Settings per user: - sites, groups and technicians, entered as tags with name suggestions; typing any name still works - how often the status table recounts (2 to 60 minutes) and how long the feed lasts (1 to 24 hours) - weather location, from the campus list in `src/places.js` - hidden panels (stat tiles, weather, feed) and hidden status columns The ding plays even with the feed hidden. Statuses and sound aren't per user. - **Storage:** on the server in `DATA_DIR/settings.json`, keyed by the lowercased Zoho email, so settings follow people between browsers. Only values that differ from `.env` are stored. This replaced the planned `localStorage` design, and the container now needs a volume (`/app/data` in Docker). - **Name suggestions** (`/api/names`, `src/names.js`): one region-wide query of recent tickets (30 days, up to 500 tickets, names only). It's refreshed at most hourly, shared by all users, and kept in `DATA_DIR/names.json`. No new Zoho scope was needed. A typed name that differs from SDP's only in case is saved with SDP's spelling. - **Server side:** - The server reads each user's settings itself; the page doesn't send them with each call. - `PUT` and `DELETE /api/settings` validate everything (`LIMITS` in `src/settings.js`). - Weather is cached per zip. - The closed feed's hard-coded 12-hour cut-off was removed, since the feed length is now a setting. ### Sign-in limited to email domains (commit `2f137af`) - **The gap:** any Zoho account can approve our client. An outside account got a session, and `/api/session` gave it the `.env` site, group and technician names. **Production v2.0.0 still has this gap until v2.1.0 is deployed.** - **The fix:** `ALLOWED_EMAIL_DOMAINS` is required. - The callback refuses other domains and revokes their Zoho token. - `requireSession` checks the domain again on every call, so older cookies stop working. - Matching is exact, so lookalike domains are refused. - `/api/names` now makes the caller's own token read one row from SDP before it returns the shared names. ### Documents - `docs/SECURITY.md`, written for MCCS IT leadership and reviewers. It covers each layer of protection, what is stored, third parties, the limits, **12 open items to investigate**, and how everything was tested. - The README, `env.example`, `NOTES.md` (decisions) and `ROADMAP.md` were updated as each piece landed. ### Verified - **Server, end to end** over a unix socket with axios stubbed: - defaults and the first-visit state - name suggestions: the region-wide query, the cache, and the access check before the cache is served - validation of every field, non-JSON bodies, and malformed JSON - saving: only values that differ from the defaults are stored, and names take SDP's spelling - the closed and workload queries use the saved region and feed length - weather per zip - reset, and saved settings surviving a restart - the domain check: outside, lookalike and missing emails, a second allowed domain, an outsider's pre-existing cookie, and startup without the setting - **Page, in jsdom** with stubbed fetch: - the setup prompt, and suggestions leaving out names already chosen - tag adding and removal, Enter, Backspace, and a typed but un-added name counting on save - a server error shown inside the dialog, and the save body - after a save: the feed restarting, the recount, the weather reloading, and hidden panels and columns - reset, and the full-width layout - **Not verified:** - anything against real SDP or Zoho - how the dialog looks; there's no Chrome or Edge on the dev Mac for a screenshot - the Gitea build of the hotfix tag ## Next steps 1. **Remove ticket subjects (first thing tomorrow).** Ticket subjects can name students or describe their situation, which is a FERPA risk. They reach the page in two places: - **Closed feed:** `fetchClosedTickets` in `src/tickets.js` requests `subject` and returns it. The page shows it in `renderTicket` in `src/public/index.html`. - **Unassigned list:** `fetchUnassigned` requests `subject` and returns it. The page shows it in `renderUnassignedList`, the hidden debug list. Drop `subject` from `fields_required` and from the returned objects, so it never leaves SDP, and update both render functions. The server never writes subjects to disk; they only pass through memory and the page. Afterwards, update `docs/SECURITY.md` (the page section, and open item 1) and `docs/NOTES.md`. 2. **Technician check at sign-in** (the ROADMAP item). Ask SDP whether the signed-in user is a technician. That probably needs a users or technicians read scope; confirm the exact scope name and endpoint in Zoho's docs. A new scope means everyone signs in again. It would also allow real pick lists for technicians. 3. **Commit** the document moves and the open items. 4. **Deploy v2.1.0**, following the checklist above. Then try it live: the quality of the name suggestions, the setup prompt, and the volume surviving a `pull` and `up -d`. 5. **Work through the open items in `docs/SECURITY.md`**, starting with MCCS IT approval for the hosting and the non-technician test. 6. **Carried over from earlier sessions:** - check that the token renews after an hour - README screenshots - links to Zoho's documentation for the web sign-in flow - the status table scrolling sideways (see `NOTES.md`) - fill in campus satellites and confirm the zip codes in `src/places.js` ## To pick up Start a Claude Code session in this repo and say something like: > Read docs/SESSION.md and docs/SECURITY.md. First, remove ticket subjects from everything the server requests and the page shows (FERPA). Then we'll add the SDP technician check at sign-in.