11 KiB
Messaging Forwarding Feature
This feature allows users with unlimited access to forward Nostr events to messaging platforms (Telegram, SimpleX, Email) and Git hosting platforms (GitHub, GitLab, Gitea, Codeberg, Forgejo) when they publish events.
Security Architecture
Multi-Layer Security
- Encrypted Salt Storage: Each user's salt is encrypted with a separate key (
MESSAGING_SALT_ENCRYPTION_KEY) - HMAC Lookup Keys: User pubkeys are hashed with HMAC before being used as database keys
- Rate Limiting: Decryption attempts are rate-limited (10 attempts per 15 minutes)
- Per-User Key Derivation: Encryption keys derived from master key + pubkey + salt
- AES-256-GCM: Authenticated encryption with random IV per encryption
Threat Mitigation
- Database Compromise: All data encrypted at rest with per-user keys
- Key Leakage: Per-user key derivation means master key leak doesn't expose all users
- Brute Force: Rate limiting prevents rapid decryption attempts
- Lookup Attacks: HMAC hashing prevents pubkey enumeration
Environment Variables
Required for Encryption
# Master encryption key (256-bit hex, generate with: openssl rand -hex 32)
MESSAGING_PREFS_ENCRYPTION_KEY=<256-bit-hex>
# Salt encryption key (256-bit hex, generate with: openssl rand -hex 32)
MESSAGING_SALT_ENCRYPTION_KEY=<256-bit-hex>
# HMAC secret for lookup key hashing (256-bit hex, generate with: openssl rand -hex 32)
MESSAGING_LOOKUP_SECRET=<256-bit-hex>
Optional for Messaging Platforms
# Telegram Bot Configuration
TELEGRAM_BOT_TOKEN=<bot-token>
TELEGRAM_ENABLED=true
# Email SMTP Configuration
SMTP_HOST=<smtp-host>
SMTP_PORT=587
SMTP_USER=<smtp-username>
SMTP_PASSWORD=<smtp-password>
SMTP_FROM_ADDRESS=<from-email>
SMTP_FROM_NAME=GitRepublic
EMAIL_ENABLED=true
# OR use SMTP API (alternative to direct SMTP)
SMTP_API_URL=<smtp-api-url>
SMTP_API_KEY=<api-key>
# SimpleX API Configuration
SIMPLEX_API_URL=<simplex-api-url>
SIMPLEX_API_KEY=<api-key>
SIMPLEX_ENABLED=true
# Git Platforms Forwarding Configuration
GIT_PLATFORMS_ENABLED=true
Setup
1. Generate Encryption Keys
# Generate all three required keys
openssl rand -hex 32 # For MESSAGING_PREFS_ENCRYPTION_KEY
openssl rand -hex 32 # For MESSAGING_SALT_ENCRYPTION_KEY
openssl rand -hex 32 # For MESSAGING_LOOKUP_SECRET
2. Store Keys Securely
Development:
- Store in
.envfile (never commit to git)
Production:
- Use secret management service (AWS Secrets Manager, HashiCorp Vault, etc.)
- Or environment variables in deployment platform
- Consider using Hardware Security Modules (HSM) for maximum security
3. Configure Messaging Platforms
Telegram
- Create a bot with @BotFather
- Get bot token
- Users will provide their chat ID when configuring preferences
- Configure SMTP settings (host, port, credentials)
- Or use an SMTP API service
- Users will provide their email addresses (to/cc)
SimpleX
- Set up SimpleX Chat API
- Configure API URL and key
- Users will provide their contact ID
Git Platforms (GitHub, GitLab, Gitea, Codeberg, Forgejo)
- Users create Personal Access Tokens with appropriate scopes
- Users provide platform, username/org, repository name, and token
- Events will be forwarded as issues or PRs on the selected platform
- Supports self-hosted instances via custom API URL
User Flow
1. User Saves Preferences (Client-Side)
// User encrypts preferences to self on Nostr (kind 30078)
const encryptedContent = await window.nostr.nip44.encrypt(
userPubkey,
JSON.stringify(preferences)
);
// Publish to Nostr (backup/sync)
const event = {
kind: 30078,
pubkey: userPubkey,
tags: [['d', 'gitrepublic-messaging'], ['enabled', 'true']],
content: encryptedContent
};
// Sign and publish
const signedEvent = await signEventWithNIP07(event);
await nostrClient.publishEvent(signedEvent);
// Send decrypted copy to server (over HTTPS)
await fetch('/api/user/messaging-preferences', {
method: 'POST',
body: JSON.stringify({
preferences,
proofEvent: signedEvent
})
});
2. Server Stores Securely
- Verifies proof event signature
- Checks user has unlimited access
- Generates random salt
- Encrypts salt with
MESSAGING_SALT_ENCRYPTION_KEY - Derives per-user encryption key
- Encrypts preferences with AES-256-GCM
- Stores using HMAC lookup key
3. Event Forwarding
When user publishes an event (issue, PR, highlight):
- Server checks user has unlimited access
- Retrieves encrypted preferences
- Decrypts (with rate limiting)
- Checks if forwarding enabled and event kind matches
- Forwards to configured platforms
API Endpoints
POST /api/user/messaging-preferences
Save messaging preferences.
Request:
{
"preferences": {
"telegram": "@username",
"simplex": "contact-id",
"email": {
"to": ["user@example.com"],
"cc": ["cc@example.com"]
},
"gitPlatforms": [
{
"platform": "github",
"owner": "username",
"repo": "repository-name",
"token": "ghp_xxxxxxxxxxxx"
},
{
"platform": "gitlab",
"owner": "username",
"repo": "repository-name",
"token": "glpat-xxxxxxxxxxxx"
},
{
"platform": "codeberg",
"owner": "username",
"repo": "repository-name",
"token": "xxxxxxxxxxxx"
},
{
"platform": "onedev",
"owner": "project-path",
"repo": "repository-name",
"token": "xxxxxxxxxxxx",
"apiUrl": "https://your-onedev-instance.com"
},
{
"platform": "custom",
"owner": "username",
"repo": "repository-name",
"token": "xxxxxxxxxxxx",
"apiUrl": "https://your-git-instance.com/api/v1"
}
],
"enabled": true,
"notifyOn": ["1621", "1618"]
},
"proofEvent": { /* Signed Nostr event (kind 30078) */ }
}
Response:
{
"success": true
}
GET /api/user/messaging-preferences
Get preferences status (without decrypting).
Response:
{
"configured": true,
"rateLimit": {
"remaining": 10,
"resetAt": null
}
}
DELETE /api/user/messaging-preferences
Delete messaging preferences.
Response:
{
"success": true
}
Security Best Practices
-
Key Management
- Never commit keys to git
- Rotate keys periodically
- Use secret management services in production
- Consider HSM for maximum security
-
Monitoring
- Monitor rate limit violations
- Alert on decryption failures
- Audit log all preference changes
-
Access Control
- Only users with unlimited access can use this feature
- Requires valid signed Nostr event proof
- Server verifies all inputs
-
Data Protection
- All data encrypted at rest
- Per-user key derivation
- HMAC lookup keys
- Rate limiting on decryption
Troubleshooting
"Decryption rate limit exceeded"
User has exceeded 10 decryption attempts in 15 minutes. Wait for the window to reset.
"Messaging forwarding requires unlimited access"
User must have relay write access (unlimited level) to use this feature.
"Failed to forward event"
Check:
- Messaging platform API credentials
- Network connectivity
- Platform-specific error logs
Email Setup
Option 1: Direct SMTP
Install nodemailer:
npm install nodemailer
Configure environment variables:
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_FROM_ADDRESS=noreply@yourdomain.com
SMTP_FROM_NAME=GitRepublic
EMAIL_ENABLED=true
Option 2: SMTP API
Use an SMTP API service (e.g., SendGrid, Mailgun, AWS SES):
SMTP_API_URL=https://api.sendgrid.com/v3/mail/send
SMTP_API_KEY=your-api-key
EMAIL_ENABLED=true
Git Platforms Setup
Supported Platforms
- GitHub (
github) - github.com - GitLab (
gitlab) - gitlab.com (also supports self-hosted) - Gitea (
gitea) - Self-hosted instances - Codeberg (
codeberg) - codeberg.org - Forgejo (
forgejo) - Self-hosted instances - OneDev (
onedev) - Self-hosted instances (requires apiUrl) - Custom (
custom) - Any Gitea-compatible API with custom URL
Creating Personal Access Tokens
GitHub
- Go to Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click "Generate new token (classic)"
- Select
reposcope - Generate and copy token
GitLab
- Go to Settings → Access Tokens
- Create token with
apiscope - Generate and copy token
Gitea/Codeberg/Forgejo
- Go to Settings → Applications → Generate New Token
- Select
reposcope - Generate and copy token
OneDev
- Go to User Settings → Access Tokens
- Create a new access token
- Select appropriate scopes (typically
write:issueandwrite:pull-request) - Generate and copy token
- Note: OneDev is self-hosted, so you must provide the
apiUrl(e.g.,https://your-onedev-instance.com)
User Configuration
Users provide:
- Platform: One of
github,gitlab,gitea,codeberg,forgejo,onedev, orcustom - Owner: Username or organization name (project path for OneDev)
- Repo: Repository name (project name for OneDev)
- Token: Personal access token (stored encrypted)
- API URL (required for OneDev, optional for custom/self-hosted): Base URL of the instance (e.g.,
https://your-onedev-instance.com)
Event Mapping
- Nostr Issues (kind 1621) → Platform Issues
- Nostr PRs (kind 1618) → Platform Pull Requests/Merge Requests (if branch info available) or Issues with PR label
- Other events → Platform Issues with event kind label
Platform-Specific Notes
- GitLab: Uses
descriptionfield instead ofbody, andsource_branch/target_branchfor PRs - OneDev: Uses
descriptionfield andsource_branch/target_branchfor PRs. RequiresapiUrl(self-hosted). API endpoints:/api/projects/{owner}/{repo}/issuesand/api/projects/{owner}/{repo}/pull-requests - GitHub: Uses
bodyfield andhead/basefor PRs - Gitea/Codeberg/Forgejo: Compatible with GitHub API format
- Custom: Must provide
apiUrlpointing to Gitea-compatible API
Security Note
All tokens are stored encrypted in the database. Users should:
- Use tokens with minimal required scopes
- Rotate tokens periodically
- Revoke tokens if compromised
Future Enhancements
- Support for more messaging platforms
- User-configurable message templates
- Webhook support
- Encrypted preferences sync across devices
- Per-repository forwarding rules
- HTML email templates