{"id":13563854,"url":"https://github.com/kannon-email/kannon","last_synced_at":"2025-12-25T14:10:02.931Z","repository":{"id":39181569,"uuid":"326366162","full_name":"kannon-email/kannon","owner":"kannon-email","description":"✉️  Cloud-Native massive mail sender for Kubernetes!","archived":false,"fork":false,"pushed_at":"2024-07-12T14:01:26.000Z","size":4463,"stargazers_count":66,"open_issues_count":18,"forks_count":10,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-11-04T16:45:51.134Z","etag":null,"topics":["email","hacktoberfest","open-source-saturday","smtp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kannon-email.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-03T09:01:56.000Z","updated_at":"2024-09-22T23:25:38.000Z","dependencies_parsed_at":"2024-01-14T03:47:52.795Z","dependency_job_id":"7f5a7536-6b23-46ef-9875-9b9c4072194c","html_url":"https://github.com/kannon-email/kannon","commit_stats":null,"previous_names":["schrodinger-hat/kannon"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kannon-email%2Fkannon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kannon-email%2Fkannon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kannon-email%2Fkannon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kannon-email%2Fkannon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kannon-email","download_url":"https://codeload.github.com/kannon-email/kannon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247074979,"owners_count":20879357,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["email","hacktoberfest","open-source-saturday","smtp"],"created_at":"2024-08-01T13:01:23.973Z","updated_at":"2025-12-25T14:10:02.916Z","avatar_url":"https://github.com/kannon-email.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Kannon 💥\n\n[![CI](https://github.com/gyozatech/kannon/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/gyozatech/kannon/actions/workflows/ci.yaml)\n\n![Kannon Logo](assets/kannonlogo.png?raw=true)\n\nA **Cloud Native SMTP mail sender** for Kubernetes and modern infrastructure.\n\n\u003e [!NOTE]\n\u003e Due to limitations of AWS, GCP, etc. on port 25, this project will not work on cloud providers that block port 25.\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [Architecture](#architecture)\n- [Quickstart](#quickstart)\n- [Configuration](#configuration)\n- [Database Schema](#database-schema)\n- [API Overview](#api-overview)\n- [Deployment](#deployment)\n- [Domain \u0026 DNS Setup](#domain--dns-setup)\n- [Sending Mail](#sending-mail)\n- [Testing \u0026 Demo Mode](#testing--demo-mode)\n- [Development \u0026 Contributing](#development--contributing)\n- [License](#license)\n\n---\n\n## Features\n\n- Cloud-native, scalable SMTP mail sending\n- gRPC API for sending HTML and templated emails\n- DKIM and SPF support for deliverability\n- Attachments support (JSONB in DB)\n- Custom fields for emails (JSONB in DB)\n- Statistics and analytics (DB + gRPC API)\n- Template management (CRUD via API)\n- Kubernetes-ready deployment\n- Postgres-backed persistence\n\n**Planned:**\n\n- Multi-node sending\n- Advanced analytics dashboard\n\n## Architecture\n\nKannon is composed of several microservices and workers:\n\n- **API**: gRPC server for mail, template, and domain management\n- **SMTP**: Handles SMTP protocol and relays mail\n- **Sender**: Sends emails from the queue\n- **Dispatcher**: Manages the sending pool and delivery\n- **Verifier**: Validates emails before sending\n- **Bounce**: Handles bounces\n- **Stats**: Collects and stores delivery statistics\n\nAll components can be enabled/disabled via CLI flags or config.\n\n\u003e **See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for a full breakdown of modules, NATS streams, topics, consumers, and message flows.**\n\n```mermaid\nflowchart TD\n    subgraph Core\n        API[\"gRPC API\"]\n        SMTP[\"SMTP Server\"]\n        Sender[\"Sender\"]\n        Dispatcher[\"Dispatcher\"]\n        Verifier[\"Verifier\"]\n        Bounce[\"Bounce\"]\n        Stats[\"Stats\"]\n    end\n    DB[(PostgreSQL)]\n    NATS[(NATS)]\n    API \u003c--\u003e DB\n    Dispatcher \u003c--\u003e DB\n    Sender \u003c--\u003e DB\n    Verifier \u003c--\u003e DB\n    Stats \u003c--\u003e DB\n    Bounce \u003c--\u003e DB\n    API \u003c--\u003e NATS\n    Sender \u003c--\u003e NATS\n    Dispatcher \u003c--\u003e NATS\n    SMTP \u003c--\u003e NATS\n    Stats \u003c--\u003e NATS\n    Verifier \u003c--\u003e NATS\n    Bounce \u003c--\u003e NATS\n```\n\n## Quickstart\n\n### Prerequisites\n\n- Go 1.25.1+\n- Docker (optional, for containerized deployment)\n- PostgreSQL database\n- NATS server (optional - embedded mode available in standalone command)\n\n### Standalone Mode (Recommended for Development/Testing)\n\nRun all Kannon components in a single process with embedded NATS (only PostgreSQL required):\n\n```sh\ngit clone https://github.com/kannon-email/kannon.git\ncd kannon\ngo build -o kannon .\n./kannon standalone --config ./config.yaml\n```\n\nThis mode:\n- Runs all components (API, SMTP, Sender, Dispatcher, Verifier, Stats, Bounce)\n- Embeds NATS server (no external NATS required)\n- Ideal for development, testing, or single-server deployments\n- Still requires a PostgreSQL database\n\n### Local Run (Manual Component Selection)\n\n```sh\ngit clone https://github.com/kannon-email/kannon.git\ncd kannon\ngo build -o kannon .\n./kannon --run-api --run-smtp --run-sender --run-dispatcher --config ./config.yaml\n```\n\n\u003e **Note:** This mode requires an external NATS server configured in your config file.\n\n### Docker Compose\n\nSee [`examples/docker-compose/`](examples/docker-compose/) for ready-to-use files.\n\n```sh\ndocker-compose -f examples/docker-compose/docker-compose.yaml up\n```\n\n- Edit `examples/docker-compose/kannon.yaml` to configure your environment.\n\n### Makefile Targets\n\n- `make test` — Run all tests\n- `make generate` — Generate DB and proto code\n- `make lint` — Run linters\n\n## Configuration\n\nKannon can be configured via YAML file, environment variables, or CLI flags. Precedence: CLI \u003e Env \u003e YAML.\n\n**Main config options:**\n\n| Key / Env Var                                   | Type     | Default        | Description                            |\n| ----------------------------------------------- | -------- | -------------- | -------------------------------------- |\n| `database_url` / `K_DATABASE_URL`               | string   | (required)     | PostgreSQL connection string           |\n| `nats_url` / `K_NATS_URL`                       | string   | (required)     | NATS server URL for internal messaging |\n| `debug` / `K_DEBUG`                             | bool     | false          | Enable debug logging                   |\n| `api.port` / `K_API_PORT`                       | int      | 50051          | gRPC API port                          |\n| `sender.hostname` / `K_SENDER_HOSTNAME`         | string   | (required)     | Hostname for outgoing mail             |\n| `sender.max_jobs` / `K_SENDER_MAX_JOBS`         | int      | 10             | Max parallel sending jobs              |\n| `sender.demo_sender` / `K_SENDER_DEMO_SENDER`   | bool     | false          | Enable demo sender mode for testing    |\n| `smtp.address` / `K_SMTP_ADDRESS`               | string   | :25            | SMTP server listen address             |\n| `smtp.domain` / `K_SMTP_DOMAIN`                 | string   | localhost      | SMTP server domain                     |\n| `smtp.read_timeout` / `K_SMTP_READ_TIMEOUT`     | duration | 10s            | SMTP read timeout                      |\n| `smtp.write_timeout` / `K_SMTP_WRITE_TIMEOUT`   | duration | 10s            | SMTP write timeout                     |\n| `smtp.max_payload` / `K_SMTP_MAX_PAYLOAD`       | size     | 1024kb         | Max SMTP message size                  |\n| `smtp.max_recipients` / `K_SMTP_MAX_RECIPIENTS` | int      | 50             | Max recipients per SMTP message        |\n| `run-api` / `K_RUN_API`                         | bool     | false          | Enable API server                      |\n| `run-smtp` / `K_RUN_SMTP`                       | bool     | false          | Enable SMTP server                     |\n| `run-sender` / `K_RUN_SENDER`                   | bool     | false          | Enable sender worker                   |\n| `run-dispatcher` / `K_RUN_DISPATCHER`           | bool     | false          | Enable dispatcher worker               |\n| `run-verifier` / `K_RUN_VERIFIER`               | bool     | false          | Enable verifier worker                 |\n| `run-bounce` / `K_RUN_BOUNCE`                   | bool     | false          | Enable bounce worker                   |\n| `run-stats` / `K_RUN_STATS`                     | bool     | false          | Enable stats worker                    |\n| `config`                                        | string   | ~/.kannon.yaml | Path to config file                    |\n\n- See [`examples/docker-compose/kannon.yaml`](examples/docker-compose/kannon.yaml) for a full example.\n\n## Database Schema\n\nKannon requires a PostgreSQL database. Main tables:\n\n- **domains**: Registered sender domains, DKIM keys\n- **messages**: Outgoing messages, subject, sender, template, attachments\n- **sending_pool_emails**: Email queue, status, scheduling, custom fields\n- **templates**: Email templates, type, metadata\n- **stats**: Delivery and open/click statistics\n- **stats_keys**: Public/private keys for stats security\n\nSee [`db/migrations/`](./db/migrations/) for full schema and migrations.\n\n## API Overview\n\nKannon exposes a gRPC API for sending mail, managing domains/templates, and retrieving stats.\n\n### Services \u0026 Methods\n\n- **Mailer API** ([proto](./proto/kannon/mailer/apiv1/mailerapiv1.proto))\n  - `SendHTML`: Send a raw HTML email\n  - `SendTemplate`: Send an email using a stored template\n- **Admin API** ([proto](./proto/kannon/admin/apiv1/adminapiv1.proto))\n  - `GetDomains`, `GetDomain`, `CreateDomain`, `RegenerateDomainKey`\n  - `CreateTemplate`, `UpdateTemplate`, `DeleteTemplate`, `GetTemplate`, `GetTemplates`\n- **Stats API** ([proto](./proto/kannon/stats/apiv1/statsapiv1.proto))\n  - `GetStats`, `GetStatsAggregated`\n\n### Authentication\n\nAll gRPC APIs use Basic Auth with your domain and API key:\n\n```\ntoken = base64(\u003cyour domain\u003e:\u003cyour domain key\u003e)\n```\n\nPass this in the `Authorization` metadata for gRPC calls:\n\n```json\n{\n  \"Authorization\": \"Basic \u003cyour token\u003e\"\n}\n```\n\n### Example: SendHTML Request\n\n```json\n{\n  \"sender\": {\n    \"email\": \"no-reply@yourdomain.com\",\n    \"alias\": \"Your Name\"\n  },\n  \"recipients\": [\"user@example.com\"],\n  \"subject\": \"Test\",\n  \"html\": \"\u003ch1\u003eHello\u003c/h1\u003e\u003cp\u003eThis is a test.\u003c/p\u003e\",\n  \"attachments\": [\n    { \"filename\": \"file.txt\", \"content\": \"base64-encoded-content\" }\n  ],\n  \"fields\": { \"custom\": \"value\" }\n}\n```\n\nSee the [proto files](./proto/kannon/) for all fields and options.\n\n## Deployment\n\n### Kubernetes\n\n- See [`k8s/deployment.yaml`](k8s/deployment.yaml) for a production-ready manifest.\n- Configure your environment via a mounted YAML file or environment variables.\n\n### Docker Compose\n\n- See [`examples/docker-compose/`](examples/docker-compose/) for local or test deployments.\n\n## Domain \u0026 DNS Setup\n\nTo send mail, you must register a sender domain and configure DNS:\n\n1. Register a domain via the Admin API\n2. Set up DNS records:\n   - **A record**: `\u003cSENDER_NAME\u003e` → your server IP\n   - **Reverse DNS**: your server IP → `\u003cSENDER_NAME\u003e`\n   - **SPF TXT**: `\u003cSENDER_NAME\u003e` → `v=spf1 ip4:\u003cYOUR SENDER IP\u003e -all`\n   - **DKIM TXT**: `smtp._domainkey.\u003cYOUR_DOMAIN\u003e` → `k=rsa; p=\u003cYOUR DKIM KEY HERE\u003e`\n\n## Sending Mail\n\n- Use the gRPC API (`SendHTML` or `SendTemplate`) with Basic Auth as above.\n- See the [proto file](./proto/kannon/mailer/apiv1/mailerapiv1.proto) for all fields and options.\n\n## Testing \u0026 Demo Mode\n\nKannon includes a **demo sender mode** for testing and development without actually sending emails. This is particularly useful for:\n\n- **Development environments** where you don't want to send real emails\n- **Testing email templates** and content without affecting deliverability\n- **CI/CD pipelines** where you need to verify email functionality\n- **Local development** without SMTP server setup\n\n### Enabling Demo Mode\n\nSet `sender.demo_sender: true` in your configuration:\n\n```yaml\nsender:\n  hostname: kannon.example.com\n  max_jobs: 10\n  demo_sender: true # Enable demo sender mode\n```\n\nOr via environment variable:\n\n```bash\nexport K_SENDER_DEMO_SENDER=true\n```\n\n### Demo Sender Behavior\n\nWhen demo mode is enabled:\n\n- **Emails are not actually sent** - they're processed through the pipeline but not delivered\n- **Statistics are still collected** - you can track delivery attempts and errors.\n- **Error simulation** - emails containing \"error\" in the recipient address will simulate delivery failures\n- **Full pipeline testing** - all components (API, dispatcher, sender, stats) work normally\n- **Template processing** - HTML templates and custom fields are processed correctly\n\nThis mode mocks the SMTP client and does not actually send emails.\n\n**IMPROVEMENTS:**\n\n- mock opens, clicks, etc.\n- mock bounce, spam, etc.\n\n### Example: Testing with Demo Mode\n\n```bash\n# Start Kannon with demo sender enabled\n./kannon --run-api --run-sender --run-dispatcher \\\n  --sender-demo-sender \\\n  --sender-hostname test.example.com \\\n  --config ./config.yaml\n\n# Send test emails via API\n# Emails will be processed but not actually sent\n# Statistics will be collected normally\n```\n\nThis makes it easy to test your email integration without setting up SMTP servers or worrying about deliverability during development.\n\n## Development \u0026 Contributing\n\nWe welcome contributions! Please:\n\n- Use [feature request](.github/ISSUE_TEMPLATE/feature_request.md) and [bug report](.github/ISSUE_TEMPLATE/bug_report.md) templates for issues\n- Follow the [pull request template](.github/PULL_REQUEST_TEMPLATE.md)\n- See the [Apache 2.0 License](./LICENSE)\n- **Read our [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines, code style, and the full contribution process.**\n\n### Local Development\n\n- Build: `go build -o kannon .`\n- Test: `make test` or `go test ./...`\n- Generate code: `make generate`\n- Lint: `make lint`\n\n### Testing\n\n- Unit and integration tests are in `internal/`, `pkg/`, and run with `go test ./...`\n- Some tests use Docker to spin up a test Postgres instance\n- **E2E tests** include comprehensive email sending pipeline testing with demo sender mode\n\n---\n\n## Local Testing for Integrations Development\n\nFor developers building integrations with Kannon, we provide a complete local testing environment using Docker Compose with demo sender mode. This allows you to test the entire email pipeline without actually sending emails or requiring SMTP server setup.\n\n### Quick Start with Docker Compose\n\nThe simplest way to get started is using the provided Docker Compose configuration:\n\n```bash\ncd examples/docker-compose/\ndocker-compose up\n```\n\nThis will start:\n- **PostgreSQL database** with automatic migrations\n- **NATS server** for internal messaging\n- **Kannon server** with all components enabled and **demo sender mode activated**\n\nThe API will be available at `localhost:50051` (gRPC).\n\n### Demo Sender Mode Benefits\n\nWhen using the docker-compose setup, demo sender mode is automatically enabled (`demo_sender: true` in `kannon.yaml`), which means:\n\n- ✅ **Complete pipeline testing** - All API endpoints, template processing, and statistics work normally\n- ✅ **No real emails sent** - Safe for development and testing environments\n- ✅ **Statistics collection** - Track delivery attempts, errors, and metrics\n- ✅ **Error simulation** - Recipients containing \"error\" will simulate delivery failures\n- ✅ **Template processing** - HTML templates, custom fields, and attachments work correctly\n- ✅ **Authentication testing** - Full gRPC API authentication flow\n\n### Configuration\n\nThe demo environment uses the configuration in `examples/docker-compose/kannon.yaml`:\n\n```yaml\nsender:\n  hostname: kannon.ludusrusso.dev\n  max_jobs: 100\n  demo_sender: true  # Mock SMTP sending\n\n# All components enabled for full testing\nrun-smtp: true\nrun-bounce: true  \nrun-dispatcher: true\nrun-verifier: true\nrun-sender: true\nrun-api: true\nrun-stats: true\n```\n\n### Testing Your Integration\n\n1. **Start the environment:**\n   ```bash\n   cd examples/docker-compose/\n   docker-compose up -d\n   ```\n\n2. **Create a test domain** (using grpcurl or your gRPC client):\n   ```bash\n   # Register a domain for testing\n   grpcurl -plaintext -d '{\"domain\":\"test.example.com\"}' \\\n     localhost:50051 kannon.admin.apiv1.AdminApiV1Service/CreateDomain\n   ```\n\n3. **Send test emails** via the API:\n   ```bash\n   # Send HTML email (will be processed but not actually sent)\n   grpcurl -plaintext \\\n     -H \"Authorization: Basic $(echo -n 'test.example.com:your-domain-key' | base64)\" \\\n     -d '{\"sender\":{\"email\":\"test@test.example.com\",\"alias\":\"Test\"},\"recipients\":[\"user@example.com\"],\"subject\":\"Test Email\",\"html\":\"\u003ch1\u003eHello World\u003c/h1\u003e\"}' \\\n     localhost:50051 kannon.mailer.apiv1.MailerApiV1Service/SendHTML\n   ```\n\n4. **Check statistics:**\n   ```bash\n   # View delivery stats (will show processed emails)\n   grpcurl -plaintext \\\n     -H \"Authorization: Basic $(echo -n 'test.example.com:your-domain-key' | base64)\" \\\n     localhost:50051 kannon.stats.apiv1.StatsApiV1Service/GetStats\n   ```\n\n### Integration Development Workflow\n\n1. **Develop against the local API** - Use `localhost:50051` as your Kannon endpoint\n2. **Test email templates** - Use `SendTemplate` API with your template designs\n3. **Verify statistics** - Check that your integration correctly handles delivery stats\n4. **Test error scenarios** - Send emails to recipients containing \"error\" to simulate failures\n5. **Validate attachments** - Test file attachments and custom fields functionality\n\n### Customizing the Environment\n\nTo modify the demo environment:\n\n1. **Edit `examples/docker-compose/kannon.yaml`** for Kannon configuration\n2. **Edit `examples/docker-compose/docker-compose.yaml`** for infrastructure changes\n3. **Restart the environment:** `docker-compose down \u0026\u0026 docker-compose up`\n\n### Production Transition\n\nWhen ready for production, simply change `demo_sender: false` in your configuration and provide real SMTP credentials. Your integration code remains unchanged.\n\n## License\n\nKannon is licensed under the Apache 2.0 License. See [LICENSE](./LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkannon-email%2Fkannon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkannon-email%2Fkannon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkannon-email%2Fkannon/lists"}