Status: Planning consolidated — v1 Purpose: Single reference document for stack, hosting, schema, and access-control decisions made so far, before build begins.
SKC previously scoped a rescue-tracking database (see SKC.md) built around one core principle:
One permanent record per cat, with separate linked records for events and history.
This spec adapts that plan into a web app the group (5–10 people) can use to view and update the current state of any cat, with a calendar of upcoming events per cat.
Constraint: App runs on an existing iMac (macOS 10.15 Catalina) that already hosts security cameras and three other websites via Apache + php-fpm. Avoid adding new runtimes/processes where possible.
Decided stack:
| Layer | Choice | Why |
|---|---|---|
| Web server | Apache + php-fpm (existing) | Already installed and managed; no new service |
| Backend | Plain PHP | No new runtime (rejected Node.js/Next.js as unnecessary weight for an internal 5–10 user tool) |
| Database | MySQL (via MacPorts) | Familiar from work use; handles light concurrent multi-user writes better than SQLite |
| Frontend | Vanilla HTML/CSS/JS (+ Alpine.js if needed for interactivity) | No build tooling/npm ecosystem needed |
| Calendar | FullCalendar (JS library, script-tag only) | No build step required |
| Hosting path | New vhost: cat.skunkworks.nz |
Name-based virtual hosting — no conflict with camera setup on bare skunkworks.nz domain |
| DNS | Managed via Rocketspark webform | Add A/CNAME record for cat.skunkworks.nz — confirm Rocketspark allows full DNS edits |
| SSL | Certbot (existing Let's Encrypt setup) | Add cat.skunkworks.nz as a new vhost + run certbot, same pattern as existing sites |
Net result: everything runs inside infrastructure already operated — new MySQL database + new folder of PHP files under a new Apache vhost. Nothing fundamentally new to learn or maintain.
Based on the original 14-table plan, with two additions: Status History and Cat Events (replacing a rigid pathway model — see Section 4).
Linking: every table uses a unique prefixed ID (CAT-0001, COL-0001, MED-0001, etc.), with Cat ID as the key link across most tables — a single cat can have multiple medical records, foster placements, events, expenses, and documents without duplicating its core record.
Problem with a fixed pipeline: cats arrive through too many different routes (microchipped vs. not, wild-born vs. stray, TNR-to-wild vs. TNR-to-feeder, existing vet history vs. none) to hardcode a single checklist per "pathway."
Decided approach — flexible, per-cat checklist:
Event Types holds the master list of possible events.Cat Events links individual cats to whichever event types are relevant to them.This reuses the same "permanent record + linked events" principle as the rest of the schema, applied to workflow instead of history — and it's what feeds the calendar (any Pending Cat Event with a due date shows up there).
Key decision: a person's Role/Title (Volunteer, Trapper, Feeder, etc. — descriptive, stored in People) is completely separate from their access level (what they can actually view/edit in the app, stored in Permissions). A Trapper might have full edit access; a Coordinator might be read-only — title tells you nothing about access.
Starting model (deliberately simple, built to extend):
Permissions table: User ID + Access Level + Scope
None / View / EditEverything or Cats only (view/edit cat status only — no colonies, expenses, tasks, etc.)Example — the known current case: a Trapper who should only ever see cat status and nothing else → Access = View, Scope = Cats only.
Why this scales without a rewrite: when a messier exception shows up later (e.g. someone who can edit Colonies but not Cats), the fix is to add another feature-scoped row for that user in the same table — not to redesign the whole permission system. Starting with one row per user; extending to one row per user-per-feature-area only when actually needed.
Two superusers: two Users rows with a "manage users" flag set. Default assumption: either superuser can edit or deactivate the other (no built-in safeguard against lockout) — flag if you'd prefer this to require confirmation from both instead.
Invite flow:
Invites row (email + single-use token + expiry, e.g. 7 days) and sends the link: cat.skunkworks.nz/set-password?token=...Users row.active = 0 — history (who-did-what) is preserved, not deleted.SKC.md (friendly stray, feral community cat, pregnant colony cat, sick cat, foster move, multi-session colony trapping) before finalising.skunkworks.nz (not just their own hosted content).SKC.md Section 6)This document reflects decisions made through project planning conversation. It should be treated as a living reference — update it as decisions change rather than starting a fresh document.