# How to Contribute to Open Notebook Development: A Complete Guide

> Contribute to Open Notebook development at lfnovo/open-notebook. Learn the issue-first workflow, async Python, and testing required for this privacy-first AI research assistant.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-26

---

**Open Notebook is a privacy-first, multi-provider AI research assistant built on a three-tier architecture (Next.js frontend, FastAPI backend, SurrealDB database) that requires an issue-first workflow, async Python code, and thorough testing for all contributions.**

Open Notebook is an open-source research platform that orchestrates multi-provider LLMs through a clean, async-first architecture. Before contributing to the lfnovo/open-notebook repository, you must understand its strict domain-driven patterns and three-tier design. The following guide covers the essential architecture, design principles, and submission workflow required to get your pull request merged.

## Architecture Overview

The codebase is organized into three distinct tiers that communicate via REST APIs, as documented in [`docs/7-DEVELOPMENT/architecture.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/7-DEVELOPMENT/architecture.md).

### Frontend (Next.js, Port 8502)

The React-based UI renders notebooks, sources, notes, chat interfaces, and podcasts. It uses **Zustand** for global UI state management and **TanStack Query** for server-state caching. All data fetches target the REST API at `http://localhost:5055`.

### API (FastAPI, Port 5055)

The async Python backend exposes CRUD endpoints for notebooks, sources, notes, chat sessions, search, and transformations. Router files in `api/routers/*.py` follow a **service pattern**, delegating business logic to service classes that invoke LangGraph workflows and the repository layer.

### Database (SurrealDB, Port 8000)

A graph and vector database storing domain records (`notebook`, `source`, `source_embedding`, `note`, `chat_session`) with native embedding storage for semantic search. The database runs on port 8000 and manages relationships between research entities.

## Core Design Principles

Every contribution must respect these five architectural constraints defined in [`docs/7-DEVELOPMENT/architecture.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/7-DEVELOPMENT/architecture.md).

**Async-First Execution** – All I/O operations must use `await` to maintain high concurrency. When modifying files in `api/routers/` or service classes, ensure all database calls and external API requests remain asynchronous.

**Domain-Driven Design** – Business logic lives in `open_notebook/domain/` (e.g., `Notebook`, `Source`, `Note` classes). Keep domain models separate from FastAPI networking code and UI components.

**LangGraph Workflows** – Multi-step AI tasks are implemented as state machines in `open_notebook/graphs/*.py`. New features requiring AI orchestration should add new graph definitions following existing patterns for source processing, chat, and transformations.

**Multi-Provider AI via Esperanto** – Never import provider-specific SDKs directly. Instead, use `provision_langchain_model` from [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) to support 8+ LLM and embedding providers through a unified interface.

**Job Queue for Background Work** – Heavy operations (source processing, podcast generation) must use the Surreal-Commands queue. Submit commands via `CommandService.submit_command_job` instead of blocking HTTP request handlers.

## Contribution Workflow

Open Notebook follows a strict issue-first workflow. Follow these steps to ensure your contribution aligns with project maintainers:

1. **Open an issue** describing the bug or feature, proposing a solution, and requesting assignment.
2. **Wait for maintainer labeling** to ensure alignment with architecture and design principles.
3. **Fork the repository** and create a feature branch (e.g., `feature/add-new-graph`).
4. **Implement the change** following async patterns, domain-driven design, and adding comprehensive tests.
5. **Run quality checks** using `uv run pytest` for tests and `uv run ruff check .` with `uv run ruff format .` for linting.
6. **Open a Pull Request** referencing the issue number (e.g., "Fixes #123"), describing changes, and including UI screenshots if applicable.

## Development Environment Setup

Clone your fork and prepare the development environment using the containerized setup defined in `Dockerfile` and [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml):

```bash

# 1. Fork the repo on GitHub, then clone your fork

git clone https://github.com/<your-username>/open-notebook.git
cd open-notebook

# 2. Create a new feature branch

git checkout -b feature/add-new-graph

# 3. Make changes (e.g., add open_notebook/graphs/my_new_workflow.py)

# 4. Run the test suite to ensure nothing is broken

uv run pytest

# 5. Lint and format the code

uv run ruff check .
uv run ruff format .

# 6. Commit and push

git add .
git commit -m "feat: add new LangGraph workflow for X"
git push origin feature/add-new-graph

# 7. Open a PR on GitHub, linking the issue (e.g., "Fixes #123")

```

## Key Implementation Patterns

### Submitting Background Jobs

When adding heavy background work, submit a command to the Surreal-Commands queue rather than blocking the request. In [`api/routers/sources.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/sources.py) or similar router files:

```python

# In an API router (e.g., api/routers/sources.py)

@router.post("/sources/{id}/process")
async def process_source(id: str, body: ProcessRequest):
    command_id = await CommandService.submit_command_job(
        app="open_notebook",
        command="process_source",
        input={"source_id": id, "options": body.options},
    )
    return {"command_id": command_id}

```

### Working with Domain Models

All business entities are defined in `open_notebook/domain/`. When extending functionality, modify or create domain classes here rather than in router files. The repository layer handles persistence logic for these domain objects, ensuring clean separation between business logic and data access.

### Adding LangGraph Workflows

New AI features require state-machine orchestration. Create new graph definitions in `open_notebook/graphs/` following the existing patterns for source processing, chat workflows, and transformations. Wire these graphs into service classes that routers invoke, maintaining the separation between HTTP handling and business logic.

## Testing and Code Quality

All new code must include unit tests in the `tests/` directory covering API endpoints, graph workflows, utilities, and database interactions. The project uses **Ruff** for linting and formatting.

Before submitting your PR:
- Run `uv run pytest` to execute the full test suite
- Run `uv run ruff check .` to identify style violations
- Run `uv run ruff format .` to auto-format Python files

Update documentation in `docs/` when API contracts or UI behaviors change, and reference [`CONFIGURATION.md`](https://github.com/lfnovo/open-notebook/blob/main/CONFIGURATION.md) for environment variables and AI provider settings.

## Summary

- Open Notebook uses a three-tier architecture: Next.js frontend (port 8502), FastAPI backend (port 5055), and SurrealDB (port 8000).
- All contributions must follow the issue-first workflow outlined in [`docs/7-DEVELOPMENT/contributing.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/7-DEVELOPMENT/contributing.md).
- Code must be async-first, domain-driven, and use `provision_langchain_model` for AI calls.
- Heavy background tasks must use `CommandService.submit_command_job` rather than blocking requests.
- All PRs require tests (`uv run pytest`), linting (`uv run ruff check .`), and formatting (`uv run ruff format .`).

## Frequently Asked Questions

### Do I need to open an issue before submitting a pull request?

Yes. Open Notebook requires an issue-first workflow documented in [`docs/7-DEVELOPMENT/contributing.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/7-DEVELOPMENT/contributing.md). You must open an issue describing the bug or feature, propose a solution, and wait for a maintainer to label it before forking and creating your feature branch. This ensures alignment with the project's architecture and design principles.

### What testing framework does Open Notebook use?

The project uses **pytest** for unit testing. Run the full test suite with `uv run pytest` before submitting your PR. All new code must include unit tests covering API endpoints, graph workflows, and database interactions in the `tests/` directory.

### How do I add support for a new AI provider?

Use the **Esperanto** abstraction layer in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py). Instead of importing provider-specific SDKs, extend the `provision_langchain_model` function to support the new provider. This maintains the unified interface to 8+ LLMs and embedding services currently supported.

### Can I run the development environment without Docker?

While the project provides `Dockerfile` and [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml) for containerized development, you can run services locally. Configure connection settings in [`CONFIGURATION.md`](https://github.com/lfnovo/open-notebook/blob/main/CONFIGURATION.md) for the frontend (port 8502), API (port 5055), and SurrealDB (port 8000). Ensure you have `uv` installed for Python dependency management and task running.