You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FloCafe has no employee clock-in/clock-out tracking today. The only employee-shaped data is the users table (main/db.ts:4224-4238 — id, name, role, pin/pin_hash, is_active), used
for login/PIN auth and KDS station assignment (station_users, main/db.ts:4182). There is no attendance or staff_clock table, and no existing doc in docs/ covers staff clock-in.
This issue proposes an Employee Attendance feature: a fourth standalone server on port 3004,
following the exact bootstrap pattern already used for the API (:3001), KDS (:3002), and
Server App (:3003) in main/index.ts's initialize() (lines 648-684) — each with its own start*() / stop*() / get*Port() / is*Running() exports and an entry in the shutdown
array (main/index.ts:811-814).
Related Existing Infrastructure (verified in code)
These precedents directly answer the open questions raised when scoping this feature:
QR + local-network pairing: main/routes/kds-info.ts (52 lines) generates a QR via the qrcode package containing a plain http://{ip}:{port} URL, and an mdns_url of http://flo.local:{port}. The IP-changes-on-DHCP-renewal problem is already solved for KDS —
not by the QR/token, but by startMdns() in main/index.ts:457-474, which publishes a bonjour-service (package.json dep ^1.4.4) mDNS record advertising the service as flo.local with each server's port in the TXT record. Devices resolve flo.local:3002 instead
of a raw IP; the per-IP QR list (ips_data in kds-info.ts) is only a fallback for networks
where multicast/mDNS is blocked (some corporate/guest Wi-Fi).
Photo/image handling precedent: product images are stored as Base64 data: URIs directly in
a SQLite column (main/routes/products.ts:491-550), not as files on disk. There is no uploads/ directory anywhere in the repo and no multer/multipart-to-disk middleware.
On-disk file convention (when SQLite-blob storage isn't right): Electron userData path, main/index.ts:224 (~/.config/flo-desktop equivalent), used today for flo.db, DB backups,
the WhatsApp auth directory (main/services/whatsapp.ts:283), and encrypted tokens
(main/services/google-drive.ts:170, main/services/master-pin.ts:17).
No ML/face-recognition dependency exists anywhere in package.json or main/ today
(confirmed by grep for face, tensorflow, onnx, opencv, ml-, vision).
Optional/cloud-adjacent feature pattern: main/services/google-drive.ts, main/services/whatsapp.ts, main/services/cloud-sync.ts — all singleton services with start()/stop(), gated by a DB-backed enabled flag, wired into main/index.ts's startup/
shutdown sequence. Feature-level enable gating precedent: requireKdsEnabled middleware
(main/middleware/security.ts:340) backed by isKdsEnabled() (main/db.ts:701).
Existing cloud + mobile pairing rail: docs/cloud-v2-plan.md documents that FloCafe already
registers with a cloud backend (FloAdmin) via main/services/cloud-sync.ts and issues a
short-lived numeric pairing code, shown as both digits and a QR, that a companion mobile app
("RevFlo") scans to pair (POST /api/pairing/redeem). This is the natural rail for any
cloud/mobile-app variant of attendance — it should not be reinvented.
License: repo is plain MIT (LICENSE), no dual-licensing or paid-tier precedent in this
codebase. docs/cloud-v2-plan.md and AGENTS.md's "optional network features" invariant are the
only precedent for cloud-gated (as opposed to fully local) functionality.
Problem
Stores need a way to record when employees start/end their shift ("attendance", distinct from POS
cash-drawer shifts — see Disambiguation below), ideally by having the employee scan a QR code on
arrival, the way KDS stations are paired today. Several open design questions block a clean
implementation:
A QR code that encodes a raw IP breaks whenever DHCP reassigns the host's local IP, but store
staff want something they can print once and stick on the wall.
Where do check-in photos live, given there's no existing "files on disk, served statically"
convention in this codebase — only DB-blob storage for the much lower-volume case of product
images?
Should check-in photos be automatically matched against a reference photo (on-device face
recognition), or just captured for a human to review?
Is any part of this a paid/cloud feature, given the codebase is plain MIT with no existing
commercial tier?
Should there be a remote/mobile check-in path (phone shares GPS, matched against the store's
registered location), and if so, does it need new cloud infrastructure or can it reuse the
existing FloAdmin/RevFlo pairing rail?
Architectural Decision
1. QR code / changing IP — Reuse the KDS pattern exactly, not a variant of it. Add main/routes/attendance-info.ts (mirrors kds-info.ts) exposing mdns_url
(http://flo.local:3004/attendance/checkin), a per-interface ip_url fallback list, and a qr_data_url PNG. Add attendance's port to the existing mDNS TXT record in startMdns()
(main/index.ts:465) alongside kds_port/server_app_port. Printed on paper, the QR stays valid
across IP changes as long as the network supports multicast DNS — which is the same assumption
the KDS pairing screen already makes today, so this introduces no new risk. On networks that block
multicast, fall back to the same per-IP QR list KDS already shows, refreshed from the settings
screen rather than printed.
2. Where check-in photos are saved — Do not reuse the products.ts Base64-in-SQLite pattern.
That pattern fits a handful of rarely-changing product images; attendance photos accumulate
per-employee, per-check-in, indefinitely, and would bloat flo.db and slow every backup/restore.
Store check-in photos as JPEG files under a new userData/attendance-photos/<user_id>/ directory
(following the existing userData-rooted convention used for WhatsApp auth and DB backups), with
only the relative path + a SHA-256 hash in a new attendance_events table row. This needs an
explicit retention policy (e.g. configurable days-to-keep, default TBD) so photo storage
doesn't grow unbounded — per AGENTS.md's data-safety invariant, any automatic purge must be a
deliberate, documented, owner-configurable policy, not an ad hoc deletion.
3. On-device AI face match — Ship as a separate, later phase, not MVP. MVP captures the
photo as an audit artifact for manager review only (no automated matching) — this ships real value
immediately with zero new runtime dependencies, consistent with AGENTS.md's "reuse before adding"
invariant. A later phase can add local, on-device face-embedding matching (e.g. a small ONNX
model bundled and run fully offline, never a cloud face-recognition API) as an opt-in enhancement.
Flag: this would be FloCafe's first ML dependency and has real bundle-size implications — coordinate
with the bundle-size work in #417 before committing to a specific model/runtime.
4. Open-source vs. paid — The LAN QR check-in, local photo capture, and manager review MVP
should stay fully open-source and 100% offline, matching every other core-POS invariant in this
repo (no reason to treat attendance differently from orders/billing/KDS). Any commercial
packaging question only plausibly applies to the cloud variant (item 5) — and even there, the
monetizable surface is FloAdmin cloud hosting/verification, not the FloCafe client code, which
stays MIT like everything else in this repo. This is a business decision outside a code issue's
scope — flagged under Open Questions below rather than decided here.
5. Cloud-supplied remote check-in (mobile app + geofencing) — Do not build new cloud
infrastructure for this. Reuse the existing FloAdmin/RevFlo pairing rail described in docs/cloud-v2-plan.md (main/services/cloud-sync.ts, /api/mobile/pairing-code, /api/pairing/redeem) so a store's already-paired RevFlo mobile app can submit GPS-tagged
check-ins without a second pairing flow. Per AGENTS.md's private-specs boundary, this repo issue
can only cover FloCafe's client-side contribution (submitting/consuming attendance events over
the existing paired-device channel, storing the store's registered lat/long + geofence radius
locally, surfacing results in the UI) — the FloAdmin-side geofence verification service and any
RevFlo UI work are out of scope here and belong in the private specs repo / FloAdmin's own
tracker. This phase must degrade gracefully offline exactly like every other optional network
feature (README.md:77-87): if the cloud is unreachable, remote check-in queues or fails
visibly, and on-site QR check-in keeps working regardless.
Disambiguation: Attendance vs. Shift
"Attendance" (this issue — employee clock-in/out) is a different concept from the open
"Shift" feature in #279 (cash-drawer open/close, cash float, business-day reconciliation — a POS
session, not a person's work hours). Naming risk: both could reasonably be called "shift." Use
"attendance" / "clock-in" / "clock-out" terminology throughout this feature to keep it distinct.
A future integration (e.g., a cashier can't open a POS shift before clocking in) is a reasonable
follow-up but is explicitly out of scope for both issues today.
Phases
Phase 1 (MVP, open-source, fully offline): attendance-server.ts on port 3004 following the
KDS bootstrap pattern; attendance-info.ts QR/mDNS route; new attendance_events table
(user_id, type [in/out], timestamp, photo_path, device_info); employee-facing
check-in screen (scan own PIN or badge + camera capture); manager-facing review/report screen;
on-disk photo storage with configurable retention.
Phase 3 (opt-in, cloud-gated): remote check-in via the paired RevFlo mobile app, GPS-tagged
and matched against the store's registered geofence, over the existing FloAdmin pairing channel.
Requires FloAdmin-side design work tracked outside this repo.
Non-Goals / What NOT to Do
Do not require internet connectivity for any Phase 1 functionality — core check-in must work
fully offline like the rest of the POS.
Do not design or implement any FloAdmin-side (server) geofencing logic in this repo — client
only, per the private-specs boundary in AGENTS.md.
Do not decide the open-source-vs-paid question inside this issue's implementation — surface it
to maintainers first (see Open Questions).
Open Questions for Maintainers (non-technical / business decisions)
Is Phase 3 (cloud/geofenced remote check-in) something FloAdmin should meter or gate behind
a paid store plan, given it depends on FloAdmin-hosted verification rather than local compute?
Does RevFlo (existing companion mobile app) already have a natural home for an "attendance"
tab, or does this need new RevFlo screens designed by that app's owners?
What's the default/configurable retention window for on-disk check-in photos?
Should Phase 2 face-match ship as a core feature or an optional downloadable model package
(to avoid inflating the base installer for stores that never enable it)?
Acceptance Criteria
Phase 1
main/attendance-server.ts exports startAttendanceServer() / stopAttendanceServer() / getAttendancePort() / isAttendanceServerRunning(), wired into main/index.ts startup and
the shutdown array, default port 3004 (env-overridable, matching KDS_PORT convention).
main/routes/attendance-info.ts returns mdns_url, ip_url, per-interface qr_data_url,
gated by a new requireAttendanceEnabled middleware / isAttendanceEnabled() DB flag.
mDNS TXT record in startMdns() includes the attendance port.
New attendance_events table (migration) with user_id, type, timestamp, photo_path, device_info; photos stored under userData/attendance-photos/<user_id>/, never in SQLite.
Configurable retention policy purges photos/rows older than N days (owner-configurable,
disabled/unlimited by default until an explicit decision is made).
Check-in/check-out works with zero network connectivity.
Manager-facing report screen lists check-ins/check-outs with photo review.
Phase 2 / 3: acceptance criteria to be defined in follow-up issues once Phase 1 ships and the
Open Questions above are resolved.
Verification
npm run lint
npm run build
npm test
Plus a manual offline check: disable networking, confirm Phase 1 check-in/check-out and photo
capture still work end-to-end.
Context
FloCafe has no employee clock-in/clock-out tracking today. The only employee-shaped data is the
userstable (main/db.ts:4224-4238—id,name,role,pin/pin_hash,is_active), usedfor login/PIN auth and KDS station assignment (
station_users,main/db.ts:4182). There is noattendanceorstaff_clocktable, and no existing doc indocs/covers staff clock-in.This issue proposes an Employee Attendance feature: a fourth standalone server on port 3004,
following the exact bootstrap pattern already used for the API (
:3001), KDS (:3002), andServer App (
:3003) inmain/index.ts'sinitialize()(lines 648-684) — each with its ownstart*()/stop*()/get*Port()/is*Running()exports and an entry in the shutdownarray (
main/index.ts:811-814).Related Existing Infrastructure (verified in code)
These precedents directly answer the open questions raised when scoping this feature:
main/routes/kds-info.ts(52 lines) generates a QR via theqrcodepackage containing a plainhttp://{ip}:{port}URL, and anmdns_urlofhttp://flo.local:{port}. The IP-changes-on-DHCP-renewal problem is already solved for KDS —not by the QR/token, but by
startMdns()inmain/index.ts:457-474, which publishes abonjour-service(package.jsondep^1.4.4) mDNS record advertising the service asflo.localwith each server's port in the TXT record. Devices resolveflo.local:3002insteadof a raw IP; the per-IP QR list (
ips_datainkds-info.ts) is only a fallback for networkswhere multicast/mDNS is blocked (some corporate/guest Wi-Fi).
userstable (main/db.ts:4224) —id,name,role,pin_hash.data:URIs directly ina SQLite column (
main/routes/products.ts:491-550), not as files on disk. There is nouploads/directory anywhere in the repo and nomulter/multipart-to-disk middleware.userDatapath,main/index.ts:224(~/.config/flo-desktopequivalent), used today forflo.db, DB backups,the WhatsApp auth directory (
main/services/whatsapp.ts:283), and encrypted tokens(
main/services/google-drive.ts:170,main/services/master-pin.ts:17).package.jsonormain/today(confirmed by grep for
face,tensorflow,onnx,opencv,ml-,vision).main/services/google-drive.ts,main/services/whatsapp.ts,main/services/cloud-sync.ts— all singleton services withstart()/stop(), gated by a DB-backed enabled flag, wired intomain/index.ts's startup/shutdown sequence. Feature-level enable gating precedent:
requireKdsEnabledmiddleware(
main/middleware/security.ts:340) backed byisKdsEnabled()(main/db.ts:701).docs/cloud-v2-plan.mddocuments that FloCafe alreadyregisters with a cloud backend (FloAdmin) via
main/services/cloud-sync.tsand issues ashort-lived numeric pairing code, shown as both digits and a QR, that a companion mobile app
("RevFlo") scans to pair (
POST /api/pairing/redeem). This is the natural rail for anycloud/mobile-app variant of attendance — it should not be reinvented.
LICENSE), no dual-licensing or paid-tier precedent in thiscodebase.
docs/cloud-v2-plan.mdandAGENTS.md's "optional network features" invariant are theonly precedent for cloud-gated (as opposed to fully local) functionality.
Problem
Stores need a way to record when employees start/end their shift ("attendance", distinct from POS
cash-drawer shifts — see Disambiguation below), ideally by having the employee scan a QR code on
arrival, the way KDS stations are paired today. Several open design questions block a clean
implementation:
staff want something they can print once and stick on the wall.
convention in this codebase — only DB-blob storage for the much lower-volume case of product
images?
recognition), or just captured for a human to review?
commercial tier?
registered location), and if so, does it need new cloud infrastructure or can it reuse the
existing FloAdmin/RevFlo pairing rail?
Architectural Decision
1. QR code / changing IP — Reuse the KDS pattern exactly, not a variant of it. Add
main/routes/attendance-info.ts(mirrorskds-info.ts) exposingmdns_url(
http://flo.local:3004/attendance/checkin), a per-interfaceip_urlfallback list, and aqr_data_urlPNG. Add attendance's port to the existing mDNS TXT record instartMdns()(
main/index.ts:465) alongsidekds_port/server_app_port. Printed on paper, the QR stays validacross IP changes as long as the network supports multicast DNS — which is the same assumption
the KDS pairing screen already makes today, so this introduces no new risk. On networks that block
multicast, fall back to the same per-IP QR list KDS already shows, refreshed from the settings
screen rather than printed.
2. Where check-in photos are saved — Do not reuse the products.ts Base64-in-SQLite pattern.
That pattern fits a handful of rarely-changing product images; attendance photos accumulate
per-employee, per-check-in, indefinitely, and would bloat
flo.dband slow every backup/restore.Store check-in photos as JPEG files under a new
userData/attendance-photos/<user_id>/directory(following the existing
userData-rooted convention used for WhatsApp auth and DB backups), withonly the relative path + a SHA-256 hash in a new
attendance_eventstable row. This needs anexplicit retention policy (e.g. configurable days-to-keep, default TBD) so photo storage
doesn't grow unbounded — per
AGENTS.md's data-safety invariant, any automatic purge must be adeliberate, documented, owner-configurable policy, not an ad hoc deletion.
3. On-device AI face match — Ship as a separate, later phase, not MVP. MVP captures the
photo as an audit artifact for manager review only (no automated matching) — this ships real value
immediately with zero new runtime dependencies, consistent with
AGENTS.md's "reuse before adding"invariant. A later phase can add local, on-device face-embedding matching (e.g. a small ONNX
model bundled and run fully offline, never a cloud face-recognition API) as an opt-in enhancement.
Flag: this would be FloCafe's first ML dependency and has real bundle-size implications — coordinate
with the bundle-size work in #417 before committing to a specific model/runtime.
4. Open-source vs. paid — The LAN QR check-in, local photo capture, and manager review MVP
should stay fully open-source and 100% offline, matching every other core-POS invariant in this
repo (no reason to treat attendance differently from orders/billing/KDS). Any commercial
packaging question only plausibly applies to the cloud variant (item 5) — and even there, the
monetizable surface is FloAdmin cloud hosting/verification, not the FloCafe client code, which
stays MIT like everything else in this repo. This is a business decision outside a code issue's
scope — flagged under Open Questions below rather than decided here.
5. Cloud-supplied remote check-in (mobile app + geofencing) — Do not build new cloud
infrastructure for this. Reuse the existing FloAdmin/RevFlo pairing rail described in
docs/cloud-v2-plan.md(main/services/cloud-sync.ts,/api/mobile/pairing-code,/api/pairing/redeem) so a store's already-paired RevFlo mobile app can submit GPS-taggedcheck-ins without a second pairing flow. Per
AGENTS.md's private-specs boundary, this repo issuecan only cover FloCafe's client-side contribution (submitting/consuming attendance events over
the existing paired-device channel, storing the store's registered lat/long + geofence radius
locally, surfacing results in the UI) — the FloAdmin-side geofence verification service and any
RevFlo UI work are out of scope here and belong in the private specs repo / FloAdmin's own
tracker. This phase must degrade gracefully offline exactly like every other optional network
feature (
README.md:77-87): if the cloud is unreachable, remote check-in queues or failsvisibly, and on-site QR check-in keeps working regardless.
Disambiguation: Attendance vs. Shift
"Attendance" (this issue — employee clock-in/out) is a different concept from the open
"Shift" feature in #279 (cash-drawer open/close, cash float, business-day reconciliation — a POS
session, not a person's work hours). Naming risk: both could reasonably be called "shift." Use
"attendance" / "clock-in" / "clock-out" terminology throughout this feature to keep it distinct.
A future integration (e.g., a cashier can't open a POS shift before clocking in) is a reasonable
follow-up but is explicitly out of scope for both issues today.
Phases
attendance-server.tson port 3004 following theKDS bootstrap pattern;
attendance-info.tsQR/mDNS route; newattendance_eventstable(
user_id,type[in/out],timestamp,photo_path,device_info); employee-facingcheck-in screen (scan own PIN or badge + camera capture); manager-facing review/report screen;
on-disk photo storage with configurable retention.
captured at employee setup, surfaced as a confidence score for the manager rather than an
auto-accept/reject gate. Coordinate model/runtime choice with perf(packaging): reduce production bundle size by scoping googleapis / @googleapis/drive #417.
and matched against the store's registered geofence, over the existing FloAdmin pairing channel.
Requires FloAdmin-side design work tracked outside this repo.
Non-Goals / What NOT to Do
fully offline like the rest of the POS.
flo.db(see Architectural Decision fix(windows): add Express path-rewriting middleware for Next.js static exports #2).must run entirely on-device.
only, per the private-specs boundary in
AGENTS.md.to maintainers first (see Open Questions).
Open Questions for Maintainers (non-technical / business decisions)
a paid store plan, given it depends on FloAdmin-hosted verification rather than local compute?
tab, or does this need new RevFlo screens designed by that app's owners?
(to avoid inflating the base installer for stores that never enable it)?
Acceptance Criteria
Phase 1
main/attendance-server.tsexportsstartAttendanceServer()/stopAttendanceServer()/getAttendancePort()/isAttendanceServerRunning(), wired intomain/index.tsstartup andthe shutdown array, default port
3004(env-overridable, matchingKDS_PORTconvention).main/routes/attendance-info.tsreturnsmdns_url,ip_url, per-interfaceqr_data_url,gated by a new
requireAttendanceEnabledmiddleware /isAttendanceEnabled()DB flag.startMdns()includes the attendance port.attendance_eventstable (migration) withuser_id,type,timestamp,photo_path,device_info; photos stored underuserData/attendance-photos/<user_id>/, never in SQLite.disabled/unlimited by default until an explicit decision is made).
Phase 2 / 3: acceptance criteria to be defined in follow-up issues once Phase 1 ships and the
Open Questions above are resolved.
Verification
npm run lint npm run build npm testPlus a manual offline check: disable networking, confirm Phase 1 check-in/check-out and photo
capture still work end-to-end.
Dependencies
blocking — see Disambiguation above.
docs/cloud-v2-plan.md/ FloAdmin: Phase 3 client work only; server-side designis out of scope for this repo.