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 privacy-sensitive 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.
At a glance
- Purpose: Bring therapy onboarding, agreements, booking, payment, private sessions, reminders and client workflows into one controlled system.
- Users: Therapy clients, the practitioner, and workshop/taster-session participants.
- Key workflows: Client onboarding, signed agreements, Stripe payment, Jitsi session access, reflection forms, reminders, SAR handling and account closure.
- Status: Live in production.
- What it demonstrates: Django/PostgreSQL workflow design, privacy-led architecture, self-hosted service integration, payment and signing webhooks, operational housekeeping and AI-assisted development under human review.
Why It Was Built
Private therapy work can easily fragment across generic video tools, commercial form builders, external signing services, payment links, reminders and email threads. That makes the workflow harder to manage and spreads sensitive context across too many places.
Transcendence replaces that scatter with a controlled workflow: one system, defined boundaries, auditable state, and no third-party advertising or behavioural profiling in the therapeutic workflow.
Technical Stack
Application Framework: Django
Django was chosen initially because it is a robust, easy-to-code Python framework for server-rendered applications, with built-in support for models, forms, authentication, admin workflows and migrations.
That made it a good fit for a workflow-heavy system with client state, agreements, payments, session access, reminders and therapist-side administration. Django's conventions also gave AI-assisted development a safer structure to work inside: models, forms, views, templates, permissions and migrations all had established places to live.
A PHP/MySQL implementation would have been technically possible for a simpler brochure-style site. For this system, with state transitions, permission rules, webhook callbacks and cross-service coordination, Django/PostgreSQL provided the better fit.
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 held in a consolidated CSS file built on top of Bootstrap, using CSS custom properties for palette, spacing and typographic scale. The CSS was developed with AI assistance and then refined through browser testing. 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 advertising or behavioural analytics
- Self-hosted video, document signing and analytics where practical; payment, SMS, email and DNS providers are bounded external services
- 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 rather than measure engagement for its own sake.
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:
- Login via 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 chat throughout, with Claude used agentically at the end of the project. 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. Nightly encrypted backups cover the database, application configuration, media and supporting service state, with offsite replication and healthcheck reporting.
Operational housekeeping runs as Django management commands on the live container rather than as raw shell or SQL jobs. Held appointment slots expire automatically. Scheduled account closures are processed on a defined cadence. 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 with flock-based concurrency control, ensuring overlapping cron invocations cannot produce duplicate sends. The crontab 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
- Workshop listing, booking and ticketing
- 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 privacy-sensitive 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 demonstrates that 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