Document purpose
This document is the authoritative technical reference for the Hellcats MVP system
(frontend + Firebase). It is designed to remain useful long-term, including for maintainers
who were not involved in the original implementation.
This is intentionally verbose. The focus is clarity, deterministic behavior, and maintainability,
not minimal text length.
1. Scope and non-goals
The MVP system is a lightweight, cost-free application that tracks participants, event placements,
and computes an MVP candidate using transparent rules. It is built for small groups where:
- operational simplicity is preferred over enterprise-grade identity management
- a single shared “truth” for all users is required
- real-time synchronization is beneficial
- hosting costs should be essentially zero
Explicit non-goals (things intentionally not implemented):
- No individual user accounts, profiles, or permissions per person.
- No complex multi-document relational schema. The state is intentionally centralized.
- No backend services (server) or API layer. Firestore acts as persistence + sync layer.
- No guaranteed conflict-free merging across simultaneous admin operations.
These choices reduce complexity and keep the system easy to operate.
2. Overall architecture
The system uses a static frontend hosted on GitHub Pages and a shared cloud state stored in Firestore.
All clients subscribe to the same Firestore document and render the same state.
High-level data flow
Browser (HTML/CSS/JS)
- renders UI
- authenticates anonymously
- checks password gate
- subscribes to Firestore state via real-time listener
- writes updates back to Firestore
Firestore (Cloud)
- stores shared application state in state/main
- stores password/admin hashes in config/security
- stores undo snapshot in state/undo
The architecture is intentionally “backendless”. Firestore provides a managed storage layer
and real-time updates. GitHub Pages provides globally available static hosting.
3. Static hosting model (GitHub Pages)
GitHub Pages serves static files only. There is no server-side computation. This provides:
- zero hosting cost
- simple deployment (push to main branch)
- stable HTTPS via GitHub-managed certificates
Important implication: Because the site is static, all runtime logic must run in the browser.
For Firebase usage without a bundler, the application imports Firebase SDK modules from the official CDN.
4. Firebase services used
The MVP system uses a minimal set of Firebase services:
- Firebase Authentication (Anonymous) — to obtain an authenticated context for Firestore rules
- Cloud Firestore — as the shared state storage and real-time synchronization mechanism
Other Firebase services (Hosting, Functions, Storage, etc.) are not required for this system
and remain disabled unless specifically needed later.
5. Authentication strategy
The system uses Firebase anonymous authentication. This is a technical mechanism that creates
an authenticated session without user accounts.
The goal is not identity management. The goal is to ensure that Firestore requests have
request.auth != null, enabling simple security rules.
Why not user accounts?
User accounts add friction (registration, password resets, permissions) and increase maintenance burden.
For a small group application, the password gate + anonymous auth is sufficient.
6. Password gate design
The password gate is a UI overlay that blocks access until a correct password is provided.
The password is never stored in the source code. Instead, the gate validates by comparing a SHA-256 hash.
Stored values
config/security
passwordHash: string // sha256(password)
adminHash: string // sha256(adminPassword) (optional but recommended)
The password gate must be understood correctly: it protects the user interface from casual access,
but it is not a substitute for proper Firestore access rules. The actual data protection is enforced by
Firestore security rules. The gate exists mainly to prevent non-members from interacting with the system.
Why a hash and not plaintext?
Storing plaintext passwords is a security anti-pattern. Hashing prevents accidental disclosure
and avoids a “secret in cleartext” problem in the database. SHA-256 is sufficient here because
the threat model is small-group access control. If stronger protection is desired, a salted hash
scheme can be added later.
7. Firestore data model
The application state is stored as a single document to simplify reasoning and synchronization:
Primary state document
state/main
persons: Person[]
events: Event[]
mvpCooldown: number
Person structure
Person
id: string
name: string
title: boolean
blocked: boolean
mvpCount: number
cooldownLeft: number
placements: Placement[]
Placement
event: number
place: number
Why single-document state?
A single-document state avoids partial updates and complicated joins. It also makes backup/export trivial:
the entire system can be restored by replacing one document.
This approach scales well for small datasets (dozens of persons, hundreds of events).
If you ever approach Firestore document size limits, you would split the model into collections,
but that is intentionally out-of-scope for now.
8. Real-time synchronization
The frontend subscribes to the state document using Firestore’s real-time listener (onSnapshot).
Whenever the document changes, all clients receive the update and re-render the UI.
This creates a “shared truth” experience similar to collaborative documents. It also reduces the need
for manual refreshes or polling.
Expected behavior
- When one user adds a person, everyone sees it immediately.
- When one user saves an event, all participants see updated placements.
- When admin performs import/reset/undo, state changes atomically for all users.
9. Write strategy and consistency
Writes are performed by updating the full top-level fields of state/main. This is a trade-off:
it is simple and deterministic, but simultaneous conflicting edits can cause last-write-wins behavior.
This is acceptable for the intended usage model: usually one person performs edits at a time, and the group
agrees on changes before performing them.
Consistency notes
- Firestore provides atomic document writes.
- Last-write-wins can happen if two users edit at the same time.
- The system intentionally does not attempt to merge edits.
10. Admin mode
Admin mode is a session-local state (browser session only). Unlocking admin mode enables destructive operations:
import, reset, and undo. This separation reduces accidental data loss.
Admin unlock design
Admin unlock validates the admin password by SHA-256 hashing the input and comparing it to
config/security.adminHash. If successful, a flag is written to sessionStorage.
This flag is not shared across users and is cleared when the browser session ends (or when the admin locks).
Admin mode is intentionally not persisted in Firestore because admin privileges should not be granted globally.
11. Export / Import
Export produces a JSON file containing the entire current application state.
Import replaces the entire state with a JSON payload.
Import is a destructive operation because it overwrites the shared document. Therefore, it is restricted
to admin mode and is paired with an automatic undo snapshot.
Recommended backup practice
- Export before major changes (season reset, rule adjustments).
- Keep backups in a shared location accessible to admins.
- Name backups with date and context (e.g. season-end).
12. Undo mechanism
Undo is implemented as a snapshot restore mechanism. Before import or reset, the system stores the current state
as a snapshot in state/undo. Undo then writes the snapshot back into state/main.
Key properties
- Deterministic: undo restores exactly the previous state.
- Safe: undo is restricted to admin mode.
- Simple: no merging, no diff logic, no partial restore.
What undo does not do
- Undo does not reconcile changes made after the snapshot.
- Undo does not maintain multiple history steps (single-step undo only).
- Undo does not attempt to interpret intent — it just restores a state.
13. Firestore security rules
The rules should ensure that only authenticated users can read/write the shared state, while allowing
the password hash document to be readable so the gate can validate.
Example rule set (minimal)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /state/{doc} {
allow read, write: if request.auth != null;
}
match /config/{doc} {
allow read: if true;
}
}
}
Note: This rule set is intentionally simple. If you want stronger restrictions (e.g. only allow reads on
config/security and no other config docs), refine the match statements accordingly.
14. Deployment and DNS
This wiki is hosted on GitHub Pages and mapped to wiki.zabini.org using a DNS CNAME record.
It is recommended to enforce HTTPS in GitHub Pages settings.
Common DNS configuration
CNAME
Name: wiki
Target: <your-github-username>.github.io
If you see NXDOMAIN, DNS propagation or record configuration is incomplete.
If you see the site but without CSS, the asset path is wrong — always use absolute paths.
15. Debugging checklist
- Plain HTML only: CSS path is broken. Verify
/assets/wiki.css returns HTTP 200.
- No background image: verify
/documentations-background-image.jpg exists and returns 200.
- Login never unlocks: check
config/security.passwordHash exists and matches SHA-256 of the password.
- Admin unlock fails: check
config/security.adminHash exists and matches SHA-256 of admin password.
- Firestore permission errors: confirm Firestore rules and anonymous auth are enabled.
16. Maintenance & extension guidelines
When extending the system, preserve the core properties: transparency, determinism, and low operational overhead.
Recommended approach:
- Prefer explicit data fields over derived hidden state.
- Document every rule that affects scoring or eligibility.
- Keep destructive operations behind admin mode.
- Export before any major structural change.
If you introduce additional documentation pages, link them from the Documentation Hub to keep navigation discoverable.