Quiet Presence is not the kind of application that should behave like a marketplace, a booking engine, or a growth funnel.
Why This Exists
I did not start this project because I wanted to build booking software. I started it because I think we live in an isolated society, and human beings need human contact.
A lot of people are carrying loneliness, disconnection, grief, transition, or social invisibility without many safe places to put it. That sounds obvious, but many of our systems are built in ways that make ordinary, bounded, in-person presence harder to offer than it should be.
Part of that is legal and institutional risk. Health systems carry so much liability, regulation, and professional boundary exposure that even forms of human contact that are calm, basic, and clearly bounded can become difficult to offer inside those structures. The result is that many people who are not looking for therapy, medicine, romance, or sex still have very few places to turn.
That gap felt real to me. I have tried a cuddle service myself, and it was worth it. Not because it replaced healthcare, and not because it promised some dramatic transformation, but because structured human presence has value on its own.
At a basic level, I do not think relationships should be treated as a means to an end. But I also recognize that in ordinary social life, relationships are shaped by practical needs, social structures, and mutual dependence. It is almost impossible to remove instrumentality altogether.
I understand how strange or even ridiculous a service like this can sound from the outside. I also understand the convenience, the practical need, and the fact that for some women facing isolation, widowhood, marginalization, or social invisibility, a clearly bounded form of human presence may be easier to reach than the alternatives. If that premise bothers someone, they can move on. The service is not for everyone.
What The Service Is
Quiet Presence grew from that recognition. I wanted something explicit, bounded, and operationally clear. Not therapy. Not romance. Not vague “connection” language. Just a service with clear edges.
The product is a bounded, in-person companionship service for adult women, especially older women, widows, marginalized women, and women facing isolation. It is explicitly non-clinical, non-sexual, consent-based, and provider-controlled. That meant the system design could not simply optimize for conversion, self-serve scheduling, or automated throughput. It had to optimize for clarity, review, defensibility, and operational control.
What That Means For The System
That pushed the application toward a very specific shape:
- static public pages
- narrow serverless handlers
- explicit workflow states
- internal admin tooling
- append-only operational records
- provider-controlled scheduling and payment
The rest of this post is mostly technical: how I used a small Cloudflare stack and AI-assisted development to create a functional booking system that keeps control on the admin side while still capturing client preferences cleanly.
From Personal Motivation to System Design
Most architecture discussions start with technology. This one started with service boundaries and with a refusal to make the offering vague.
Quiet Presence has a few system-defining constraints:
- requests are reviewed, not auto-booked
- the provider retains discretion at every stage
- legal copy, intake wording, and operational behavior must stay aligned
- payment is an administrative workflow step, not a retail checkout
- confirmation should happen only after the right conditions are complete
Those product rules become system rules very quickly. If people are coming to the service because they want something real but clearly bounded, the software has to reinforce those boundaries instead of softening them.
If the service depends on careful screening and explicit expectations, then the app cannot collapse everything into one “book now” flow. It needs separate stages, durable state transitions, and enough internal tooling to support human review without turning the software into a large back-office platform.
That is the core design tension of the project: keep the stack small, while still treating workflow integrity seriously.
Another way to say it is this:
The software is not just there to process requests. It is there to protect the shape of the service.
That is also where AI became useful. AI made it much easier to generate and iterate on the moving pieces of a real workflow system: forms, routes, validation, admin flows, payment plumbing, email templates, and supporting documentation. But that only works if the system constraints are already clear. Otherwise AI just helps you build the wrong thing faster.
High-Level Architecture
At a high level, the app follows a simple split:
- public-facing static pages in
public/ - Cloudflare Pages Functions for server-side workflow handlers
- Cloudflare D1 for durable workflow state
- a private admin interface for internal review and progression
- email and payment integrations as narrow adapters around specific workflow steps
Static HTML"] --> Intake["Pages Functions
Intake + Agreement + Time Selection"] Admin["Private Admin UI
Review + Scheduling + Payment"] --> AdminApi["Admin API Routes"] Intake --> D1["Cloudflare D1"] AdminApi --> D1 AdminApi --> Email["Email Delivery"] AdminApi --> Payment["Payment Provider"] AdminApi --> Audit["Audit Events"] Intake --> Audit
This is a deliberately small system. There is no SPA frontend framework, no ORM, no large service mesh, and no queueing fabric. The project leans on:
- static HTML where possible
- direct SQL where useful
- route handlers with narrow responsibilities
- explicit status models instead of hidden workflow logic
That simplicity is not accidental. It is part of the risk posture, and it also makes the system much easier to build and evolve with AI assistance because the moving parts stay legible.
Architecture Decisions
The stack is small on purpose. A few decisions shaped the whole implementation.
1. Cloudflare Pages instead of a full application framework
Most of the product is explanatory content plus a handful of workflow actions. Static pages plus Pages Functions fit that shape better than introducing a large framework with more runtime and maintenance overhead.
2. D1 and direct SQL instead of an ORM-heavy model
The application logic revolves around explicit workflow states and operational records. Direct SQL keeps those transitions visible. It also makes it easier to reason about audit trails, payment attempts, and status constraints without hiding too much behavior behind abstractions.
3. Admin-controlled progression instead of user-side self-booking
This is the biggest product and architecture decision in the system. The user can express preferences, acknowledge agreements, and choose from offered options, but the admin side decides when the request advances. That preserves provider discretion and keeps the software aligned with the service boundaries.
Workflow Sequence
At the workflow level, the system is intentionally linear. The client can provide information and respond to concrete steps, but the application does not collapse into open self-booking.
The important design point is that the client participates in the workflow, but the admin side advances it.
The service also uses a comprehensive written client agreement, and the workflow requires that agreement to be acknowledged before scheduling and payment can proceed. That written agreement is part of how the service keeps expectations, boundaries, and operational terms explicit from the start.
The Most Important Design Choice: Separate State by Concern
The core workflow is split into three distinct state domains:
- intake review
- booking and scheduling
- payment
That separation shows up directly in the data model.
1. Intake state
The intake_submissions table holds the public request and screening record. Its status values are:
newreviewedaccepteddeclinedarchived
This is important because accepted does not mean “the session is booked.” It only means the request may move forward.
2. Booking state
Once an intake is ready for scheduling, the workflow moves into bookings, with statuses such as:
approved_for_schedulingtime_offeredtime_confirmedsession_confirmedcompletedcanceled
This prevents intake review from being overloaded with scheduling semantics.
3. Payment state
Payment lives separately in booking_payments, with statuses like:
pendingpaidfailedexpiredrefundedpartially_refunded
Again, this matters. A booking can have a confirmed time while payment is still unresolved. A payment can be retried without rewriting the entire booking record. The system models reality as a workflow, not as a single “order status.”
Why this split matters
This kind of separation is often what keeps an application understandable six months later. It also makes AI-assisted iteration much safer, because each part of the workflow has a clearer boundary.
It gives you:
- cleaner reasoning about each phase
- more defensible audit history
- less overloaded status logic
- easier admin UI decisions
- fewer accidental shortcuts in the workflow
For Quiet Presence, it also reinforces an important product truth: review, scheduling, and payment are related, but they are not the same thing.
User Preferences vs Admin Control
One of the easiest mistakes in a booking product is to confuse “the user can express preferences” with “the user controls the workflow.”
Quiet Presence separates those cleanly.
| Concern | User side | Admin side |
|---|---|---|
| Intake | provides contact info, boundaries, preferences, safety/access notes | reviews fit and decides whether request proceeds |
| Agreement | acknowledges written agreement | issues agreement request and decides when workflow can advance |
| Scheduling | can select from offered options | creates booking, offers options, records confirmed time |
| Payment | follows hosted payment request | creates payment request, reconciles status, records outcome |
| Confirmation | receives final confirmation | decides when session is fully confirmed |
That split is what keeps the product from drifting into generic self-serve booking software. The user gets a voice. The admin retains control.
Public Site: Static by Default
The public-facing part of Quiet Presence is intentionally simple.
The main pages live as static HTML, with Tailwind delivered through a CDN and Cloudflare Pages serving the final output. That keeps the public surface area small and predictable. There is very little that can break at runtime unless a request crosses into an actual workflow action.
This is a good fit for the product because most of the site is explanatory before it is transactional:
- what the service is
- what it is not
- who it is for
- what the boundaries are
- what the legal and policy conditions are
That content should load fast, remain easy to audit, and stay easy to align with the internal source-of-truth documents.
The public interactivity is narrow and operational:
- submit intake
- acknowledge the client agreement
- confirm one of the offered scheduling options
Everything else stays content-first. That keeps the public surface area simple while still letting the system collect the client’s preferences, constraints, and intake details in a structured way.
Pages Functions: Small Handlers, Real Workflow
The server side is built with Cloudflare Pages Functions. This is a good middle ground for the application:
- enough backend capability to validate workflow steps
- little infrastructure overhead
- easy deployment alongside the static site
The app uses functions for a few critical flows:
- intake submission
- agreement acknowledgment
- time-option confirmation
- admin review and admin mutations
These handlers are not trying to become a generic backend platform. Their job is to enforce state transitions and record what happened.
That distinction matters. In many systems, route handlers gradually absorb business rules, integration logic, rendering logic, and validation until they become impossible to reason about. Quiet Presence is already moving in the opposite direction.
The repo includes an explicit layered direction:
domain/for pure workflow and state rulesapplication/for use casesinfrastructure/for storage and third-party adapters- interface routes and views as thin boundaries
That is the right kind of complexity here: not complexity for its own sake, but just enough separation to keep workflow rules testable and visible.
That mattered for AI-assisted development too. Small route handlers, explicit use cases, and direct SQL are much easier to generate, inspect, and correct than a large abstraction-heavy framework stack.
D1 as the Workflow Ledger
Cloudflare D1 is doing more than simple form storage in this project. It acts as the durable workflow ledger for the service.
The schema captures:
- intake submissions
- intake notes
- audit events
- bookings
- booking payments
- booking time options
Two details stand out.
Audit events are first-class
The audit_events table exists from the beginning. That is a strong signal about the kind of system this is trying to be.
The app is not just storing the current record. It is also recording operational actions such as:
- emails sent
- booking updates
- payment creation
- payment reconciliation
- final confirmation
For a workflow with screening, boundaries, scheduling discretion, and payment handling, this is exactly the right instinct. When something goes wrong, you want more than a current row state. You want a trace of the important actions.
Payment retries are modeled cleanly
Payment attempts are append-oriented rather than overwritten in place. Older payment attempts are superseded, and the current attempt is resolved by selecting the unsuperseded record.
That gives the project a better operational model:
- retrying payment does not destroy history
- reconciliation can target a specific attempt
- refunds and partial refunds have a place in the model
For a small application, this is a meaningful improvement over the more common pattern of stuffing payment state into a few mutable columns on the booking itself.
The Admin System Is the Real Product Engine
The public site explains the service, but the private admin system is where the operational model actually lives.
This is where the provider can:
- review requests
- request or track the written agreement
- create bookings
- offer time options
- confirm selected times
- create payment requests
- reconcile payment
- send final session confirmation
The admin workflow code makes the next-step logic explicit instead of burying it in operator intuition. The UI derives hints such as:
- complete intake review
- request written agreement
- wait for agreement acknowledgment
- create booking
- offer time options
- request or reconcile payment
- send final confirmation
That is a strong design decision.
In a workflow-heavy app, one of the easiest ways to create errors is to make the operator mentally reconstruct the state machine. Quiet Presence instead makes the next action legible inside the interface.
Another detail I like: the admin interface is moving toward server-rendered HTML with JavaScript as progressive enhancement, rather than making the browser the source of workflow truth. That is a better fit for an internal tool where reliability and clarity matter more than frontend cleverness.
This is the central product decision in the software: control stays with the admin side. The client can express preferences, acknowledge agreements, and select from offered options, but the system does not hand over final workflow control.
Scheduling Is Provider-Controlled by Design
One of the clearest architectural choices in Quiet Presence is the refusal to turn scheduling into open self-booking.
Instead, the scheduling flow works like this:
- a booking is approved for scheduling
- the provider offers one or more time options
- the client selects from those offered options through a private tokenized link
- the system records a confirmed time
This is a subtle but important system shape.
It preserves:
- provider discretion
- explicit availability control
- lower ambiguity for clients
- a record of which options were offered and which one was selected
The booking_time_options table supports this well. Each option is a concrete record with a status:
offeredselectedexpiredwithdrawn
There is even a unique index enforcing only one selected option per booking.
That kind of constraint is exactly where databases are useful. If the workflow requires a single selected outcome, the data layer should help enforce it.
This is also the balance I wanted from the user side: clients should be able to communicate preferences and respond to concrete options, but not be pushed into a generic self-serve calendar flow that weakens provider control.
Payment Is a Workflow Step, Not a Storefront
Another strong choice is how payments are framed.
Quiet Presence does not treat payment like e-commerce checkout. The payment request is created only after the workflow reaches the appropriate state. The system also checks eligibility before allowing the request to proceed.
That means:
- the booking must be in an allowed state
- the client agreement must already be acknowledged
- existing successful payment states block duplicate requests
This is the right model for the product. Payment is an administrative confirmation step attached to an already-reviewed service interaction, not a public impulse purchase path.
The payment adapter boundary is also clean. Provider-specific logic sits behind a narrower interface, which keeps the use cases from depending too heavily on raw provider responses. That is small-scale architecture discipline, but it pays off quickly once retries, reconciliation, and refunds enter the system.
Email Is Treated as Part of Operations
Email in this app is not generic notification spam. It is part of the workflow contract.
There are specific templates for specific moments:
- intake received
- agreement request
- agreement received
- time proposal
- time confirmed
- payment request
- payment received
- session confirmation
This is exactly how transactional messaging should be handled in a workflow-centric app. Each message corresponds to a clear state transition or operational action.
The system also records successful sends and failures in audit events. That matters because email is often where operational gaps become user-facing confusion. If a message is part of the process, it should also be part of the record.
The final confirmation step can optionally attach an .ics calendar artifact, which is a good example of small, practical product engineering. It does not add platform complexity, but it does make the confirmed state feel operationally complete.
Security and Trust Model
The security model is intentionally narrow.
On the public side:
- Turnstile protects intake submission from abuse
- private tokens gate agreement acknowledgment and time-option confirmation
On the admin side:
- Cloudflare Access is the intended production protection layer
- a shared admin token remains as a fallback path for local use and recovery
This is not a maximalist security architecture, but it is coherent for the current system size. The important thing is that the trust boundaries are clear:
- public pages explain
- public routes collect or confirm bounded actions
- admin routes control workflow mutation
As the project grows, there is room to harden the admin side further, but the existing model already shows the right instinct: keep privileged workflow actions behind a separate boundary.
AI Helped Build It Faster, Not Define It
One of the practical benefits of this project is that AI made it much easier to go from concept to functioning system.
AI helped accelerate work like:
- drafting route handlers
- generating and refining SQL-backed workflow flows
- shaping admin UI interactions
- producing email template scaffolding
- iterating on docs, specs, and operational wording
But the reason that worked is that the system had a strong center:
- clear service boundaries
- explicit workflow stages
- admin-controlled decisions
- user preference capture without user-side workflow control
That is the pattern I keep finding with AI-assisted software work. AI is strong at helping you assemble a functional system quickly once the shape is clear. It is much weaker at deciding what the shape should be.
Reuse: What Can Be Carried Forward
One useful side effect of building the system this way is that a lot of it is reusable.
Not reusable in the sense of a generic “booking SaaS,” but reusable as a pattern for other high-trust services that need screening, controlled progression, and clear boundaries.
At the code level, the reusable parts are things like:
- explicit workflow state models
- admin-first progression logic
- audit event recording
- tokenized one-step client actions
- email templates tied to workflow events
- small serverless route handlers around a narrow data model
At the product level, the reusable idea is stronger:
- let the user express preferences
- keep decision-making and progression on the admin side
- separate review state from scheduling state from payment state
- treat legal and operational alignment as part of the system, not as separate paperwork
That pattern could apply to other services where self-booking is the wrong model:
- screening-based care-adjacent services
- private consultation workflows
- specialist intake and approval systems
- any bounded service where trust and discretion matter more than speed
This is one of the reasons AI is so useful here. Once a pattern like this becomes clear, it becomes much easier to reuse the structure, regenerate the moving pieces, and adapt the workflow to a new domain without starting from zero every time.
The Architecture Is Small, but Not Naive
What makes this project interesting is not that it uses exotic technology. It does not.
What makes it interesting is that it applies system thinking to a relatively small application.
It uses:
- explicit state transitions instead of ambiguous flags
- layered separation where workflow rules justify it
- direct SQL where clarity matters more than abstraction
- progressive enhancement for admin rather than framework maximalism
- auditability as a built-in concern
- product constraints as architecture inputs
That is often the difference between an app that merely works and an app that stays understandable.
Lessons From Building It
Four lessons stand out from this system.
1. Workflow clarity is a product feature
For high-trust services, ambiguity is a system defect. Clear stages, explicit statuses, and aligned documents are not just implementation details. They are part of the product experience.
2. Small stacks work well when the state model is strong
Static pages, serverless functions, and direct SQL can carry quite a lot of product weight if the workflow boundaries are modeled carefully.
3. AI works best when the workflow is explicit
AI can help create a working booking system surprisingly quickly, but only if the system has real boundaries, real states, and real control points. Otherwise you just get fast confusion.
4. Not every app should optimize for self-service
A lot of software defaults to removing humans from the loop. Quiet Presence is a good example of the opposite pattern: use software to support careful human control, not to erase it.
That is not anti-automation. It is appropriate automation for a service built around trust, boundaries, and the value of ordinary human presence.
Closing
Quiet Presence is a modest application in terms of raw footprint, but the system design is shaped by unusual constraints: high trust, clear boundaries, provider discretion, and operational defensibility.
Those constraints pushed the app toward a cleaner architecture than many larger systems ever reach:
- clear state separation
- explicit workflow gates
- a narrow backend surface
- an admin tool that reflects real operations
- user preference capture without surrendering admin control
- documentation that tries to stay aligned with code and policy
That is probably the broader lesson here.
Good application architecture is not about making a system look advanced. It is about making the system faithfully express the realities of the product it serves.