Skip to content

Serverless backend for RAVN Mail license activation and OpenRouter API key management

License

Notifications You must be signed in to change notification settings

ravnmail/activate

Repository files navigation

RAVN Mail Activation Service

Linted with Biome

A serverless backend for RAVN Mail license activation and OpenRouter API key management. This service handles license validation through LemonSqueezy, provides trial periods, and manages OpenRouter API keys for AI features.

Architecture

The service is built on AWS using:

  • CloudFront: SSL termination and global CDN for desired domain (ex. activate.ravnmail.com)
  • Lambda Functions: Three serverless functions for activate, validate, and webhook endpoints
  • DynamoDB: NoSQL database for storing license data and API keys
  • SSM Parameter Store: Secure storage for API keys and secrets
  • WAF (Optional): Rate limiting and security protection

API Endpoints

POST /trial

Starts a new trial period for a user.

Request:

{
  "instanceName": "37fb320965936bec5fe5f805154f3b73174f74bb4f5bd86f21c51e3c5188848c",
  "email": "user@example.com"
}

Response:

{
  "instanceId": "abc123",
  "instanceName": "37fb320965936bec5fe5f805154f3b73174f74bb4f5bd86f21c51e3c5188848c",
  "licenseKey": "trial-8449a605b53f-5480d2ade13c-mrwabv7n",
  "user_name": "John Doe",
  "user_email": "user@example.com",
  "ai_mode": "saas",
  "ai_details": {
    "token": "sk-or-v1-xxxxx",
    "limit": 2,
    "limit_remaining": 2,
    "expires_at": "2026-02-01T00:00:00Z"
  }
}

POST /activate

Activates a new license or starts a trial. Creates OpenRouter API key immediately for SaaS licenses.

Request:

{
  "instanceName": "37fb320965936bec5fe5f805154f3b73174f74bb4f5bd86f21c51e3c5188848c",
  "licenseKey": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}

Response:

{
  "instanceId": "abc123",
  "instanceName": "37fb320965936bec5fe5f805154f3b73174f74bb4f5bd86f21c51e3c5188848c",
  "licenseKey": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "user_name": "John Doe",
  "user_email": "user@example.com",
  "ai_mode": "saas",
  "ai_details": {
    "token": "sk-or-v1-xxxxx",
    "limit": 6,
    "limit_remaining": 6,
    "expires_at": "2025-02-10T00:00:00Z"
  }
}

Note: For BYOK mode, ai_details will be undefined as users must provide their own OpenRouter key.

POST /validate

Validates an existing license and only creates a new OpenRouter API key if the old one has expired and the license is still valid.

Request:

{
  "licenseKey": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}

Response: Same as activate endpoint

Behavior:

  • If key is still valid: Returns existing key details with updated limit_remaining
  • If key has expired AND license is active: Creates new key with fresh credits
  • If license is inactive: Clears ai_details and returns error

POST /webhook

Handles LemonSqueezy webhooks for license updates.

Headers:

  • X-Signature: HMAC-SHA256 signature of the payload

Events Handled:

  • license_key_created
  • license_key_updated (disables API key if license becomes inactive)
  • Subscription events (acknowledged but primary handling is through license_key_updated)

Deployment

Prerequisites

  1. AWS CLI configured with appropriate credentials
  2. Node.js 22.x or later
  3. AWS CDK CLI installed: pnpm install -g aws-cdk
  4. ACM certificate for desired domain (ex. activate.ravnmail.com; must be in us-east-1 for CloudFront)
  5. LemonSqueezy account with API key
  6. OpenRouter account with API key

Installation

# Install dependencies
pnpm install

# Bootstrap CDK (first time only)
cdk bootstrap aws://ACCOUNT-ID/eu-west-1

Deployment Steps

  1. Set the required environment variables:

    export CERTIFICATE_ARN="arn:aws:acm:us-east-1:ACCOUNT:certificate/xxxxx"
    export DOMAIN_NAME="activate.ravnmail.com"
  2. Optional: Enable WAF

    export ENABLE_WAF=true
  3. Deploy the stack:

    cdk deploy --context certificateArn=$CERTIFICATE_ARN --context domainName=$DOMAIN_NAME

    Or with WAF:

    cdk deploy --context certificateArn=$CERTIFICATE_ARN --context domainName=$DOMAIN_NAME --context enableWaf=true

    Alternatively, use environment variables only:

    cdk deploy
  4. Configure API keys in SSM Parameter Store:

    # Set LemonSqueezy API key
    aws ssm put-parameter \
      --name /ravn/activate/lemonsqueezy-api-key \
      --value "YOUR_LEMONSQUEEZY_API_KEY" \
      --type SecureString \
      --overwrite
    
    # Set OpenRouter API key
    aws ssm put-parameter \
      --name /ravn/activate/openrouter-api-key \
      --value "YOUR_OPENROUTER_API_KEY" \
      --type SecureString \
      --overwrite
    
    # Set LemonSqueezy webhook secret
    aws ssm put-parameter \
      --name /ravn/activate/webhook-secret \
      --value "YOUR_WEBHOOK_SECRET" \
      --type SecureString \
      --overwrite
  5. Configure CloudFront DNS:

    • After deployment, note the CloudFront distribution domain name from outputs
    • Create a CNAME record in your DNS:
      desired domain (ex. activate.ravnmail.com) -> d1234567890abc.cloudfront.net
      
  6. Configure LemonSqueezy Webhook:

    • Go to LemonSqueezy Dashboard → Settings → Webhooks
    • Add webhook URL: https://activate.ravnmail.com/v1/webhook
    • Select events: license_key_created, license_key_updated
    • Copy the signing secret and update the SSM parameter

OpenRouter API Key Lifecycle

Understanding how OpenRouter keys are managed to prevent credit exploitation:

On Activation (POST /v1/activate)

  1. User activates license with LemonSqueezy
  2. OpenRouter API key is created immediately with credits based on plan
  3. Key expires according to subscription period (monthly keys for subscriptions)
  4. Key details stored in DynamoDB

On Validation (POST /v1/validate)

  1. Client calls validation endpoint periodically
  2. System checks LemonSqueezy license status
  3. If existing key has NOT expired: Returns existing key with updated usage stats
  4. If existing key HAS expired AND license is active: Creates new key with fresh credits
  5. If license is inactive: Clears API key and returns error

Key Benefits

  • No exploitation: Keys created immediately on activation ensures only legitimate users get credits
  • Automatic renewal: Keys auto-renew when expired if subscription is active
  • Cost control: One key per active subscription period, not on every validation
  • Usage tracking: limit_remaining shows current credit usage

Configuration

Product Configuration

Edit config/products.json to modify product variants, pricing, and AI credits:

{
  "store_id": 37188,
  "trial": {
    "credits_usd": 2,
    "duration_days": 30,
    "ai_mode": "saas"
  },
  "products": {
    "ai_subscription": {
      "product_id": 697533,
      "ai_mode": "saas",
      "variants": {
        "starter_monthly": {
          "variant_id": 1097689,
          "credits_usd": 6,
          "duration": "monthly"
        }
      }
    }
  }
}

Security Features

Implemented Security Measures

  1. Webhook Signature Verification: All LemonSqueezy webhooks are verified using HMAC-SHA256
  2. SSL/TLS: All traffic uses HTTPS via CloudFront
  3. Secrets Management: API keys stored in SSM Parameter Store with encryption
  4. DynamoDB Encryption: All data encrypted at rest with AWS-managed keys
  5. Point-in-Time Recovery: Enabled on DynamoDB for data protection
  6. IAM Least Privilege: Lambda functions have minimal required permissions
  7. CORS: Configured to allow specific headers and methods only
  8. Rate Limiting (when WAF enabled): 100 requests per 5 minutes per IP
  9. AWS Managed Rules (when WAF enabled): Protection against common exploits

Security Best Practices

  1. Trial Abuse Prevention: One trial per email address tracked in database
  2. License Validation: All activations verified with LemonSqueezy API
  3. API Key Rotation: OpenRouter keys automatically renewed monthly
  4. Expiration Handling: Keys automatically disabled when license expires
  5. Input Validation: All endpoints validate required fields
  6. Error Handling: No sensitive information leaked in error messages

Monitoring

CloudWatch Logs

  • Lambda execution logs: /aws/lambda/ravn-activate, /aws/lambda/ravn-validate, /aws/lambda/ravn-webhook
  • Retention: 30 days

CloudWatch Metrics

  • Lambda invocations, errors, duration
  • DynamoDB read/write capacity
  • CloudFront requests, error rates
  • WAF blocked requests (if enabled)

Recommended Alarms

  1. Lambda error rate > 5%
  2. DynamoDB throttling
  3. WAF blocked request rate spike
  4. CloudFront 5xx error rate > 1%

Development

Local Development

# Install dependencies
pnpm install

# Compile TypeScript
pnpm run build

# Watch for changes
pnpm run watch

# Run tests
pnpm run test

CDK Commands

# List stacks
cdk list

# Synthesize CloudFormation template
cdk synth

# Compare deployed stack with current state
cdk diff

# Deploy stack
cdk deploy

# Destroy stack (use with caution!)
cdk destroy

Troubleshooting

Common Issues

  1. Certificate Error on Deploy

    • Ensure certificate is in us-east-1 region
    • Verify certificate ARN is correct
  2. Webhook Signature Verification Fails

    • Ensure webhook secret in SSM matches LemonSqueezy
    • Check X-Signature header is passed through CloudFront
  3. OpenRouter Key Creation Fails

    • Verify OpenRouter API key in SSM is valid
    • Check OpenRouter account has sufficient credits
  4. Trial Already Used Error

    • Check email-index in DynamoDB for existing trials
    • Verify trial_started_at field

License

This project is licensed under the MIT License. See the LICENSE file for details.

About

Serverless backend for RAVN Mail license activation and OpenRouter API key management

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published