This is Beacon, a lightweight service for handling email consent and opt-outs. Built on .NET 10, it works independently of your CRM, ERP, or marketing automation tool. Generate secure, temporary opt-out links, validate them instantly, and let other systems check consent status via the REST API.
Try it out! Feel free to play around in our live demo instance, available on beacon-demo.melosso.com. Use
Beacon-Api-Keyas your access token.
Beacon centralizes consent and communication preferences, so your other systems don’t have to. Organize permissions into Buckets (e.g., newsletters, campaigns) and let recipients manage their preferences via secure, embeddable links.
Beacon handles consent for your automation tools, simple and efficient:
- Generates cryptographically signed opt-out URLs
- Validates permission changes without database lookups (unless configured otherwise)
- Keeps your sending systems lightweight, no complicated logic required
Beacon adapts to your setup:
- Supports SQLite, SQL Server, PostgreSQL and MySQL
- Admin panel for managing consent buckets and records
- Optional double opt-in confirmations
- Form builder for signup pages
- Granular permissions and security (encrypted data, hashed emails, rate limiting)
We've prepared two methods to deploy Beacon. It's up to you to choose your preferred method:
First generate your secrets. Compose reads this file, so create it before starting:
# Create the .env file
[ -f .env ] && echo ".env already exists! Aborting." && exit 1; ADMIN_KEY=$(openssl rand -base64 48 | tr -d '\n'); ENC_KEY=$(openssl rand -base64 32); printf "BEACON_SIGNING_KEY=%s\nBEACON_ENCRYPTION_KEY=%s\nBEACON_PEPPER=%s\nBEACON_ADMIN_API_KEY=%s\n" "$(openssl rand -base64 32)" "$ENC_KEY" "$(openssl rand -base64 32)" "$ADMIN_KEY" > .env && echo "Your X-Api-Key is: $ADMIN_KEY"Then setup your compose file:
services:
beacon:
image: ghcr.io/melosso/beacon:latest
ports:
- "5000:5000" # Public API
- "5001:5001" # Admin panel
volumes:
- beacon_data:/app/data # Database storage
- beacon_core:/app/.core # Encryption keys
env_file: .env
environment:
- BEACON_CONNECTION_STRING=Data Source=/app/data/Beacon.db
- BEACON_API_PORT=5000
- BEACON_ADMIN_PORT=5001
# Host-based routing, e.g. only used when running reverse proxy
# - BEACON_API_HOSTS=beacon-api.example.com
# - BEACON_ADMIN_HOSTS=beacon-admin.example.com
# - BEACON_ALLOWED_ORIGINS=https://app.example.com
# - BEACON_TRUST_FORWARDED_HEADERS=true
# - BEACON_KNOWN_PROXIES=10.0.0.0/8
volumes:
beacon_data:
beacon_core:# Start the container
docker compose up -dAccess the Admin panel at http://localhost:5001 and API at http://localhost:5000.
Download the latest release from Releases.
- Install .NET 10 Runtime:
winget install --id Microsoft.DotNet.Runtime.10 -e- Set the required keys:
function New-Key { $b = New-Object byte[] 32; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); [Convert]::ToBase64String($b) }
$adminKey = New-Key
"BEACON_ENCRYPTION_KEY", "BEACON_SIGNING_KEY", "BEACON_PEPPER" | ForEach-Object { [Environment]::SetEnvironmentVariable($_, (New-Key), "Machine") }
[Environment]::SetEnvironmentVariable("BEACON_ADMIN_API_KEY", $adminKey, "Machine")
Write-Host "Your X-Api-Key is: $adminKey"- Install service:
.\Beacon.bat install
.\Beacon.bat start- Open browser → http://localhost:5000 / http://localhost:5001
Store your API key somewhere safe, it is your access to the admin panel. Any sensitive value left in appsettings.json is encrypted in-place on first run; values supplied through the environment are never written to that file.
For production deployments with host-based routing, see the Configuration section.
Beacon will provide you a simple, non-customizable API that does one thing: securely store permissions for an e-mail address in a bucket. Your application can use this API to create a new permission state in the bucket–and return a token. This JWT-token contains all data, allowing the user to access its data without putting load on the database:
https://beacon.acme-corporation.com/u/v1.eyJiIjoicTEtY2FtcGFpZ24iLCJlIjoia...You can incorporate this in your newsletters, system notifications, or anything you'd like – allowing your user to configure their permissions in decentralized system and keeping them outside of your data source:
As Beacon is an API-first platform, all consent management operations should be handled programmatically. While manual execution via the web UII is possible, integration typically involves automating these calls within your specific workflow. The first step requires creating a permission state for an email address in a bucket–which triggers the automatic creation of the target bucket if it is not already present.
Creates consent records and returns a signed opt-out token ([{"token":"<signed_jwt>"}]).
curl -X POST http://localhost:5000/api/tokens/generate \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '[{
"bucket": "q1-campaign",
"email": "user@example.com",
"name": "Jane Doe",
"permissions": {
"newsletter": true,
"marketing": false
}
}]'Response is an array of [{"token":"<signed_token>","doubleOptIn":false}]. Access the first element for single-item requests.
If you're planning on updating the permission record after insertion, may want to use configure skipPermissionUpdate to prevent overwriting (user) updated permissions.
User clicks the token link to update preferences.
GET /u/{token}
Query consent status before sending email.
curl -X POST http://localhost:5000/api/consent/check \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"bucket": "q1-campaign", "email": "user@example.com", "permission": "newsletter"}'curl -X POST http://localhost:5000/api/consent/override \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"bucket": "q1-campaign", "email": "user@example.com", "permission": "newsletter", "status": "OptedIn"}'curl -X DELETE http://localhost:5000/api/admin/buckets/q1-campaign \
-H "X-Api-Key: your-api-key"# Supported languages: en (default), de, fr, nl, pl, es, it, pt, ja
curl -X POST http://localhost:5000/api/tokens/generate \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '[{
"bucket": "q1-campaign",
"email": "beige@example.com",
"name": "Beige le Brown",
"permissions": {
"newsletter": true,
"marketing": false
},
"customFields": {
"externalId": "external-reference"
}
"allowReplay": false,
"expiryDays": 30,
"language": "nl",
"skipPermissionUpdate": true
}]'Depending on your environment, these settings are changed in your .env, docker-compose.yml or appsettings.json file. Please see the configuration section for all details.
Free for open source projects and personal use under the EUPL 1.2 license. For more information, please see the license file.
Contributions are always welcome! Please submit issues and pull requests, using the templates we provided.