Darkdot Files (beta)

Encrypted-only file storage.

For high-stakes enterprise storage, journalists, and activists. Darkdot Files accepts PGP-encrypted uploads and stores them by ciphertext hash. We never see your plaintext or filenames.

Create account →Sign in

How it works

Anonymous by default
A random 16-character account number and a password are your identity. No email or name required. Add a PGP key later for two-factor sign-in.
Encrypted uploads only
Every object must be OpenPGP ciphertext. The server inspects the packet structure without decrypting and refuses plaintext before it is stored.
Content-addressed
Objects are named by the SHA-256 of their ciphertext. Identical uploads deduplicate to the same object.
HTTP and S3
A Bearer-token HTTP API, plus an S3 SigV4 subset for rclone.
Pay with privacy
Top up a credit balance with Monero (XMR); usage deducts from it.
Tor
The console is also an onion service: http://k5git7r4pyskwl7qt6iobl4cvattn7tdhrpzmzl3ubkyuwpzbkbeanqd.onion

Quick Start

  1. Create an account and save your account number

    Create an account with a password. You get a 16-character account number - the only identifier we issue. Store it like a password; it can't be recovered.

  2. Generate a PGP keypair

    All uploads must be encrypted to a key you control. We never see your private key.

    gpg --full-generate-key
    gpg --armor --export your@email > pubkey.asc
  3. Create a bucket and an access key

    In the console, create a bucket on the R2 or S3 backend, then mint an access key. The sk_… secret is shown once; the DDF… key id names the key. The bucket's backend selects its data endpoint.

    export DDF_ENDPOINT=https://r2.files.darkdot.com   # R2 buckets
    # export DDF_ENDPOINT=https://s3.files.darkdot.com  # S3 buckets
    export DDF_BUCKET=<your-bucket-name>
    export DDF_SECRET=sk_…
  4. Encrypt and upload

    POST the encrypted file to the bucket. The server hashes the ciphertext and returns the canonical URL in the Location header - you never compute SHA-256 yourself. The console shows each backend's maximum object size.

    curl
    gpg --encrypt --recipient your@email file.pdf
    
    curl -X POST \
      -H "Authorization: Bearer $DDF_SECRET" \
      --data-binary @file.pdf.gpg \
      "$DDF_ENDPOINT/$DDF_BUCKET"
    
    # → 201 Created
    # Location: $DDF_ENDPOINT/$DDF_BUCKET/3a7bd2f9c8e1...d4f0
    python
    import os, requests
    
    with open('file.pdf.gpg', 'rb') as f:
        r = requests.post(
            os.environ['DDF_ENDPOINT'] + '/' + os.environ['DDF_BUCKET'],
            headers={'Authorization': 'Bearer ' + os.environ['DDF_SECRET']},
            data=f,
        )
    print(r.headers['Location'])  # canonical URL
    rclone
    # ~/.config/rclone/rclone.conf
    [darkdot]
    type = s3
    provider = Other
    # endpoint for S3 buckets: https://s3.files.darkdot.com
    endpoint = https://r2.files.darkdot.com
    access_key_id = DDF…
    secret_access_key = sk_…
    
    # multipart upload is unsupported; this forces a single-part PUT
    rclone --s3-upload-cutoff 5G copy file.pdf.gpg darkdot:<your-bucket-name>/
  5. Download and decrypt

    curl -H "Authorization: Bearer $DDF_SECRET" \
      -o file.pdf.gpg "$DDF_ENDPOINT/$DDF_BUCKET/<sha256>"
    
    gpg --decrypt file.pdf.gpg > file.pdf