Skip to content

Getting Started

Nabeel Wasif edited this page Sep 29, 2026 · 2 revisions

Getting Started

Two supported setups: fully local and demo-compatible. pnpm setup asks which one you want, writes a working .env, and can run the remaining commands for you. Everything on this page is what it does, in case you would rather do it by hand.

Prerequisites: Node 22+, pnpm 11+. Docker for the fully local path. These are enforced (engines plus engine-strict), so an unsupported runtime fails at install with a clear message instead of somewhere confusing later.


1. The short version

git clone https://github.com/Anonify-v2-0/Anonify2.0.git && cd Anonify2.0
pnpm install
pnpm setup

pnpm setup asks:

  1. How to run it: fully local, or demo-compatible.
  2. Who can reach it: just you (--private), or public and shared (--public).
  3. Which services: Postgres / RustFS / Tesseract, or Neon / Vercel Blob / Mistral.
  4. Which AI provider and model, if any: it discovers models, pages and searches large catalogs, shows advertised capabilities (and list prices where the provider publishes them), and verifies the model with two synthetic requests before saving. See AI Providers.
  5. What the limits should be: quotas, rate limits, email parser limits, attachment and mailbox expansion.
  6. Whether to keep the secrets already in .env.

Who can reach it is a separate question from which services you use, because they are separate facts: a self-hosted instance on Neon is still your instance and gets your allowances. Answer "just me" and there are no daily quotas at all. Answer "public and shared" and one visitor's workbook stops being everyone's budget.

Every default it prints is read from the code that enforces it, so what you see is what is in force. Anything you leave alone is written into .env as a commented line, so the file says what the default is instead of leaving it to be discovered. Sizes are asked for and written as sizes (32MB, 512KB, 1.5GB), never as byte counts. A plain number is still read as bytes, so an existing .env keeps working.

Setup can finish the job (since 1.4.0)

At the end, setup offers to run what comes next: start Postgres and RustFS, migrate, warm OCR and start the app on the host, or run everything in Docker. It never mixes the two, because the app container would already hold the port pnpm dev wants. Each command is shown before it runs and runs only on an explicit choice. A failure offers retry, skip or stop, and stopping prints exactly the commands that did not run.

Scripted / non-interactive

pnpm setup --local --defaults --yes   # no questions: local, profile defaults
pnpm setup --public                   # a shared instance: strict limits
pnpm setup --demo                     # the hosted demo's services
pnpm setup --force                    # overwrite .env without asking
pnpm setup --help                     # every flag

--yes and non-interactive runs stay offline for AI: existing AI settings are kept, and no model discovery or billable probe happens. Run pnpm ai verify separately to verify a model in a script.

Re-running it is safe. It backs up the previous .env and offers to reuse the secrets already in it, which matters more than it sounds. Documents are sealed with per-document keys wrapped by ENCRYPTION_KEY, so a new key does not reset anything. It makes everything already stored permanently unreadable.


2. Fully local: nothing leaves your machine

Postgres and RustFS (S3-compatible storage) run in Docker, OCR runs through Tesseract, and the optional model pass can run through Ollama, LM Studio or llama.cpp. No external service is involved at any point.

pnpm install
pnpm setup --local      # generates the secrets; can start everything for you
docker compose up -d    # Postgres, RustFS, migrations, then Anonify itself
                        # http://localhost:3000

docker compose up -d runs the whole application. The app image is built from this repo, the schema is applied before the app starts, and nothing needs to be installed on the host beyond Docker. pnpm install and pnpm setup are there to write .env, which is where the encryption key and the fingerprint secret come from.

What Compose starts

Service Port Credentials Purpose
app 3000 none Anonify, built from this repo
postgres (Postgres 17) 5432 anonify / anonify the database, in place of Neon
rustfs 9000 (API), 9001 (console) anonify / anonify-dev-secret S3-compatible storage, in place of Vercel Blob
rustfs-volume-init none none gives the data volume to RustFS's user, then exits
rustfs-init none none idempotently creates the anonify bucket, then exits
migrate none none applies migrations and the workflow schema, then exits
scheduler none none opt-in expiry sweep (--profile scheduler); see Deployment

Every published port binds to 127.0.0.1 only, and data lives in named volumes across restarts. The credentials are development defaults. Do not reuse them anywhere reachable from outside your machine.

Port already taken? Set APP_PORT, POSTGRES_PORT, RUSTFS_PORT or RUSTFS_CONSOLE_PORT in .env. The addresses used inside the Compose network are fixed.

The app starts only after migrate exits successfully, so a fresh up never serves against an unapplied schema. The image is a Next.js standalone build on node:22-slim: Debian rather than Alpine, because sharp, @napi-rs/canvas and the pdf.js renderer ship native binaries. Durable runs live in Postgres through @workflow/world-postgres, and instrumentation.ts starts the worker that polls for jobs. Without that worker, uploads are accepted and never processed.

Upgrading from MinIO? The local stack moved to RustFS in 1.3.0 and reuses your existing anonify_minio-data volume. The import is one-way, so back the volume up first. See Upgrading.

Developing with the app on the host

Start only the dependencies, so port 3000 stays free:

docker compose up -d postgres rustfs rustfs-init
pnpm db:migrate         # apply the schema
pnpm ocr:warm           # optional: fetch the OCR model now rather than on the first scan
pnpm dev                # http://localhost:3000

The resulting .env

ANONIFY_PROFILE=self-hosted
DATABASE_URL=postgresql://anonify:anonify@localhost:5432/anonify
DATABASE_DRIVER=postgres
STORAGE_DRIVER=s3
S3_ENDPOINT=http://localhost:9000
S3_BUCKET=anonify
S3_ACCESS_KEY_ID=anonify
S3_SECRET_ACCESS_KEY=anonify-dev-secret
OCR_PROVIDER=tesseract
ENCRYPTION_KEY=...       # generated by pnpm setup
FINGERPRINT_SECRET=...   # generated by pnpm setup
# optional, a local model:
AI_PROVIDER=ollama
AI_MODEL=qwen3-vl:4b-instruct
AI_MODEL_CAPABILITIES=...   # written by setup / pnpm ai verify

Without even Docker

Leave STORAGE_DRIVER unset and uploads go to .anonify-storage/ on disk. You still need a Postgres somewhere, because the redaction record lives in it.


3. A local model (optional)

ollama pull qwen3-vl:4b-instruct          # a small vision model CI also tests against
pnpm ai verify --provider ollama --model qwen3-vl:4b-instruct

pnpm setup --local offers the first local server it finds (Ollama, then LM Studio, then llama.cpp). With the app in Docker, localhost in OLLAMA_BASE_URL is mapped to the Docker host automatically. On Linux, Ollama must listen on an interface Docker can reach. Details: AI Providers §3.


4. Demo-compatible: the same services as the hosted demo

pnpm install
pnpm setup --demo
# fill in the keys it lists
pnpm db:migrate
pnpm dev
Variable Service Needed for
DATABASE_URL Neon The database. Use the pooled string.
BLOB_READ_WRITE_TOKEN Vercel Blob Storage. The browser uploads directly to it.
MISTRAL_API_KEY Mistral OCR for scanned documents (OCR_PROVIDER=mistral).
AI_GATEWAY_API_KEY Vercel AI Gateway Optional: the contextual detection pass.
CRON_SECRET your host Gates the expiry sweep.

ANONIFY_PROFILE=demo selects the strict rate limits and small daily quotas the shared demo runs with.


5. AI detection is optional

With no model configured, pattern detection, manual redaction, search, rules, learned rule offers and export all work. Only the contextual pass (names, prose addresses, roles) and Hush are unavailable, and the document says the model pass did not run. See Redaction Engine.


6. OCR, the first time

Tesseract downloads its model the first time it reads a scanned document (~2–13 MB depending on OCR_TESSERACT_MODEL) and caches it in .cache/tesseract (gitignored). pnpm ocr:warm fetches it during setup instead, so the wait comes at a moment when waiting is expected. In a container, point TESSERACT_CACHE_PATH at a mounted volume. Engine and model are separate choices: Configuration §4.


7. First redaction, end to end

  1. Open http://localhost:3000.
  2. Drag in a file, or several (they become a batch, with one review pass and one download at the end), or a mailbox (one document per message).
  3. Pick a preset. It changes what is looked for and nothing else. See Redaction Presets.
  4. Wait for processing. Progress streams, and a closed tab does not cancel it.
  5. Review. Accept or reject suggestions (grouped by value), click a word, drag across text or a region, redact a cell, row or column, search (Ctrl/⌘+F) and Redact all, write a rule, or ask Hush (Ctrl/⌘+K). Nothing is removed that you did not accept. See Review Workspace.
  6. Export. Choose a method per category (mask, pseudonymize, tokenize, encrypt), and optionally several copies. The artifact is re-opened and read adversarially before it is delivered. Beside it you get a report (counts, methods, what you declined, both checksums) and, for a reversible method, a vault that is shown once and stored nowhere. See Export and Anonymisation.
  7. Download. The link is signed, lasts five minutes, and is checksum-verified as it streams.

Documents expire after 1 h, 6 h, 24 h or 3 days (extendable up to 72 h from upload). A scheduled sweep then deletes the source, the normalized model, every export and the row. If you self-host, you have to run that sweep yourself: see Deployment.


Next

Clone this wiki locally