An Eight Mile project · open source
RACS
Reader Access Control System
Keep the door moving, even when the network does not.
A camera-first access reader that validates campaign cards locally, stops repeat entry across nearby devices, and reconciles every decision with a central API.
Reader
Flutter 3
API
Spring Boot 4
Store
MySQL + SQLite
Sync
WS + WebRTC

At the entrance
One focused flow from camera to decision.
RACS keeps the interface deliberately spare. Operators prepare their campaign once, then each presentation produces a clear outcome while the system handles persistence and synchronisation underneath.
Operator path
campaign → decision
01 · Prepare
Download the campaign
Choose the active campaign and place its authorised card set on every reader.
02 · Read
Frame the code
The full-screen camera focuses the operator on one task: presenting the pass cleanly.
03 · Decide
Validate on device
Format, card state, validity window and prior use are checked against local data.
04 · Reconcile
Share the decision
The scan is stored locally, submitted centrally and broadcast to connected readers.
Legible outcomes
The answer is readable at a glance.
A busy entrance should not require interpretation. Every card ends in a distinct state, while the exact machine flag remains attached for audit and reporting.
Access granted
Valid, current and unused.
PASSED_OK
Already presented
Seen by this reader, a peer or the API.
DUPLICATE
Access declined
Unknown, expired or invalidated card.
REJECTED
Network resilience
The venue can lose signal. The entrance keeps context.
RACS uses the server when it is available and local evidence when it is not. Nearby readers cooperate directly, closing the window where one pass could be accepted twice at separate doors.
Continuity layers
Keep admitting offline
local-first
Campaign cards and prior scans live in SQLite on the reader, so a dropped uplink does not stop local checks.
Warn nearby readers
peer-to-peer
Readers form a WebRTC mesh. A successful scan can mark the same card used on peers without waiting on the public network.
Catch up safely
durable retry
Transient API failures enter a local dead-letter queue and retry later; local scan history keeps the attempted decision visible.
Operational control
More than a scanner.
The reader includes the small set of controls a deployment needs: campaign selection, local card management, scan history, queued work, diagnostics and connection settings.
Campaign sync
Select the event campaign, download its cards and see the local count before doors open.
My scans
Review a paginated on-device history with card label, outcome and exact timestamp.
Every attempt retained
Accepted and rejected presentations are stored locally and recorded centrally when reachable.
Retry visibility
Queued requests and application logs give operators evidence when connectivity needs attention.
System map
A local decision engine with a central authority.
The Flutter reader owns the time-critical path. The API remains authoritative for identity, reporting and cross-venue history, while WebSocket signalling lets nearby readers establish a direct WebRTC data mesh.
Access decision data path
01
Flutter reader
Camera UI + operator workflow
Dart 3.11
02
Local state
Cards, scans + retry queue
SQLite
03
Reader mesh
Peer signalling + scan sync
WS / WebRTC
04
RACS API
Auth, resources + reports
Spring Boot 4
05
System record
Campaigns, access + audit
MySQL 8
Reader identity
JWT-authenticated profile
Duplicate boundary
local → mesh → server
Failed delivery
SQLite dead-letter queue
Reader pipeline · 8 steps
Every step explicit. One auditable outcome.
ScannerService composes small domain steps instead of hiding access logic in the camera view. Each rejection carries its context and server flag, and early failures are still persisted and submitted for audit.
ScannerService._runPipeline
ordered execution
01
Parse
↳ numeric card value
02
Card lookup
↳ local campaign match
03
Validity
↳ active date window
04
Invalidation
↳ not revoked
05
Used check
↳ not seen locally / by peer
06
Submit
↳ server duplicate authority
07
Mark used
↳ local state committed
08
Broadcast
↳ peer readers notified
2 repositories
The field device and control plane evolve independently.
The seam is deliberately clear: JSON over HTTP for resources, authenticated WebSockets for live signalling, and stable server flags for decision outcomes.
HTTP surface
Resource APIs plus reporting built around the access record.
The API exposes ordinary, filterable resources rather than coupling administration to the reader client. Swagger UI is enabled for local development.
/api/card
Cards
Bulk creation, campaign filtering and validity state.
/api/campaign
Campaigns
Access windows and associated locations.
/api/reader
Readers
Reader identities, credentials and profiles.
/api/scan
Scans
Create, audit, filter and enrich with card data.
/api/report/*
Reports
Summary, usage, location, reader and duplicate views.
/api/user · /role
Access model
Users, roles and granular permissions.
Run it locally
Start the database and API, then bring a reader online.
Development exposes HTTPS on 3443 and a companion HTTP connector on 5001. Readers accept a host with port and derive the HTTP and WebSocket schemes from their build flags.
reader / bootstrap
git clone https://github.com/IbiliAze/racs-app.git cd racs-app flutter pub get dart run build_runner build --delete-conflicting-outputs flutter run --dart-define=APP_ENV=dev --dart-define=API_SECURE=false
api / bootstrap
git clone https://github.com/IbiliAze/racs-api.git cd racs-api docker compose -f deploy/docker/docker-compose.dev.yml up -d cp .env.example .env ./mvnw spring-boot:run
Set a real JWT secret
Copy the API environment example and replace its placeholder before starting the service.
Point readers at the host
Enter host and port in Settings, then authenticate and select a campaign before syncing cards.
Verify both halves
Run Flutter analysis and tests for the reader; use Maven tests and packaging for the API.
Behind the project
Built by Eight Mile in London, as part of our infrastructure and cloud work — the same engineers who build and run systems like it for clients.