sdp-request-watcher/docs/SESSION.md
Skylar Grant 3e41565ac1 Add security document and move session notes into docs
docs/SECURITY.md covers the layers of protection, what is stored, third
parties, limits and open items to investigate. SESSION.md moves to docs/
and is rewritten as the handoff for removing ticket subjects next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:45:46 -04:00

98 lines
7.1 KiB
Markdown

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