A high-performance Open Badges server implementation using Bun and Hono, supporting both Open Badges 2.0 and 3.0 specifications.
- Open Badges Standards Support:
- Open Badges 2.0 compliant
- Open Badges 3.0 with W3C Verifiable Credentials implementation
- Backward compatibility between versions
- Badge Handling:
- Badge creation and issuance
- Badge verification with cryptographic proofs
- Badge baking for both PNG and SVG formats
- Assertion extraction from baked badges
- Authentication:
- Passwordless email authentication
- WebAuthn support
- OAuth 2.0 with support for machine-to-machine authentication
- Modern Stack:
- Built with Bun and Hono for high performance
- TypeScript with strict mode
- PostgreSQL with Drizzle ORM
- RESTful API design
- Deployment:
- Docker containerization
- Easy self-hosting
- OpenAPI/Swagger documentation
- Developer Experience:
- Semantic versioning
- Comprehensive documentation
- Automated releases and container builds
- Bun >= 1.0.0
- Node.js >= 18.0.0 (for development tools)
- Docker and Docker Compose (for containerized deployment)
# Clone the repository
git clone https://github.com/your-org/bun-badges.git
cd bun-badges
# Install dependencies
bun install# Start development server with hot reload
bun run dev
# Type checking
bun run typecheck
# Build for production
bun run build
# Start production server
bun run start# Full development environment (includes Canvas for image processing)
bun run dev:docker
# Start with rebuild (after dependency changes)
bun run dev:docker:build
# Lightweight development environment (no Canvas dependency)
bun run dev:light
# Lightweight environment with rebuild
bun run dev:light:build
# Stop containers
bun run dev:docker:down
# or
bun run dev:light:down# Create .env file from example
cp .env.example .env
# Edit .env with your configuration
# Build and start containers in production mode
docker-compose up -d
# Check logs
docker-compose logs -f
# Stop containers
docker-compose downThe project provides a ready-to-use Docker image that can be pulled from GitHub Container Registry or built locally.
# Pull the latest image
docker pull ghcr.io/rollercoaster-dev/bun-badges:latest
# Run the container
docker run -p 3000:3000 --name bun-badges \
-e DATABASE_URL=postgres://postgres:postgres@host.docker.internal:5432/bun_badges \
-e NODE_ENV=production \
ghcr.io/rollercoaster-dev/bun-badges:latest# Build the image
docker build -t bun-badges:local .
# Run the container
docker run -p 3000:3000 --name bun-badges \
-e DATABASE_URL=postgres://postgres:postgres@host.docker.internal:5432/bun_badges \
bun-badges:localWe provide a sample docker-compose.example.yml that you can use as a starting point:
# Copy the example file
cp docker-compose.example.yml docker-compose.yml
# Edit as needed
# Then run
docker-compose up -dFor more detailed instructions, configuration options, and best practices, see our Docker Deployment Guide.
When running the Docker image, you can configure it with the following environment variables:
| Variable | Description | Default |
|---|---|---|
PORT |
The port the server listens on | 3000 |
NODE_ENV |
Environment (production, development) | production |
DATABASE_URL |
PostgreSQL connection string | Required |
LOG_LEVEL |
Logging level (debug, info, warn, error) | info |
HOST |
Host to bind to | 0.0.0.0 |
The application requires a PostgreSQL database. In a production environment, we recommend:
- Using a managed PostgreSQL service
- Or running PostgreSQL in a separate container (as shown in
docker-compose.example.yml) - Properly securing your database connection with TLS and strong passwords
Following our PostgreSQL Guidelines, the application:
- Uses Drizzle ORM to interact with the database
- Maintains migrations in the
/drizzledirectory - Uses JSONB columns for flexible Open Badges JSON structures
- Indexes key fields for optimal performance
# Build for production
bun run build
# Start production server
bun run startsrc/
├── routes/ # API route definitions
├── controllers/ # Request handlers
├── services/ # Business logic
├── models/ # Data models
├── middleware/ # Custom middleware
├── utils/ # Helper functions - see [Utils Documentation](src/utils/README.md)
└── __tests__/ # Test directories - see Testing section for details
GET /health- Server health status
POST /auth/email/start- Start email authentication flowPOST /auth/email/verify- Verify email authentication codePOST /auth/webauthn/register- Register WebAuthn credentialPOST /auth/webauthn/authenticate- Authenticate with WebAuthn
POST /oauth/register- Register a new OAuth clientPOST /oauth/token- Request an access tokenPOST /oauth/introspect- Introspect a tokenPOST /oauth/revoke- Revoke a token- See Headless OAuth Documentation for machine-to-machine authentication
GET /issuers- List all issuersGET /issuers/:id- Get a specific issuerPOST /issuers- Create a new issuerPUT /issuers/:id- Update an issuer
GET /badges- List all badgesGET /badges/:id- Get a specific badgePOST /badges- Create a new badgePUT /badges/:id- Update a badgeDELETE /badges/:id- Delete a badgeGET /badges/bake/:badgeId/:assertionId- Bake an assertion into a badge imagePOST /badges/extract- Extract assertion from a baked badge
GET /assertions- List all assertions (with optional filtering)GET /assertions/:id- Get a specific assertion (add?format=ob3for Open Badges 3.0 format)POST /assertions- Issue a badge (add"version": "ob3"in request body for OB3.0)POST /assertions/:id/revoke- Revoke a badge assertion
GET /verify/:id- Verify a badge assertionPOST /verify- Verify a badge assertion from provided JSON
See the API Documentation section for more details on using these endpoints.
API errors are returned with a standardized JSON response body:
{
"error": "A human-readable error message describing the issue"
}The HTTP status code accompanying the response indicates the nature of the error (e.g., 400 for Bad Request, 401 for Unauthorized, 404 for Not Found, 500 for Internal Server Error).
Copy .env.example to .env and update as needed:
# Server Configuration
PORT=7777
NODE_ENV=development
# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=badges
DB_USER=badges_user
DB_PASSWORD=badges_password
DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
# Docker Configuration
POSTGRES_DB=${DB_NAME}
POSTGRES_USER=${DB_USER}
POSTGRES_PASSWORD=${DB_PASSWORD}
# JWT Configuration
JWT_SECRET=your-secret-key
JWT_EXPIRY=15m
REFRESH_TOKEN_EXPIRY=7d
The project uses a comprehensive test suite including unit, integration, and end-to-end tests. To simplify running tests in different environments, we've implemented the following:
-
Environment Configuration:
.env.test- Local test environment configuration.env.ci- CI environment configuration
-
Test Setup Script:
- Located at
scripts/run-tests.sh - Run specific test types:
scripts/run-tests.sh [unit|integration|e2e|database|all] - Automatically detects environment and sets appropriate variables
- Located at
-
Docker Database Integration:
- Tests use a dedicated PostgreSQL container defined in
docker-compose.test.yml - Set
SKIP_DOCKER=trueto use an existing database connection - CI environments automatically use service containers instead of Docker
- Tests use a dedicated PostgreSQL container defined in
-
Database Connection:
- Automatic fallback and retry mechanisms
- Connection pooling optimized for tests
- Default configuration uses port 5434 to avoid conflicts with development database
# Run all tests
bun test
# Run specific test types using the helper script
scripts/run-tests.sh unit # Only unit tests
scripts/run-tests.sh integration # Only integration tests
scripts/run-tests.sh e2e # Only end-to-end tests
scripts/run-tests.sh database # Database connection tests
# Manual test configuration
SKIP_DOCKER=true bun test # Skip Docker setup, use existing database
INTEGRATION_TEST=true bun test tests/integration # Only run integration tests
E2E_TEST=true bun test tests/e2e # Only run E2E testsThe CI/CD pipeline is configured to run tests in GitHub Actions:
- Sets up a PostgreSQL service container
- Creates and migrates the test database
- Runs unit, integration, and E2E tests separately
- Uses the CI-specific environment configuration
See .github/workflows/ci-tests.yml for the complete configuration.
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
MIT
The API is documented using OpenAPI/Swagger. When running the server, you can access the API documentation at:
http://localhost:7777/docs
This provides an interactive interface to explore and test the API endpoints.
For detailed information about the Open Badges 3.0 implementation, including the Verifiable Credentials data model, cryptographic verification, and example usage, see Open Badges 3.0 Documentation.
The application supports HTTPS for secure communication. Here's how to set it up:
- Generate self-signed certificates for development:
bun run gen:certs- Update your
.envfile with the following settings:
USE_HTTPS=true
TLS_CERT_FILE=./certs/cert.pem
TLS_KEY_FILE=./certs/key.pem
- Start the development server with HTTPS enabled:
bun run dev:httpsFor production, you should use proper certificates from a trusted certificate authority:
- Obtain SSL/TLS certificates from a trusted Certificate Authority
- Place the certificates in the
./certsdirectory or another location of your choice - Set the environment variables in your
.envfile:
USE_HTTPS=true
TLS_CERT_FILE=/path/to/your/cert.pem
TLS_KEY_FILE=/path/to/your/key.pem
TLS_PASSPHRASE=your-passphrase-if-needed
The Docker configuration is already set up to mount the ./certs directory into the container.
Note: Self-signed certificates are not suitable for production use and will trigger security warnings in browsers. Always use certificates from a trusted Certificate Authority for production environments.
Current limitations:
- Bun currently only supports HTTPS over HTTP/1.1
- HTTP/2 is not yet supported
This project uses PostgreSQL as its database system, following these principles:
- Primary Storage: PostgreSQL is used as the primary database for all badge data
- ORM Integration: Drizzle ORM is used for database interactions, providing type safety and query building
- Migration Strategy: Database migrations are versioned and maintained in the
/drizzledirectory - Flexible Schema Design: JSONB columns are used for storing dynamic Open Badges JSON structures
- Performance Optimization: Key fields are indexed for optimal query performance
The application requires a PostgreSQL database (version 13 or higher recommended). Connection is configured via the DATABASE_URL environment variable, which should be in the following format:
postgres://username:password@hostname:port/database
The core schema includes tables for:
- Issuers (issuer_profiles)
- Badges (badge_classes)
- Assertions (badge_assertions)
- Keys and credentials (signing_keys)
- Status lists for revocation
Each table maintains both structured fields for querying and JSONB fields for the complete badge data.
This project follows Semantic Versioning for releases. For details on how to create and manage releases, see our Versioning Guide.
# Create a new release (version determined from commit messages)
bun run release
# Push changes and tags to trigger a build
git push --follow-tags origin mainDocker images are automatically built and published to GitHub Container Registry (ghcr.io) when new version tags are pushed.