Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 46 additions & 31 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,62 @@
# TapAuth architecture
# How TapAuth works

TapAuth is a local-first NFC attendance and reservation kiosk designed for a Raspberry Pi. A private on-device registry keeps card identity available without MySQL; MySQL stores the complete operational records, and Firebase Realtime Database is an optional, retryable remote copy.
TapAuth is local-first because the Raspberry Pi is the one device that should remain useful even when another service is unavailable.

## Runtime flow
## A tap, from card to record

When a card is tapped, the ACR122U reader sends its UID to the Flask application. TapAuth normalizes that UID so raw reader bytes and common text formats resolve to one stable value.

The application checks the private on-device registry first:

```text
ACR122U NFC reader
|
v
Raspberry Pi / Flask ----> MySQL
| \----> private on-device NFC registry
| |
| +---- durable Firebase retry queue
v
Firebase Realtime Database ----> public-safe activity page
ACR122U reader
|
v
UID normalization
|
v
On-device registry
|
+-- known card --> check in, check out, or appointment
|
+-- unknown card --> short-lived registration session
```

The reader runs continuously and reconnects after hardware interruptions. A card tap creates a short-lived session. Card identity is checked against the durable local registry first, so an assigned card remains recognized through service restarts and MySQL outages. Registry records reconcile back to MySQL when it becomes available.
I thought about relying only on MySQL here, but that makes a registered card appear unknown whenever the connection has a temporary problem. The registry avoids that. A successful database lookup or registration refreshes the local copy, and a background worker reconciles locally saved students when MySQL returns.

## Where each kind of data belongs

| Data | Primary location | Other copies |
| --- | --- | --- |
| Card identity fallback | `data/registered_cards.json` | Reconciled with MySQL |
| Students | MySQL `users` | Private Firebase copy without NFC UID |
| Attendance | MySQL `logs` | Public-safe Firebase event copy |
| Reservations | MySQL `reservations` | Private Firebase copy |
| 3D model files | Pi-local `uploads/models` | Never copied to Firebase |

## Data ownership
The local registry uses atomic replacement, keeps a backup copy, and applies restricted file permissions when supported. Its directory is ignored by Git.

- `users`: private student profile and card association in MySQL; mirrored to private Firebase data without the NFC UID.
- `data/registered_cards.json`: private Pi-local identity fallback with restricted file permissions, atomic replacement, and a backup copy; ignored by Git.
- `logs`: complete local attendance record; mirrored to the public path with event and timing fields only.
- `reservations`: requester and request details in MySQL; mirrored to private Firebase data without NFC UIDs or local file paths.
- `uploads/models`: Pi-local 3D files; ignored by Git and never copied to Realtime Database.
## When something is offline

Firebase paths live below the configurable `tapauth` root. Database rules allow public reads only for `tapauth/logs`; browsers cannot write any path.
- **MySQL unavailable:** known cards are still recognized and new registrations can be saved locally. Local identities are queued for reconciliation.
- **Firebase unavailable:** normal local operations continue. Failed remote writes remain in `firebase_sync_queue` for retry.
- **NFC reader disconnected:** the scanner keeps retrying instead of terminating the service.
- **Browser refreshed:** the server remains the source of truth for real NFC sessions.

## Availability model
MySQL still owns complete attendance and reservation operations. The registry specifically removes MySQL as a single point of failure for card recognition and registration.

Attendance and reservations complete against local MySQL even when Firebase is unavailable. Failed cloud writes enter `firebase_sync_queue`, and the background worker retries them without blocking the kiosk.
## Privacy boundary

## Security boundary
Oh! This part matters: the public activity feed never needs a student's card UID or full profile.

- Student registration must match the latest NFC UID and tap counter and expires after 120 seconds.
- The management dashboard requires `TAPAUTH_ADMIN_CODE` and is intended for a trusted local network.
- Public API responses remove NFC UIDs and model storage paths.
- `.env`, database exports, uploaded models, Firebase secrets, and service-account files are excluded from Git.
- The Firebase browser key identifies the public web application; it is not an administrator credential.
- Public responses contain sanitized event and timing fields.
- NFC UIDs and model-file paths are removed from public Firebase records.
- Registration must match the latest physical tap and expires after 120 seconds.
- The local management page requires `TAPAUTH_ADMIN_CODE`.
- `.env`, registry data, uploads, exports, and server credentials stay outside Git.

For an internet-facing deployment, place the Flask service behind HTTPS, network authentication, and a reverse proxy rather than exposing port 5000 directly.
If Flask is exposed beyond a trusted local network, place it behind HTTPS, authentication, and a reverse proxy.

## Extension points
## Main extension points

Database access, Firebase synchronization, NFC reading, and presentation are separated into modules. A future hosted API, email provider, or object-storage adapter can be added without changing the kiosk interaction model.
The reader, database, Firebase adapter, local registry, and interface are separate modules. A hosted API, email service, or object-storage adapter can be added without rewriting the NFC interaction itself.
39 changes: 26 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
# Changelog

All notable TapAuth changes are documented here.
This file records the changes I would want to know about before updating an installed TapAuth kiosk.

## Unreleased
## 1.1.0 — 2026-07-29

I kept running into one uncomfortable edge case: a student could finish registration, tap again, and still look unregistered if MySQL was slow or unavailable. This release makes card recognition local-first.

### Changed

- Added a durable on-device NFC registry with atomic writes and backup recovery.
- Allowed student registration and recognition to continue without an active MySQL connection.
- Added automatic reconciliation between the local registry and MySQL.
- Normalized ACR122U UIDs across raw bytes and common text formats.
- Added legacy-record self-healing, database retries, and a recent-registration cache.
- Expanded Recent Logs to the latest 25 records with a responsive **See more** control.
- Added the server dependencies needed by registration tests in CI.
- Reworked the public documentation around the actual project story and runtime flow.

### Fixed

- Added a durable, atomic on-device NFC registry that recognizes assigned cards without MySQL and reconciles automatically when MySQL returns.
- Canonicalized ACR122U UIDs across raw bytes and common text formats, with legacy-record self-healing, database retries, and a recent-registration fallback cache.
- Expanded Recent Logs to the latest 25 records with responsive row fitting and a See more control.
- Fixed newly registered cards appearing unregistered during the NFC cooldown window.
- Added a post-write persistence check and registration-flow regression tests.
- Added the server dependencies required by registration tests to CI.
- Fixed database outages being reported as though a card had never been registered.
- Added post-write persistence checks and regression coverage for offline registration.

## 1.0.0 — 2026-07-22

This was the first public TapAuth release: the point where the AIRHub NFC prototype became a reusable Raspberry Pi project.

- Renamed the project to TapAuth.
- Added short-lived tap-bound student registration without a shared student code.
- Added local and Firebase-backed student, attendance, and reservation data.
- Added tap-bound registration without a shared student registration code.
- Added NFC attendance, 3D printing requests, and teacher appointments.
- Added MySQL storage and Firebase-backed private/public data boundaries.
- Added secure local 3D model storage and reservation queue positions.
- Added the student and reservation management dashboard.
- Added Raspberry Pi startup, reader recovery, update tooling, CI, and open-source documentation.
- Added student and reservation management.
- Added Raspberry Pi startup, reader recovery, update tools, CI, and community files.
- Removed student identity from the publicly readable Firebase activity feed.
- Added automated privacy boundary tests and complete GitHub community templates.
- Removed obsolete machine-specific recovery notes and the deprecated Firestore-named sync alias.
16 changes: 9 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Contributing to TapAuth

Thanks for helping improve TapAuth.
TapAuth is a personal project, but I am happy to review focused improvements that keep the kiosk dependable and understandable.

1. Fork the repository and create a focused branch.
2. Keep the kiosk lightweight and usable at 1366×700.
3. Never commit `.env`, database exports, NFC identifiers, or Firebase server credentials.
4. Run the checks documented in the README.
5. Explain hardware, schema, and UI behavior changes in the pull request.
1. Fork the repository and create one branch for one clear change.
2. Keep the interface lightweight and usable at 1366 × 700.
3. Never commit `.env`, student records, database exports, NFC UIDs, or server credentials.
4. Run the checks listed in the README.
5. Explain what changed, why it matters at the kiosk, and how you verified it.

For NFC changes, include the reader model and Raspberry Pi OS version used for testing. For database changes, make `schema.sql` safe to run against an existing installation.
For NFC changes, mention the reader model and Raspberry Pi OS version you tested. For database changes, keep `schema.sql` safe to run on an existing installation.

Oh! If a change adds a dependency, please explain why the Pi needs it. Keeping the runtime small is part of the project.
Loading