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:
- Password authentication with strong password policies
- TOTP (time-based one-time password) support
- Passkey / FIDO2 / WebAuthn support
- Admin account hardening and MFA
- Encryption at rest for therapy session notes using pgcrypto PGP symmetric key encryption; sensitive therapeutic content never stored in plaintext
- HTTPS-only deployment with secure cookies
- Django's built-in CSRF protection
- Environment-based configuration with secrets passed via environment variables, kept out of source control and out of image layers
- Docker secrets and non-root container execution are identified hardening improvements for the production estate
- Separation between application logic and deployment configuration
- Audit trails for security-relevant events (TOTP recovery, account closure, SAR requests)
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:
- No third-party tracking scripts of any kind
- Self-hosted video, document signing, and analytics; no commercial third-party processing of client data
- Bounded, purpose-driven reflection forms rather than open-ended onsite journalling
- No behavioural advertising logic anywhere in the system
- Therapeutic information treated as sensitive by default, regardless of formal classification
- Therapy session notes encrypted at rest; only readable with the application key
- Subject access request (SAR) workflow built in: clients can request their data; the practice prepares and delivers a PDF disclosure through the platform
- Account closure workflow with a seven-day grace period, credit balance snapshot, and full audit trail; the client is not simply deleted on request, the process is managed deliberately
- The human therapist remains responsible for interpretation and care; AI is not used to provide autonomous guidance to clients
- A cookies policy that documents the actual architecture, accurately, including what is absent
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:
- A social network or community platform
- An open journalling platform
- A gamified mental health app
- A client-facing AI therapist
- A behavioural tracking and engagement system
- A marketing funnel dressed as care
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:
- Creating a test client account
- Completing a form or reflection
- Signing an agreement and verifying the webhook callback
- Booking a session and checking state transitions
- Completing or simulating payment
- Verifying session access
- Inspecting logs and database state
- Testing the browser-facing workflow across relevant edge cases
- Restarting services and verifying deployment behaviour
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:
- TLS certificate handling and reverse proxy configuration
- Static file routing and Django's
STATIC_ROOT/MEDIA_ROOTseparation - Docker service networking and container inter-communication
- Environment variable and secrets management across environments
- Database migration sequencing
- Webhook callbacks from Stripe and DocuSeal: idempotency and failure handling
- Email deliverability configuration: a reminder that email remains one of the most deceptively complex parts of any internet-connected system; SPF, DKIM, DMARC, reputation, and provider-side filtering all require attention, and anyone who configured Sendmail or Demon Internet mail in the 1990s will find the lesson unchanged in substance if not in detail
- DNS propagation and Cloudflare caching behaviour
- Production vs. development configuration differences
- Container log inspection and service restart behaviour
- Backup and restore planning
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:
- Automated regression test coverage for high-risk state transitions
- Further passkey and MFA hardening
- Restore verification drills; backup infrastructure is in place and running, but restore paths have not been formally tested under simulated failure conditions
- Improved Jitsi access control before wider rollout
- Better therapist pre-session review pages
- Monitoring uses two layers: UptimeRobot for external uptime checks and alerting, and a self-hosted Gatus instance for internal endpoint and service health monitoring. The temptation to reach for a local Nagios installation was resisted; the combination of a lightweight modern monitor and a free external check covers the actual need without the overhead
- Deployment documentation
- Accessibility audit and improvements
- Multi-therapist support is a planned future enhancement; the platform is currently single-practitioner but the architecture has been designed with that extension in mind. It will require a dedicated practitioner agreement model and onboarding flow rather than a stretch of the existing client model, and that separation has been kept clean from the outset
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:
- Requirements analysis and scope judgement in a sensitive domain
- Privacy-by-design applied as an engineering constraint, not a policy document
- Full-stack Django development: models, forms, views, auth, admin, migrations
- Self-hosted service integration: Jitsi, DocuSeal, Matomo
- Third-party API integration: Stripe, Cloudflare, SMS and email reminder services
- Docker Compose deployment and operational management
- Security implementation: TOTP, passkeys, CSRF, HTTPS, secrets management
- Database-layer encryption of sensitive therapy notes using pgcrypto PGP symmetric key encryption
- The discipline to build what the problem needs and refuse what it does not
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