entrytwo

docs / getting-started / installation.md

Installation

This guide gets a self-hosted entrytwo instance running.

entrytwo is AI-native double-entry accounting with a Git-style data model. The AI posts real journal entries when confident. Full history, branches, blame. Self-hosted.


Deploy: Most production setups use a VPS with Docker and HTTPS. You can also install and run locally (e.g. on a Mac) for real use: same full app, like classic non-cloud accounting software. Runs on your VPS or locally.

Let AI install it for you

Not a terminal person? You can still get this running. Point an AI coding agent at this page (and the repo) and ask it to install entrytwo for you — for example Claude Code, OpenClaw, Hermes, Cursor, or a similar tool that can run commands on your machine or VPS. Share your goal (local Mac vs VPS + domain), paste errors back if something fails, and keep secrets (passwords, API keys) out of chats you do not trust. The steps below remain the source of truth; the agent just walks them with you. Canonical agent files: llms.txt and SKILL.md.

Prerequisites

  • Node.js 20+
  • PostgreSQL 14+ (or use the included Docker Compose)
  • Git
  • Docker & Docker Compose (strongly recommended for production-like setups)

Optional but useful:

  • A domain + reverse proxy (Caddy, Nginx, Traefik) for HTTPS
  • SMTP for magic-link login (and invitation emails)
  • Google and/or Microsoft Entra ID OAuth apps for staff sign-in
  • LLM API key (OpenAI, Anthropic, xAI, etc.)

Quick Start (Recommended - Docker Compose)

  1. Clone and enter the directory:
git clone https://github.com/felixkrusch/entrytwo.git
cd entrytwo/entrytwo_v1
  1. Copy the example environment:
cp .env.example .env
  1. Edit .env and set at minimum:
  • DATABASE_URL (or use the compose defaults)
  • POSTGRES_PASSWORD (required by compose)
  • AUTH_SECRET - generate with: openssl rand -base64 48 (≥ 32 characters)
  • ENCRYPTION_KEY - generate with: openssl rand -hex 32 (64 hex chars; bank feeds / secrets)
  • JOB_RUNNER_SECRET - generate with: openssl rand -base64 48
  • UPDATER_TOKEN - generate with: openssl rand -base64 48 (for the updater sidecar)
  • LICENSE_VERIFY_URL=https://entrytwo.com/api/license/verify (required later to activate a paid et1. key; the 30-day trial needs no key. Humans buy at Buy; agents must not complete Stripe checkout.)
  • At least one LLM key if you want AI/chat features (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
  • NEXT_LLM_PROVIDER and NEXT_LLM_MODEL (defaults are usually fine for development)
  1. Start the stack:
docker compose up -d

This starts:

  • app (Next.js on port 3000)
  • db (PostgreSQL)
  • worker (background jobs)
  • updater (isolated sidecar for signed updates - see Self-Hosted Updates)
  • redis
  1. Run migrations and seed:
docker compose exec app npx prisma migrate deploy
docker compose exec app npm run db:seed

(Local non-Docker development can use npx prisma migrate dev instead of deploy.)

  1. Open http://localhost:3000

On first visit with no admin users, you will be able to create the initial ADMIN account.


Handler mode (one compose per client)

entrytwo is not multi-tenant. One running stack = one legal entity.

If you keep books for several companies (bookkeeper, agency, “handler”), give each client its own compose project:

  • Own directory (or at least own .env, volumes, and backups)
  • Own COMPOSE_PROJECT_NAME (e.g. entrytwo-acme)
  • Own host port if they share a machine (3000:3000, 3001:3000, …)
  • Own secrets (POSTGRES_PASSWORD, AUTH_SECRET, ENCRYPTION_KEY, …)
  • Own .etbackup files — never restore company A’s backup into company B

RAM: plan ≥ 2 GB per stack. Chat lock on for bulk agent work; branch before bulk import; never post below 0.85 confidence; a human closes the period.


Environment Variables (Key Ones)

Copy .env.example and fill in:

Required (local or production)

  • DATABASE_URL
  • AUTH_SECRET (≥ 32 characters)
  • POSTGRES_PASSWORD (when using compose)
  • LICENSE_VERIFY_URLhttps://entrytwo.com/api/license/verify (paid key activation; trial needs no key)

Required in production (app refuses to start / health stays degraded without these)

  • ENCRYPTION_KEY (64 hex chars)
  • JOB_RUNNER_SECRET
  • AUTH_URL (public origin, e.g. https://books.example.com)
  • AUTH_TRUST_HOST=true (behind nginx/caddy/traefik)
  • At least one real sign-in method:
    • SMTP: SMTP_HOST, SMTP_USER, SMTP_PASS (and ideally SMTP_FROM), and/or
    • Google: GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET, and/or
    • Microsoft: MICROSOFT_ENTRA_ID_ID + MICROSOFT_ENTRA_ID_SECRET (+ issuer)

Strongly recommended

  • UPDATER_TOKEN - shared secret for the updater sidecar
  • LLM key + NEXT_LLM_PROVIDER / NEXT_LLM_MODEL

For production / updates

  • UPDATE_MANIFEST_URL - points at your signed release manifest
  • Cosign settings (COSIGN_IDENTITY, COSIGN_PUBLIC_KEY, etc.)
  • STRIPE_WEBHOOK_SECRET / PayPal secrets (if using processor feeds)

Full list and explanations are in the app .env.example. Production auth go-live: app repo docs/VPS-Auth-Install.md.


Manual Postgres (without Docker)

  1. Create the database:
CREATE DATABASE entrytwo;
CREATE USER entrytwo WITH PASSWORD 'entrytwo_dev_password';
GRANT ALL PRIVILEGES ON DATABASE entrytwo TO entrytwo;
  1. Set in .env:
DATABASE_URL="postgresql://entrytwo:entrytwo_dev_password@localhost:5432/entrytwo"
  1. Install deps, migrate, seed:
npm install
npx prisma migrate dev
npm run db:seed
npm run dev

First Admin Bootstrap

entrytwo is designed so the very first user in a fresh installation becomes an ADMIN.

  • If no ADMIN exists, the sign-in / onboarding flow allows creating one (company language + standard chart of accounts).
  • After the first admin exists, further users join via invitation (Admin → Users). They must sign in with the exact invited email using Google, Microsoft, or magic link.
  • Roles: ADMIN, ACCOUNTANT, or VIEWER.
  • In production, ADMIN must enable TOTP (2FA) before using the app.

See First Steps for the first-login experience.


Authentication (summary)

EnvironmentHow you sign in
Local developmentInstant email (“Continue”) — no SMTP required
ProductionMagic link (SMTP) and/or Google OAuth and/or Microsoft Entra ID

Instant email is disabled when NODE_ENV=production. Configure OAuth redirect URIs to {AUTH_URL}/api/auth/callback/google and .../microsoft-entra-id.


Health and Basic Verification

  • App health: GET /api/health
  • Production should report "ready": true and auth.productionReady: true
  • After docker compose up -d, the app container has a healthcheck on this endpoint

Production VPS (short)

Do these before expecting a green production boot:

  1. SecretsAUTH_SECRET, ENCRYPTION_KEY, JOB_RUNNER_SECRET, POSTGRES_PASSWORD (fresh per install).
  2. Sign-in — full SMTP (SMTP_HOST + USER + PASS) and/or Google/Microsoft OAuth. Instant email is off in production.
  3. Public URLAUTH_URL=https://your.domain, AUTH_TRUST_HOST=true.
  4. DNS — A record for the hostname → VPS IP before HTTPS. If certificates failed while DNS was missing, restart Caddy/nginx after DNS works.
  5. Deploydocker compose up -d --build (entrypoint runs migrations). Confirm /api/healthready: true.
  6. First admin — bootstrap, then TOTP for ADMIN.

RAM: plan ≥ 2 GB for one stack; ~4 GB if you run multiple installs (separate compose projects, ports, and hostnames) on one machine.

Packaging details and troubleshooting: app repo docs/PRODUCTION.md and docs/VPS-Auth-Install.md.


Common Next Steps

  • Read First Steps
  • Set up at least one LLM key and test the chat
  • Configure SMTP and/or OAuth so teammates can sign in
  • (Production) HTTPS, strong secrets, and the VPS auth checklist in the app repo
  • (Later) Bank feeds, updates, and licensing

See also: