DShare is a minimal, keyboard-first file/text sharing tool built with Django.
Docs: https://docs.dshare.me
You don’t “navigate” the UI: you type keywords.
- Type
divya→ upload a file or paste clipboard text - Type
moti→ download the latest file or view the latest text
Those two keywords are treated as global aliases and are intended to never change.
This repo also adds optional accounts (one-time email verification + 30‑day session retention) with passkeys (WebAuthn) preferred and password (required) + optional PIN as a fallback.
- What this is
- How it feels to use
- Commands
- Public vs Private mode
- Security model (read this)
- Retention & cleanup
- Local setup
- Email setup (Resend SMTP)
- Passkeys (WebAuthn)
- Production checklist
- GitHub Pages & Wiki
DShare is for “I need to move a file / a chunk of text between two devices right now”:
- phone ↔ laptop
- home PC ↔ work PC
- VM ↔ host
- any two devices that can open a URL
It’s intentionally not a chat app, not a drive, and not end‑to‑end encrypted storage.
It has one core idea:
The homepage is a blank-ish screen where typing triggers actions.
- Open the site on device A.
- Type
divya→ choose a file (or type/pasteto upload clipboard text). - Open the site on device B.
- Type
moti→ it downloads the latest public file or shows the latest public text.
- Type
/registerand enter your email + password (and optional PIN fallback). - Click the verification email link once (verifies + logs in).
- From then on, just open the site and type
divya/moti.
If you ever get logged out (new browser/device), type /login:
- tries passkey first (if you added one)
- falls back to email + password/PIN
All commands are typed directly on the home page (no input fields).
| Command | What it does |
|---|---|
divya |
Reveals the upload section |
moti |
Downloads/views the latest stored file/text |
/register |
Create account (email verification sent) |
/login |
Log in (passkey first; password/PIN fallback) |
/logout |
Log out |
/passkey |
Add a passkey (requires you to be logged in + email verified) |
/paste |
Upload clipboard text |
/copy |
Copy latest stored text to clipboard |
/clear |
Clear stored file/text (and attempts to clear clipboard) |
/status (or /me) |
Tells you if you’re in public or private mode |
/help |
Quick cheat sheet |
Notes:
- Commands can also start with
\(alias). - If you see a faint
PUBLICwatermark, you’re not logged in (public mode).
DShare has two “lanes” that share the exact same divya/moti muscle memory:
- Anyone who can reach your URL can overwrite the public slot.
- Useful for quick, no-login transfers.
- Dangerous if deployed to the open internet without constraints.
Public defaults:
- shorter retention (default 1 day)
- smaller upload limit (default 10 MB)
- simple per-IP throttling (to reduce abuse)
- Each verified user gets a private “slot”.
- Retention defaults to 30 days.
- Still not “secure storage” — it’s a convenience slot for recent transfer.
- No password needed for everyday use: once verified, you typically stay logged in for ~30 days (sliding session).
- Passkeys are the primary login method: phishing-resistant and easy.
- Private shares are isolated per user.
- Server operator can see your uploads/text (this is not end‑to‑end encrypted).
- Public mode is world-writable.
- The “latest upload wins” — there’s no history UI by design.
If you run DShare on a public URL, anonymous users can:
- upload illegal or malicious files
- overwrite your public slot
- attempt denial-of-service (upload spam, clear spam)
Mitigations you can enable even while remaining “public”:
- keep the upload size limit small (
DSHARE_PUBLIC_MAX_UPLOAD_BYTES) - keep public retention short (
DSHARE_PUBLIC_TTL_SECONDS) - keep rate limits low (
DSHARE_PUBLIC_UPLOAD_LIMIT,DSHARE_PUBLIC_CLEAR_LIMIT) - deploy behind Cloudflare / WAF / rate limiting
- restrict access by IP if you can (VPN, Tailscale, allowlists)
If you truly need “public for everyone in the world”, treat it like running a public pastebin/filedrop: you’ll need strong external controls.
- Public data is auto-expired after
DSHARE_PUBLIC_TTL_SECONDS(default 1 day). - Private data is auto-expired after
DSHARE_USER_TTL_SECONDS(default 30 days). - Expiry happens lazily (on access/upload) to avoid cron requirements.
/clearclears the current lane (public if logged out, private if logged in).
- Python 3.10+
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserverOpen http://localhost:8000/ (prefer localhost over 127.0.0.1 if you plan to test passkeys).
This repo loads .env automatically in dshare/settings.py, for local/dev convenience.
Copy .env.example to .env and fill values as needed.
If you have Resend and a sender like do-not-reply@divyeshvishwakarma.com:
- In Resend, verify your domain
divyeshvishwakarma.com(DNS DKIM/SPF as Resend shows). - Create a Resend API key.
- Set env vars (example):
DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.resend.com
EMAIL_PORT=587
EMAIL_USE_TLS=1
EMAIL_HOST_USER=resend
EMAIL_HOST_PASSWORD=YOUR_RESEND_API_KEY
DEFAULT_FROM_EMAIL=do-not-reply@divyeshvishwakarma.comThen type /register on the home page.
Email styling note: the verification email HTML is intentionally “stealth” (black-on-black) to match the theme. Some email clients override colors; the link should still be clickable (the message body is one big link).
Passkeys are the preferred login method:
- phishing-resistant
- no password reuse
- fast login on new devices
- HTTPS in production
- for local dev:
http://localhostis treated as a secure context by most browsers
WebAuthn binds credentials to an RP ID (domain). Set:
DSHARE_RP_ID=yourdomain.comLocally, use DSHARE_RP_ID=localhost and open http://localhost:8000/.
- After verifying email, type
/passkeyonce to register a passkey. - Later,
/loginwill try passkey first automatically.
If you deploy DShare publicly, do these first:
- run behind HTTPS
- set a real
SECRET_KEY - set
DEBUG=False - set
ALLOWED_HOSTSto your domain(s) - consider secure cookie flags:
SESSION_COOKIE_SECURE=TrueCSRF_COOKIE_SECURE=TrueSESSION_COOKIE_SAMESITE='Lax'(or'Strict'depending on your needs)
- configure static files for Django (
collectstatic) if you serve your own assets - decide whether you truly want public mode enabled
Also consider putting DShare behind a reverse proxy (nginx) and/or Cloudflare for rate limiting.