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
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

> A calm, persistent real-time queue for small service teams.

Story 2 replaces the local visual prototype state with a transactional Supabase PostgreSQL engine. Anonymous browser identities can create or join queues; a one-time queue capability grants staff membership; customer, staff, and public-display clients converge through filtered Realtime invalidations followed by authoritative revisioned snapshots.
Next uses a transactional Supabase PostgreSQL engine. Anonymous browser identities can create or join queues; a one-time queue capability grants staff membership; customer, staff, and public-display clients converge through filtered Realtime invalidations followed by authoritative revisioned snapshots.

The application is not publicly deployed.
The production application is live at [next-queue-omega.vercel.app](https://next-queue-omega.vercel.app). It runs on Vercel Hobby with one Supabase Free project in `us-east-1`; no paid service, trial, analytics, storage add-on, or custom domain is part of the deployment.

## Product surfaces

Expand Down Expand Up @@ -42,6 +42,12 @@ Open [http://localhost:3000/demo](http://localhost:3000/demo). Create a queue, s

The local stack is development-only, uses default local credentials, and must not be exposed to public traffic.

## Live demo

Open the [production demo](https://next-queue-omega.vercel.app/demo), create a queue, and save the staff code when it appears: the raw code is shown once and cannot be recovered. The creator is immediately authorized. To test synchronization, open the customer, staff, and display links in separate tabs or isolated browser profiles. A second staff profile must claim access with the code. Queue state persists remotely, while staff membership and customer ownership follow each profile's anonymous session.

Clearing site data, signing out, or changing browsers loses that anonymous identity. Losing both the creating staff session and the one-time code means staff access cannot be recovered through the product. Do not enter sensitive or regulated personal information; a display name is optional.

## Commands

```bash
Expand All @@ -56,6 +62,7 @@ npm run db:types:check
npm run test:integration
npm run test:realtime
npm run test:e2e
npm run test:production
npm run format
npm run format:check
npm run lint
Expand All @@ -65,6 +72,8 @@ npm run build
npm audit
```

`npm run test:production` is a read-only smoke check against the public URL. Set `PRODUCTION_BASE_URL` to test another deployment. `PRODUCTION_QUEUE_SLUG` optionally adds HTTP health checks for an existing synthetic queue; browser checks remain on public pages so the suite never creates an anonymous identity or mutates remote records.

`db:reset` drops only the local database, replays every migration, and reapplies safe seed data. `db:types` regenerates `src/lib/supabase/database.types.ts`; do not edit that file manually.

## Command and authorization model
Expand All @@ -79,7 +88,20 @@ Only `queues` and display-safe `queue_entries` are in `supabase_realtime`. Each

## Cost boundary

The intended hosted validation target is one Supabase Free project only: no card, trial, compute upgrade, paid backup, PITR, log drain, support plan, custom domain, or usage-based add-on. Current Free projects are limited to two active projects, 500 MB database size, and may pause after roughly one week of low activity. No keep-alive is used to evade pausing. Re-check [official pricing](https://supabase.com/pricing) before provisioning.
Production uses one Supabase Free project and one Vercel Hobby project only: no card, trial, compute upgrade, paid backup, PITR, log drain, paid analytics, paid storage, support plan, custom domain, or usage-based add-on. Free projects have finite database, bandwidth, build, function, and connection quotas and Supabase may pause an inactive project. No keep-alive is used to evade pausing. Re-check provider limits before relying on the service.

This portfolio deployment is provided as-is, without a commercial SLA. It has database-enforced authorization and basic access-attempt throttling, but not enterprise abuse protection, guaranteed recovery, or capacity for unbounded traffic.

## Production deployment

Vercel is connected to `XonkelX/next-queue`, uses `main` as the production branch, Node.js 22.x, and the standard Next.js build. Only these Production variables are configured:

```text
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
```

Deployments are produced by pushing reviewed commits to `main`; a manual equivalent is `npx vercel deploy --prod`. The browser uses only public Supabase configuration—never a service-role key or database password. See the [Story 3 production validation](docs/releases/story-3-production-validation.md) for the release evidence and remaining risks.

## Documentation

Expand All @@ -89,3 +111,4 @@ The intended hosted validation target is one Supabase Free project only: no card
- [Authorization and security](docs/security/authorization.md)
- [Motion system](docs/design/motion-system.md)
- [Product scope](docs/product/scope.md)
- [Story 3 production validation](docs/releases/story-3-production-validation.md)
2 changes: 2 additions & 0 deletions docs/architecture/realtime-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Queue and entry messages from one transaction can arrive rapidly, so a 75 ms deb

There is no healthy-connection polling, Presence, global schema channel, custom heartbeat write, or private-table subscription.

An `online` event performs an authoritative refresh before restoring the visible `CONNECTED` state. A failed refresh remains reconnecting or enters the adapter's error path; network reachability alone is not treated as successful convergence.

## Commands and retry

A new user intent gets a UUID request ID. Automatic retry must retain that UUID. The RPC validates actor/type reuse, performs its transaction, and returns a snapshot. Buttons show local pending feedback but do not invent an authoritative result. Typed conflicts restore the control and explain the recoverable state. Realtime is confirmation/invalidation; the RPC result may update the UI immediately.
Expand Down
73 changes: 73 additions & 0 deletions docs/releases/story-3-production-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Story 3 production validation

Validated on 2026-07-17 against [next-queue-omega.vercel.app](https://next-queue-omega.vercel.app).

## Deployment architecture

- Vercel Hobby project `next-queue`, connected only to `XonkelX/next-queue`
- production branch `main`, repository root, Next.js preset, Node.js 22.x, standard build
- one existing Supabase Free project in `us-east-1`
- filtered Realtime publication containing only `queues` and `queue_entries`
- Web Analytics and Speed Insights disabled; no custom domain, paid analytics, paid storage, add-on, trial, or payment method

Production has only the following public client configuration, scoped to Production (values intentionally omitted):

```text
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
```

The application runtime has no service-role key, database password, Supabase access token, or other server credential.

## Production QA

One synthetic queue, `North Star Café QA`, was used. Separate browser contexts represented two staff members, five customers, and a public display; nine contexts were connected at the busiest point. Queue creation and anonymous sign-in persisted across refresh. A second staff identity was denied before authorization, received a generic invalid-code error, then successfully claimed membership; the code disappeared from the form and never appeared in a URL.

Customer numbers were unique and ordered. Staff, customers, and display converged without refresh for join, call, complete, skip, pause, reopen, and close. Pausing denied new joins while allowing active service to finish; reopening restored joins; closing denied joins and calls. Terminal entries did not return after every surface was reloaded.

Two authorized staff issued call-next actions nearly concurrently. Exactly one waiting entry became serving, the other command received the expected conflict, and both screens converged without violating the one-serving invariant. Remote same-request-ID validation produced one side effect and one revision increment, and mismatched command reuse was rejected.

While a display was offline, staff changed queue state. Restoring connectivity triggered an authoritative snapshot, returned the indicator to connected, showed the latest revision, and introduced no duplicate entries. This exercise exposed and fixed a stale reconnect indicator; a regression test now covers the successful online refresh transition.

## Privacy and security inspection

Public snapshots and rendered display state contained queue numbers, not customer UUIDs or private names. The display had no private-table subscription. Page HTML contained no authentication token, private name, or staff code. Eleven production JavaScript bundles were scanned for service credentials and QA secrets with zero matches. No raw code appeared in a URL, report, commit, or PR text. Browser console and page-error collection found zero errors or warnings relevant to hydration, subscriptions, promises, or logging.

Database validation reconfirmed RLS, RPC-only writes, cross-queue staff isolation, staff-code hashing, private-name ownership, idempotency, and exact Realtime publication. The public client cannot query internal command receipts or security-sensitive tables.

## Accessibility, responsive, and motion results

Keyboard-only navigation, skip link, visible focus, mobile navigation, labeled access-code and join forms, command pending states, status announcements, focus retention, offline/reconnect messaging, dark theme, and 200% zoom were manually checked. No focus theft or focus loss occurred during Realtime changes. Queue status does not rely on color alone.

Automated Axe checks covered home, demo, about, customer, staff, and display routes: zero serious and zero critical findings. Widths 320, 375, 768, 1024, and 1440 px had no horizontal overflow or clipped controls. The public display was also checked at 1920×1080. Reduced-motion mode removed translation/stagger behavior while keeping state visible and interactive. New items animated once; unchanged snapshots and reconnect did not replay the queue.

## Automated and release validation

- Vitest: 33 passed
- pgTAP: 62 passed
- integration: 11 passed
- Realtime: 9 passed
- Playwright application suite: 15 passed
- production QA smoke with temporary queue: 13 passed, 0 skipped; its temporary anonymous identities were removed during cleanup
- formatting, lint, typecheck, clean database reset, database lint, generated types, production build, audit (0 vulnerabilities), and diff checks passed
- Story 2 post-merge GitHub Actions: quality, database, and browser jobs passed

The ongoing production smoke suite is read-only and is not part of ordinary PR CI. Browser checks cover only public pages; an optional temporary queue slug adds server-rendered HTTP health checks without starting an anonymous browser session.

## Cleanup

The synthetic queue and all related private entries, memberships, access records, attempts, commands, and events were removed. Final counts were zero for all eight application tables: `queues`, `queue_entries`, `queue_entry_private`, `queue_staff_memberships`, `queue_staff_access`, `queue_staff_access_attempts`, `queue_commands`, and `queue_events`. Twenty-three temporary anonymous users were removed during full QA and two more during final post-restore smoke validation, leaving zero Auth users. All QA browser sessions were closed and the one-time staff credential was discarded.

## Final revalidation

On 2026-07-25, the complete suite was rerun before opening the Story 3 PR. Newly published advisories were resolved by updating Next.js to 16.2.12 and pinning patched transitive Sharp and Minimatch versions; `npm audit` returned zero vulnerabilities. Supabase had automatically paused the inactive Free project as documented. The existing project was restored without an upgrade or payment, returned to `ACTIVE_HEALTHY`, and passed anonymous Auth and production route validation. The production smoke suite then passed 10 tests; one display-only test was skipped because the temporary QA queue had already been deleted. Final cleanup again confirmed zero rows in all eight application tables and zero Auth users. Final review hardened the ongoing smoke suite to avoid browser navigation to queue routes, which creates anonymous identities by design; the current read-only suite passes 9 tests and leaves no remote records.

## Free-tier limitations and remaining risks

- Supabase may pause an inactive Free project, causing a cold start or temporary unavailability.
- Supabase Free and Vercel Hobby impose finite database, bandwidth, connection, build, and function quotas.
- Anonymous ownership and staff membership are lost when the browser session or site data is cleared.
- A lost one-time staff code cannot be recovered; access depends on an existing authorized session or the saved code.
- Access-attempt throttling is not a substitute for a WAF, bot management, or advanced abuse protection.
- The architecture is suitable for small queues, not unbounded concurrent clients or enterprise-scale workloads without capacity planning.
- The portfolio deployment has no commercial uptime, support, backup, or recovery SLA.
Loading
Loading