Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nt3.io/llms.txt

Use this file to discover all available pages before exploring further.

Entri is open source and designed to be self-hostable. The same codebase that powers app.nt3.io can run on your own infrastructure with no managed service dependencies.

Quickstart (local Docker)

For evaluating Entri or running it on a laptop:
git clone https://github.com/nt3-io/platform.git entri
cd entri
cp .env.example .env
docker compose up -d
pnpm install
pnpm migrate
pnpm dev:api      # API on http://localhost:3333
pnpm dev          # Web app on http://localhost:4200
docker compose up boots three containers — MongoDB, Redis, and TypeSense — bound to 127.0.0.1 only, with persistent named volumes. That covers everything Entri needs to run locally; no external accounts required.
The local Compose stack runs without authentication and binds to loopback. Do not expose those ports to the internet or to a shared LAN.

Architecture overview

Entri is an Nx monorepo with three deployable apps:
AppStackDefault port
apps/apiNestJS (REST + GraphQL)3333
apps/appReact + Vite4200
apps/cliNestJS Commandern/a
Runtime dependencies:
  • MongoDB — primary datastore (Mongoose 8)
  • Redis — distributed cache and websocket pub/sub
  • TypeSense (or Algolia) — full-text search (optional; falls back to MongoDB regex)

Configuration

All configuration is via environment variables. Copy .env.example to .env and fill in what you need. The minimum required for the app to boot:
APP_HOST=https://entri.example.com
NX_BACKEND_URL=https://entri.example.com
BETTER_AUTH_SECRET=<32+ char secret `openssl rand -base64 32`>
BETTER_AUTH_URL=https://entri.example.com
MONGO_URI=mongodb://...
REDIS_URI=redis://...
Optional but recommended:
# Search backend — choose one
SEARCH_PROVIDER=typesense
TYPESENSE_HOST=typesense.internal
TYPESENSE_API_KEY=...

# AI translation — at least one provider
ANTHROPIC_API_KEY=...
OPENAI_API_KEY=...
GOOGLE_GENERATIVE_AI_API_KEY=...

# Outbound email — see "Outbound email" section below
EMAIL_FROM="Entri <noreply@example.com>"
RESEND_API_KEY=...
# or SMTP_HOST=..., SMTP_PORT=..., SMTP_USER=..., SMTP_PASS=...

# OAuth login
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
See .env.example in the repo for the complete list with inline documentation.

Search backend

Set SEARCH_PROVIDER to pick a search backend. Each option targets a different deployment shape:
ValueBackendBest for
typesenseSelf-hosted TypeSenseSelf-hosters who want full-text search without a SaaS dependency
algoliaHosted AlgoliaTeams already on Algolia or those wanting zero search ops
(unset)MongoDB regex fallbackTiny instances or development — works but slow at scale
After enabling a real search backend for the first time, backfill existing data:
curl -X POST -H "Authorization: Bearer <api_token>" \
  https://entri.example.com/api/v1/admin/search/sync/<projectId>

Outbound email

Entri sends transactional email for invitations, welcome messages, and password resets. Two transports are supported. Pick whichever fits your stack — both produce identical output. EMAIL_FROM is required whenever a real transport is configured. Use an address on a domain you control. If neither transport is configured, emails are logged to the console (useful for local dev).
TransportSetBest for
ResendRESEND_API_KEYCloud deployments where you want a managed sender
SMTPSMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSSelf-hosters using their own MTA, AWS SES, Mailgun, Gmail, etc.
When both are set, SMTP wins. Force a specific provider with EMAIL_PROVIDER=resend or EMAIL_PROVIDER=smtp.
# Option A — Resend
EMAIL_FROM="Entri <noreply@example.com>"
RESEND_API_KEY=re_...

# Option B — SMTP (e.g. AWS SES on us-east-1)
EMAIL_FROM="Entri <noreply@example.com>"
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_SECURE=false        # true for port 465, false for 587/STARTTLS
SMTP_USER=AKIA...
SMTP_PASS=...
The API verifies the SMTP connection on startup and logs a warning if it fails — sends will still be attempted, so misconfigurations show up in your logs without crashing the boot.

Production deployment

Entri ships with a multi-stage Dockerfile at the repo root that produces a single image containing both the API and the built web bundle. The reference deployment runs on Google Cloud Run, but the image is platform-agnostic — anywhere that runs OCI containers will work.
docker build -t entri:latest .
docker run --rm \
  -p 3333:3333 \
  --env-file .env \
  entri:latest
For multi-instance deployments (recommended for production), you’ll need:
  1. A managed MongoDB — Atlas, DocumentDB, or self-hosted with replication.
  2. A managed Redis — Memorystore, ElastiCache, Upstash, or self-hosted.
  3. A managed TypeSenseTypeSense Cloud or self-hosted on the same network as the API.
  4. Static asset hosting — the API serves the built web bundle by default; no separate hosting needed unless you want a CDN.
  5. A reverse proxy with TLS termination (Cloud Run / ALB / nginx).
The websocket progress streams (used by import/export) rely on Redis pub/sub for fan-out across instances. Make sure all API replicas talk to the same Redis.

Running migrations

The API does not auto-migrate. Run migrate-mongo up against your MongoDB before starting a new version:
MONGO_URI=mongodb://... pnpm migrate
In CI/CD, run this as a release-step job before rolling out the new image.

Updating

git pull
docker compose pull           # if using Compose locally
pnpm install
pnpm migrate
# rebuild and redeploy your image

Going further