A high-performance, production-ready IMAP server powering Cybertemp's email retrieval infrastructure. Built with Rust for speed, security, and reliability.
💬 Discord
·
📜 ChangeLog
·
- About
- Architecture
- Features
- Prerequisites
- Installation
- Configuration
- Database Setup
- Running the Server
- IMAP Commands
- Authentication
- Important Notes
- Troubleshooting
- Development Status
- ChangeLog
THIS README IS ENTIRLY GENERATED BY AI I DID NOT WRITE THIS SHI
This is the actual IMAP server currently powering Cybertemp - enabling users to access their temporary and private emails through standard IMAP clients like Thunderbird, Apple Mail, Outlook, and mobile email apps.
This server is designed as a secure IMAP retrieval server that:
- Listens on port 143 (standard IMAP) or custom port
- Authenticates users via token-based authentication with Supabase
- Retrieves emails from PostgreSQL database
- Supports subscription-based access control
- Implements domain whitelisting and email/sender banning
- Handles CREATE/DELETE inbox operations
- Provides FETCH, SEARCH, LIST, and SELECT commands
- Built with async Tokio for concurrent connections
- Filters banned senders in real-time
┌─────────────────────┐
│ IMAP Client │
│ (Thunderbird, │
│ Apple Mail, etc) │
└──────────┬──────────┘
│ Port 143
▼
┌───────────────────────────────────────┐
│ Rust IMAP Server (Tokio) │
│ - Token authentication │
│ - Subscription validation │
│ - Concurrent connection handler │
│ - Email retrieval & filtering │
│ - Inbox management (CREATE/DELETE) │
│ - Ban list enforcement │
└──────────┬────────────────────────────┘
│
├──────────────┬──────────────┐
▼ ▼ ▼
┌──────────────┐ ┌────────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Supabase │ │ Caching │
│ (Emails, │ │ - Auth │ │ (Domains) │
│ Inboxes, │ │ - Tokens │ │ │
│ Domains, │ │ - Users │ │ │
│ Bans) │ │ │ │ │
└──────────────┘ └────────────────┘ └─────────────┘
Self-hosted mode: When USE_SUPABASE_BANS=false and USE_SUPABASE_DOMAINS=false, PostgreSQL handles domains and bans locally. Inboxes are always stored in PostgreSQL.
- ✅ Production-Ready: Currently serving Cybertemp's IMAP infrastructure
- ✅ Token-Based Auth: Secure authentication using IMAP tokens
- ✅ Subscription Control: Validates active subscriptions before access
- ✅ Standard IMAP Commands: LOGIN, LIST, SELECT, FETCH, SEARCH, STATUS, NOOP, LOGOUT
- ✅ Inbox Management: CREATE and DELETE mailboxes via IMAP
- ✅ Domain Validation: Cached domain checking with 60s refresh
- ✅ Ban System: Real-time sender/email/domain banning with exact/contains matching
- ✅ Multipart MIME: Proper text extraction from multipart messages
- ✅ Quoted-Printable: Automatic decoding of encoded content
- ✅ Concurrent Connections: Async Tokio runtime for multiple clients
- ✅ PostgreSQL Storage: Reliable email retrieval with indexing
- ✅ Supabase Integration: Auth, users, domains, bans (optional for self-hosting) (optional for self-hosting)
- ✅ Self-hosted Mode: Optional PostgreSQL-only operation without external dependencies
- ✅ HTTP Request Filtering: Rejects HTTP requests to IMAP port
- ✅ Subdomain Support: Create inboxes on parent and child domains
- ✅ Graceful Error Handling: Detailed logging with tracing
- Rust 1.70+ (for compilation)
- PostgreSQL 12+ (for email storage and optional self-hosted features)
-
- Supabase Account (optional, for authentication and advanced features)
- Linux/Windows/macOS (any platform supporting Rust)
- Port 143 Access (standard IMAP port, or use custom port)
-
Install Rust (if not already installed):
# Linux/macOS curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Windows # Download from https://rustup.rs/
-
Clone the repository:
git clone https://github.com/sexfrance/imap-server.git cd imap-server -
Build the project:
# Development build cargo build # Production build (optimized) cargo build --release
Create a .env file in the project root with the following variables:
# PostgreSQL Database (REQUIRED)
DATABASE_URL=postgresql://admin:admin@localhost:5432/cybertemp
# Supabase Configuration (OPTIONAL - set to false for self-hosted)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your-service-role-key-here
# Self-hosted toggles (OPTIONAL - default true for backward compatibility)
USE_SUPABASE_BANS=true
USE_SUPABASE_DOMAINS=true
# IMAP Server Settings (OPTIONAL)
IMAP_BIND=0.0.0.0:143 # Default: 0.0.0.0:143| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
✅ Yes | PostgreSQL connection string for retrieving emails |
SUPABASE_URL |
❌ No | Your Supabase project URL (optional) |
SUPABASE_SERVICE_KEY |
❌ No | Supabase service role key (optional) |
USE_SUPABASE_BANS |
❌ No | Use Supabase for bans (default: true) |
USE_SUPABASE_DOMAINS |
❌ No | Use Supabase for domains (default: true) |
IMAP_BIND |
❌ No | IMAP bind address (default: 0.0.0.0:143) |
Run the SQL schema located in SQL/schema.sql:
psql -U postgres < SQL/schema.sqlThis creates:
emailstable with columns:id(UUID primary key)mailbox_owner(email address owning the inbox)mailbox(mailbox name, default 'INBOX')subject,body,htmlfrom_addr,to_addrstimestamp,flags,sizecreated_at
inboxtable (for self-hosted inbox management):id(UUID primary key)email_address(TEXT, unique)user_id(UUID, nullable)created_at(TIMESTAMPTZ)
domainstable (for self-hosted domain management):id(UUID primary key)domain(TEXT)user_id(UUID)active(BOOLEAN)cloudflare_domain(BOOLEAN)created_at(TIMESTAMPTZ)
banstable (for self-hosted ban management):id(UUID primary key)scope('email', 'domain', 'sender')value(TEXT)match_type('exact' or 'contains')status('active' or 'inactive')created_at(TIMESTAMPTZ)
- Indexes on
mailbox_owner,mailbox,timestamp, and other tables
If using Supabase for advanced features (USE_SUPABASE_*=true), create these tables in your Supabase project:
CREATE TABLE imap_tokens (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
expires_at TIMESTAMPTZ NOT NULL,
last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_imap_tokens_token ON imap_tokens(token);
CREATE INDEX idx_imap_tokens_user_id ON imap_tokens(user_id);CREATE TABLE users (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
subscription_status TEXT DEFAULT 'inactive', -- 'active', 'trialing', 'past_due', 'inactive'
subscription_end_date TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);CREATE TABLE domains (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
domain TEXT NOT NULL,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
active BOOLEAN DEFAULT true,
cloudflare_domain BOOLEAN DEFAULT false, -- Global domain accessible by all users
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_domains_user_id ON domains(user_id);
CREATE INDEX idx_domains_domain ON domains(domain);CREATE TABLE inbox (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email_address TEXT NOT NULL UNIQUE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_inbox_user_id ON inbox(user_id);
CREATE INDEX idx_inbox_email ON inbox(email_address);CREATE TABLE bans (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
scope TEXT NOT NULL, -- 'email', 'domain', 'sender'
value TEXT NOT NULL, -- email address, domain, or sender pattern
match_type TEXT DEFAULT 'exact', -- 'exact' or 'contains'
status TEXT DEFAULT 'active', -- 'active' or 'inactive'
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_bans_scope_status ON bans(scope, status);Example bans:
-- Block specific sender
INSERT INTO bans (scope, value, match_type, status) VALUES
('sender', 'spammer@example.com', 'exact', 'active');
-- Block any sender containing 'spam'
INSERT INTO bans (scope, value, match_type, status) VALUES
('sender', 'spam', 'contains', 'active');
-- Block entire domain
INSERT INTO bans (scope, value, match_type, status) VALUES
('domain', 'spamhaus.com', 'exact', 'active');
-- Block specific email address from being created
INSERT INTO bans (scope, value, match_type, status) VALUES
('email', 'admin@yourdomain.com', 'exact', 'active');cargo runcargo build --release
./target/release/imap-serverPort 143 requires root privileges:
# Option 1: Run as root
sudo ./target/release/imap-server
# Option 2: Grant port binding capability (recommended)
sudo setcap CAP_NET_BIND_SERVICE=+eip ./target/release/imap-server
./target/release/imap-server
# Option 3: Use alternative port
IMAP_BIND=0.0.0.0:1143 ./target/release/imap-serverCreate /etc/systemd/system/cybertemp-imap.service:
[Unit]
Description=Cybertemp IMAP Server
After=network.target postgresql.service
[Service]
Type=simple
User=your-user
WorkingDirectory=/path/to/imap-server
EnvironmentFile=/path/to/imap-server/.env
ExecStart=/path/to/imap-server/target/release/imap-server
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable cybertemp-imap
sudo systemctl start cybertemp-imap
sudo systemctl status cybertemp-imap| Command | Description | Example |
|---|---|---|
CAPABILITY |
List server capabilities | CAPABILITY |
LOGIN |
Authenticate with email and token | LOGIN user@domain.com your-imap-token |
LIST |
List all mailboxes (inboxes) for authenticated user | LIST "" "*" |
SELECT |
Select a mailbox to work with | SELECT inbox@domain.com |
FETCH |
Retrieve email content | FETCH 1 (BODY[]) |
SEARCH |
Search emails in selected mailbox | SEARCH ALL |
STATUS |
Get mailbox statistics | STATUS "inbox@domain.com" (MESSAGES) |
CREATE |
Create a new inbox | CREATE newbox@domain.com |
DELETE |
Delete an inbox | DELETE oldbox@domain.com |
NOOP |
No operation (keep-alive) | NOOP |
LOGOUT |
Close connection | LOGOUT |
# Fetch entire message body
FETCH 1 (BODY[])
# Fetch text part only (peek without marking as seen)
FETCH 1 (BODY.PEEK[TEXT])
# Fetch multiple messages
FETCH 1:5 (BODY[])
# Fetch all messages
FETCH 1:* (BODY[])
# Search all emails
SEARCH ALL
# Search by sender
SEARCH FROM "sender@example.com"
# Search by subject
SEARCH SUBJECT "Important"
- User generates IMAP token in Cybertemp web interface
- Token is stored in Supabase
imap_tokenstable with expiration - User connects via IMAP client using:
- Username:
user@example.com - Password:
generated-imap-token
- Username:
- Server validates:
- Token exists and hasn't expired
- Token belongs to the provided email
- User has active subscription (
active,trialing, orpast_duewith future end date)
- Access granted if all checks pass
- Last used timestamp updated on successful login
The IMAP server enforces subscription checks:
- ✅ Active: Full access
- ✅ Trialing: Full access (trial period)
⚠️ Past Due: Access granted ifsubscription_end_dateis in the future- ❌ Inactive/Canceled: Access denied
Users must have an active subscription to use IMAP.
- Users can only create inboxes on domains they own (linked via
user_idindomainstable) - Cloudflare domains (
cloudflare_domain = true) are accessible by all users - Subdomains are supported: If user owns
example.com, they can createanything@sub.example.com - Domains must be active (
active = true) to allow inbox creation - Domain cache refreshes every 60 seconds for performance
The server implements a 3-tier ban system:
- Email bans (
scope=email): Prevents specific email addresses from being created - Domain bans (
scope=domain): Blocks mailbox creation on banned domains - Sender bans (
scope=senderor legacyscope=email): Filters emails from banned senders in FETCH/SEARCH
Match types:
exact: Must match exactly (case-insensitive)contains: Blocks if value is substring of sender/email/domain
Legacy support: Old format contains:value in ban value is automatically normalized.
- ✅ HTTP request rejection: Rejects HTTP requests sent to IMAP port
- ✅ Echoed response filtering: Ignores client-echoed server responses
- ✅ Subscription validation: Enforces active subscription requirement
- ✅ Token expiration: Tokens have expiration dates
- ✅ User isolation: Users can only see their own inboxes
- ✅ Domain ownership: Users can only create inboxes on their domains
- ✅ Real-time bans: Sender bans applied during email retrieval
- Connection pooling: PostgreSQL connection pool for efficiency
- Domain caching: 60-second cache to reduce Supabase queries
- Concurrent handling: Tokio async runtime for multiple simultaneous clients
- Indexed queries: Database indexes on
mailbox_owner,timestamp
Check:
# Verify PostgreSQL is running
systemctl status postgresql # Linux
# or
pg_ctl status # Windows/macOS
# Test connection
psql -U admin -d cybertemp -c "SELECT 1;"
# Verify DATABASE_URL format
echo $DATABASE_URL # Should be: postgresql://user:pass@host:port/dbnameSolution:
- Generate a new token in Cybertemp web interface
- Check
imap_tokenstable for token existence andexpires_at - Verify token hasn't been deleted
-- Check token in Supabase
SELECT * FROM imap_tokens WHERE token = 'your-token';Check:
-- Verify user subscription status
SELECT u.id, u.subscription_status, u.subscription_end_date, au.email
FROM users u
JOIN auth.users au ON u.id = au.id
WHERE au.email = 'user@example.com';Fix: Update subscription status to active or trialing:
UPDATE users SET subscription_status = 'active' WHERE id = 'user-uuid';Check:
-- Verify domain exists and is active
SELECT * FROM domains WHERE domain = 'yourdomain.com';Fix: Add domain or activate it:
-- Add domain
INSERT INTO domains (domain, user_id, active, cloudflare_domain)
VALUES ('yourdomain.com', 'user-uuid', true, false);
-- Or activate existing domain
UPDATE domains SET active = true WHERE domain = 'yourdomain.com';Solution:
# Linux: Grant capability
sudo setcap CAP_NET_BIND_SERVICE=+eip ./target/release/imap-server
# Or use non-privileged port
export IMAP_BIND=0.0.0.0:1143
./target/release/imap-serverCheck:
- User is authenticated correctly
inboxtable has entries for the user:
SELECT * FROM inbox WHERE user_id = 'user-uuid';- Create an inbox manually:
INSERT INTO inbox (email_address, user_id)
VALUES ('test@yourdomain.com', 'user-uuid');Check:
- Emails exist in PostgreSQL for the selected mailbox:
SELECT COUNT(*) FROM emails WHERE mailbox_owner = 'inbox@domain.com';- Check if sender is banned:
SELECT * FROM bans WHERE scope IN ('sender', 'email') AND status = 'active';Solution: Check if email address is explicitly banned:
SELECT * FROM bans WHERE scope = 'email' AND value = 'your-inbox@domain.com';Remove the ban if necessary:
DELETE FROM bans WHERE id = 'ban-uuid';
-- or deactivate
UPDATE bans SET status = 'inactive' WHERE id = 'ban-uuid';RUST_LOG=debug ./target/release/imap-serverThis provides detailed logs for:
- Authentication attempts
- Token validation
- Subscription checks
- Domain lookups
- Email retrieval
- Ban filtering
| Component | Status | Notes |
|---|---|---|
| Rust Implementation | ✅ Production | Actively maintained |
| Token Authentication | ✅ Production | Stable and secure |
| Subscription Validation | ✅ Production | Enforces active subscriptions |
| PostgreSQL Storage | ✅ Production | Stable and indexed |
| Supabase Integration | ✅ Production | Auth, users, domains, inboxes, bans |
| Domain Caching | ✅ Production | 60s refresh cycle |
| Ban System | ✅ Production | Email, domain, sender filtering |
| IMAP Commands | ✅ Production | LOGIN, LIST, SELECT, FETCH, SEARCH, etc |
| Inbox Management | ✅ Production | CREATE/DELETE via IMAP |
| MIME Parsing | ✅ Production | Multipart, quoted-printable support |
| Concurrent Connections | ✅ Production | Tokio async runtime |
| Documentation | ✅ Complete | You're reading it! |
v0.1.0 ⋮ 11/01/2025
! Initial production release for Cybertemp IMAP
+ Token-based authentication with Supabase
+ Subscription validation (active, trialing, past_due)
+ Domain validation with caching (60s refresh)
+ Ban system (email, domain, sender with exact/contains matching)
+ IMAP commands: LOGIN, LIST, SELECT, FETCH, SEARCH, STATUS
+ Inbox management: CREATE/DELETE via IMAP
+ Multipart MIME parsing with text extraction
+ Quoted-printable decoding
+ Subdomain support for inbox creation
+ HTTP request filtering
+ PostgreSQL email storage with indexing
+ Concurrent connection handling with Tokio
+ Real-time sender ban filtering in FETCH/SEARCH
+ Last used timestamp tracking for tokens
+ Graceful error handling and logging
+ Self-hosted mode support (optional PostgreSQL-only operation)- Discord: discord.cyberious.xyz
- Email: support@cybertemp.xyz
- Issues: GitHub Issues
MIT License - See LICENSE file for details
- Built with ❤️ for the Cybertemp community
- Powered by Rust 🦀, Tokio, PostgreSQL, and Supabase
- IMAP RFC 3501 compliant (subset)
Currently powering Cybertemp's IMAP infrastructure 📬