# PaperJet PaperJet is a self-hosted, single-user PDF annotation editor. It keeps the uploaded PDF immutable, stores annotations separately, autosaves the working layer, and flattens supported annotations only when exporting. ## Run with Docker 1. Copy `.env.example` to `.env`. 2. Replace `SECRET_KEY` with a long random value. Compose refuses to start without it. 3. Build and start the single-container application: ```sh docker compose up --build -d ``` The container serves the React application through nginx and proxies `/api` requests to the loopback-only FastAPI process. Open `http://localhost:4982` (or the value of `HTTP_PORT`). The first visit asks you to create the single master password. The `db`, `pdf_storage`, and `thumbnails` volumes contain application data. Back them up before maintenance or upgrades. Put the frontend behind HTTPS when exposing PaperJet beyond localhost and set `COOKIE_SECURE=true`. ### Run a Forgejo-published image Set `PAPERJET_IMAGE` in `.env` to the desired registry tag, for example the `latest` or commit-SHA tag published by Forgejo. Then pull and start without building locally: ```sh docker compose pull docker compose up -d --no-build ``` The published image exposes only port 80. Your outer reverse proxy should target the Compose port configured by `HTTP_PORT`. ## Development Backend: ```sh cd backend pip install -e ".[dev]" uvicorn app.main:app --reload ``` Frontend, in a second terminal: ```sh cd frontend npm ci npm run dev ``` The Vite server proxies `/api` to `http://localhost:8000`. ## Validation ```sh # container docker compose --env-file .env.example config --quiet docker compose up --build -d curl http://localhost:4982/api/v1/health docker compose ps # frontend cd frontend npm test -- --run npm run typecheck npm run lint npm run build # backend cd ../backend pytest -q mypy app/ ruff check . ``` Stop the local stack with `docker compose down`. Do not use `-v` unless you intend to remove the local application volumes. The editor uses PDF.js for display and PyMuPDF for export. Stored annotation coordinates are PDF points with a top-left origin relative to the unrotated CropBox; this is the contract shared by the editor and export renderer.