Skip to content

LeftCurveAeon/fireclaw

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

___________.__               _________ .__
\_   _____/|__|______   ____ \_   ___ \|  | _____ __  _  __
 |    __)  |  \_  __ \_/ __ \/    \  \/|  | \__  \\ \/ \/ /
 |     \   |  ||  | \/\  ___/\     \___|  |__/ __ \\     /
 \___  /   |__||__|    \___  >\______  /____(____  /\/\_/
     \/                    \/        \/          \/

A self-hosted email firewall that sits between your email client and your upstream IMAP/SMTP provider.

TypeScript Node.js Next.js SQLite License: MIT pnpm


FireClaw intercepts every email your client sends or receives. It evaluates each message against a set of configurable rules and blacklist entries, then decides whether to allow, flag, quarantine, or reject it — before it ever reaches your upstream provider or your inbox.

No cloud vendor. No subscription. No data leaving your machine unless you explicitly allow it.

Your email client  →  FireClaw (SMTP :2525 / IMAP :1143)  →  Gmail / Outlook / any IMAP+SMTP provider
                              ↓
                    Rule engine evaluates every message
                              ↓
                 allow  /  flag  /  quarantine  /  reject
                              ↓
                    Admin dashboard at :3000

Table of Contents


Why FireClaw?

Most email filtering solutions are either cloud-based (your mail goes through someone else's servers) or locked inside a mail server you have to run yourself. FireClaw is different:

Feature FireClaw Cloud filters Self-hosted mail server
Works with any existing provider Yes Varies No
Data stays on your machine Yes No Yes
No MX record changes required Yes No No
Admin dashboard included Yes Yes Varies
Regex + keyword rule engine Yes Varies Varies
Inbound + outbound filtering Yes Varies Yes
Zero recurring cost Yes No Yes

You keep your Gmail, Outlook, Fastmail, or any provider you already use. FireClaw acts as a transparent local proxy: your email client talks to it, it talks to your provider.


How It Works

FireClaw exposes two local endpoints that mimic standard email protocols:

Outbound (SMTP firewall) — your email client sends mail to localhost:2525. The firewall parses the message, runs it through your rules, and either forwards it to your upstream SMTP server or quarantines/rejects it.

Inbound (IMAP proxy) — your email client connects to localhost:1143. The proxy authenticates locally, then maintains a live connection to your upstream IMAP server. Quarantined inbound messages are hidden from the client until you release them through the dashboard.

Inbound scanner (IMAP poller) — independently polls your upstream INBOX on a configurable interval. Any message matching a quarantine rule is moved to a dedicated mailbox on the server side, keeping it invisible to your email client entirely.

Rule evaluation

Every message passes through the same evaluator for both inbound and outbound traffic:

  1. Blacklist check — sender or recipient address/domain is compared against the blacklist. A match always results in quarantine.
  2. Rule check — each enabled rule is tested in order. Multiple rules can match; the highest-priority action wins.

Action priority (lowest → highest): allowflagquarantinereject


Quick Start

Prerequisites

npm install -g pnpm

1. Clone the repository

git clone https://github.com/leftcurveaeon/fireclaw.git
cd fireclaw

2. Install dependencies

pnpm install

3. Configure your environment

cp .env.example .env

Open .env and fill in your upstream email provider credentials. Here is a Gmail example:

# Upstream SMTP
UPSTREAM_SMTP_HOST=smtp.gmail.com
UPSTREAM_SMTP_PORT=587
UPSTREAM_SMTP_SECURE=false
UPSTREAM_SMTP_USER=you@gmail.com
UPSTREAM_SMTP_PASS=your-app-password   # Use an App Password, not your account password

# Upstream IMAP
UPSTREAM_IMAP_HOST=imap.gmail.com
UPSTREAM_IMAP_PORT=993
UPSTREAM_IMAP_SECURE=true
UPSTREAM_IMAP_USER=you@gmail.com
UPSTREAM_IMAP_PASS=your-app-password

Gmail users: generate an App Password in your Google account settings. Your regular password will not work with IMAP/SMTP direct access.

4. Start the stack

pnpm dev

This starts both the backend server and the Next.js admin dashboard in watch mode with hot reload.

Service Address
Admin dashboard http://localhost:3000
REST API http://localhost:8787
SMTP firewall localhost:2525
IMAP proxy localhost:1143

5. Point your email client at FireClaw

Update your email client's outgoing and incoming server settings:

Outgoing mail (SMTP)

Setting Value
Server localhost
Port 2525
Security None
Authentication None (or set SMTP_AUTH_USER/SMTP_AUTH_PASS in .env)

Incoming mail (IMAP)

Setting Value
Server localhost
Port 1143
Security None
Authentication None (or set IMAP_AUTH_USER/IMAP_AUTH_PASS in .env)

Your email client will now route all traffic through FireClaw. Open http://localhost:3000 to manage rules and review quarantined messages.


Configuration Reference

All configuration lives in .env at the project root. Most settings can also be updated live from the admin dashboard without restarting.

Ports

Variable Default Description
API_PORT 8787 REST API and admin backend
SMTP_LISTEN_PORT 2525 Local SMTP firewall endpoint
IMAP_PROXY_LISTEN_PORT 1143 Local IMAP proxy endpoint

Feature toggles

Variable Default Description
IMAP_PROXY_ENABLED true Enable the local IMAP proxy
IMAP_POLL_ENABLED true Enable the background IMAP inbox scanner
IMAP_POLL_INTERVAL_SEC 45 How often to scan upstream INBOX (seconds)
QUARANTINE_MAILBOX OC-QUARANTINE Mailbox name created on the upstream server for quarantined inbound messages

Upstream provider

Variable Example Description
UPSTREAM_SMTP_HOST smtp.gmail.com Your provider's SMTP hostname
UPSTREAM_SMTP_PORT 587 SMTP port (587 for STARTTLS, 465 for TLS)
UPSTREAM_SMTP_SECURE false true for port 465 (TLS), false for 587 (STARTTLS)
UPSTREAM_SMTP_USER you@gmail.com SMTP login username
UPSTREAM_SMTP_PASS app-password SMTP password or app password
UPSTREAM_IMAP_HOST imap.gmail.com Your provider's IMAP hostname
UPSTREAM_IMAP_PORT 993 IMAP port
UPSTREAM_IMAP_SECURE true Use TLS for the IMAP connection
UPSTREAM_IMAP_TLS_REJECT_UNAUTHORIZED true Set to false if your environment uses a custom certificate chain (e.g. corporate proxy)
UPSTREAM_IMAP_REJECT_MAILBOX [Gmail]/Trash Mailbox used for hard-rejected inbound messages (provider-specific)
UPSTREAM_IMAP_USER you@gmail.com IMAP login username
UPSTREAM_IMAP_PASS app-password IMAP password or app password

Local authentication (optional)

Leave these blank to allow unauthenticated local connections (appropriate for single-user local use). Set them if you need to restrict who can connect to the local endpoints.

Variable Description
SMTP_AUTH_USER Username required by email clients connecting to the local SMTP port
SMTP_AUTH_PASS Password for the above
IMAP_AUTH_USER Username required by email clients connecting to the local IMAP port
IMAP_AUTH_PASS Password for the above

Web app

Variable Default Description
FRONTEND_ORIGIN http://localhost:3000 Allowed CORS origin for the API
NEXT_PUBLIC_API_BASE http://localhost:8787 API URL used by the Next.js frontend

Rule System

Rules are the core of FireClaw. Each rule specifies what to look for in a message and what to do when it matches.

Rule fields

Field What is checked
from Sender address
to Recipient address(es)
subject Email subject line
body Plain-text and HTML body content
any All of the above simultaneously

Rule actions

Action What happens
allow Message is forwarded normally (default when no rules match)
flag Message is forwarded and recorded in the quarantine log with flagged-forwarded status
quarantine Message is stored locally; not forwarded. Review and release from the dashboard.
reject Message is rejected at the SMTP level with a 554 error. The sender's email client receives a permanent delivery failure.

Pattern matching

Each rule can match using either plain substring search or a full regular expression:

  • Substring match — case-insensitive by default. Set caseSensitive: true to require exact casing.
  • Regex match — full JavaScript RegExp syntax. Invalid patterns are safely skipped (logged as errors, never crash the server).

Example: flag phishing-style keywords

{
  "name": "Phishing keywords",
  "field": "any",
  "pattern": "\\b(verify your account|urgent action required|confirm your identity)\\b",
  "isRegex": true,
  "caseSensitive": false,
  "action": "flag"
}

Example: reject outbound mail to a specific domain

{
  "name": "Block competitor domain",
  "field": "to",
  "pattern": "competitor.com",
  "isRegex": false,
  "caseSensitive": false,
  "action": "reject"
}

Example: quarantine financial scam patterns

{
  "name": "Financial scams",
  "field": "any",
  "pattern": "\\b(wire transfer|bitcoin wallet|crypto investment|guaranteed returns)\\b",
  "isRegex": true,
  "caseSensitive": false,
  "action": "quarantine"
}

Priority and conflicts

When multiple rules match the same message, the highest-priority action wins:

allow (0)  <  flag (1)  <  quarantine (2)  <  reject (3)

A message matching both a flag rule and a quarantine rule will be quarantined.

Default rules

FireClaw ships with a set of example rules (all disabled by default). You can enable them from the dashboard or use them as starting templates:

  • English profanity filter (common words)
  • English profanity filter (obfuscated with spaces/asterisks)
  • Phishing keyword detection
  • Financial scam keyword detection

Blacklist

The blacklist lets you block individual email addresses or entire domains without writing a rule. A blacklist match always results in quarantine.

Block a specific address:

{ "value": "spam@example.com", "type": "email" }

Block an entire domain:

{ "value": "spammy-domain.com", "type": "domain" }

Both inbound (checked by the IMAP poller) and outbound (checked by the SMTP firewall) messages are evaluated against the blacklist. A match on either sender or recipient triggers quarantine.


Quarantine

Quarantined messages are stored in the local SQLite database. The full raw RFC 822 message is preserved, so nothing is lost.

Message statuses

Status Meaning
quarantined Held, not delivered
flagged-forwarded Delivered, but logged for review
released Manually approved by an admin and forwarded to the original destination
discarded Permanently dismissed — not forwarded

Inbound vs outbound

Outbound quarantine — the SMTP firewall catches the message before it reaches your provider. Releasing it re-sends via your upstream SMTP.

Inbound quarantine — the IMAP poller detects the message in your upstream INBOX, moves it to the OC-QUARANTINE mailbox on the server, and hides it from the IMAP proxy so it never appears in your email client. Releasing an inbound message removes it from quarantine so it reappears in your client on the next IMAP sync.


Admin Dashboard

The Next.js dashboard at http://localhost:3000 provides a full management interface:

  • Dashboard — live status of all services (SMTP, IMAP proxy, IMAP poller) and recent quarantined messages
  • Rules — create, edit, enable/disable, and delete filtering rules with a live preview
  • Blacklist — manage blocked addresses and domains with optional notes
  • Quarantine inbox — review held messages, read full content, and release or discard them
  • Settings — update all configuration values at runtime without restarting the server

REST API

The backend exposes a REST API at http://localhost:8787. All endpoints accept and return JSON.

Health & status

GET  /api/health    →  { ok: true, timestamp }
GET  /api/status    →  service status + current config (passwords are redacted)

Rules

GET    /api/rules          →  list all rules
POST   /api/rules          →  create a rule
PUT    /api/rules/:id      →  update a rule (partial updates supported)
DELETE /api/rules/:id      →  delete a rule

Rule object:

{
  "name": "Block Nigerian prince scams",
  "field": "any",
  "pattern": "nigerian prince",
  "isRegex": false,
  "caseSensitive": false,
  "action": "quarantine",
  "enabled": true
}

Blacklist

GET    /api/blacklist       →  list all blacklist entries
POST   /api/blacklist       →  add an entry
DELETE /api/blacklist/:id   →  remove an entry

Blacklist entry object:

{ "value": "spam@example.com", "type": "email", "notes": "Known spam sender" }

type is either "email" (exact address match) or "domain" (matches any address at that domain).

Quarantine

GET  /api/quarantine               →  list messages (query: ?limit=100, max 500)
POST /api/quarantine/:id/release   →  release and forward the message
POST /api/quarantine/:id/discard   →  discard permanently

Configuration

GET  /api/config   →  list all config key-value pairs
PUT  /api/config   →  update config values (flat key-value object)

Database

GET  /api/database/backup             →  download a full SQLite backup (.db file)
POST /api/database/clear              →  reset the entire database
POST /api/database/clear-quarantine   →  clear quarantine messages only

Debug

Test a pattern against a string without sending any mail:

POST /api/debug/test-pattern
// Request
{
  "pattern": "urgent action",
  "text": "This is urgent action required, please verify your account",
  "isRegex": false,
  "caseSensitive": false
}

// Response
{
  "pattern": "urgent action",
  "result": true,
  "error": null
}

Architecture

This is a pnpm workspace monorepo with two packages:

fireclaw/
├── apps/
│   ├── server/                        # Node.js + TypeScript backend
│   │   └── src/
│   │       ├── index.ts               # Bootstrap: initializes DB and starts all services
│   │       ├── smtp/
│   │       │   └── smtpFirewallServer.ts  # SMTP receive → parse → evaluate → forward/quarantine/reject
│   │       ├── imap/
│   │       │   ├── imapProxy.ts       # IMAP proxy with local auth and upstream credential isolation
│   │       │   └── imapPoller.ts      # Background INBOX scanner, moves matches to quarantine mailbox
│   │       ├── rules/
│   │       │   └── evaluator.ts       # Pure rule engine: (message, rules, blacklist) → decision
│   │       ├── api/
│   │       │   └── httpServer.ts      # Express REST API
│   │       ├── db/
│   │       │   ├── database.ts        # SQLite init and schema migration
│   │       │   ├── repository.ts      # Data access layer
│   │       │   └── defaultRules.ts    # Seed rules (disabled by default)
│   │       └── types/
│   │           └── index.ts           # Shared TypeScript types
│   └── web/                           # Next.js 14 admin dashboard
│       ├── app/                       # App Router pages
│       ├── components/                # React components
│       └── lib/                       # API client, types, configuration helpers
├── .env.example                       # Configuration template
├── package.json                       # Workspace root + dev/build scripts
└── pnpm-workspace.yaml

Database schema

All persistent state lives in a single SQLite file at apps/server/data/firewall.db:

Table Purpose
config Runtime configuration (key-value pairs)
rules Filtering rules with field, pattern, action
blacklist Blocked email addresses and domains
quarantine_messages Full raw message stored alongside metadata and matched rules
sync_state IMAP poller cursor — tracks the last scanned message UID
client_deleted_uids UIDs deleted by the IMAP client — prevents re-appearance after sync

Key design decisions

Credential isolation — upstream IMAP/SMTP credentials never leave the server process. Email clients authenticate against local credentials (or bypass auth entirely). A compromised email client cannot extract your upstream provider password.

No message mutation — FireClaw stores the original raw RFC 822 message and forwards it byte-for-byte. Message headers are not rewritten or injected.

Stateless rule engine — the evaluator (rules/evaluator.ts) is a pure function: (message, rules, blacklist) → decision. It carries no internal state between calls, making it straightforward to test and reason about.

ES modules throughout — the server uses "type": "module" with NodeNext module resolution. All internal imports include .js extensions as required by the TypeScript + ESM convention.


Known Limitations

  • SMTP is plaintext by default. STARTTLS is disabled on the local SMTP endpoint. This is safe for loopback connections but should not be exposed on a network interface without TLS termination in front of it.
  • IMAP auth methods. The proxy supports LOGIN and AUTHENTICATE PLAIN. Some clients that require CRAM-MD5, OAUTH2, or other mechanisms are not supported in the current version.
  • Inbound quarantine release. Releasing an inbound message updates the local quarantine status and moves the IMAP UID back to the client-visible set. The message reappears in the email client on the next IMAP sync — it is not pushed immediately.
  • Custom TLS certificates. If your environment intercepts TLS (e.g. a corporate proxy), set UPSTREAM_IMAP_TLS_REJECT_UNAUTHORIZED=false as a temporary workaround. This disables certificate validation for the upstream IMAP connection.

Contributing

Contributions are welcome. To get started:

# Fork the repository on GitHub, then:
git clone https://github.com/leftcurveaeon/fireclaw.git
cd fireclaw
pnpm install

# Start in development mode
pnpm dev

# Type-check both apps
pnpm typecheck

Good first contributions

  • Tests — the rule evaluator (rules/evaluator.ts) is a pure function and a natural starting point for unit tests. SMTP and IMAP flows are good candidates for integration tests with real fixture messages.
  • Additional rule fields — adding headers like reply-to or cc is a focused change in evaluator.ts and types/index.ts.
  • SMTP TLS — the local SMTP endpoint does not yet support STARTTLS. Adding optional certificate configuration would improve compatibility with stricter email clients.
  • Docker packaging — a Dockerfile and docker-compose.yml for easy deployment.
  • IMAP IDLE support — replace the polling interval with push-based notifications from the upstream server.

Reporting issues

Please open a GitHub issue and include:

  1. A description of what you expected vs. what happened
  2. Relevant log output from the server (the firewall logs every rule evaluation to stdout by default)
  3. Your .env configuration with all credentials replaced by placeholder values

License

MIT — see LICENSE for details.


FireClaw is not affiliated with any email provider.
Your upstream credentials are used only to forward mail to your provider and are never transmitted anywhere else.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages