Abstract Background

Overview

Open-Source CRM for Developers

openCRM is an open-source, metadata-driven, multi-tenant CRM learning project for developers. It shows how objects, fields, pages, apps, permissions, and workflows can be shaped through configuration instead of being hardcoded into the product.

Admin Control Surface

Configuration areas available in the app

Everything below is implemented and available in the current codebase admin + standard surfaces, including the broader capability areas built on top of those configuration tools.

App Builder

Create apps, brand them, and attach object navigation by team use case.

Dashboards

Configure metric/list/chart widgets with typed filters and owner scopes.

Data Loading

Bulk insert/update/upsert via external IDs with tracked import jobs and row errors.

Permissions

Grant object-level CRUD, View All, Modify All, and app access by permission set.

Permission Set Groups

Bundle permission sets into assignable groups for cleaner access administration.

Sharing Rules

Materialize group access to user-owned records using criteria and access level.

Duplicate Rules

Detect likely duplicates on create and edit with multi-field matching plus warn or block actions.

Assignment Rules

Route new records to a user or queue with ordered, first-match criteria logic.

Groups

Organize users for sharing targets and ownership-based access modeling.

Queues

Use queue ownership buckets and claim flow for distributed intake and triage.

Record Ownership Enforcement

Record ownership is enforced at row level: user-owned, queue-owned, claim flow, and share-aware access checks.

List Views

Configure saved filters, columns, sorting, sharing, pinning, and custom logic expressions per object.

Audit Trail

Track field changes and owner transitions with timestamped history for accountability and compliance.

Objects

Manage object identity, behavior flags, metadata-level governance, and where-used delete protection.

Fields & Types

Define typed fields, options, external IDs, uniqueness, lookup targets, and field where-used checks.

Validation Rules

Protect data integrity with per-object condition logic and field constraints.

Record Pages

Assign record page layouts per app and permission context with visibility rules.

Delete Safety

Block object/field deletes when metadata still depends on them, with grouped where-used diagnostics.

Capability coverage

The same configuration layer powers the broader platform capabilities below, from data modeling and validation to access, automation, collaboration, and productivity UX.

Metadata Platform

Build your CRM model at runtime

  • Custom objects and custom fields
  • Typed EAV storage (Record + FieldData)
  • Lookup relationships across objects
  • Picklist option model with active/inactive states
  • AutoNumber fields with prefix + counter control
  • Per-object record page layout assignments
  • Metadata dependency index for object/field where-used

Data Quality

Block bad data before it lands

  • Validation rules with ALL / ANY / CUSTOM expressions
  • Duplicate rules with multi-field matching and warn/block actions
  • Field-level and system-aware validation conditions
  • Picklist-safe operators and active-option checks
  • Typed number/date constraints and formula compatibility
  • Inline or toast validation feedback delivery

Ownership + Access

Enterprise-style permission model

  • Permission sets and permission set groups
  • App-level and object-level access controls
  • View All / Modify All overrides
  • Queue ownership with claim flow
  • Group-based RecordShare materialization
  • Tenant isolation by organizationId
  • Delete permission and share-aware record delete checks

Operational Rules

Rule engines and asynchronous processing

  • Create-time assignment rules (user or queue)
  • Criteria-based sharing rule recomputation
  • Background worker via pg-boss queues
  • Import job pipeline (PENDING -> RUNNING -> COMPLETE)
  • Lookup resolution via external IDs during import
  • Error CSV export with safe sanitization

Productivity UI

Fast navigation and list intelligence

  • App builder with tailored navigation
  • List views with filters, sort, expressions, and sharing
  • Dashboard widgets (metric/list/chart)
  • Global search with ranking and scoped access
  • Record owner scope filters for lists/widgets
  • Pinned views and user default preferences

Collaboration + Audit

Context and accountability by default

  • Record comments with @mentions
  • Assignment and mention notifications
  • Field history tracking for changed values
  • Owner history tracking for reassignment events
  • Protected file attachments with MIME validation
  • Import, ownership, and rule activity observability
  • Hard record delete with inbound lookup cleanup

Supported Field Types

Build object schemas from primitive, relational, and system-generated types.

Text
TextArea
Number
Date
DateTime
Checkbox
Phone
Email
Url
Lookup
Picklist
File
AutoNumber
External ID is supported for Text fields and powers bulk data loading + lookup resolution.

Tech Stack

Core technologies used in the project

openCRM uses a straightforward TypeScript web stack: Next.js and React for the app, Prisma and PostgreSQL for data, NextAuth for authentication, and a small set of supporting libraries for UI, jobs, validation, and state.

Application

The main web app runs on the Next.js App Router with React and TypeScript.

Next.js 16React 19TypeScript

Data Layer

Metadata, records, and access data are stored in PostgreSQL and accessed through Prisma.

Prisma ORMPostgreSQL

Authentication

User sign-in and session handling are built on NextAuth.

NextAuth

UI Layer

The interface uses Tailwind for styling and Radix plus shadcn/ui primitives for components.

Tailwind CSSRadix UIshadcn/ui

Runtime Support

State, validation, and background work use a focused set of supporting libraries.

TanStack QueryZodZustandpg-boss

Testing

Unit and component testing run through the Vitest-based test setup.

Vitest

Architecture Center

System architecture and execution diagrams

Context, boundaries, data model, authorization flow, lifecycle behavior, and async import processing in one place.

00

User Types and Access Boundaries

openCRM uses two runtime user roles. Both roles are authenticated users inside the same tenant and both are constrained by object permissions and row-level ownership/share logic.

Standard User

Business-facing app operator

Works in the standard app surface (`/app/*`) for daily record operations, list views, dashboards, comments, imports, and queue claim flows based on assigned permissions.

Admin User

Configuration operator

Works in the admin surface (`/admin/*`) to manage metadata and policy: apps, objects, fields, permissions, groups, queues, validation, duplicate rules, assignment rules, and sharing rules.

How They Are Similar

Same security model

Both are tenant-scoped users authenticated through the same session stack. In non-admin record access paths, both still require object grants plus row access (owner, queue, or share) unless global overrides are explicitly granted.

01

System Context

Public users interact with one web runtime. The app coordinates database, worker, and file storage boundaries.

02

Runtime Containers

Two cooperating runtimes share one data store: request-time Next.js and background worker execution.

03

Domain Core (Metadata + EAV)

Object and field definitions are metadata. Record values are typed rows in FieldData, with dependency indexing and duplicate-rule metadata alongside safe delete checks.

04

Record Authorization Decision Flow

Object permission gate executes first. Row-level ownership/share checks run only after object grant.

05

Create Record Lifecycle

Record creation layers validation, duplicate detection, assignment evaluation, and share materialization before commit.

06

Delete Record Lifecycle

Record delete is synchronous and transactional: permission gate, inbound lookup cleanup, hard delete, then storage cleanup.

07

Import + Async Job Pipeline

Imports are staged: synchronous job creation, asynchronous row processing via worker queue.