Skip to content

dele-to/dele-to

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

DELE.TO πŸ”₯ License: MIT Buy me a Coffee

DELETO - Share credentials securely with E2E encryption | Product Hunt

From Latin dΔ“lΔ“tō β€” "erase, destroy."

Secure credential sharing with client-side AES-256 encryption, zero-knowledge architecture, and automatic self-destruction. Alternative to PasswordPusher, Yopass and Bitwarden Send

DELE.TO is a modern, secure platform for sharing sensitive information like passwords, API keys, and credentials. Built with Next.js 14 and featuring client-side encryption, your data is encrypted in your browser before it ever reaches server.

πŸ”— https://dele.to β€” Try it instantly in your browser. No signup required.

πŸ“‹ Table of Contents

✨ Features

  • πŸ” Client-side AES-256-GCM encryption - Data encrypted in your browser
  • πŸ”‘ Zero-knowledge architecture - Server never sees your encryption keys
  • ⏰ Auto-expiration - Set custom expiration times (15m to 7 days)
  • πŸ‘οΈ View limits - Burn-after-reading with configurable view counts
  • πŸ”’ Optional password protection - Add extra security layer
  • 🌐 URL fragment keys - Encryption keys never sent to server
  • πŸ’Ύ Redis storage - Encrypted data with automatic TTL cleanup
  • πŸ“± Responsive design - Works on all devices

πŸ“Έ Screenshots

Create a new share View a share Share created confirmation

πŸš€ Quick Start

Prerequisites

  • Node.js 20+ - Download here
  • pnpm (recommended) - Install with npm install -g pnpm
  • Git - For cloning the repository

Optional for Production:

  • Redis - For production scaling (uses local file storage by default)

Step-by-Step Setup

1. Clone and Navigate

git clone https://github.com/dele-to/dele-to.git
cd dele-to

2. Install Dependencies

# Using pnpm (recommended)
pnpm install

# Or using npm
npm install

# Or using yarn
yarn install

3. Environment Configuration (Optional for Development)

For Development: No configuration needed! The app works out of the box using local file storage.

For Production:

Deploy with Vercel

Create your environment file for Redis storage:

cp .env.example .env

Edit .env with your configuration:

# Redis Configuration (Required for production)
KV_REST_API_URL=your_redis_url_here
KV_REST_API_TOKEN=your_redis_token_here

# Security Salt (Change this in production!)
SALT=your-super-secret-salt-change-me-in-production

πŸ’‘ Development Note: The app automatically uses local file storage (./secure-shares/ directory) when Redis is not configured, making it perfect for development and testing without any setup.

4. Start Development Server

pnpm dev

The app will be available at http://localhost:3000

5. Test the Application

  1. Open http://localhost:3000 in your browser
  2. Click "Share Securely" to create a test share
  3. Enter some test data and create a share
  4. Copy the generated link and test viewing it

Running with Docker

Manual Setup (without Redis - local file storage)

docker build -t dele-to .
## This persists the shares data to your host machine (as Redis is not used)
docker run -p 3000:3000 -v "$(pwd)/.secure-shares:/app/.secure-shares" --name dele-to dele-to

To run with Redis (recommended for production):

docker compose up --build -d

The application will be available at http://localhost:3000.

πŸ—οΈ Architecture Overview

πŸ—οΈ Architecture Overview (Click to expand)

DELE.TO uses a zero-knowledge architecture where encryption happens entirely client-side:

πŸ“Š System Architecture Diagram (Click to expand)
graph TB
    subgraph "Client Browser"
        A[User Input] --> B[Generate AES-256 Key]
        B --> C[Generate Random IV]
        C --> D[Encrypt Data Client-Side]
        D --> E[Base64 Encode]
        E --> F[Send to Server]
        
        subgraph "URL Fragment"
            G[Encryption Key]
            G -.->|Never sent to server| H[URL Hash #key]
        end
        
        B --> G
    end
    
    subgraph "Server (Zero-Knowledge)"
        F --> I[Receive Encrypted Data]
        I --> J[Store in Redis/File]
        J --> K[Generate Share ID]
        K --> L[Return Share URL]
        
        subgraph "Storage"
            M[(Redis/File System)]
            N[Encrypted Content]
            O[IV]
            P[Metadata]
            J --> M
            M --> N
            M --> O  
            M --> P
        end
    end
    
    subgraph "Recipient Browser"
        L --> Q[Access Share URL]
        Q --> R[Extract Key from URL Fragment]
        R --> S[Fetch Encrypted Data]
        S --> T[Decrypt Client-Side]
        T --> U[Display Plaintext]
        
        subgraph "Auto-Destruction"
            V[View Count Check]
            W[TTL Expiration]
            X[Delete from Storage]
            S --> V
            V --> W
            W --> X
        end
    end
    
    subgraph "Security Features"
        Y[AES-256-GCM]
        Z[Zero-Knowledge]
        AA[URL Fragment Keys]
        BB[Auto-Expiration]
        CC[View Limits]
        DD[Optional Password]
    end
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style D fill:#f3e5f5
    style G fill:#ffebee
    style I fill:#e8f5e8
    style M fill:#fff3e0
    style R fill:#f3e5f5
    style T fill:#f3e5f5
    style X fill:#ffebee
Loading

πŸ” Encryption & Decryption Process

πŸ” Encryption & Decryption Process (Click to expand)

1) Key & IV Generation (Client-Side)

// AES-256 key
const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]) 
// 96-bit IV (12 bytes)
const iv = crypto.getRandomValues(new Uint8Array(12))
  • Keys never leave the browser. Fresh IV per encryption. IV is not secret.

2) Encrypt

const encoder = new TextEncoder()
const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, encoder.encode(plaintext))
  • AES-256-GCM provides confidentiality + authentication (integrity).

3) Encode for Transport

const encryptedContent = btoa(String.fromCharCode(...new Uint8Array(encrypted)))
const ivString = btoa(String.fromCharCode(...iv))
  • Base64 makes binary safe for JSON/HTTP.

4) Decrypt (Recipient Browser)

// fragment contains the raw key material exported earlier (never sent to server)
const keyBytes = /* decode from fragment */
const importedKey = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM" }, true, ["decrypt"]) 

// iv and encryptedContent come from server response
const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, importedKey, encryptedBytes)
const plaintext = new TextDecoder().decode(decrypted)

Notes on encryptedContent and iv

  • encryptedContent: Base64 ciphertext, meaningless without the key.
  • iv: Base64 12-byte IV. Required for decryption; safe to store alongside ciphertext.

Data Flow Example

Data Flow Example (Click to expand) Let's trace what happens when you encrypt "my secret password":

Input: "my secret password"

Step 1: Key Generation

πŸ”‘ AES-256 Key: [32 random bytes] 
   Example: 4a7d1ed414474e4033ac29ccb8653d9b...

Step 2: IV Generation

🎲 IV (12 bytes): [random bytes]
   Example: nb4WF+rLL5dokQnO (base64)

Step 3: Encryption

πŸ“ Plaintext: "my secret password"
πŸ”’ AES-256-GCM Encryption
πŸ“¦ Ciphertext: [encrypted bytes]
   Example: uJN+TP0nsj2oFPRQOS7+tJIwZko= (base64)

Step 4: What Gets Sent to Server

{
  "encryptedContent": "uJN+TP0nsj2oFPRQOS7+tJIwZko=",
  "iv": "nb4WF+rLL5dokQnO",
  "title": "",
  "expirationTime": "1h",
  "maxViews": 1,
  "requirePassword": false,
  "linkType": "standard"
}

Step 5: Share URL Creation

πŸ”— URL: https://dele.to/view/abc123#[base64-encoded-key]
                                      ↑
                              This key NEVER goes to server

πŸ§ͺ Testing

# Run all tests
pnpm test

# Generate coverage report
pnpm test:coverage

# Test specific encryption functions
pnpm test -- --testPathPattern=crypto.test.ts

πŸ“Š For detailed test status and coverage reports, see TEST_STATUS.md

Storage Options

File Storage (Development Default)

  • βœ… No setup required - works out of the box
  • πŸ“ Location: ./secure-shares/ directory (auto-created)
  • 🎯 Perfect for: Development, testing, and local demos
  • ⚠️ Limitations: Not suitable for production scaling or multiple servers

Redis Storage (Production Recommended)

  • πŸ”§ Setup: Add Redis URL and token to .env or Vercel environment variables
  • ☁️ Provider: Upstash (free tier available)
  • πŸš€ Benefits: Automatic TTL, better performance, horizontally scalable

🧰 Development Tips

🧰 Development Tips (Click to expand)
  1. Hot Reload: Changes to code automatically refresh the browser
  2. TypeScript: Full TypeScript support with type checking
  3. Tailwind: Use Tailwind classes for styling
  4. Components: UI components are in components/ui/
  5. Crypto: Encryption logic is in lib/crypto.ts

Troubleshooting

β€’ Module Not Found Errors

# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
pnpm install

β€’ Build Errors

# Clear Next.js cache
rm -rf .next
pnpm build

πŸ› οΈ Tech Stack

  • Framework: Next.js 14 (App Router)
  • Language: TypeScript
  • Styling: Tailwind CSS
  • UI Components: Radix UI + shadcn/ui
  • Database: Redis (Upstash) with file system fallback
  • Encryption: Web Crypto API (AES-256-GCM)
  • Icons: Lucide React
  • Fonts: Geist Sans & Geist Mono

πŸ“ Project Structure

πŸ“ Project Structure (Click to expand)
secure-share-v2/
β”œβ”€β”€ __tests__/                 # Jest tests
β”‚   β”œβ”€β”€ crypto.test.ts
β”‚   └── share-simple.test.ts
β”œβ”€β”€ app/                       # Next.js app directory
β”‚   β”œβ”€β”€ about/
β”‚   β”œβ”€β”€ actions/
β”‚   β”œβ”€β”€ alternatives/
β”‚   β”œβ”€β”€ create/
β”‚   β”œβ”€β”€ frame/
β”‚   β”œβ”€β”€ miniapp/
β”‚   β”œβ”€β”€ view/[id]/
β”‚   β”œβ”€β”€ vs/
β”‚   β”œβ”€β”€ globals.css
β”‚   β”œβ”€β”€ layout.tsx
β”‚   β”œβ”€β”€ not-found.tsx
β”‚   └── page.tsx
β”œβ”€β”€ components/                # React components
β”‚   β”œβ”€β”€ ui/                    # shadcn/ui components
β”‚   β”œβ”€β”€ access-tips.tsx
β”‚   β”œβ”€β”€ console-message.tsx
β”‚   β”œβ”€β”€ farcaster-debug.tsx
β”‚   β”œβ”€β”€ farcaster-provider.tsx
β”‚   β”œβ”€β”€ farcaster-ready.tsx
β”‚   β”œβ”€β”€ footer.tsx
β”‚   β”œβ”€β”€ inline-tip.tsx
β”‚   β”œβ”€β”€ password-input.tsx
β”‚   β”œβ”€β”€ security-tips.tsx
β”‚   └── theme-provider.tsx
β”œβ”€β”€ hooks/
β”‚   β”œβ”€β”€ use-farcaster.ts
β”‚   β”œβ”€β”€ use-mobile.tsx
β”‚   └── use-toast.ts
β”œβ”€β”€ lib/                       # Utility libraries
β”‚   β”œβ”€β”€ crypto.ts              # Encryption utilities
β”‚   β”œβ”€β”€ farcaster.ts
β”‚   └── utils.ts
β”œβ”€β”€ public/                    # Static assets
β”‚   β”œβ”€β”€ .well-known/
β”‚   β”‚   └── farcaster.json
β”‚   β”œβ”€β”€ favicon.ico
β”‚   β”œβ”€β”€ favicon.png
β”‚   └── SEO.png
β”œβ”€β”€ styles/
β”‚   └── globals.css
β”œβ”€β”€ README.md
β”œβ”€β”€ package.json
└── tailwind.config.ts

🀝 Contributing

We welcome contributions! Here's how to get started:

Development Setup

  1. Fork the repository on GitHub
  2. Clone your fork locally
  3. Create a feature branch
    git checkout -b feature/amazing-feature
  4. Make your changes and test thoroughly
  5. Commit with conventional commits
    git commit -m "feat: add amazing feature"
  6. Push to your fork
    git push origin feature/amazing-feature
  7. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

πŸ“ž Support


About

Share sensitive credentials and secrets securely with client-side AES-256 encryption, zero-knowledge architecture, and automatic self-destruction.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages