# Calypso — Full Technical & Architectural Specification ## Project Overview Calypso is an internal security utility built for founders, developers, creators, and general users to share confidential text, environment secrets, private keys, database credentials, and one-time passwords without exposing them to persistent storage, chat logs, email threads, or web servers. Developed and maintained by OBILODEV (info@obilodev.com). --- ## Threat Model & Security Architecture ### 1. Zero-Knowledge Cryptography (Client-Side AES-GCM-256) - **Encryption Stage**: - When the user types or pastes text on `/secrets/create`, the browser executes `window.crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"])`. - A 96-bit cryptographic IV (`window.crypto.getRandomValues(new Uint8Array(12))`) is created. - The plaintext is encrypted using AES-GCM. - The payload sent to the HTTP server is an opaque string formatted as `e2ee:{"iv":"","ct":""}`. - The raw encryption key is kept exclusively in browser memory. - **Key Passing**: - The generated share link appends the Base64 key as a URL fragment: `https://example.com/secrets/{uuid}#key=`. - According to RFC 3986 §3.5, URI fragments are client-only and are strictly never transmitted in HTTP request lines, headers, or server access logs. - **Decryption Stage**: - When the recipient opens the link, `window.location.hash` is parsed on the client device. - Upon clicking "Reveal Secret", the client imports the key and decrypts the ciphertext locally. ### 2. Chatbot & Crawler Shield - Problem: Platforms such as Slack, Discord, Microsoft Teams, WhatsApp, Twitter, and iMessage immediately deploy headless HTTP scrapers to generate link unfurls and rich previews. On standard secret-sharing sites, this consumes the single view before the human recipient clicks the link. - Calypso Solution: Calypso inspects incoming User-Agent strings against a matrix of over 20 bot signatures (Slackbot, Twitterbot, Discordbot, WhatsApp, FacebookExternalHit, TelegramBot, Applebot, LinkedInBot, etc.). - When a crawler is detected, Calypso serves a static OpenGraph preview template (`secrets.preview`) with title and description metadata without executing decryption, incrementing view counters, or shredding the secret. ### 3. Intermediary Human Reveal Screen - Human visitors are not immediately served the decrypted secret. Instead, they are presented with a confirmation step informing them of the message view limits and self-destruction consequences. - Only when the human recipient clicks "Reveal Secret" (`?reveal=1`) does the counter increment and the decryption routine execute. ### 4. Cryptographic Shredding - Upon view limit exhaustion or time-based expiry: - The database field containing the ciphertext is overwritten with a 64-character pseudo-random string (`Str::random(64)`). - The record is soft-deleted. - Scheduled background worker `php artisan secrets:purge` runs every 10 minutes to force-delete all shredded and expired rows permanently. ### 5. Sender Revocation ("Burn Before Reading") - Senders receive an unpredictable 32-character destruction token (`destroy_token`). - If an erroneous recipient was selected or the confidential matter is resolved, the sender can trigger `/secrets/{uuid}/burn?token={destroy_token}` to shred and delete the secret remotely. ### 6. Anonymized Access Logging - To maintain auditability while adhering to privacy compliance (GDPR, CCPA), IP addresses stored in visit logs are truncated to the `/24` subnet for IPv4 (e.g. `203.0.113.0`) and the `/64` subnet for IPv6 using bitwise masking. --- ## REST API Specification ### Endpoint: Create Secret - `POST /api/v1/secrets` - Headers: `Content-Type: application/json`, `Accept: application/json` - Rate Limit: 60 requests / minute - Request Body: ```json { "content": "Secret content string (max 19000 chars)", "password": "Optional password protection", "expires_in": 60, "max_views": 1 } ``` - Response (201 Created): ```json { "success": true, "data": { "id": "c1f7a42b-...", "url": "https://example.com/secrets/c1f7a42b-...", "destroy_token": "a8f3b2...", "destroy_url": "https://example.com/secrets/c1f7a42b-.../burn?token=a8f3b2...", "expires_at": "2026-09-22T12:00:00Z", "max_views": 1 } } ``` ### Endpoint: Reveal Secret - `GET /api/v1/secrets/{uuid}` - Query or Body: `password` (if password-protected) - Response (200 OK): ```json { "success": true, "data": { "id": "c1f7a42b-...", "content": "Secret content string", "views": 1, "max_views": 1, "expires_at": "2026-09-22T12:00:00Z", "created_at": "2026-09-21T12:00:00Z" } } ``` ### Endpoint: Burn Secret - `DELETE /api/v1/secrets/{uuid}` or `POST /api/v1/secrets/{uuid}/burn` - Body or Header: `{"token": "a8f3b2..."}` or `X-Destroy-Token: a8f3b2...` - Response (200 OK): ```json { "success": true, "message": "Secret destroyed and shredded successfully." } ``` --- ## Frequently Asked Questions (FAQ) ### Can server administrators or hosting providers access my secrets? No. Because client-side AES-GCM-256 encryption executes directly in your browser prior to network transmission, and the key resides solely in the URL hash fragment (`#key=...`), the server only receives an encrypted envelope without the decryption key. ### Does sharing a link on Slack, Discord, or WhatsApp burn the view? No. Calypso's crawler shield recognizes link preview bots from major chat platforms and returns OpenGraph metadata without counting as a view or destroying the secret. ### What happens when a secret is burned? The secret content column in the database is overwritten with a 64-character random cryptographic string and deleted. Recovery is mathematically impossible. ### Contact & Support - Maintainer: OBILODEV - Contact Email: info@obilodev.com - Organization Website: https://obilodev.com