Secret — Secure, Zero-Knowledge Encrypted Note & File Sharing

secret.larger.io — Share passwords, notes, and files securely with end-to-end encryption. Your data is encrypted in the browser using XChaCha20-Poly1305 — the server never sees your content. Self-hosted with a single Docker container. No accounts, no tracking, no logs.

A modern, open-source alternative to PrivateBin, OneTimeSecret, and Yopass — built with Svelte 5, Hono, and TypeScript.

Features

  • Zero-knowledge encryption — XChaCha20-Poly1305 (client) + AES-256-GCM (server)
  • Text & files — Notes, documents, images, any file type. Up to 10 files per note, drag & drop.
  • Burn after read — Destroyed after the first view
  • Password protection — Optional, Argon2id key derivation
  • Auto-expiry — 5 minutes to 30 days
  • Read limits — Auto-delete after N reads
  • Delete token — Manually delete a note at any time
  • File previews — Images, PDF, video, audio rendered in-browser after decryption
  • Chunked uploads — Stream large files in chunks with progress tracking (up to 500 MB)
  • S3 storage — Optional S3-compatible backend (AWS, MinIO, R2) for large files
  • QR codes — Share links easily on mobile
  • i18n — 10 languages (en, fr, es, de, pt, it, ja, zh, ru, ko)
  • Self-hostable — Single Docker container, customizable branding

Quick Start

git clone https://github.com/largerio/secret.git
cd secret
cp .env.example .env

# Generate a server encryption key (REQUIRED)
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
# Paste the output into .env as SERVER_ENCRYPTION_KEY

docker compose up -d

Open http://localhost:3000. API documentation is available at /api/v1/docs (Scalar).

SDK

Use the JavaScript/TypeScript SDK to interact with any Secret instance programmatically:

npm install @secret/sdk-js
import { SecretClient } from "@secret/sdk-js";

const client = await SecretClient.create({
  baseUrl: "https://secret.example.com",
  apiKey: "your-api-key",
});

// Create a note
const { id, keyFragment } = await client.createNote({ text: "Hello, World!" });
const shareUrl = client.buildShareUrl(id, keyFragment);

// Read a note from a share URL
const parsed = SecretClient.parseShareUrl(shareUrl);
const { payload } = await client.readNote(parsed.id, parsed.keyFragment);
console.log(payload.text); // "Hello, World!"

How It Works

Browser                                  Server
┌──────────────────────┐            ┌──────────────────┐
│ 1. Generate key      │            │                  │
│ 2. Encrypt (XChaCha) │──blob──►   │ 3. Encrypt (AES) │
│                      │            │ 4. Store          │
│ URL: /note/id#key    │            │                  │
│        └─ never sent │            │ Never sees key   │
└──────────────────────┘            └──────────────────┘

The encryption key lives in the URL fragment (#key), which browsers never send to the server.

Configuration

All settings via environment variables. See .env.example for the full list.

Variable Default Description
SERVER_ENCRYPTION_KEY Required. AES-256-GCM key (32 bytes, base64)
APP_NAME Secret Application name
APP_URL http://localhost:3000 Public URL
APP_PRIMARY_COLOR #6366f1 Brand color
MAX_FILE_SIZE 10485760 Max file size in bytes (10 MB)
MAX_FILES_PER_NOTE 10 Max files per note
MAX_EXPIRY 604800 Max expiry in seconds (default: 7 days, max: 30 days)
API_KEY API key for SDK clients (optional)
API_KEY_1, API_KEY_2 Multiple API keys (optional)
CHUNK_SIZE 4194304 Chunk size for large uploads (4 MB)
MAX_CHUNKED_FILE_SIZE 524288000 Max chunked upload size (500 MB)
PORT 3000 Server port

Warning: Never change SERVER_ENCRYPTION_KEY after deployment — all existing notes become unreadable.

S3 Storage (optional)

Files are stored locally by default. For larger files, enable S3-compatible storage:

STORAGE_BACKEND=s3
S3_BUCKET=my-bucket
S3_REGION=us-east-1
S3_ENDPOINT=http://minio:9000    # MinIO / R2
S3_ACCESS_KEY_ID=your-key
S3_SECRET_ACCESS_KEY=your-secret
S3_FORCE_PATH_STYLE=true         # Required for MinIO
MAX_FILE_SIZE=104857600           # 100 MB

Compatible with AWS S3, MinIO, and Cloudflare R2.

Updating

docker compose pull           # Pull new image
docker compose up -d          # Restart
docker image prune -f         # Clean up

Data lives in a Docker volume — updates never delete your notes.

Reverse Proxy

Caddy (automatic HTTPS):

secret.example.com {
    reverse_proxy localhost:3000
}

Nginx:

server {
    listen 443 ssl;
    server_name secret.example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        client_max_body_size 600M;
    }
}

Set client_max_body_size to match MAX_CHUNKED_FILE_SIZE (or MAX_FILE_SIZE if chunked uploads are not used).

Development

pnpm install
pnpm dev          # API + web dev servers
pnpm test         # 422 tests, 100% backend coverage
pnpm lint         # Biome lint + format
pnpm build        # Production build
pnpm typecheck    # TypeScript strict

Structure

apps/api/         Hono API (Node.js, SQLite, Drizzle ORM, OpenAPI)
apps/web/         SvelteKit frontend (Svelte 5, Tailwind CSS 4)
packages/sdk-js/  JS/TS SDK (SecretClient, encrypt/decrypt flows)
packages/crypto/  libsodium + AES-256-GCM encryption
packages/shared/  Zod schemas, types, constants, crypto test vectors
messages/         i18n (10 languages)

Security

Layer Details
Client encryption XChaCha20-Poly1305 (192-bit nonce, AEAD)
Server encryption AES-256-GCM (defense-in-depth)
Password KDF Argon2id (64 MiB, 3 iterations)
Write auth PoW (Cap.js SHA-256) for browser, API keys for SDK
Token comparison Timing-safe (crypto.timingSafeEqual)
Key hygiene Zeroed after use (sodium.memzero)
Privacy No IP logging, no cookies, no tracking
Database SQLite secure_delete, WAL mode
Docker Non-root, read-only filesystem, dropped capabilities
HTTP Strict CSP, HSTS (preload), Permissions-Policy, per-IP rate limiting
Storage Path traversal protection, S3 key validation
Validation Zod schemas with max length constraints

See SECURITY.md for the vulnerability disclosure policy.

License

MIT

Contributing

See CONTRIBUTING.md.

Top categories

Loading Svelte Themes