WRI is a web application where students and graduates can prove their skills through a verified digital credential system. They upload evidence, take assessments, get verified by a human reviewer, and receive a credential that employers can validate.
In the current job market, employers often cannot verify claims made on resumes. WRI solves this by creating a chain of trust:
- Student uploads proof of their skills
- Student takes a standardized assessment
- A human verifier reviews the evidence and assessment
- If approved, a digital credential is issued
- The student shares the credential with an employer
- The employer validates it through the platform
This ensures that every credential is backed by verified evidence and a human decision.
| Layer | Technology | Purpose |
|---|---|---|
| Backend | NestJS (TypeScript) | REST API server |
| Database | PostgreSQL 16 | Data storage |
| ORM | Prisma | Database access and migrations |
| File Storage | MinIO | Store uploaded evidence files (S3-compatible) |
| Authentication | JWT | Stateless token-based auth |
| Frontend | Next.js 16 (React 19) | User interface |
| Styling | Tailwind CSS | UI styling |
| Containers | Docker Compose | Local infrastructure (Postgres + MinIO) |
wri/
├── backend/ ← NestJS API server
│ ├── prisma/ ← Database schema and migrations
│ ├── src/
│ │ ├── modules/ ← Feature modules (identity, evidence, etc.)
│ │ ├── infrastructure/ ← Database, storage, events
│ │ └── shared/ ← Guards, utilities, common code
│ └── test/ ← E2E tests
├── frontend/ ← Next.js web application
│ └── src/
│ ├── app/ ← Pages (App Router)
│ ├── components/ ← Reusable UI components
│ ├── lib/ ← API client, auth helpers
│ └── types/ ← TypeScript type definitions
├── docs/ ← Project documentation
├── docker-compose.yml ← Local infrastructure
└── README.md ← This file
Before starting, install:
- Node.js 20 or newer
- npm (comes with Node.js)
- Docker Desktop (for PostgreSQL and MinIO)
- Git
Verify installation:
node --version # Should show v20+
npm --version # Should show 10+
docker --version # Should show Docker 24+git clone <repository-url> wri
cd wri
# Create environment files from examples
Copy-Item .env.example .env
Copy-Item backend/.env.example backend/.envdocker compose up -dWait 10 seconds, then verify:
docker ps
# Should show wri-postgres (healthy) and wri-minio (healthy)cd backend
npm install
npx prisma generate
npx prisma migrate deploy
npx ts-node prisma/seed.ts --size=smallnpm run start:devVerify backend is running:
# Open a new terminal
curl http://localhost:3001/api/v1/health
# Should return: {"status":"ok",...}# Open a new terminal
cd frontend
npm install
npm run devOpen browser: http://localhost:3000
| Variable | Value | Purpose |
|---|---|---|
WRI_DB_USER |
wri |
PostgreSQL username |
WRI_DB_PASSWORD |
wri-password |
PostgreSQL password |
WRI_DB_NAME |
wri |
Database name |
WRI_DB_PORT |
5432 |
PostgreSQL port |
WRI_MINIO_ROOT_USER |
wri |
MinIO access key |
WRI_MINIO_ROOT_PASSWORD |
wri-password |
MinIO secret key |
| Variable | Value | Purpose |
|---|---|---|
DATABASE_URL |
postgresql://wri:wri-password@localhost:5432/wri |
Database connection |
WRI_AUTH_JWT_SECRET |
(32+ char string) | JWT signing key |
WRI_APP_PORT |
3001 |
Backend port |
WRI_STORAGE_ENDPOINT |
http://localhost:9000 |
MinIO endpoint |
WRI_STORAGE_ACCESS_KEY_ID |
wri |
MinIO access key |
WRI_STORAGE_SECRET_ACCESS_KEY |
wri-password |
MinIO secret key |
WRI_SECURITY_CORS_ORIGINS |
http://localhost:3000 |
Frontend origin for CORS |
cd backend
# Generate Prisma client (after schema changes)
npx prisma generate
# Run migrations (create tables)
npx prisma migrate deploy
# Seed test data (many demo users + sample workflows)
npx ts-node prisma/seed.ts --size=small
# Bootstrap seed (clean MVP: only admin + roles + 1 assessment)
npx ts-node prisma/seed.ts --bootstrap
# Reset everything (drop and recreate)
npx ts-node prisma/seed.ts --reset
# View database in browser
npx prisma studioThe seed creates test accounts you can use to login:
With --size=small (full demo data):
| Password | Role | |
|---|---|---|
student.test@wri.local |
LocalPass123! |
Student |
graduate.test@wri.local |
LocalPass123! |
Graduate |
employer.test@wri.local |
LocalPass123! |
Employer |
verifier.test@wri.local |
LocalPass123! |
Verifier |
admin.test@wri.local |
LocalPass123! |
Admin |
With --bootstrap (clean MVP start):
| Password | Role | |
|---|---|---|
admin@wri.local |
AdminPass123! |
Admin |
cd backend
npm testcd frontend
npx eslint src/cd frontend
npm run build| Role | How to get it | What they can do |
|---|---|---|
| Student | Self-register | Upload evidence, take assessments, request verification, receive credentials |
| Graduate | Self-register | Same as Student (graduate-specific profile) |
| Employer | Self-register | Create organizations, validate shared credentials |
| Verifier | Assigned by Admin | Review and approve/reject verification requests |
| Auditor | Assigned by Admin | View audit trail (backend API only) |
| Admin | Assigned by Admin | Manage users, assign roles, manage assessments, view audit |
1. Student registers → POST /auth/register
2. Student logs in → POST /auth/login → receives JWT token
3. Student uploads evidence → POST /evidence → POST /evidence/:id/files
4. Student takes assessment → POST /assessments/:id/attempts → POST /attempts/:id/submit
5. Student requests verification → POST /verifications (links evidence + assessment)
6. Verifier logs in → sees verification queue → GET /verifications
7. Verifier starts review → POST /verifications/:id/start
8. Verifier reviews evidence → POST /verifications/:id/review/evidence
9. Verifier reviews assessment → POST /verifications/:id/review/assessment
10. Verifier approves → POST /verifications/:id/approve → eligibility granted
11. Student issues credential → POST /credentials
12. Student shares credential → POST /credentials/:id/share → gets a share token
13. Student gives token to employer
14. Employer logs in → validates credential → POST /organizations/credential-validation
15. All actions recorded in audit log → GET /audit/records
| Page | URL | Role |
|---|---|---|
| Home | / |
Public |
| Login | /login |
Public |
| Register | /register |
Public |
| Dashboard | /dashboard |
Student/Graduate |
| Profile | /profile |
All authenticated |
| Evidence | /evidence |
Student/Graduate |
| Assessments | /assessments |
Student/Graduate |
| Take Assessment | /assessments/:id/attempt |
Student/Graduate |
| Verifications | /verifications |
Student/Graduate |
| Credentials | /credentials |
Student/Graduate |
| Employer Dashboard | /employer |
Employer |
| Organization | /employer/organization |
Employer |
| Validate Credential | /employer/validate |
Employer |
| Shared Credentials | /employer/shared-credentials |
Employer |
| Verifier Dashboard | /verifier |
Verifier/Admin |
| Admin Dashboard | /admin |
Admin |
| Action | Method | Endpoint |
|---|---|---|
| Register | POST | /api/v1/auth/register |
| Login | POST | /api/v1/auth/login |
| Get profile | GET | /api/v1/users/me |
| Submit evidence | POST | /api/v1/evidence |
| Upload file | POST | /api/v1/evidence/:id/files |
| List assessments | GET | /api/v1/assessments |
| Start attempt | POST | /api/v1/assessments/:id/attempts |
| Submit attempt | POST | /api/v1/attempts/:id/submit |
| Request verification | POST | /api/v1/verifications |
| Approve verification | POST | /api/v1/verifications/:id/approve |
| Issue credential | POST | /api/v1/credentials |
| Share credential | POST | /api/v1/credentials/:id/share |
| Validate credential | POST | /api/v1/organizations/credential-validation |
| Assign role | POST | /api/v1/admin/identity/assign-role |
| View audit | GET | /api/v1/audit/records |
Full API documentation: docs/API_CONTRACT_MVP.md
- This is a development/demo project, not production-ready
- No email verification or password reset
- No refresh token endpoint (users re-login after 1 hour)
- No logout endpoint (token is cleared client-side)
- Rate limiting is in-memory only (resets on server restart)
- File uploads are not scanned for malware
- HTTPS is not enforced (local development uses HTTP)
- Password policy requires only 8+ characters with letters and numbers
Before demonstrating the project:
- Docker is running (
docker psshows healthy containers) - Backend is running on port 3001 (
/api/v1/healthreturns OK) - Frontend is running on port 3000
- Database is seeded with test data
- You can login as
student.test@wri.local/LocalPass123! - You can login as
verifier.test@wri.local/LocalPass123! - You can login as
employer.test@wri.local/LocalPass123!
Docker containers won't start:
docker compose down -v # Remove old volumes
docker compose up -d # Start freshPrisma migration fails (authentication error):
docker compose down -v
docker compose up -d
# Wait 10 seconds
cd backend
npx prisma migrate deployBackend won't start (port in use):
# Check what's using port 3001
netstat -ano | findstr :3001
# Kill the process or change WRI_APP_PORT in backend/.envFrontend can't connect to backend (CORS error):
- Check that
WRI_SECURITY_CORS_ORIGINS=http://localhost:3000is inbackend/.env - Restart the backend after changing
.env
Login returns "Invalid credentials":
- Make sure you ran the seed:
npx ts-node prisma/seed.ts --size=small - Use the exact credentials from the test data table above
"Could not load profile" after login:
- The backend must be running when the frontend makes API calls
- Check browser console for network errors
| Document | Purpose |
|---|---|
| PROJECT_OVERVIEW.md | What this project does |
| USER_FLOWS.md | Step-by-step user journeys |
| API_CONTRACT_MVP.md | All backend endpoints |
| DATABASE_MVP_MAP.md | Database tables |
| SECURITY_MODEL_MVP.md | Authentication and security |
| FRONTEND_GUIDE.md | Frontend development guide |
| DESIGN_SYSTEM_v1.md | UI style guide |
| MANUAL_BACKEND_TESTING_GUIDE.md | Manual API testing |