Inspiration

Nearly 1 in 3 women worldwide, about 840 million, will experience partner or sexual violence in their lifetime, and women who experience partner violence are almost twice as likely to face depression (WHO, 2025).

Mental health support exists, but almost every tool assumes you are safe while you use it. It asks for an account, keeps your history, sends notifications, and looks exactly like what it is. For a woman whose partner checks her phone, each of those is a risk. Asking for help can be the most dangerous moment.

We wanted to build mental health support for the women who can't safely ask for it: something that leaves no trace, hides in plain sight, and disappears the moment someone walks in.

What it does

Sanctum is a private mental health and safety toolkit, with no account needed. Try it at findyourmindateasewith.tech.

  • Therapy chat. A calm, private AI companion that listens without judging. Conversations are encrypted and permanently deleted after 24 hours. If a message sounds like a crisis, hotline numbers always appear, whatever the AI says.
  • Guided breathing. A full-screen breathing exercise (Calm, Box, or 4-7-8) with an optional calming soundscape and gentle haptic cues on phones, for moments when words are too much.
  • Legal guidance. Plain-language answers about protective orders, custody, housing, and next steps.
  • SOS messenger. She types a few panicked words, Sanctum turns them into a clear message, encrypts it, and hides it inside an ordinary photo: one from her camera roll, or a brand-new everyday scene created by AI. Her message is never sent to the image AI. Her sister receives a sunset; Sanctum's Decode page reveals the message. To anyone else, it's just a sunset.
  • Quick exit. One tap of the Quick Exit button, or three taps of Esc on a computer, jumps to an everyday site like Weather.com. Pressing Back never returns to Sanctum.
  • App disguise. Added to the home screen, Sanctum can look like a Weather, Calculator, Notes, or News app, with its own name and icon.

How we built it

  • Frontend: React 19, TypeScript, Vite, and Tailwind CSS, installable as a web app and deployed on Vercel.
  • Backend: Spring Boot 3.5 on Java 21, PostgreSQL with Flyway migrations, and Bucket4j rate limiting, deployed as a Docker container on Railway.
  • AI: Google Gemini for the chats and SOS messages, and a Gemini image model for cover photos. Both sit behind interfaces, with prompts kept in version-controlled files.
  • Breathing: the soundscape is synthesized live with the Web Audio API (no audio files), and haptics use the Vibration API on Android and the Taptic Engine on iPhone.

We designed for the worst case: a leak.

  • No accounts. A session is a random ID, nothing more.
  • Encryption at rest. Messages are sealed with AES-256-GCM using a random IV per value, with the session and column bound in as associated data, so ciphertext can't be copied between rows.
  • Hard deletion. Sessions expire after 24 hours (capped at 7 days) and are erased on quick exit.
  • Logs that forget. A Logback filter drops any log line containing an IP address, UUID, or email.
  • No third parties on page load. Fonts and icons are self-hosted, so no outside server learns about a visit.
  • No invented answers. If Gemini is unavailable, Sanctum says so rather than making up a reply.
  • Sound off by default. Audio can be overheard, so the breathing soundscape never plays unless she turns it on.

Hiding a message in a photo

Each pixel stores red, green, and blue values from 0 to 255. We replace the least significant bit of each channel with one bit of our payload, which changes a color by at most

$$\frac{1}{255} \approx 0.4\%$$

and is invisible to the eye. Each pixel holds 3 bits, so a \( W \times H \) image can carry

$$\text{capacity} = \frac{3WH}{8} \text{ bytes}$$

A small \( 512 \times 512 \) photo holds about 98 KB, far more than any SOS message. The payload is ["SNCT"][length][AES-GCM ciphertext], adding just 36 bytes of overhead. Even if someone extracts the bits, all they get is ciphertext. The output PNG is rebuilt from raw pixels, so EXIF data, GPS location, and timestamps are stripped. AI-generated cover photos go through the same path: the image model returns a JPEG, which we re-encode as a clean PNG before hiding anything in it.

Challenges we ran into

  • Browsers won't let a site erase history. Our first quick exit jumped away, but pressing Back brought Sanctum right back. Browsers deliberately block sites from deleting history, so we worked around it:
    1. We push decoy history entries before leaving.
    2. A tiny script at the very top of the page redirects any decoy, or any page reached with Back or Forward after an exit, before the app even loads.
    3. We handle pages the browser restores from its back/forward cache, which don't reload at all.

We tested it by pressing Back 15 times after an exit. Sanctum never rendered once.

  • Image compression destroys hidden messages. Hidden bits only survive lossless PNGs, but phone photos are usually JPEG or HEIC. We convert any photo to PNG in the browser before upload, which also strips its metadata.
  • The real name flashing in the tab. A disguised app that shows "Sanctum" in the tab for half a second before loading isn't disguised. The saved disguise is now applied by an inline script before the page first paints.
  • Android install getting stuck. Our disguised app icons were generated in the browser, but Android builds a home-screen app by having Google's servers download the icons, and they couldn't reach ones that only existed on the phone. We now ship real icon and manifest files for every built-in disguise.
  • Balancing AI help with safety. Crisis detection runs as a deterministic check on the user's message, independent of the model, so hotline numbers can't be skipped by a bad AI response.

Accomplishments that we're proud of

  • A quick exit that really leaves nothing to go back to.
  • Working steganography, end to end, with authenticated encryption, plus AI-generated cover photos that never see the message.
  • A breathing exercise that works with eyes closed, through sound and touch alone.
  • A backend covered by unit and Testcontainers integration tests, with an 80% coverage gate on services.
  • A live deployment anyone can try.

What we learned

  • Threat modeling changes everything. Designing for "what if her partner picks up the phone?" affected every decision, from log formats to where fonts load from to keeping sound off by default.
  • How browsers really handle history, including the back/forward cache, pushState, and navigation types, and what a website can and can't control.
  • Steganography and authenticated encryption in practice: why AES-GCM's tag matters, and why hidden data only survives lossless formats.
  • Safe AI design: never fabricate, never block emergency information, keep the model out of safety-critical checks, and give each model only the data it needs.
  • How installable web apps work on Android, and why a manifest's icons must be real files.

What's next for Sanctum

  • Partnering with domestic violence organizations to review our content and prompts.
  • Crisis resources beyond the US.
  • More languages.
  • Custom disguises that install reliably on Android.

Built With

Share this project:

Updates

Submission history