Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ certs/
vhost/
CHANGELOG.md
docker-compose.example.*
dist/
16 changes: 11 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# MongoDB connection info
DB_USER=
DB_PASS=
DB_HOST=127.0.0.1
# Where shared credentials are kept: memory, mongodb or dynamodb (required).
# See settings.contract.json for every setting.
CREDENTIAL_STORE=mongodb

# MongoDB connection info: DB_URI, or (as in 2.x, for MongoDB Atlas) DB_HOST
# with DB_USER and DB_PASS. Not both.
DB_URI=mongodb://127.0.0.1:27017
#DB_USER=
#DB_PASS=
#DB_HOST=your-cluster.mongodb.net
DB_NAME=verifier-plus-DEV
DB_COLLECTION=credentials

Expand All @@ -14,4 +20,4 @@ HTTPPORT=80
EXCHANGES=on
# Where wallets reach the exchange (optional; defaults to the address the
# browser used to reach the page)
EXCHANGE_SERVER_URL=https://verifierplus.org
#EXCHANGE_SERVER_URL=https://verifierplus.org
132 changes: 132 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
unit:
name: Unit tests and types
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm test
- run: npx tsc --noEmit

stores:
name: Store contract tests (memory, MongoDB, DynamoDB Local)
runs-on: ubuntu-latest
services:
mongo:
image: mongo:7
ports: ["27017:27017"]
dynamodb:
image: amazon/dynamodb-local:3.3.2
ports: ["8000:8000"]
env:
TEST_STORES: memory,mongodb,dynamodb
TEST_MONGO_URI: mongodb://localhost:27017
DYNAMODB_ENDPOINT: http://localhost:8000
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm test

# Proves the Lambda build: dist/lambda/run.sh serves the app with the memory
# store. It does not prove the Lambda Web Adapter, DynamoDB or API Gateway;
# the staging deployment's tests do.
lambda-shape:
name: Lambda build and smoke test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run build:lambda
- name: Start dist/lambda/run.sh
run: |
CREDENTIAL_STORE=memory EXCHANGES=off PORT=3000 dist/lambda/run.sh > server.log 2>&1 &
for _ in $(seq 1 60); do
if curl -sf http://localhost:3000/health/ready; then exit 0; fi
sleep 1
done
echo "/health/ready did not answer 200 within 60 s"
cat server.log
exit 1
- run: npx playwright install --with-deps chromium
- run: npx playwright test --project=API --project=DesktopChrome --project=Lambda
env:
PLAYWRIGHT_TEST_URL: http://localhost:3000
- if: ${{ !cancelled() }}
run: cat server.log
- if: ${{ !cancelled() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: playwright-report-lambda-shape
path: playwright-report/
retention-days: 14

# Proves the Docker image self-hosters run: MongoDB store, exchanges on.
docker:
name: Docker image with MongoDB
runs-on: ubuntu-latest
services:
mongo:
image: mongo:7
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .nvmrc
cache: npm
- run: docker build -t verifier-plus:ci .
- name: Start the image beside MongoDB
run: |
docker run -d --name verifier-plus \
--network "${{ job.services.mongo.network }}" -p 3000:3000 \
-e CREDENTIAL_STORE=mongodb -e DB_URI=mongodb://mongo:27017 -e EXCHANGES=on \
verifier-plus:ci
for _ in $(seq 1 60); do
if curl -sf http://localhost:3000/api/healthz | grep -q '"healthy":true'; then
curl -s http://localhost:3000/api/healthz
exit 0
fi
sleep 1
done
echo "/api/healthz did not report healthy within 60 s"
curl -s http://localhost:3000/api/healthz || true
docker logs verifier-plus
exit 1
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test --project=API --project=DesktopChrome --project=Lambda
env:
PLAYWRIGHT_TEST_URL: http://localhost:3000
- if: ${{ !cancelled() }}
run: docker logs verifier-plus
- if: ${{ !cancelled() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: playwright-report-docker
path: playwright-report/
retention-days: 14
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

# production
/build
/dist/

# misc
.DS_Store
Expand Down
28 changes: 26 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
# verifier-plus Changelog

## Unreleased
## 3.0.0
### Breaking
- `CREDENTIAL_STORE` and `EXCHANGES` are required. Docker users who set only `DB_USER`, `DB_PASS` and `DB_HOST` must add `CREDENTIAL_STORE=mongodb`, and `EXCHANGES=on` to keep requesting credentials from the Learner Credential Wallet (or `EXCHANGES=off`). See the README's "Upgrading from 2.x".
- `NEXT_PUBLIC_EXCHANGE_SERVER_URL` is replaced by `EXCHANGE_SERVER_URL`, which is optional and defaults to the address the browser used to reach the page. The old name is no longer read; the server logs a warning when it is set.
- A missing or invalid setting no longer crashes the server or a request. The server starts, routes that need the setting answer 503 naming it, and `/health/ready` reports it.
- Shared credentials expire one year after they are shared (`CREDENTIAL_TTL_DAYS`). Records stored by 2.x have no expiry date and are treated as expiring one year after they were created.
- The MongoDB store creates a unique index on `id` and a TTL index on `expiresAt`. A collection that already has a different index named `id_1` cannot share credentials until that index is removed; `/health/ready` names it.
- `npm test` runs the unit tests (Vitest). The Playwright tests moved to `npm run test:e2e`.
### Added
- Credential stores: `memory` (local development and tests), `mongodb` and `dynamodb`.
- `DB_URI`, a plain `mongodb://` or `mongodb+srv://` connection string, as an alternative to `DB_HOST`, `DB_USER` and `DB_PASS`.
- A 350 KB limit on a shared credential: larger ones get 413 "Credential too large to share".
- `/health/ready`: 200 when every setting is valid and the credential store answers, otherwise 503 naming what is wrong.
- `/api/config`: whether exchanges are on, and where wallets reach them. The landing page reads it.
- Limits that keep every request inside AWS Lambda's: a 20 second deadline on `/api/verify` and on `/api/proxy`'s fetch (504), a 5 MB limit on request bodies (413), and a 5 MB limit on documents `/api/proxy` fetches (502).
- `npm run build:lambda`, which builds `dist/lambda/` for AWS Lambda with the Lambda Web Adapter.
- `settings.contract.json`, every environment variable the app reads, checked against the code by a unit test.
- CI on every pull request: unit tests, the store tests against MongoDB and DynamoDB Local, the Lambda build with a smoke test, and the Docker image with MongoDB.
### Changed
- Node 24; Next 15.5.29
- Node 24; Next 15.5.29.
- `GET /api/credentials/{id}` no longer returns MongoDB's `_id`.
- `EXCHANGES=on` is refused on AWS Lambda: exchanges are kept in process memory.
- The Playwright tests no longer compare screenshots; they check the verification icons and that images loaded.
### Fixed
- A credential that cannot be read (unknown, unshared or expired) returns 404 with an error body, instead of 500.
- `/api/verify` answers 400 to a body that is not JSON, instead of 500.
- The Docker image listens on every interface, so `/api/healthz` works inside the container.

## 2.0.2
### Added
Expand Down
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ ENV PORT=3000

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
# ENV HOSTNAME="0.0.0.0"
# Listen on every interface. Docker sets HOSTNAME to the container id, which
# would make server.js listen on that interface only, so /api/healthz could
# not reach the server at localhost.
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

Loading
Loading