Transcendence.site: Technical Case Study

Role: Designer, architect, and sole developer
Status: Live in production
URL: transcendence.site
Stack: Django · PostgreSQL · Docker Compose · Jitsi · DocuSeal · Stripe · GatewayAPI · Matomo · Nginx · Cloudflare


Overview

Transcendence is a privacy-focused online platform for hypnotherapy and cognitive behavioural work. It is not a brochure site with a contact form attached. The platform handles client onboarding, signed therapeutic agreements, session booking, payment, private video sessions, structured reflection tools, therapist-side client management, appointment reminders by email and SMS, public taster-session booking, and subject access request fulfilment, as a coherent, integrated system.

The project was designed and built from scratch, by a single developer, for a regulated professional context. Every decision was made against two constraints that pull in opposite directions: the system must be capable enough to replace a scattered collection of third-party tools, and it must handle sensitive health data with the discretion that work requires.


Why It Was Built

The alternative: stitching together generic video tools, commercial form builders, external document signing services, payment links, and email threads, produces a system where therapeutic information is scattered across platforms operating under terms the practice cannot control. That is an acceptable situation for scheduling a haircut. It is not acceptable for therapy.

Transcendence replaces that scatter with a controlled environment: one system, defined boundaries, auditable state, and no third-party advertising or profiling infrastructure anywhere in the chain.


Technical Stack

Application Framework: Django

Django was the right choice because the problem is workflow-heavy and model-driven, not primarily a content-publishing problem. The framework provides mature, well-tested support for authentication, form handling, model relationships, migrations, permissions, admin tooling, and secure defaults. Its conventions impose useful discipline on a solo project, and its admin interface provides immediate operational utility without requiring a separate backoffice build.

A PHP/MySQL implementation would have been technically possible for a simpler site, and the author has more than ample experience of both its quirks and its vulnerability surface. For this system, with its state transitions, permission rules, webhook callbacks, and cross-service coordination, Django/PostgreSQL provides a substantially better fit. Perl might offer a certain elegance to those who know it well, but in a project where AI tooling contributes substantially to the codebase, readability and ease of maintenance over time carry real weight. The choice was about matching the stack to the shape of the problem, not to the habits of the developer.

Database: PostgreSQL

The data is relational and stateful. Clients, bookings, agreements, payments, sessions, and reflection records all carry relationships, constraints, and dependencies that need reliable transactional behaviour. PostgreSQL fits that model cleanly. Django's migration system supports controlled schema evolution over time.

MySQL or MariaDB would have been the obvious alternative. PostgreSQL was the stronger choice for several concrete reasons: pgcrypto, used here for PGP symmetric encryption of therapy notes, is a PostgreSQL-native extension with no direct MySQL equivalent. Transactional DDL means schema migrations can be rolled back cleanly if something goes wrong, which matters in a system where client data is live. PostgreSQL's reputation for correctness and constraint enforcement over raw speed reflects the right set of priorities for a system handling sensitive health records.

Deployment: Docker Compose

The platform is deployed via Docker Compose on a Linux server. Docker provides a repeatable, auditable way to manage the application alongside its supporting services: database, signing system, analytics, video infrastructure, and reverse proxy, with clear separation between them. It also makes the difference between development and production environments legible and controllable.

Version control is via Git with GitHub as the remote. Deployment is a straightforward pull-and-restart workflow: changes are committed locally, pushed to GitHub, pulled onto the production server, and services restarted via Docker Compose. Ansible was considered at the outset and prototyped, but for a single-server deployment of this scale it added process overhead without meaningful operational benefit. Removing it kept the deployment path simple, auditable, and operable without additional tooling or abstraction.

Reverse Proxy and TLS: Nginx Proxy Manager / Cloudflare

TLS termination and routing are handled by Nginx Proxy Manager, which manages certificate provisioning and renewal, upstream routing, and header forwarding without requiring manual Nginx configuration. Cloudflare sits in front for DNS and a small number of edge rules. The combination means this layer required minimal setup and has needed little attention since, which is the intended outcome.

Video Sessions: Jitsi (self-hosted)

Private one-to-one sessions run over Jitsi, self-hosted. The decision not to use Zoom or Teams was deliberate: commercial video platforms process session metadata under their own terms of service. A practitioner cannot reasonably tell a therapy client that their session data passed through a third-party commercial infrastructure stack. Self-hosting closes that gap. Session access is tied to booking and client state within the platform.

Document Signing: DocuSeal (self-hosted)

Therapy requires signed agreements before work begins: informed consent, therapeutic agreements, preparation forms. DocuSeal provides the e-signature workflow; Django records agreement state and receives webhook callbacks to update the platform accordingly. Self-hosting means signed client documents do not leave the practice's own infrastructure. The integration keeps agreement completion part of the coherent onboarding workflow rather than a detached manual process.

Payments: Stripe

Session fee collection is handled through Stripe. Stripe manages PCI-DSS compliance for card processing; no card data touches the application server. The Django platform tracks payment state, session access, and package status internally. The payment system connects to booking state, client access rules, and taster session flows.

Appointment Reminders: GatewayAPI / Infomaniak

SMS appointment reminders are dispatched via GatewayAPI; email via Infomaniak. Clients control which reminder types they receive from their account settings. Reminder dispatch state is recorded per client to prevent duplicate sends and support operational review. Both services sit behind a thin integration layer and could be swapped without changes to the core application logic.

Analytics: Matomo (self-hosted)

Usage analytics are collected via a self-hosted Matomo instance. No Google Analytics, no Meta Pixel, no third-party behavioural data. Page view and interaction data stays on infrastructure the practice controls, is not profiled against individuals, and is not shared or sold. This is the only analytics setup consistent with a privacy policy that means what it says.

Frontend

The frontend is server-rendered Django templates. The UI is built on Bootstrap 5 for layout and components, with jQuery present primarily to satisfy django-mfa2 dependencies and support a small number of interaction patterns. Flatpickr handles date and time picking in booking flows. These are loaded from CDN with subresource integrity hashes.

Site-specific styling is a consolidated CSS file built on top of Bootstrap, using CSS custom properties for the design system: palette, spacing, and typographic scale defined once and applied consistently. The CSS was developed with AI assistance and refined iteratively. One of the more practically useful applications of AI tooling in this project was the ability to wholesale rewrite, reorganise, and sanitise the stylesheet on demand, a task that would have consumed days of careful, sanity-testing work in an earlier era. Anyone who spent time hunting a single broken selector across Netscape, IE, and early Firefox will appreciate the contrast. Custom JavaScript is minimal and purposeful: cookie banner handling, session countdown, arrival state, and form sequence unlocking. There is no frontend build pipeline; assets are versioned by query string. The visual identity, including logotype and colour palette, was developed as part of the project.


Security Architecture

Security is treated as part of the product, not as decoration applied after deployment.

The platform is built around:

The distinction between security and privacy matters here and is treated as such. Security asks whether unauthorised people can access data. Privacy asks whether the data should be collected or stored at all. Both questions need answers; they are not the same question.


Privacy by Design

Privacy is a structural principle, not a compliance statement added at the end.

Design choices that follow from this:

The platform is designed to support therapy. It is not designed to turn client distress into engagement metrics.


What Was Deliberately Not Built

Restraint is part of the design. The platform does not aim to become:

These decisions were made explicitly and held under pressure as scope temptation arose during development. The goal is therapeutic usefulness, not maximum feature surface.


Development Approach

The project was developed feature by feature using a method closer to feature proving than formal test-driven development. Features are built, run, and verified through actual end-to-end workflows rather than unit assertions in isolation.

A typical proving cycle for a new feature covers:

This approach is necessary for a system where a single user action crosses Django, Stripe, DocuSeal, Jitsi, email, browser session handling, and reverse proxy configuration simultaneously. It is not a substitute for automated regression coverage; that is identified as future work, with priority on the highest-risk state transitions.


Operational Lessons

A system that looks straightforward on a whiteboard earns its complexity in production. Areas that required careful attention:

The real work of a project like this is not writing the first version of a feature. It is making the feature survive contact with browsers, proxies, certificates, containers, users, and the general operational friction of a running system.


AI-Assisted Development

AI tooling was used as a development assistant throughout the build, using GPT-5 throughout, with Claude used on the development box for ongoing work from mid-project onwards. The assistance covered architecture discussion, Django model design, form logic, template iteration, deployment debugging, webhook handling, CSS refinement, and documentation drafting.

AI was not treated as authoritative. All code and configuration required human review, adaptation, and verification before use. This was especially true for authentication flows, payment handling, signed document workflows, database migrations, and anything touching production deployment or client privacy.

In practice, AI tooling reduced the distance between idea and working feature. It did not remove the need for systems judgement, particularly in a domain where the cost of a poorly considered decision is not a broken UI element but a breach of therapeutic trust.


Backup and Operational Housekeeping

Scheduled operations are managed via cron, with each job logging to a dedicated directory and the backup chain running in staggered sequence through the early hours.

Five distinct backup targets run nightly: full PostgreSQL dump, application configuration, Nginx Proxy Manager state, Django media, and DocuSeal templates and state. Encrypted backups are synced to a Hetzner Storage Box with a 30-day remote retention policy, with a healthcheck ping confirming successful offsite replication.

Operational housekeeping runs as Django management commands on the live container rather than as raw shell or SQL jobs. Held appointment slots expire every five minutes. Scheduled account closures are processed hourly. DocuSeal artefacts are pruned after seven days, enforcing at the operational layer the same data minimisation principle stated in the privacy policy.

Appointment reminders run on five-minute intervals with flock-based concurrency control, ensuring overlapping cron invocations cannot produce duplicate sends. Each reminder kind: morning email, near-time email, day-before SMS, near-time SMS, holds its own lock file, so one slow job does not block the others.

The crontab is not an afterthought. It reflects the same design discipline as the application: clear boundaries, logged outcomes, and no assumption that things will simply work without being watched.


Future Work

Identified development priorities include:


Summary

Transcendence.site is a full-stack Django application for a regulated professional context, deployed on self-managed Linux infrastructure via Docker Compose, integrating a suite of self-hosted and carefully selected external services under a coherent privacy-led architecture.

It demonstrates:

The project's central argument is that good therapy infrastructure needs the same qualities as good systems infrastructure: clear boundaries, reliable state, minimal unnecessary exposure, careful access control, useful feedback, human responsibility, and respect for failure modes.


Code Availability

The repository is private but the code is available on request at no charge. Installation, customisation, and ongoing support are available at standard rates.


Built by LIS ltd · Made in Scotland