The Herald is the ever-watchful and enigmatic bot, designed to guide, inform, and protect the DSB community in the sprawling digital world. True to its name, The Herald serves as a messenger and beacon, delivering essential updates and insights to empower users on their DevSecOps journey.
Current Responsibilities:
- Security Newsletter: The Herald curates and delivers a cutting-edge security newsletter, ensuring users stay informed about the latest developments, threats, and best practices in cybersecurity and DevSecOps. Its updates are both timely and actionable, published every hour via automated RSS feed monitoring.
Planned Features:
- New Video Announcements: Acting as the digital voice of the DSB founder, The Herald will announce newly released videos, offering summaries and insights into their content.
graph TB
subgraph "AWS Cloud - us-east-2"
EB[EventBridge Rule<br/>15 min schedule]
subgraph "Lambda Function"
LF[the-herald-handler<br/>Python 3.13<br/>1GB RAM / 5min timeout]
LL[Lambda Layer<br/>Python Dependencies]
end
PS[Parameter Store<br/>/the-herald/prod/<br/>- discord-token<br/>- guild-id]
CW[CloudWatch Logs<br/>30-day retention]
IAM[IAM Role<br/>Lambda Execution]
EB -->|Invoke with<br/>handler_type: newsletter| LF
LF -->|Uses| LL
LF -->|Read secrets| PS
LF -->|Write logs| CW
IAM -->|Grants permissions| LF
end
subgraph "External Services"
RSS[RSS Feeds<br/>Security News]
DC[Discord API<br/>Guild/Channel]
end
LF -->|Fetch feeds| RSS
LF -->|Post messages| DC
style LF fill:#FF9900
style LL fill:#FF9900
style EB fill:#FF4F8B
style PS fill:#527FFF
style CW fill:#FF9900
style IAM fill:#DD344C
style RSS fill:#90EE90
style DC fill:#5865F2
Active Features:
- Newsletter publishing (every hour via EventBridge)
- RSS feed parsing and Discord message posting
- AWS Parameter Store for secrets management
- CloudWatch Logs for monitoring
Disabled Features:
- Event notifications (EventBridge rule commented out)
- DynamoDB reminder tracking (table and IAM policies commented out)
AWS Resources:
- Region: us-east-2
- Lambda Runtime: Python 3.13
- Lambda Memory: 1024 MB (1 GB)
- Lambda Timeout: 300 seconds (5 minutes)
- Parameter Store Prefix:
/the-herald/prod/ - Log Retention: 30 days
Environment Variables:
PARAMETER_STORE_PREFIX:/the-herald/prod/LOG_LEVEL:INFOAWS_REGION: Set automatically by Lambda (us-east-2)
This application runs as an AWS Lambda function with EventBridge scheduling. The deployment consists of two main components: the Lambda function code and a Lambda layer containing Python dependencies.
All build operations are managed through tasks.py using the invoke task runner. This Python-based build system handles building both the Lambda layer and deployment package, placing artifacts in the terraform/ directory for Terraform Cloud deployment.
Available tasks:
# Build Lambda layer with Python dependencies
invoke build-layer
# Build Lambda deployment package
invoke build-package
# Build both layer and deployment package
invoke build-all
# Apply Terraform changes (builds first, then deploys)
invoke apply
# Clean all build artifacts
invoke clean
# List all available tasks
invoke --listPrerequisites:
- Python 3.13+
invokepackage installed (pip install invoke)- AWS CLI configured (for manual deployment)
- Terraform CLI (for infrastructure deployment)
The Lambda layer packages all Python dependencies separately from the application code, reducing deployment package size and enabling dependency reuse.
To build the Lambda layer, run:
invoke build-layerThis will:
- Create a
terraform/lambda_layer/python/directory structure required by AWS Lambda - Install all dependencies from
lambda-requirements.txt(runtime dependencies only) - Optimize the layer by removing test files,
__pycache__, and unnecessary metadata - Generate
terraform/lambda_layer.zipready for deployment
The layer is deployed automatically when using Terraform (see "Deploying with Terraform Cloud" section below). For manual deployment using AWS CLI:
aws lambda publish-layer-version \
--layer-name the-herald-dependencies \
--zip-file fileb://terraform/lambda_layer.zip \
--compatible-runtimes python3.13 \
--description "Python dependencies for Discord bot Lambda function"The command will return a JSON response containing the layer version ARN. Save this ARN to reference in your Lambda function configuration.
The Lambda layer follows an independent versioning strategy from the application code:
When to create a new layer version:
- When adding new Python dependencies to
lambda-requirements.txt - When updating existing dependency versions
- When changing the Python runtime version (e.g., from Python 3.11 to 3.13)
When NOT to create a new layer version:
- When modifying application code in the
lambda/app/directory - When updating
lambda/lambda_handler.py - When changing configuration files (e.g.,
app/static/config.yaml)
Best practices:
- Version naming: Use semantic versioning in layer descriptions (e.g., "v1.0.0", "v1.1.0") to track changes
- Immutability: Never modify an existing layer version; always create a new version
- Testing: Test new layer versions in a development environment before updating production Lambda functions
- Cleanup: Periodically delete unused layer versions to reduce storage costs (keep at least the last 2-3 versions)
- Documentation: Document dependency changes in commit messages when updating
lambda-requirements.txt
Example workflow:
# 1. Update lambda-requirements.txt with new dependencies
echo "new-package==1.2.3" >> lambda-requirements.txt
# 2. Build the new layer
invoke build-layer
# 3. Deploy with Terraform (recommended)
invoke apply
# OR deploy manually with AWS CLI
aws lambda publish-layer-version \
--layer-name the-herald-dependencies \
--zip-file fileb://terraform/lambda_layer.zip \
--compatible-runtimes python3.13 \
--description "v1.1.0 - Added new-package for feature X"Checking current layer version:
# List all versions of the layer
aws lambda list-layer-versions --layer-name the-herald-dependencies
# Get details of a specific layer version
aws lambda get-layer-version \
--layer-name the-herald-dependencies \
--version-number 1The Lambda deployment package contains the application code (lambda/app/ directory) and the Lambda handler entry point (lambda/lambda_handler.py). This package is separate from the Lambda layer and should be updated whenever application code changes.
To build the deployment package, run:
invoke build-packageThis will:
- Create a
terraform/lambda_deployment_package/directory with the application structure - Copy the
lambda/app/directory (including all services, clients, and configuration) - Copy
lambda/lambda_handler.pyas the Lambda entry point - Include
app/static/config.yamlfor RSS feed configuration - Exclude test files,
__pycache__, and.pycfiles - Generate
terraform/lambda_deployment_package.zip(under 50MB)
The recommended deployment method is using Terraform Cloud, which manages all AWS resources including the Lambda function, EventBridge rules, Parameter Store parameters, and IAM roles.
Prerequisites:
- Terraform Cloud account configured
- Terraform CLI >= 1.0 installed
- AWS credentials configured in Terraform Cloud workspace
- Discord bot token and guild ID
Deployment steps:
-
Build the deployment packages:
invoke build-all
-
Configure Terraform variables in Terraform Cloud workspace or create
terraform/terraform.tfvars:DISCORD_TOKEN = "your-discord-bot-token" DISCORD_GUILD_ID = "your-discord-guild-id" aws_region = "us-east-2" environment = "prod"
-
Initialize and apply Terraform:
cd terraform terraform init terraform plan terraform applyOr use the convenience task:
invoke apply
Terraform will create all required AWS resources and deploy both the Lambda layer and deployment package. The build artifacts in terraform/lambda_layer.zip and terraform/lambda_deployment_package.zip will be uploaded automatically.
The repository includes a GitHub Actions workflow that automatically deploys to AWS Lambda on every push to the main branch.
Required GitHub Secrets:
TF_API_TOKEN: Terraform Cloud API token for authenticationDISCORD_TOKEN: Discord bot authentication tokenDISCORD_GUILD_ID: Discord server (guild) ID
Workflow steps:
- Checkout code
- Install
uvpackage manager with caching enabled - Setup Python 3.13 using
uv - Install dependencies with
uv sync - Build Lambda packages (
uv run invoke build-all) - Setup Terraform with Cloud authentication
- Run Terraform init, plan, and apply
The workflow can also be triggered manually via the GitHub Actions UI (workflow_dispatch).
Current Infrastructure:
- Lambda Function: Python 3.13 runtime, 1GB memory, 5-minute timeout
- EventBridge Schedule: Newsletter publishing every hour
- Parameter Store: Discord token (SecureString) and guild ID in
us-east-2 - CloudWatch Logs: 30-day retention
- IAM Roles: Least privilege access for Lambda execution
Note: Event notifications and DynamoDB reminder tracking are currently disabled. Only newsletter functionality is active.
See terraform/README.md for detailed Terraform documentation.
If you prefer manual deployment without Terraform Cloud, you can use the AWS CLI:
-
Build the deployment packages:
invoke build-all
-
Deploy the Lambda layer:
aws lambda publish-layer-version \ --layer-name the-herald-dependencies \ --zip-file fileb://terraform/lambda_layer.zip \ --compatible-runtimes python3.13 \ --description "Python dependencies for Discord bot" -
Create or update the Lambda function:
# For new function creation aws lambda create-function \ --function-name the-herald-handler \ --runtime python3.13 \ --role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda-execution-role \ --handler lambda_handler.main \ --zip-file fileb://terraform/lambda_deployment_package.zip \ --timeout 300 \ --memory-size 1024 \ --layers arn:aws:lambda:us-east-2:ACCOUNT_ID:layer:the-herald-dependencies:VERSION \ --environment Variables="{PARAMETER_STORE_PREFIX=/the-herald/prod/,LOG_LEVEL=INFO}" # For existing function updates aws lambda update-function-code \ --function-name the-herald-handler \ --zip-file fileb://terraform/lambda_deployment_package.zip
-
Create Parameter Store parameters:
aws ssm put-parameter \ --name "/the-herald/prod/discord-token" \ --value "YOUR_DISCORD_BOT_TOKEN" \ --type SecureString \ --region us-east-2 aws ssm put-parameter \ --name "/the-herald/prod/guild-id" \ --value "YOUR_DISCORD_GUILD_ID" \ --type String \ --region us-east-2
-
Create EventBridge rule for newsletter publishing:
aws events put-rule \ --name the-herald-newsletter-schedule \ --schedule-expression "rate(15 minutes)" \ --region us-east-2 aws events put-targets \ --rule the-herald-newsletter-schedule \ --targets "Id"="1","Arn"="arn:aws:lambda:us-east-2:ACCOUNT_ID:function:the-herald-handler","Input"='{"handler_type":"newsletter","source":"eventbridge.schedule"}' \ --region us-east-2 aws lambda add-permission \ --function-name the-herald-handler \ --statement-id AllowEventBridgeNewsletter \ --action lambda:InvokeFunction \ --principal events.amazonaws.com \ --source-arn arn:aws:events:us-east-2:ACCOUNT_ID:rule/the-herald-newsletter-schedule \ --region us-east-2
Update the deployment package whenever you make changes to:
- Application code in the
lambda/app/directory lambda/lambda_handler.pyentry pointapp/static/config.yamlfeed configuration
Quick update workflow:
# 1. Make code changes
# 2. Rebuild deployment package
invoke build-package
# 3. Deploy with Terraform
invoke apply
# OR deploy with AWS CLI
aws lambda update-function-code \
--function-name the-herald-handler \
--zip-file fileb://terraform/lambda_deployment_package.zip \
--region us-east-2The deployment package must be under 50MB (uncompressed). If you exceed this limit:
- Move dependencies to the Lambda layer: Ensure all Python packages are in
lambda-requirements.txtand the layer, not the deployment package - Remove unnecessary files: The build script already excludes test files and
__pycache__ - Check for large static files: Review
lambda/app/static/for any large files that can be moved to S3
The build script will warn you if the package exceeds 50MB.
If you'd like to contribute to this project, you'll want to check out the Contributing Documentation.
Thank you so much for making "The Herald" what it is. You all bring this person to life.
