cryptoscreen is a native SwiftUI prototype for one-time, privacy-preserving messages. It combines a sealed-message flow with a proximity-style reader: text stays scrambled until the reader covers the top of the screen or uses the manual reveal control.
The current app is intentionally small and native. It encrypts messages on device, stores ciphertext through a Cloudflare Worker API backed by Neon Postgres, and renders plaintext only after the server consumes a one-time read attempt.
- Create a sealed one-time message.
- Protect the message with a six-digit PIN.
- Generate a shareable deep link with a high-entropy fragment secret.
- Open the message with up to three PIN attempts.
- Delete the encrypted payload after a correct PIN.
- Delete the encrypted payload after the third wrong PIN.
- Delete unopened links after 30 days.
- Render the plaintext through the privacy reader without selectable text.
- Redact the app while screen recording, mirroring, app switching, or shortly after screenshot detection.
- Destroy the visible reader session after iOS reports a screenshot.
- Avoid putting plaintext into logs, clipboard actions, share sheets, or accessibility labels.
- Serve
cryptoscreen.app, support/privacy pages, and Apple association metadata from Cloudflare Workers. - V2 design work for Pro encrypted image attachments is tracked in docs/V2_PRO_IMAGES.md.
The app talks only to https://cryptoscreen.app/api. It must never connect directly to Neon.
If you want the backend explained as a lesson, start with docs/DATABASE_AND_STORAGE.md. It explains what is safe to expose in an open-source repo, what the database stores, and how encrypted image objects are deleted from R2.
The production model is:
- The sender encrypts plaintext locally.
- The server stores only ciphertext and metadata.
- The decryption key is not sent to the server.
- The recipient must have both the link secret and PIN.
- The server allows three online PIN attempts.
- A correct PIN atomically returns ciphertext and deletes the row.
- The third wrong PIN atomically deletes the row.
- Unopened links expire and are deleted after 30 days.
- Service-owned retained demo/review rows can be reused for App Review and TestFlight invocation testing.
Read the detailed design in docs/SECURITY.md.
The checked-in database schema is intentionally public in database/schema.sql. It contains structure and deletion logic, not production credentials or user data.
The product target is App Clip first:
- Sender creates a sealed message.
- Sender shares a cryptoscreen link.
- Recipient opens the link.
- iOS launches the App Clip if the full app is not installed.
- The App Clip asks for the PIN and consumes the message.
The repo includes an App Clip target that reuses the same reader and sealed-message code in an App Clip-specific open flow. Production App Clip release still requires Apple Developer/App Store Connect configuration, Associated Domains, and on-device invocation testing.
Requirements:
- Xcode 26.5 or newer
- iOS 17.0 deployment target or newer
- Node.js 22 and pnpm 10 for the Cloudflare Worker
Build from the command line:
xcodebuild \
-project PrivacyScreen.xcodeproj \
-scheme PrivacyScreen \
-destination 'platform=iOS Simulator,name=iPhone 17' \
buildBuild the App Clip target from the command line:
xcodebuild \
-project PrivacyScreen.xcodeproj \
-scheme PrivacyScreenClip \
-destination 'platform=iOS Simulator,name=iPhone 17' \
buildRun with Xcode or the simulator tooling of your choice.
Install dependencies:
pnpm installCreate a local Wrangler config:
cp wrangler.example.jsonc wrangler.jsoncReplace the placeholders in wrangler.jsonc with your Cloudflare account, domain, and Apple app values. The real wrangler.jsonc is intentionally ignored by Git because it contains deployment-specific identifiers.
Generate Worker types and validate the bundle:
pnpm run types
pnpm run checkRequired production secrets:
DATABASE_URL
SERVER_PIN_PEPPER
Deploy after Cloudflare CLI auth is available:
pnpm exec wrangler secret put DATABASE_URL
pnpm exec wrangler secret put SERVER_PIN_PEPPER
pnpm run deployFor the first deployment of a new Worker, Wrangler may require the secrets before wrangler secret put can target an existing script. In that case, deploy once with wrangler deploy --secrets-file <temporary-json-file>, then delete the temporary file immediately.
The Worker serves:
/web landing page/privacyApp Store privacy URL/supportApp Store support URL/m/<message-id>universal link and App Clip invocation page/.well-known/apple-app-site-association/api/stats/api/feedback/api/messages/api/messages/<message-id>/attachment/api/messages/<message-id>/consume/api/read-sessions/<read-session-id>/attachment/api/read-sessions/<read-session-id>/events
V2 encrypted image attachments use a private Cloudflare R2 bucket bound to the Worker as ATTACHMENTS. The bucket must not be public; all upload and download access goes through the Worker. The checked-in wrangler.example.jsonc includes the required binding shape.
Optional public-site variables:
GITHUB_REPOSITORY_URL
X_PROFILE_URL
SUPPORT_EMAIL
FEEDBACK_EMAIL
FEEDBACK_FROM_EMAIL
/api/feedback sends onboarding feedback through a server-side Cloudflare send_email binding named FEEDBACK_EMAIL_SENDER. The iOS app never receives SMTP or email-provider credentials.
PrivacyScreen/
CaptureShield.swift Screen recording, screenshot, and app-switcher redaction
PrivacyReaderView.swift Scrambled/revealed reader UI
ProximitySensor.swift Proximity/manual reveal state
SealedMessageAPI.swift Cloudflare Worker API client
SealedMessageCrypto.swift Client-side sealing, PIN proof, link parsing
SealedMessageRootView.swift Compose/open/read flow
TextLineWrapper.swift Monospaced line wrapping and scramble helpers
PrivacyScreenClip/
Info.plist App Clip metadata
PrivacyScreenClip.entitlements
database/
schema.sql Neon/Postgres schema and atomic consume function
docs/
DATABASE_AND_STORAGE.md Beginner-friendly backend and storage walkthrough
SECURITY.md Threat model and production API/database design
V2_PRO_IMAGES.md Pro encrypted image attachment architecture
.github/
ISSUE_TEMPLATE/ Public issue templates
PULL_REQUEST_TEMPLATE.md Contributor pull request checklist
src/
worker.ts Cloudflare Worker website and API
wrangler.example.jsonc Copyable Worker config template
- Normal screenshots cannot be prevented reliably with public iOS APIs. The app can redact and destroy the visible reader session after screenshot notification, and it can redact during screen recording/mirroring.
- The App Clip target is present and builds locally, but still needs production invocation testing from a real App Clip URL.
- Associated Domains and App Clip capabilities must be enabled for the Apple App IDs before TestFlight/App Store signing with entitlements.
- A six-digit PIN is not enough by itself. The design requires the high-entropy link secret plus the PIN.
Contributions are welcome. Start with CONTRIBUTING.md, and open pull requests using the template in .github/.
For vulnerabilities, do not open a public issue. Follow SECURITY.md.
MIT. See LICENSE.