Certo is a comprehensive digital credentials platform for issuing, managing, and verifying badges and certificates based on the Open Badges 3.0 specification and the W3C Verifiable Credentials data model.
Certo is an open-source platform that enables organizations and individuals to issue verifiable digital credentials (badges and certificates) following global standards. Whether you're running educational programs, workshops, certification courses, or need to recognize achievements, Certo provides a complete solution for creating, issuing, and verifying digital credentials.
- Issuers create badge templates (Achievements) defining what the badge represents, criteria for earning it, and associated skills
- Issuers issue credentials to recipients either individually or in bulk via CSV upload
- Recipients receive their credentials and can view them in their dashboard
- Anyone can verify the authenticity of a credential using the public verification page
- Recipients can share their verified credentials on LinkedIn and other platforms
Certo implements:
- Open Badges 3.0: IMS Global Learning Consortium standard for digital credentials
- W3C Verifiable Credentials: Cryptographically secure and tamper-evident credentials
- Decentralized Identifiers: For robust identity management
- Educational Institutions: Issue course completion certificates and academic achievements
- Training Organizations: Provide verifiable certificates for workshops and training programs
- Event Organizers: Award attendance or participation badges for conferences and meetups
- Companies: Recognize employee skills, certifications, and professional development
- Open Source Communities: Acknowledge contributions and participation
- Professional Associations: Issue membership credentials and professional certifications
- Create Achievements: Design badge templates with customizable criteria, skills, and metadata
- Issue Credentials: Issue digital certificates for workshops, courses, events, and projects
- Batch Issuance: Upload CSV files to issue credentials to multiple recipients simultaneously
- Role-Based Access: Separate issuer and recipient roles with appropriate permissions
- Cryptographic Verification: Verify badge authenticity using cryptographic proofs and the Verifiable Credentials model
- Revocation Support: Maintain revocation lists for invalidated credentials
- Tamper-Evident: All credentials are cryptographically signed and tamper-evident
- LinkedIn Integration: Share certificates directly on LinkedIn
- Multiple Export Formats: Export credentials in various formats
- Public Verification: Anyone can verify credential authenticity via the verification page
- Evidence Attachments: Attach supporting evidence and narratives to credentials
- Endorsements: Third-party endorsement support for credentials
- Dashboard: View and manage all issued and received credentials
- Open Standards: Full compliance with Open Badges 3.0 specification
certo/
├── src/
│ ├── backend/ # Strapi 5.x backend (TypeScript)
│ │ ├── src/
│ │ │ ├── api/ # API endpoints
│ │ │ │ ├── achievement/ # Badge templates/definitions (Open Badges 3.0)
│ │ │ │ ├── credential/ # Issued credentials/badges
│ │ │ │ ├── profile/ # User profiles (issuers & recipients)
│ │ │ │ ├── evidence/ # Evidence attachments
│ │ │ │ ├── endorsement/ # Third-party endorsements
│ │ │ │ └── revocation-list/# Credential revocation lists
│ │ │ ├── admin/ # Strapi admin customizations
│ │ │ ├── components/ # Reusable content components
│ │ │ ├── middlewares/ # Custom middlewares
│ │ │ └── utils/ # Utility functions
│ │ ├── config/ # Strapi configuration
│ │ ├── database/ # Database migrations
│ │ ├── public/ # Static files & uploads
│ │ ├── scripts/ # Utility scripts
│ │ ├── types/ # TypeScript type definitions
│ │ ├── Dockerfile # Backend Docker configuration
│ │ └── package.json # Backend dependencies
│ │
│ └── frontend/ # Nuxt 3 frontend (Vue 3 + TypeScript + Una UI)
│ ├── pages/ # Application pages
│ │ ├── index.vue # Home page
│ │ ├── issue.vue # Badge issuance (CSV upload support)
│ │ ├── verify.vue # Credential verification
│ │ ├── dashboard.vue # User dashboard
│ │ └── ... # Other pages
│ ├── components/ # Vue components
│ ├── composables/ # Vue composables
│ ├── stores/ # Pinia state management
│ ├── middleware/ # Route middleware (auth, etc.)
│ ├── api/ # API client libraries
│ ├── types/ # TypeScript types
│ ├── plugins/ # Nuxt plugins
│ ├── assets/ # CSS, images, SVG components
│ ├── public/ # Static public files
│ ├── e2e/ # Playwright E2E tests
│ ├── Dockerfile # Frontend Docker configuration
│ └── package.json # Frontend dependencies
│
├── netlify/ # Netlify serverless functions
│ └── functions/
│ └── og-credential/ # Open Graph image generation
├── docker-compose.yml # Docker Compose configuration
├── README.md # This file
├── LICENSE # AGPL-3.0 License
├── CONTRIBUTING.md # Contribution guidelines
└── CODE_OF_CONDUCT.md # Code of conduct
- Docker and Docker Compose
- Node.js 18-22.x (for local development)
- Node.js 18.x - 22.x
- npm 6.0.0 or higher
- PostgreSQL 13+ (or use SQLite for development)
- Backend: Strapi 5.15.0, TypeScript 5.x, Node.js 18-22.x
- Frontend: Nuxt 3.10+, Vue 3.4+, TypeScript 5.x, Una UI
- Database: PostgreSQL (production) or SQLite (development)
- Authentication: JWT with jose library
- Testing: Vitest (unit), Playwright (E2E)
- Clone the repository:
git clone https://github.com/schrodinger-hat/certo.git
cd certo- Create environment variables:
Create a .env file in the src/backend directory with the following variables:
# Required Strapi Secrets
ADMIN_JWT_SECRET=your-admin-jwt-secret
JWT_SECRET=your-jwt-secret
APP_KEYS=your-app-keys
# Database Configuration (PostgreSQL)
DATABASE_CLIENT=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=certo
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapi
# Server Configuration
HOST=0.0.0.0
PORT=1337
# Frontend URL (for CORS)
FRONTEND_URL=http://localhost:3000Create a .env file in the src/frontend directory:
# API Configuration
NUXT_PUBLIC_API_URL=http://localhost:1337
NUXT_PUBLIC_WEBSITE_URL=http://localhost:3000
# Optional: Google Analytics
NUXT_PUBLIC_GTAG=your-gtag-id- Start the Docker containers:
This will start the backend (Strapi), frontend (Nuxt 3), and PostgreSQL database.
- Wait for the containers to initialize. On first run, the backend will automatically:
- Create sample data for testing
- Configure all necessary API permissions
Default login credentials (same for admin panel and frontend):
- Email:
[email protected] - Password:
certo
Sample data includes:
- A Strapi admin user (for the admin panel at
/admin) - An API user (for frontend authentication)
- A profile (configured as both Issuer and Recipient)
- A sample achievement ("Welcome to Certo")
- A sample credential/badge awarded to the admin user
Permissions automatically configured:
- Authenticated users can access all API endpoints (profiles, achievements, credentials, etc.)
- Public users can read and verify badges
Note: Seed data is only created on the first run. If you need to reset the database, run
docker-compose down -vto remove volumes, then start again.
- Access the applications:
If you prefer to run the applications locally without Docker, you'll need to set up PostgreSQL separately or use SQLite for development.
- Set up your database (PostgreSQL or SQLite)
- Create the
.envfile as described above - Install and run:
cd src/backend
npm install
npm run develop # Development mode with auto-reload
# or
npm run dev # Alternative dev commandThe Strapi admin panel will be available at http://localhost:1337/admin
First-time setup: Create an admin account when prompted on first launch.
- Create the
.envfile as described above - Install and run:
cd src/frontend
npm install
npm run devThe frontend will be available at http://localhost:3000
- Start the backend first (it runs on port 1337)
- Start the frontend (it connects to the backend API)
- Create an admin account in Strapi
- Register a user account in the frontend
- Set user role to "issuer" in Strapi admin to issue badges
The API follows the Open Badges 3.0 specification. Swagger UI (OpenAPI) is available at http://localhost:1337/documentation.
Public Endpoints:
GET /api/credentials/:id- Retrieve public credential data (Open Badges 3.0 format)POST /api/credentials/verify- Verify credential authenticityGET /api/revocation-list/check/:id- Check if a credential is revoked
Authenticated Endpoints:
POST /api/achievements- Create badge templates (issuer only)POST /api/credentials/batch-issue- Issue credentials to multiple recipientsGET /api/profiles/me- Get current user profileGET /api/profiles/:id/credentials- Get user's credentialsPOST /api/endorsements- Create credential endorsementsPOST /api/evidence- Attach evidence to credentials
- Achievement: Badge template/definition (Badge Class in Open Badges 3.0)
- Credential: Issued badge/certificate (Badge Assertion in Open Badges 3.0)
- Profile: User profile (issuer or recipient)
- Evidence: Supporting documentation for credentials
- Endorsement: Third-party validation of credentials
- Revocation List: List of revoked credentials
For complete API documentation, access the Strapi admin panel at http://localhost:1337/admin after setup.
Unit Tests (Vitest):
cd src/frontend
npm run test:unit # Run once
npm run test:unit:watch # Run in watch modeEnd-to-End Tests (Playwright):
cd src/frontend
npm run test:e2eE2E tests cover:
- User registration and login flows
- Badge issuance workflow
- Credential verification
- Dashboard functionality
- Navigation and routing
The backend uses Strapi's testing framework. Tests can be run from the backend directory:
The easiest way to deploy Certo is using Docker Compose:
This will start all required services (backend, frontend, and database) in production mode.
cd src/backend
npm install
npm run build
npm startcd src/frontend
npm install
npm run build
npm startMake sure to set secure values for:
ADMIN_JWT_SECRET- Strong random string for admin authenticationJWT_SECRET- Strong random string for user authenticationAPP_KEYS- Comma-separated list of random stringsDATABASE_PASSWORD- Secure database password- Update
FRONTEND_URLandNUXT_PUBLIC_API_URLwith your production URLs
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the GNU Affero General Public License v3.0 - see the LICENSE file for details.
