A self-hostable health data platform that combines structured medical records with AI-assisted conversations. Upload lab results, health checkups, and personal health context — then chat with LLM providers using that data as grounded context.
Built with Next.js 15, TypeScript, PostgreSQL, and an optional Redis cache layer for production deployments.
- Features
- Architecture
- Workflows
- Project Structure
- Requirements
- Installation
- Configuration
- Development
- Testing
- Troubleshooting
- Contributing
- FAQ
- License
| Capability | Description |
|---|---|
| Health data ingestion | Upload PDFs and images; automatic parsing into structured JSON |
| Multi-provider LLM support | OpenAI, Anthropic, Google Gemini, and Ollama (local) |
| Privacy-first deployment | Run fully local with Docling + Ollama — no cloud required |
| Cloud mode | Managed blob storage and Trigger.dev background jobs |
| Internationalization | 10 locales via next-intl |
| Redis cache layer | Optional persistence for API response caching with graceful degradation |
| Credential encryption | AES-256-CBC for stored LLM API keys |
flowchart TB
subgraph Client
UI[Next.js App Router UI]
end
subgraph Application
API[API Routes and Server Actions]
Auth[NextAuth Credentials]
Parser[Health Data Parser]
Cache[Cache Service]
end
subgraph Data
PG[(PostgreSQL)]
Redis[(Redis)]
end
subgraph External
Docling[Docling Serve]
LLM[LLM Providers]
Blob[Vercel Blob]
Trigger[Trigger.dev]
end
UI --> API
API --> Auth
API --> Parser
API --> Cache
Cache --> Redis
API --> PG
Parser --> Docling
Parser --> LLM
API --> Blob
API --> Trigger
| Mode | Storage | Parsing | Background jobs |
|---|---|---|---|
local |
Filesystem (public/uploads) |
Docling (Docker) | In-process |
cloud |
Vercel Blob | Upstage / cloud APIs | Trigger.dev |
sequenceDiagram
participant User
participant API as /api/health-data
participant Parser as PDF Parser
participant DB as PostgreSQL
participant Cache as Redis
User->>API: POST multipart file
API->>DB: Create record (status PARSING)
API->>Parser: parseHealthData()
Parser-->>API: Structured JSON + OCR metadata
API->>DB: Update record (status COMPLETED)
API->>Cache: Invalidate cached entry
API-->>User: Parsed health data
sequenceDiagram
participant User
participant Middleware
participant Chat as /api/chat-rooms
participant LLM as Provider API
User->>Middleware: Request /chat/[id]
Middleware->>Middleware: Verify JWT session
User->>Chat: POST message
Chat->>LLM: Stream completion with health context
LLM-->>User: Assistant response
open-health/
├── docs/ # Engineering documentation and audit notes
├── messages/ # i18n translation files (10 locales)
├── prisma/ # Database schema, seed data, migrations
├── public/ # Static assets and local upload directory
├── src/
│ ├── actions/ # Next.js server actions
│ ├── app/ # App Router pages and API routes
│ ├── components/ # React UI components (shadcn/ui)
│ ├── context/ # React context providers
│ ├── hooks/ # Custom React hooks
│ ├── lib/
│ │ ├── api/ # API helpers (auth guards)
│ │ ├── config/ # Typed environment configuration
│ │ ├── encryption/ # AES encryption for API keys
│ │ ├── errors/ # Application error types
│ │ ├── health-data/ # Parsers (PDF, vision, document)
│ │ ├── logger/ # Structured JSON logging
│ │ └── redis/ # Connection manager and cache service
│ ├── trigger/ # Trigger.dev background tasks
│ ├── auth.ts # NextAuth configuration
│ └── instrumentation.ts # Server lifecycle hooks
├── docker-compose.yaml # Local stack: Postgres, Redis, Docling, app
├── Containerfile # Production container image
└── vitest.config.ts # Unit test configuration
Design decisions:
- Infrastructure code lives under
src/lib/with clear sub-modules - API auth guards are centralized in
src/lib/api/ - Redis is optional — the app degrades gracefully when
REDIS_ENABLED=false
- Node.js 20+
- Docker or Podman (recommended for local stack)
- PostgreSQL 15+
- Redis 7+ (optional, enabled by default)
git clone https://github.com/OpenHealthForAll/open-health.git
cd open-health
cp .env.example .env
# Edit .env — generate AUTH_SECRET and ENCRYPTION_KEY (see Configuration)
docker compose --env-file .env up --buildOpen http://localhost:3000 and register an account.
npm install
cp .env.example .env
# Start PostgreSQL and Redis locally, then:
npx prisma db push
npx prisma db seed
npm run devCopy .env.example to .env and configure the following:
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
AUTH_SECRET |
NextAuth signing secret (32+ random bytes, base64) |
ENCRYPTION_KEY |
AES-256 key for API key storage (32 bytes, base64) |
NEXT_PUBLIC_URL |
Public URL of the application |
Generate secrets:
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"| Variable | Default | Description |
|---|---|---|
REDIS_URL |
redis://localhost:6379 |
Redis connection URL |
REDIS_ENABLED |
true |
Set to false to disable caching |
REDIS_KEY_PREFIX |
open-health: |
Namespace prefix for cache keys |
REDIS_CONNECT_TIMEOUT_MS |
10000 |
Connection timeout |
REDIS_MAX_RETRIES |
10 |
Max retries per request |
| Variable | Values | Description |
|---|---|---|
DEPLOYMENT_ENV |
local or cloud |
Controls storage and parser backends |
LOG_LEVEL |
debug, info, warn, error |
Logging verbosity |
| Variable | Description |
|---|---|
OPENAI_API_KEY |
Platform OpenAI key |
ANTHROPIC_API_KEY |
Platform Anthropic key |
GOOGLE_API_KEY |
Platform Google key |
BLOB_READ_WRITE_TOKEN |
Vercel Blob storage |
TRIGGER_PROJECT_ID |
Trigger.dev project |
TRIGGER_SECRET_KEY |
Trigger.dev secret |
npm install # Install dependencies
npm run dev # Start dev server (port 3000)
npm run typecheck # TypeScript validation
npm run lint # ESLint
npm run test # Unit tests (Vitest)
npm run validate # typecheck + lint + test
npm run build # Production builddocker compose up database redis docling-serve # Infrastructure only
docker compose up --build # Full stackWhen running Ollama on the host machine:
- macOS:
http://docker.for.mac.localhost:11434 - Windows:
http://host.docker.internal:11434
Tests use Vitest and cover infrastructure modules:
npm run test # Run all tests once
npm run test:watch # Watch modeTest files are co-located with source: src/**/*.test.ts
Covered areas:
- Environment configuration parsing
- Redis key building and config
- Cache service serialization
- Application error types
Ensure ENCRYPTION_KEY is set in .env before running npm run dev. Generate a valid 32-byte base64 key (see Configuration).
- Verify Redis is running:
docker compose ps redis - Check
REDIS_URLmatches your environment - Disable Redis temporarily:
REDIS_ENABLED=false
Confirm Docling is running:
docker compose logs docling-servenpx prisma db push
npx prisma generatenpm install
npx prisma generate
npm run typecheck- Fork the repository
- Create a feature branch:
git checkout -b feat/your-feature - Run validation before committing:
npm run validate - Open a pull request with a clear description
See CONTRIBUTING.md for detailed guidelines.
Commit conventions: Use imperative mood (feat:, fix:, chore:, docs:, test:, refactor:).
Can I run this without Redis?
Yes. Set REDIS_ENABLED=false. The application skips caching and continues to work normally.
Is my health data sent to cloud LLMs automatically?
Only when you configure cloud LLM providers and use cloud deployment mode. Local mode with Ollama keeps inference on your machine.
What file formats are supported?
PDF, PNG, JPEG, and other image formats. PDFs are converted to images for vision-model parsing.
How are API keys stored?
LLM provider API keys are encrypted at rest using AES-256-CBC with your ENCRYPTION_KEY.
Does this replace medical advice?
No. OpenHealth is an informational tool. Always consult qualified healthcare professionals for medical decisions.
See LICENSE.