📚 Docs / Deployment — Building & Packaging for Production

Deployment — Building & Packaging for Production

Complete guide to building, packaging, and distributing Lawyer Assistant as a production desktop application for Windows, macOS, and Linux.

1. Build overview

Architecture

Production Build
├── Frontend (React + Vite)
│   ├── npm run build → dist/
│   ├── Static HTML/JS/CSS
│   └── CSP-hardened (no unsafe-eval)
├── Electron Shell
│   ├── npm run build → dist/main/
│   ├── Preload script (IPC bridge)
│   └── Main process (window, Python bridge)
└── Backend (Python + Models)
    ├── Bundled in extraResources/python/
    ├── requirements.txt
    └── All .py files

Key difference from dev:


2. Prerequisites

ToolVersionPurpose
Node.js18+Frontend build, Electron
Python3.11+Backend runtime
npm9+Dependency management
GitAnyVersion control

Platform-specific:


3. Building the frontend

Step 1: Install dependencies

bashcd frontend
npm install

Step 2: Build

bashcd frontend
npm run build

What it does:

  1. TypeScript compilation (tsc)
  2. Vite production build (vite build)
  3. CSP injection (production policy, no unsafe-inline scripts)
  4. CSP validation (scripts/check-csp.mjs) — fails on violation
  5. Output → frontend/dist/

Output structure

frontend/dist/
├── index.html          ← CSP <meta> tag injected
├── assets/
│   ├── index-[hash].js
│   ├── index-[hash].css
│   └── ...
└── ...

Verification

bashcd frontend
npm run check:csp

Expected output:

✓ CSP meta tag found in dist/index.html
✓ Production CSP does not contain 'unsafe-eval'
✓ Production CSP does not contain 'unsafe-inline' in script-src

4. Building Electron

Step 1: Install dependencies

bashcd frontend/electron
npm install

Step 2: Build main process

bashcd frontend/electron
npm run build

What it does:

  1. TypeScript compilation (tsc)
  2. Path alias resolution (tsc-alias)
  3. Electron security check (scripts/check-electron-security.mjs) — fails on violation
  4. Output → frontend/electron/dist/main/

Output structure

frontend/electron/dist/main/
├── index.js           ← Main process entry
├── preload.js         ← IPC bridge
├── window.js          ← BrowserWindow creation
├── python-bridge.js   ← Backend lifecycle
└── ...

Verification

bashcd frontend/electron
npm run check:security

Expected output:

✓ All BrowserWindows use sandbox: true
✓ All BrowserWindows use contextIsolation: true
✓ All BrowserWindows use nodeIntegration: false
✓ No --no-sandbox flag found

5. Full build (frontend + Electron)

Single command

bash# From project root
npm run build

What it does:

  1. npm run build:frontendcd frontend && npm run build
  2. npm run build:electroncd frontend/electron && npm run build

Both security checks run automatically — build fails on violations.


6. Packaging

Packaging creates platform-specific installers using electron-builder.

Package configuration

File: frontend/electron/package.json"build" section

json{
  "build": {
    "appId": "com.lawyerassistant.app",
    "productName": "Lawyer Assistant",
    "directories": {
      "output": "dist-electron"
    },
    "files": [
      "dist/**/*",
      "../dist/**/*",
      "!**/node_modules/**"
    ],
    "extraResources": [
      {
        "from": "../../backend",
        "to": "python",
        "filter": [
          "**/*.py",
          "requirements.txt"
        ]
      }
    ],
    "win": {
      "target": "nsis",
      "icon": "../../assets/icon.ico"
    },
    "mac": {
      "target": "dmg",
      "icon": "../../assets/icon.icns"
    },
    "linux": {
      "target": "AppImage",
      "icon": "../../assets/icon.png"
    }
  }
}

Package for all platforms

bash# From project root
npm run package

Creates installers for current platform only.

Platform-specific packaging

Windows:

bashnpm run package:win

macOS:

bashnpm run package:mac

Linux:

bashnpm run package:linux

Output

frontend/electron/dist-electron/
├── win-unpacked/              ← Windows executable (unpacked)
├── Lawyer Assistant Setup 1.0.0.exe  ← Windows installer (NSIS)
├── mac/                       ← macOS app bundle
├── Lawyer Assistant-1.0.0.dmg  ← macOS installer
├── linux-unpacked/            ← Linux executable (unpacked)
└── Lawyer Assistant-1.0.0.AppImage  ← Linux installer

7. Backend bundling

What's bundled

The backend is bundled as extraResources/python/:

extraResources/python/
├── backend/
│   ├── main.py
│   ├── legal_retrieval/
│   │   ├── __init__.py
│   │   ├── config.py
│   │   ├── embedder.py
│   │   └── ...
│   ├── scripts/
│   └── tests/ (optional, can be excluded)
├── requirements.txt
└── (no .venv — created on first run)

Installation on first run

When the user launches the packaged app:

  1. Electron checks for .venv in app data directory
  2. If missing:
    • Runs python -m venv .venv
    • Runs .venv\Scripts\pip install -r requirements.txt
    • Downloads models (BGE-M3, BGE-Reranker) on first query
  3. Starts backend: .venv\Scripts\python main.py

User sees:


8. Code signing (optional)

Why sign?

Windows code signing

Prerequisites:

Configuration:

json{
  "build": {
    "win": {
      "certificateFile": "path/to/certificate.pfx",
      "certificatePassword": "${env.WIN_CSC_PASSWORD}",
      "signAndEditExecutable": true
    }
  }
}

Package with signing:

bashexport WIN_CSC_PASSWORD=your_password
npm run package:win

macOS code signing

Prerequisites:

Configuration:

json{
  "build": {
    "mac": {
      "identity": "Developer ID Application: Your Name (TEAM_ID)",
      "hardenedRuntime": true,
      "gatekeeperAssess": false,
      "entitlements": "entitlements.mac.plist"
    }
  }
}

Package with signing:

bashnpm run package:mac

9. Distribution

Option 1: GitHub Releases

bash# Build for all platforms (requires Mac/Windows/Linux machines)
npm run package:win
npm run package:mac
npm run package:linux

# Upload to GitHub Releases
gh release create v1.0.0 \
  frontend/electron/dist-electron/*.exe \
  frontend/electron/dist-electron/*.dmg \
  frontend/electron/dist-electron/*.AppImage \
  --title "v1.0.0" \
  --notes "Release notes here"

Option 2: Direct download

Host installers on your website:

https://yoursite.com/downloads/
├── Lawyer-Assistant-1.0.0-win.exe
├── Lawyer-Assistant-1.0.0-mac.dmg
└── Lawyer-Assistant-1.0.0-linux.AppImage

Option 3: Auto-updater

Use electron-updater for automatic updates:

Installation:

bashcd frontend/electron
npm install electron-updater

Configuration:

typescript// frontend/electron/src/main/index.ts
import { autoUpdater } from 'electron-updater';

app.on('ready', () => {
  autoUpdater.checkForUpdatesAndNotify();
});

Requires:


10. Platform-specific considerations

Windows

Installer type: NSIS (default) or Squirrel

Installation path: C:\Users\<username>\AppData\Local\Lawyer Assistant\

Auto-start: Optional (add registry key)

Portable version:

json{
  "build": {
    "win": {
      "target": ["nsis", "portable"]
    }
  }
}

macOS

Installer type: DMG (default) or PKG

Installation path: /Applications/Lawyer Assistant.app

Gatekeeper: Requires code signing for Catalina+

Notarization: Required for macOS 10.15+ (Apple notary service)

bash# Notarize after signing
xcrun notarytool submit \
  "Lawyer Assistant-1.0.0.dmg" \
  --apple-id "your@email.com" \
  --team-id "TEAM_ID" \
  --password "app-specific-password" \
  --wait

Linux

Installer types:

Multiple targets:

json{
  "build": {
    "linux": {
      "target": ["AppImage", "deb", "rpm"]
    }
  }
}

11. Build optimization

Reduce bundle size

Frontend:

Backend:

json  {
    "extraResources": [{
      "from": "../../backend",
      "to": "python",
      "filter": ["**/*.py", "requirements.txt", "!**/tests/**"]
    }]
  }

Models:

Faster builds

Skip type checking (not recommended for production):

bashnpm run build -- --skipTypeCheck

Parallel builds:

bash# Build frontend and Electron in parallel
npm run build:frontend & npm run build:electron

Cache Electron binaries:

bashexport ELECTRON_CACHE=~/.electron-cache

12. CI/CD for releases

GitHub Actions workflow

File: .github/workflows/release.yml

yamlname: Release
on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [windows-latest, macos-latest, ubuntu-latest]
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: npm install
      
      - name: Build
        run: npm run build
      
      - name: Package
        run: npm run package
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.os }}-build
          path: frontend/electron/dist-electron/*
      
      - name: Create Release
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: frontend/electron/dist-electron/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Trigger release

bashgit tag -a v1.0.0 -m "Release 1.0.0"
git push origin v1.0.0

GitHub Actions builds for all platforms and creates a release automatically.


13. Testing the packaged app

Before distributing

  1. Smoke test:
bash   # Windows
   "frontend/electron/dist-electron/win-unpacked/Lawyer Assistant.exe"
   
   # macOS
   open "frontend/electron/dist-electron/mac/Lawyer Assistant.app"
   
   # Linux
   ./frontend/electron/dist-electron/linux-unpacked/lawyer-assistant
  1. Verify:
    • App starts without errors
    • Backend spawns successfully
    • Models download on first query
    • Chat works end-to-end
    • File upload works
    • Workspace selection works
  2. Fresh user test:
    • Test on a machine without dev tools installed
    • Verify first-time setup (venv creation, model download)
    • Check installer user experience

14. Troubleshooting packaging

IssueCauseFix
Build failsSecurity check failedRun npm run check:security
Large bundleModels bundledExclude models/ from extraResources
Backend won't startPython not foundBundle Python runtime (pyinstaller)
Missing dependenciesrequirements.txt outdatedUpdate requirements
Icon not showingWrong icon formatConvert to .ico (Win), .icns (Mac)
"Unknown publisher"Not code signedSign the executable
macOS Gatekeeper blocksNot notarizedNotarize the DMG

15. Version management

Update version

File: package.json (root, frontend, electron)

Update in all three:

json{
  "version": "1.0.1"
}

File: backend/legal_retrieval/__init__.py

python__version__ = "1.0.1"

Automated version bump

bash# Patch version (1.0.0 → 1.0.1)
npm version patch

# Minor version (1.0.0 → 1.1.0)
npm version minor

# Major version (1.0.0 → 2.0.0)
npm version major

Updates package.json and creates a git tag.


16. Production environment variables

Set these in the packaged app environment:

Env VarProduction ValuePurpose
NODE_ENVproductionVite production mode
PLR_LOG_LEVELWARNINGLess verbose logs
PLR_GPU_MANAGER_ENABLED1Enable GPU management
ELECTRON_DISABLE_SECURITY_WARNINGStrueHide dev warnings

Set in Electron main process:

typescriptprocess.env.NODE_ENV = 'production';
process.env.PLR_LOG_LEVEL = 'WARNING';

DocCoverage
SECURITY.mdSecurity hardening, CSP, sandboxing
CONFIGURATION.mdEnvironment variables, settings
TROUBLESHOOTING.mdCommon failures
DEVELOPMENT.mdDev environment, debugging
FRONTEND.mdFrontend build process
ARCHITECTURE.mdSystem overview

Questions, answered

Short, self-contained answers about this guide.

How do I build a distributable app?

The Electron pipeline builds platform packages — AppImage and deb/rpm for Linux, a .app for macOS, and an unpacked Windows build. The docs list the exact dist-electron paths and packaging commands for each OS.

Are the AI models bundled in the installer?

No. Models total roughly 14GB, so they are downloaded on first run rather than bundled — keeping installers small. Embeddings, the reranker, and the Ollama LLM are fetched at setup time.

How do I automate releases?

A GitHub Actions workflow triggers on release, builds all three platforms, and creates a release artifact automatically. The docs include a smoke-test checklist to run on the packaged app before shipping.