Skip to content

About

Open source PRISMA review platform website, supporting the full process from BibTex/RIS import, to deduplication, abstract and full-text screening, conflict arbitration and data extraction.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

DOI

Prismatica logo

PRISMATICA

An open source review platform,following the PRISMA (Preferred Reporting Items for Systematic reviews and Meta-Analyses) protocol, built with Next.js, React, and TypeScript.

Full information about the PRISMA guidelines can be found at https://www.prisma-statement.org/.

Dashboard

Prismatica is designed for teams running evidence reviews, offering a clear workflow path and and auditability:

  • Structured PRISMA workflow from import to export
  • Multi-user collaboration with role-aware controls
  • Server-side state and signed HTTP-only sessions
  • Full-text PDF handling with validation and provenance
  • Configurable review thresholds and conflict handling
  • Audit trail and project-level traceability

Quick Start

1) Install dependencies

npm install

2) Run locally (HTTP)

npm run dev -- --hostname 127.0.0.1 --port 3000

Open http://127.0.0.1:3000.

3) Run locally (HTTPS)

npm run dev:https -- --hostname 127.0.0.1 --port 3000

This uses Next.js experimental local HTTPS support.

Features

  • Dashboard with PRISMA counts, audit trail, and progress indicators
  • Sign-in, optional captcha-protected registration, and server-managed sessions
  • Admin controls for password reset, account deletion, registration policy, checkout windows, and PDF upload size
  • Multi-project workspace with per-project navigation
  • Team membership management with owner safeguards
  • RIS and BibTeX import with provenance and review flow
  • Dedup candidate review with side-by-side comparisons
  • Title/abstract screening with append-only decisions, undo route support, and temporary checkout-based queue control
  • Full-text review with PDF upload, validation, streaming, DOI linking, and temporary checkout-based queue control
  • Dedicated bulk PDF upload page with paper assignment, DOI link pills, filtering, and per-file results
  • Conflict handling in full-text and extraction phases
  • Extraction templates (text, single-choice, multi-choice)
  • Extraction submissions with temporary checkout-based queue control, consensus routes, and configurable extraction voting
  • Export and report validation endpoints
  • Theme preferences (light, dark, system)
  • Path-based routing and refined UI components
  • Accessible navigation links, a skip link, keyboard menus, visible focus, and responsive review layouts

Accessibility and Mobile Layout

Page navigation uses links, including support for opening views in a new tab. Press Tab to reveal Skip to main content, which bypasses the header and sidebar. Keyboard focus is visible on links and controls, and page changes move focus to the main content.

The account menu supports Enter/Space to open or activate an item, Up/Down to open or move through items, Home/End to reach the first or last item, and letters to find an item by its initial. Escape closes the menu and returns focus to its button; Tab/Shift+Tab closes it and moves focus out. Opening mobile navigation focuses its first link; Escape closes it and returns focus to the navigation toggle.

On smaller screens, screening and duplicate queues start collapsed below the review panels. Full-text decision controls and extraction forms appear before their PDF viewers. The workflow diagram adapts from six columns to three, two, or one as the screen narrows.

Bulk PDF Upload

In Full text, open PDFs Upload beside Reviewed to manage PDFs for all full-text papers, including papers already reviewed. The table has DOI link pills for retrieval, search, pagination, and an Only papers missing PDFs filter.

  1. Choose Select PDFs and select one or more files, or choose a PDF from a paper's row.
  2. Assign each selected file to its paper. Choosing a paper with an existing PDF replaces its attachment.
  3. Choose Upload … PDFs to upload the batch. Each file shows its result, and failed files can be retried without uploading successful files again.

Files upload individually in sequence, with the configured size limit applied to each PDF. Existing PDFs can also be replaced or deleted from the table; deletion uses a browser confirmation. PDF upload is optional for full-text decisions.

Workflow Overview

  1. Create an account, sign in, and create a review project.
  2. Import records from RIS or BibTeX with batch provenance.
  3. Review deduplication candidates (when present).
  4. Screen title/abstract decisions (include/maybe/exclude).
  5. Advance studies to full-text, upload/validate PDFs, and review outcomes.
  6. Resolve conflicts and advance eligible studies to extraction.
  7. Define extraction templates and collect reviewer submissions.
  8. Build extraction consensus and validate/export reporting output.

Architecture Snapshot

  • Frontend: Next.js App Router + React UI
  • Backend: Next route handlers under app/api
  • State: Configurable server-side store
    • Default: JSON state file with atomic writes
    • Optional: PostgreSQL-backed state (PRISMATICA_STORAGE_MODE=postgres)
  • Auth: Signed HTTP-only cookie sessions with server-side checks in route handlers
  • Users/Settings persistence:
    • Included in the active state store (JSON or PostgreSQL)
    • Optional incremental PostgreSQL sync while workflows stay on JSON (PRISMATICA_USERS_SYNC_POSTGRES=true)
  • Files: PDF storage adapter
    • Local filesystem by default
    • MinIO/S3-compatible object storage via AWS SDK (PRISMATICA_OBJECT_STORAGE_PROVIDER=minio), with optional local fallback during migration

Core modules:

  • lib/serverStore.ts: persistence, business logic, and workflow mutations
  • scripts/postgres-state-io.mjs: PostgreSQL read/write adapter for full app state
  • lib/postgresUsersSync.ts: incremental PostgreSQL sync for users and auth/checkout settings
  • lib/serverAuth.ts: session and cookie handling
  • lib/serverRoute.ts: route helpers (auth, JSON, file responses)
  • lib/pdfStorage.ts: PDF storage abstraction (local/minio)
  • lib/objectStorage.ts: MinIO/S3-compatible object storage client
  • lib/workflow.ts: stage and decision-state progression logic
  • components/prisma-review-app.tsx: main application shell and view orchestration
  • components/prisma-review-ui.tsx: reusable UI presentation components

System Requirements

  • Node.js 20.9 or newer
  • npm

Ubuntu/Debian install example:

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

Verify:

node --version
npm --version

Scripts

npm run dev          # Next.js development server
npm run dev:https    # Dev server with experimental HTTPS
npm run build        # Production build
npm run start        # Production server (defaults)
npm run start:prod   # Production server bound to 127.0.0.1:3000
npm run check        # TypeScript and API authentication checks
npm run check:navigation # Navigation, keyboard, and responsive layout checks

Accounts and Access

No demo reviewer accounts are pre-created.

  • Register the first reviewer from the sign-in page.
  • Create projects and invite members from project settings.

A separate administrator account is seeded on startup by default:

  • Email: admin@prismatica.local
  • Password: change-me-admin

For non-local environments, set PRISMATICA_ADMIN_PASSWORD explicitly.

Server Storage and Sessions

For production, set a stable session secret and move data outside the repo:

Default behavior when PRISMATICA_DATA_FILE is not set:

  • State file: ./data/prismatica-state.json (relative to the project root)
  • PDF folder: ./data/pdfs/

Production example:

export PRISMATICA_SESSION_SECRET="replace-with-a-long-random-string"
export PRISMATICA_DATA_FILE="/var/lib/prismatica/prismatica-state.json"
npm run build
npm run start -- --hostname 0.0.0.0 --port 3000

Optional environment variables:

export PRISMATICA_INVITE_PASSWORD="temporary-password-for-invited-users"
export PRISMATICA_ADMIN_EMAIL="admin@example.com"
export PRISMATICA_ADMIN_PASSWORD="replace-this-default-admin-password"
export PRISMATICA_REGISTRATION_ENABLED="false"
export PRISMATICA_CAPTCHA_SECRET="replace-with-a-long-random-string"
export PRISMATICA_SECURE_COOKIES="true"
export PRISMATICA_ALLOWED_ORIGINS="https://prismatica.example.org"
export PRISMATICA_USERS_SYNC_POSTGRES="true"
export PRISMATICA_STORAGE_MODE="postgres"
export PRISMATICA_OBJECT_STORAGE_PROVIDER="minio"
export PRISMATICA_S3_ENDPOINT="http://127.0.0.1:9000"
export PRISMATICA_S3_REGION="us-east-1"
export PRISMATICA_S3_BUCKET="prismatica-pdfs"
export PRISMATICA_S3_ACCESS_KEY="minio-access-key"
export PRISMATICA_S3_SECRET_KEY="minio-secret-key"
export PRISMATICA_S3_FORCE_PATH_STYLE="true"

Session Secret Management

  • PRISMATICA_SESSION_SECRET signs session cookies; keep it private and high-entropy.
  • Use a long random value (at least 32 bytes of entropy).
  • Store it in environment variables or a secrets manager, not in git.
  • Use a different secret per environment (development, staging, production).
  • Rotating the secret invalidates all active sessions and requires users to sign in again.

Generate a strong secret:

openssl rand -base64 48

or

node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"

Notes:

  • Use PRISMATICA_SECURE_COOKIES=true only when serving over HTTPS.
  • PRISMATICA_REGISTRATION_ENABLED=false disables public registration for new data files.
  • Uploaded PDFs are stored under a sibling pdfs/ folder near PRISMATICA_DATA_FILE unless PRISMATICA_OBJECT_STORAGE_PROVIDER=minio is enabled.

Security Features

Prismatica uses server-side checks for the main web and API security boundaries:

  • Sessions are signed with PRISMATICA_SESSION_SECRET and stored in HTTP-only cookies.
  • PRISMATICA_SECURE_COOKIES=true marks session cookies as secure when the app is served over HTTPS.
  • Project pages are guarded before the workspace shell renders; unauthenticated /projects/* requests redirect to sign-in, and unauthorized authenticated users receive a not-found response.
  • API routes that read or mutate review data require an authenticated session and enforce project member, project owner, or admin permissions server-side.
  • Login and registration attempts are rate-limited to 10 attempts per minute by submitted email and client IP. The current limiter is in-memory, so use a shared store such as Redis or PostgreSQL if you run multiple app instances.
  • Registration can be disabled with PRISMATICA_REGISTRATION_ENABLED=false; when enabled, registration uses a signed captcha challenge.
  • Unsafe API methods (POST, PUT, PATCH, DELETE) are protected by a same-origin Origin/Referer check in proxy.ts. Extra trusted origins can be added with comma-separated PRISMATICA_ALLOWED_ORIGINS.
  • PDF uploads are restricted to PDF content, validated by an admin-configurable size limit (25 MB by default) and header, and served with private no-store caching and X-Content-Type-Options: nosniff.

PDF uploads use base64 JSON, so request bodies are approximately one-third larger than the PDF file. next.config.mjs sets experimental.proxyClientMaxBodySize to 150 MB to accommodate the admin form's maximum 100 MB PDF setting. The saved PDF limit (for example, 25 MB) remains enforced by the client and API. If configuring larger files outside the admin form, increase the proxy buffer limit accordingly. Changes to the Next.js configuration require a server restart and, in production, a rebuild before restarting.

PDF Object Storage: MinIO

Set PRISMATICA_OBJECT_STORAGE_PROVIDER=minio with the PRISMATICA_S3_* variables above to store new PDF uploads in MinIO or another S3-compatible service. Object keys use:

reports/<project-id>/<report-id>-<sha256>.<extension>

During migration, MinIO mode still reads from the local PDF folder when an object is missing. Disable that fallback after backfill and verification:

export PRISMATICA_PDF_LOCAL_FALLBACK="false"

Backfill existing local PDFs into MinIO:

npm run backfill:pdfs:minio -- --dry-run
npm run backfill:pdfs:minio

The backfill verifies each PDF checksum before upload and updates report storagePath values to the MinIO object key. It works with either JSON state or PRISMATICA_STORAGE_MODE=postgres.

Users And Preferences: PostgreSQL Migration (Incremental)

If you are rebuilding review data from scratch, you can migrate only accounts and auth preferences first.

What this migrates:

  • authSettings.registrationEnabled
  • authSettings.screeningCheckoutWindowMinutes
  • authSettings.extractionCheckoutWindowMinutes
  • users (profile fields, password hash/salt, admin flag, theme)

What this does not migrate:

  • projects, studies, reports, decisions, extraction, events, dedup candidates

Run:

export DATABASE_URL="postgresql://user:password@127.0.0.1:5432/prismatica"
export PRISMATICA_SOURCE_STATE_FILE="./data/prismatica-state.json" # optional
npm run migrate:users:postgres

To keep PostgreSQL users/auth settings updated while the app still uses JSON for review workflows, enable runtime sync:

export PRISMATICA_USERS_SYNC_POSTGRES="true"

When enabled (and DATABASE_URL is set), these mutations sync to PostgreSQL automatically:

  • registration
  • profile updates (including password changes)
  • admin auth-settings updates
  • invited user creation
  • admin users management

Note: when PRISMATICA_STORAGE_MODE=postgres is enabled, full review state is persisted in PostgreSQL relational tables; PRISMATICA_USERS_SYNC_POSTGRES is only needed for the legacy JSON-primary transition mode.

Schema file:

  • db/users_preferences.sql

PostgreSQL As Primary Review State Store

To make PostgreSQL the primary store for review workflow state (projects, studies, reports, decisions, extraction, events), set:

export PRISMATICA_STORAGE_MODE="postgres"
export DATABASE_URL="postgresql://user:password@127.0.0.1:5432/prismatica"

Behavior:

  • Prismatica persists users, auth settings, projects, imports, studies, reports, decisions, extraction state, events, and dedup candidates in PostgreSQL relational tables.
  • If the relational tables are empty on first start, Prismatica can bootstrap from PRISMATICA_DATA_FILE (or default ./data/prismatica-state.json) when available.
  • Legacy app_state_store is read only as a compatibility fallback during transition; new writes target relational tables.
  • After cutover, keep JSON only as backup/export fallback.

Network Access Patterns

Local subnet development

npm run dev -- --hostname 0.0.0.0 --port 3000

Then open http://<server-lan-ip>:3000.

If the LAN IP changes, add it in next.config.mjs under allowedDevOrigins and restart the dev server.

Public HTTP access (not recommended for production)

npm run build
npm run start -- --hostname 0.0.0.0 --port 3000

Then open http://<server-hostname-or-ip>:3000.

Recommended Production TLS: Caddy Reverse Proxy

Prismatica is intended to run behind Caddy with Next.js bound to localhost (127.0.0.1:3000).

Deployment assets:

  • deploy/caddy/Caddyfile: IP mode with tls internal
  • deploy/caddy/Caddyfile.letsencrypt: domain mode with public certificates
  • deploy/caddy/prismatica.service: systemd service for the Next.js app

Mode A: IP-only HTTPS (Caddy Internal CA)

Use this when no domain is available.

  1. Edit deploy/caddy/Caddyfile and replace 203.0.113.10 with your server IP.
  2. Activate config:
sudo cp deploy/caddy/Caddyfile /etc/caddy/Caddyfile
sudo systemctl reload caddy
  1. Install and start app service:
sudo cp deploy/caddy/prismatica.service /etc/systemd/system/prismatica.service
sudo systemctl daemon-reload
sudo systemctl enable --now prismatica
  1. Trust Caddy local root certificate on each client:
# On server
sudo cp /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt /tmp/caddy-local-root.crt

Debian/Ubuntu client trust:

sudo cp caddy-local-root.crt /usr/local/share/ca-certificates/caddy-local-root.crt
sudo update-ca-certificates

Fedora/RHEL client trust:

sudo cp caddy-local-root.crt /etc/pki/ca-trust/source/anchors/caddy-local-root.crt
sudo update-ca-trust

Mode B: Domain HTTPS (Let's Encrypt)

Use this for browser-trusted public certificates.

  1. Point DNS A/AAAA records to your server.
  2. Edit deploy/caddy/Caddyfile.letsencrypt and set your domain.
  3. Activate config:
sudo cp deploy/caddy/Caddyfile.letsencrypt /etc/caddy/Caddyfile
sudo systemctl reload caddy

Caddy will automatically request and renew certificates.

Switching TLS modes

# IP-only mode (internal CA)
sudo cp deploy/caddy/Caddyfile /etc/caddy/Caddyfile

# Domain mode (Let's Encrypt)
sudo cp deploy/caddy/Caddyfile.letsencrypt /etc/caddy/Caddyfile

sudo systemctl reload caddy

Production Hardening Checklist

  • Set a strong PRISMATICA_SESSION_SECRET
  • Set a strong PRISMATICA_ADMIN_PASSWORD
  • Enable PRISMATICA_SECURE_COOKIES=true when using HTTPS
  • Set PRISMATICA_ALLOWED_ORIGINS if the public browser origin differs from the app server origin behind a proxy
  • Keep Next.js behind localhost and reverse proxy through Caddy
  • Restrict filesystem permissions on data and PDF storage
  • Open only required firewall ports (443; optionally 80 for ACME HTTP challenge)
  • Keep Caddy internal CA materials restricted to trusted admins (if using internal CA mode)

Audit history retention

Administrators can set Registered Users → Review Settings → Audit history limit. The default is 100 events across all reviews, configurable from 1 to 10,000. The newest events are retained; lowering the limit removes older events, and raising it cannot recover events already removed. This applies to file and PostgreSQL storage. Existing settings without a history limit use 100; PostgreSQL adds the preference column automatically when its schema is initialized.

Dedup exclusions, keeping both entries, reopening pairs, and bulk inclusion appear in the project audit. Previously missing events cannot be recreated by this fix.

Run npm run check:audit for isolated regression checks using temporary data and a simulated PostgreSQL client; it does not start a server or contact a database.

Global review preferences are stored in review_settings and exposed through /api/admin/review-settings using the reviewSettings application-state field.

License

Prismatica is distributed under the MIT License.

About

Open source PRISMA review platform website, supporting the full process from BibTex/RIS import, to deduplication, abstract and full-text screening, conflict arbitration and data extraction.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages