This project is a Basic system to gather and store consent from users regarding various permissions. The system offers strong audit guarantees through its architecture
User documentation lives in the Astro Starlight site in frontend/docs. Run it with cd frontend/docs && nvm use && npm ci && npm run dev. It has two reading paths:
- Partner developers: deploy, configure ORCID, embed the widget, receive webhooks, integrate with APICURON.
- Administrators: dashboard, clauses, consent review, webhooks, audit trail.
Contributor notes are in Development below. Known gaps and planned work are in MISALIGNMENTS.md.
This project aims to offer a solution that can help partner platforms and APICURON maintain compliance with GDPR regulations, streamlining compliance and reducing risk factors when sharing personal data.
The APICURON Consent Management Platform is designed to be an infrastructure component deployed on the scientific resource's infrastructure, making compliance a more straightforward, default approach.
In accordance with the regulation mentioned above, this section will outline the specifications that need to be aligned to offer proper, reasonable mechanisms enabling compliance.
- Storage System
- Includes auditing features to be used for demonstrating compliance
- A user interface for providing and withdrawing consent
- Integrates a mechanism to present the privacy policy documents to users.
- Collects informed, explicit, and unambiguous consent.
- An application programming interface
- Gives secure access to consent-related data
- Offers means to react to consent withdrawals (e.g, webhooks) actively
- Portability
This system needs to be easily portable and deployable on various infrastructure environments (on-prem, cloud, etc).
The specification mentioned above does help apply GDPR principles and comply with regulations. This does not mean that this tool is a complete solution for GDPR compliance. On two main axes, the storage abstraction layer and the full extent of GDPR regulation.
- In the storage abstraction, this tool offers a way to prevent the application itself from deleting data or tampering but does not prevent the underlying infrastructure layer, (filesystem, diskI/O) from tampering, ensuring the compliance of these storage infrastructure layers is at the responsibility of the hosting organization.
- Complete Compliance: Here are some examples of articles where the specification above is not fit to assist in compliance.
-
Article 15 Right of access by the data subject
The specification includes a system that only stores consent and does not fall within other personal data that may be stored on different infrastructure components. -
Article 17 Right to erasure
The specification describes only a system to store consent data, which does not fall within the right to erasure, as it is a necessary processing of data to demonstrate compliance with the regulation. Any other personal data outside the scope of the specification is subject to this regulation.
Following the technical specification mentioned above. This section will describe the concrete decisions that are taken. The solution is only composed of three components: a web server, a database, and a web component.
The web component is a user interface component that users can use to state their consent explicitly. This component is supposed to be easily embedded in the scientific resource’s web interface with little effort and configuration. When the user explicitly gives their consent, this component would send a request to the web server in this system.
Tools used: Angular, Angular Elements
The web server acts as the core of the system. Once the web component submits consent data (either offer or withdrawal of consent), the web server stores the information in the database before propagating this information to the relevant parties.
Tools used: Node.js
The choice of KurrentDB (formerly EventStoreDB) is critical as its Event Sourcing pattern inherently provides a verifiable, anti-tampering audit trail by design. This native feature directly supports the GDPR's Demonstration of Compliance obligation
The Event Sourcing pattern defines an approach to handling operations on data that's driven by a sequence of events, each of which is recorded in an append-only store. Application code raises events that imperatively describe the action taken on the object.
Although the Event Sourcing pattern was initially developed to improve scalability in microservices by handling concurrent update operations, this pattern serves the aim of this project case by offering an audit trail by design, which is necessary to comply with regulations requiring verifiable, anti-tampering records for consent storage. These guarantees are essential in this case. Although traditional relational databases can offer an audit trail, they require advanced configurations that can become difficult to maintain & reproduce in different environments or through database versions.
Tools used: KurrentDB![][image1]Figure depicting Event Sourcing pattern architecture, source: azure
After identifying the elements of the system, this section will elaborate on the interactions of these elements within and outside the system. Of course, these interactions independently describe very little. Therefore, this section will describe these interactions through workflows with overlapping logical components and implementations that all serve different purposes based on the workflow.
Using the web component, users can offer or withdraw consent. The web component would make API calls to the consent management web server, which performs multiple actions in parallel.
- Update the event log in the database with the new event
- Publish a new state in the API, which means that every subscriber on the web API can get an event about the updated consent of the user, which includes APICURON & partner resources.
The system architecture mandates that the Web Server function as the authoritative Query Interface for consent data. This interface facilitates the synchronous retrieval of the aggregated consent state for a specified data subject. Critically, this function is designed to operate independently of the system's asynchronous event-driven workflows (e.g., consent recording or update propagation). This design ensures that partner resources, utilizing this API endpoint, can consistently and immediately access the most recent, consolidated consent record necessary for lawful data processing.
Each project pins its Node version in .nvmrc. Run nvm use in each folder.
| Project | Folder | Node | Framework |
|---|---|---|---|
| Backend | backend/ |
22 (lts/jod) |
NestJS 11, CQRS, Drizzle + SQLite, KurrentDB |
| Consent widget | frontend/consent-lib/ |
16 (lts/gallium) |
Angular 14 library + Angular Elements |
| Dashboard | frontend/dashboard/ |
24 | Angular 22 |
| Docs | frontend/docs/ |
24 | Astro Starlight |
cp .env.example .env # set JWT_SECRET and the ORCID sandbox credentials
docker compose run --rm --no-deps backend npm ci # the dev containers mount the source folders
docker compose run --rm --no-deps dashboard npm ci
docker compose up -d # API :3000 (Swagger at /docs), dashboard :4200, KurrentDB UI :2113To run the backend on the host instead, start only kurrentdb (docker compose up -d kurrentdb) and set KURRENTDB_CONNECTION_STRING=kurrentdb://localhost:2113?tls=false. Build the widget once (npm run build:wc in frontend/consent-lib) so the backend can serve it at /widget/widget.js (the dev compose mounts it and sets WIDGET_DIR).
Register an app at https://sandbox.orcid.org with the redirect URI http://localhost:3000/orcid/oauth/callback, then set ORCID_SANDBOX=true together with the sandbox client id and secret. (Inferring sandbox mode from a sandbox ORCID_API_URL still works but logs a deprecation warning.)
Command (in backend/) |
Purpose |
|---|---|
npm run start:dev |
Watch mode |
npm test / npm run test:e2e |
Jest unit tests / e2e against a real KurrentDB (docker compose up -d kurrentdb) |
npm run lint / npm run format |
ESLint / Prettier |
npm run db:generate |
Generate a migration from src/database/schema into drizzle/ (applied automatically on startup) |
npm run create-admin |
Create an admin interactively (runs the compiled CLI: build or start:dev first) |
npm run build / npm run start:prod |
Production build (dist/src/main.js) |
The KurrentDB integration is the local library backend/libs/nestjs-kurrent, imported as @biocomputingup/nestjs-kurrent. To rebuild the SQLite read model, delete the file at DB_FILE_PATH and restart; the projector replays every event.
Command (in frontend/consent-lib/) |
Purpose |
|---|---|
npm run watch:app |
Library + Tailwind in watch mode + demo app |
npm run build:lib |
Build the Angular library |
npm run build:wc |
Build the production web component into packages/apicuron-consent-wc/ (build:wc:dev for a dev build) |
npm run serve:demo |
Serve the demo page using the built widget |
npm run test:lib |
Karma tests |
In frontend/dashboard/: npm start serves on :4200, and npm test runs Vitest. The API URL defaults to src/environments/environment.ts; deployed containers load it at runtime from /config.json (API_URL).
- Production:
docker compose -f docker-compose.prod.yml up -d --build(see the docs site, Deploy). - Widget packages: push a tag
consent-lib-vX.Y.Z;.github/workflows/release-consent-lib.ymlpublishes@biocomputingup/apicuron-consentand@biocomputingup/apicuron-consent-wc(needs theNPM_TOKENsecret). Then pin that version inbackend/package.json. - CI:
.github/workflows/ci.ymlruns the backend (unit + e2e with KurrentDB), consent-lib and dashboard tests.
Copyright (C) the APICURON CMP contributors.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See LICENSE.