Offline-first, multilingual digital informed consent for field research in Namibia.
ConsentLink helps researchers turn a research proposal into a clear, approved consent form, then collect consent in the field. It works with no internet, in the participant's own language, and with read-aloud and voice answers. On-device AI drafts the form, and the researcher approves every section before anyone sees it.
ConsentLink 2.0 is a full rebuild of the original ConsentLink prototype (Ndamonako & Indongo, 2026), developed as a group prototype project for HCA820S – Human-Centred AI at the Namibia University of Science and Technology (NUST).
For researchers
- Upload a proposal (PDF, DOCX, ODT, TXT, scans), or describe your study by voice or text, and get a draft consent form
- On-device AI drafting (no internet, proposal never leaves the phone), with source page, changes and confidence shown for every section
- A rule checker for the 10 required consent elements (plus 4 more for studies with minors)
- Section-by-section human approval: nothing reaches a participant unapproved
- Google-Forms-style form builder: text, choice, date, signature, thumbprint, audio, photo and GPS fields
- Analytics: recruitment progress, agreement rate, comprehension by language
- Export to PDF, Word (.docx) and CSV
For participants
- Large text, read-aloud on every screen, and voice or typed answers
- Equal-weight Yes / No choices (no nudging)
- Consent by signature, thumbprint or verbal recording, with a witness or interpreter
- Minors: guardian consent plus the child's own assent; the child's "No" always stops participation
- Optional feedback pop-up after completion (never shown to minors)
For institutions
- Each institution's template, logo and colours apply automatically to its researchers
- Admins upload a template; the app reads its fonts, spacing and margins and clips the logo from a PDF
- Dynamic theming per institution (UNAM, NUST, IUM, Welwitchia and more), with ConsentLink green as the default
Languages: English, Afrikaans, Deutsch, Otjiherero, Khoekhoegowab, Rukwangali, Silozi and Oshiwambo. AI translations into local languages are always checked by a fluent human reviewer before use.
Uploads: executables, scripts and macro-enabled documents are blocked.
ConsentLink is designed around the two-dimensional HCAI framework: high automation and high human control.
| Principle | In ConsentLink |
|---|---|
| Human oversight | AI drafts; researchers approve each section; fluent reviewers sign off translations |
| Transparency | Every AI section shows its source page, what changed and its confidence |
| Accountability | Append-only audit log of approvals, exports and withdrawals |
| Privacy & security | On-device AI, encrypted local storage, row-level security per institution |
| Fairness | Comprehension and completion compared across language groups |
| Autonomy | Participants control language, pace, text size and audio, and can say no without penalty |
Every screen must also pass our 11 design rules based on the UX laws (Hick, Fitts, Jakob, Miller, Peak-End, Aesthetic-Usability, hierarchy, consistency, accessibility, feedback, user control). See the Figma file.
| Layer | Technology |
|---|---|
| App (Android, iOS, desktop) | Flutter, Riverpod, go_router |
| Local storage | drift (SQLite) + SQLCipher |
| Sync | PowerSync + Supabase |
| Backend | Supabase: Auth (email confirmation), Postgres + RLS, Storage, Edge Functions |
| On-device AI | llama.cpp with Gemma 4 E2B (fallback: Qwen 3.5 0.8B), GGUF format |
| Speech | whisper.cpp (EN/AF/DE), flutter_tts, recorded native-speaker audio |
| Document service | Python, FastAPI, PyMuPDF, python-docx, OpenCV, oletools, ClamAV |
| Export | pdf + printing, docx_template, CSV |
| CI/CD | GitHub Actions |
ConsentLink/
├─ app/ Flutter app
│ ├─ lib/
│ │ ├─ core/ theme, router, feature flags, l10n (P1)
│ │ ├─ contracts/ interfaces + models (ALL)
│ │ ├─ fakes/ fake implementations of each contract
│ │ ├─ features/ screens (P1)
│ │ ├─ data/ database, repositories, sync, export (P2)
│ │ ├─ ai/ model runtime, prompts, rules, speech (P3)
│ │ ├─ main_dev.dart runs on fakes
│ │ └─ main_demo.dart runs on real implementations
│ ├─ l10n/ translations (.arb)
│ └─ test/
├─ service/ Python FastAPI document service (P4)
├─ supabase/ migrations, seed data, RLS, functions (P2)
├─ schemas/ shared JSON schemas (ALL)
├─ notebooks/ SUS + fairness analysis (P4)
├─ docs/ decisions, API notes, model card
└─ .github/ workflows, CODEOWNERS, PR template (P2)
- Flutter (stable channel) and Android Studio or Xcode
- Python 3.12+
- Supabase CLI (optional; Docker is needed only for a local Supabase)
- A physical Android phone with 6 GB+ RAM for testing on-device AI
git clone https://github.com/DrGitman/ConsentLink.git
cd ConsentLink
cp .env.example .env # fill in values from the team (never commit .env)cd app
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter run -t lib/main_dev.dartflutter run -t lib/main_demo.dart \
--dart-define=USE_REAL_AI=true \
--dart-define=USE_REAL_SYNC=true \
--dart-define=USE_REAL_TEMPLATES=trueAny service can be switched back to its fake by setting its flag to false.
The template upload screen can be pointed at the Python service with
--dart-define=TEMPLATE_SERVICE_URL=http://10.0.2.2:8000 for the Android
emulator (use a reachable host address on a physical device). Upload also
requires an authenticated user's Supabase access token through
templateAccessTokenProvider; this build leaves that provider unconfigured
until the P2 auth integration is connected. Do not pass bearer tokens or
service-role keys with --dart-define.
cd service
py -m venv .venv
.venv\Scripts\python.exe -m pip install -e ".[dev]"
$env:SUPABASE_JWT_SECRET = "<local signing secret provided by P2>"
$env:SUPABASE_URL = "<your Supabase project URL>"
$env:SUPABASE_ANON_KEY = "<your Supabase anon key>"
$env:SUPABASE_TEMPLATE_BUCKET = "<private template bucket with caller upload permission>"
.venv\Scripts\python.exe -m uvicorn app.main:app --reloadThe service uses SUPABASE_JWT_SECRET to verify bearer tokens and SUPABASE_URL plus SUPABASE_ANON_KEY to access private Supabase Storage with the caller's JWT. SUPABASE_TEMPLATE_BUCKET must name a private bucket whose Storage policies allow the signed-in caller to upload. Never use a service-role key in client requests.
All four variables are required for the complete service. Keep their values in the deployment platform's secret/environment configuration; do not commit them or include them in support logs. For local development, set them in the current PowerShell session as shown above. The development command uses --reload; for deployment, run Uvicorn without reload, bind to the platform-provided port, and terminate it through the platform's normal process manager.
POST /v1/files/scan accepts one multipart file up to 25 MB. It checks PDF, DOCX, ODT, TXT and PNG/JPEG/TIFF signatures, rejecting unsupported types, executables, scripts and macro-enabled documents. Files are checked in memory and not saved.
This endpoint is for templates (they are uploaded to Storage anyway). Research proposals are never uploaded: the app only checks their type and size on the phone before on-device drafting.
POST /v1/templates/upload accepts one multipart PDF or DOCX file up to 25 MB. It scans and analyzes the file before uploading it to the configured bucket with the caller's JWT, then returns its storage_path, detected style properties, confidence values, PDF logo candidates, and detected text sections (sections, each with title and text). POST /v1/templates/analyse returns the same analysis fields, including sections, for an existing object and accepts JSON with storage_path in bucket/object/path format. Section extraction uses document headings and typography, so it is best-effort; documents without detectable headings return an empty list. The service does not identify or enforce the ten required consent elements or determine whether wording is official; the app must retain those safeguards. Unsafe, unsupported, or invalid uploads are rejected without being stored. Pass the returned storage_path to POST /v1/logos/extract with a 1-based PDF page and optional crop array of four page coordinates; the extracted PNG is written to the same bucket under logos/. All Storage operations use the caller's JWT.
PDF and DOCX style extraction is supported. Logo candidate detection and extraction currently require PDF input. The 1–60 page limit applies only to consent-form templates sent to this service for style analysis or logo extraction; research proposals, theses, and mini-theses are not uploaded to this template service. DOCX pagination is renderer-dependent, so DOCX templates are limited by the shared 25 MB upload cap instead. Password-protected PDFs, malformed templates, and documents without extractable PDF text return validation errors. Supabase Storage operations have a 20-second timeout. Open http://localhost:8000/health to check the service or http://localhost:8000/docs for API documentation. Run the service tests from service/ with:
.venv\Scripts\python.exe -m pytestDeployment health checks should use GET /health (or the versioned GET /v1/health) and require HTTP 200. Service error logs are JSON records with stable event names and limited diagnostic fields; they intentionally omit request bodies, filenames, storage paths, tokens, upstream response bodies, and exception messages. Treat application and platform logs as operational data and restrict access through the deployment provider.
supabase start
supabase db reset # applies migrations + seed dataModel files are not stored in Git. The app downloads the model after sign-in, on Wi-Fi (about 1.3 GB). For development, put the GGUF file path in .env (see .env.example).
We use trunk-based development: main is the only long-lived branch. Each member works on short-lived task branches under their own prefix:
| Member | Prefix | Example |
|---|---|---|
| P1 · UI & accessibility | ui-dev/ |
ui-dev/7-onboarding-screens |
| P2 · Data, sync & export | data-dev/ |
data-dev/9-powersync-setup |
| P3 · On-device AI & speech | ai-dev/ |
ai-dev/12-rule-checker |
| P4 · Python service & HCI | py-dev/ |
py-dev/15-template-style-api |
Read CONTRIBUTING.md before your first branch.
| Role | Member | GitHub |
|---|---|---|
| P1 · UI & accessibility | Orilio Naobeb | @DrGitman |
| P2 · Data, sync & export | Erastus Shingenge | @erastusshingenge-maker |
| P3 · On-device AI & speech | Amaury Cansa | @1Cansa |
| P4 · Python service & HCI | Kristofina Shipalanga | @Tuilika |
Lecturer: Mr Naftali N. Indongo, HCA820S, NUST.
All screens, the design system, diagrams, HCI roadmap and task plan are in the ConsentLink Figma file: add link.
| Figma page | What's there |
|---|---|
| App Identity | Logo, design system, the 11 design rules |
| Screen Artboards | All screens, numbered (e.g. 04.3) |
| Development Diagrams | Architecture, contracts, work packages, sequences, integration plan |
| Design Diagrams | User flows and information architecture |
| HCI Roadmap | Course concepts applied, UX laws, personas, stakeholder session |
| Task Distribution and Planning | Roles, day-by-day plan, version control |
| Wireframes / Animations | Low-fi screens, motion specs |
Refer to screens by their number in issues and pull requests.
| Date | Milestone |
|---|---|
| Sun 4 – Mon 5 Oct | Contracts + fakes merged, AI model spike |
| Tue 6 Oct | Stakeholder engagement session |
| Fri 9 Oct | Integration day |
| Sun 11 Oct | v0.1.0 internal demo |
| Mon 12 Oct | Usability test round 1 |
| Fri 16 Oct | Usability test round 2 (SUS + fairness check) |
| Sun 18 Oct | v1.0.0 release |
- Use test data only in development and shared environments. Never commit real participant data.
- Real data may only be collected under an approved ethics clearance.
- Studies with minors require ethics approval that covers minors, guardian consent and child assent.
Builds on ConsentLink: A Multilingual Digital Informed Consent Application for Research Data Collection in Namibia (Ndamonako & Indongo, 2026, NUST).
AI assistance was used to help resolve conflicts in the repository.
To be decided by the team.