Nousvigil
🔒
🔒 In Dev

Stays Put

A booking engine for physical spaces rented by the hour — built to prove the lock and the till, nothing else.

Type
Web App
Languages
TypeScript · SQL
Platforms
Self-hosted
booking postgresql tdd

The name is a working title — the project’s own constitution rules out anything grander until a real workshop has used it.

Football pitches, photo studios, dance halls, event spaces — physical resources rented by the hour, for a small business with no investors and no ambition to scale past a couple hundred clients. A notebook already handles the calendar, and on a few axes it’s better than software: free, no learning curve, works without internet.

This project builds only the two things a notebook can’t: the lock — a slot is actually held, visibly, without anyone standing there to enforce it — and the till — the promise comes with money behind it.

The lock, proven at the database

The central invariant — no combination of accepted bookings can ever produce two overlapping exclusive claims on the same resource — isn’t held by application code. It’s a PostgreSQL exclusion constraint:

EXCLUDE USING gist (resource_id WITH =, during WITH &&) WHERE (mode = 'exclusive')

The domain layer above it is pure — no SQL, no connection pool, no clock, no timezone — and a second, independent implementation recomputes the same invariant directly in SQL as a standing audit, comparing row by row against what the constraint actually enforced.

Tested like it means it

108 automated tests. Generative property tests throw randomly generated resource trees and reservation sets at a real PostgreSQL instance and verify the formal non-overlap rule holds — not just the weaker consequence the exclusion constraint alone would catch. A concurrency test opens two real transactions against the same node simultaneously and asserts exactly one wins. Four mechanical guards fail the build, not just review, on scope creep: no vendor name anywhere in the code, no reserved identifiers, no stray timezone library, import boundaries between domain and data enforced structurally.

Where it stands

Two increments in: the lock itself (resource tree, claim derivation, turnaround buffers, the booking state machine) and a public read contract — three unauthenticated GET endpoints for availability and quoting, with read/write coherence checked by its own generative test. No payments, no customer record on a booking, no UI, no auth — deliberately, and documented as such rather than left ambiguous.

← Back to Portal