# How to Contribute to the Kaneo Project: Complete Contributor Guide

> Want to contribute to the Kaneo project? Follow this guide to fork the repo, set up your local environment, and submit your first pull request. Learn how to get involved with usekaneo/kaneo today.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: how-to-guide
- Published: 2026-08-08

---

**To contribute to the Kaneo project, fork the repository on GitHub, clone your fork locally, install dependencies with `pnpm install`, configure environment variables from `.env.sample`, and run `pnpm dev` to start the development servers before submitting a pull request that follows Conventional Commits and passes Biome linting and Vitest testing.**

Kaneo is a self-hosted project management platform built as a **pnpm monorepo** with TurboRepo. Contributing to this open-source project requires understanding its three-tier architecture—Backend API, Frontend Web App, and Shared Packages—while adhering to strict code quality standards enforced through automated tooling. This guide walks you through the complete contribution workflow, from initial environment setup to merging your first pull request.

## Prerequisites and System Requirements

Before contributing to Kaneo, ensure your development environment meets the following specifications defined in the root [`package.json`](https://github.com/usekaneo/kaneo/blob/main/package.json):

- **pnpm** ≥10 (specified in the `packageManager` field)
- **Node.js** ≥18
- **PostgreSQL** (required for running integration tests locally)

The project strictly uses **Biome** for code formatting with specific rules: spaces for indentation, double quotes for strings, and mandatory semicolons.

## Setting Up the Local Development Environment

Follow these steps to configure your local Kaneo development environment:

1. **Fork and clone the repository**:

   ```bash
   git clone https://github.com/<your-username>/kaneo.git
   cd kaneo
   ```

2. **Install monorepo dependencies**:

   ```bash
   pnpm install
   ```

3. **Configure environment variables** by creating `.env` files in both the API and Web app directories, copying from `.env.sample`. Required variables are documented in the root [`README.md`](https://github.com/usekaneo/kaneo/blob/main/README.md) and the Environment Configuration section of [`CLAUDE.md`](https://github.com/usekaneo/kaneo/blob/main/CLAUDE.md).

4. **Start the development servers**:

   ```bash
   pnpm dev
   ```

   This command launches the API server on port **1337** and the Web app dev server on port **5173**, accessible at `http://localhost:5173`.

## Understanding the Kaneo Architecture

Contributing effectively requires familiarity with how the codebase is organized in [`pnpm-workspace.yaml`](https://github.com/usekaneo/kaneo/blob/main/pnpm-workspace.yaml).

### Backend API Structure

The API resides in `apps/api/` and is built with **Hono** (Node.js) using a controller-based pattern:

- **Database**: PostgreSQL accessed via Drizzle ORM, with schema definitions centralized in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts)
- **Authentication**: Better Auth
- **Validation**: Valibot for input sanitization
- **Documentation**: OpenAPI via `hono-openapi`

Controllers contain business logic and are organized under `apps/api/src/workspace/controllers/`, keeping route definitions thin.

### Frontend Web App Structure

The React 19+ application lives in `apps/web/` and uses **Vite** for tooling:

- **Routing**: TanStack Router
- **State Management**: TanStack Query for server state, Zustand for client state
- **Styling**: Tailwind v4 with Radix UI primitives
- **Data Fetching**: Typed fetchers in `apps/web/src/fetchers/` consumed by query hooks in `apps/web/src/hooks/queries/`

### Shared Packages

Reusable code lives in `packages/`:

- **`packages/libs/`**: Thin wrappers around Hono and other utilities
- **`packages/mcp/`**: Model Context Protocol server implementation (see [`packages/mcp/src/server.ts`](https://github.com/usekaneo/kaneo/blob/main/packages/mcp/src/server.ts))

## Contribution Workflow and Standards

### Finding Work

Browse the **"good first issue"** label on GitHub Issues or join the community Discord at `https://discord.gg/rU4tSyhXXU` for guidance.

### Branch Naming and Commits

Create feature branches following the `fix/` or `feat/` naming convention:

```bash
git checkout -b fix/clear-unused-keys

```

All commits must follow **Conventional Commits** format (`feat:`, `fix:`, `docs:`), enforced by the pre-commit hook in `.husky/pre-commit`.

### Code Quality Enforcement

Before submitting, run the linter and fix issues automatically:

```bash
pnpm run lint

```

The pre-commit hook automatically executes `pnpm run build` to verify the entire monorepo compiles correctly.

### Testing Requirements

Validate your changes against the test suite:

- **Unit tests**: `pnpm test` (Vitest)
- **Integration tests**: `pnpm test:integration` (requires PostgreSQL connection)

## Adding New Features: Code Examples

### Creating a New API Endpoint

When adding endpoints to [`apps/api/src/tasks/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/tasks/index.ts), use the OpenAPI decorator pattern and Valibot validation:

```typescript
// apps/api/src/tasks/index.ts
import { Hono } from "hono";
import { describeRoute, validator } from "hono-openapi";
import * as v from "valibot";
import { createTask } from "./controllers/create-task";

const tasks = new Hono()
  .post(
    "/",
    describeRoute({
      operationId: "createTask",
      tags: ["Tasks"],
      description: "Create a new task",
    }),
    validator("json", v.object({
      title: v.string(),
      projectId: v.string(),
    })),
    async (c) => {
      const { title, projectId } = c.req.valid("json");
      const task = await createTask({ title, projectId });
      return c.json(task, 201);
    },
  );

export default tasks;

```

Business logic belongs in dedicated controllers within `apps/api/src/tasks/controllers/` to maintain separation of concerns.

### Adding a Frontend Query Hook

Data fetching follows a strict pattern: fetchers wrap API calls, and hooks consume them via TanStack Query:

```typescript
// apps/web/src/hooks/queries/tasks/use-tasks.ts
import { useQuery } from "@tanstack/react-query";
import { getTasks } from "@/fetchers/tasks/get-tasks";

export function useTasks(projectId: string) {
  return useQuery({
    queryKey: ["tasks", projectId],
    queryFn: () => getTasks(projectId),
  });
}

```

The fetcher `getTasks` resides in [`apps/web/src/fetchers/tasks/get-tasks.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/tasks/get-tasks.ts) and returns typed task arrays.

### Implementing Localization

Kaneo uses i18next with JSON translation files stored in the `i18n/` directory:

```tsx
import { useTranslation } from "react-i18next";

export function CloseButton() {
  const { t } = useTranslation();
  return <button>{t("common:actions.close")}</button>;
}

```

Add new keys to [`i18n/en-US.json`](https://github.com/usekaneo/kaneo/blob/main/i18n/en-US.json) first, then run `pnpm i18n:check:fix` to propagate changes to other locale files.

## Summary

- **Kaneo** is a pnpm monorepo with TurboRepo requiring Node.js ≥18 and pnpm ≥10
- **Architecture** splits into `apps/api/` (Hono/Drizzle), `apps/web/` (React/Vite), and `packages/` (shared utilities)
- **Development** starts with `pnpm install`, environment configuration, and `pnpm dev` for concurrent server startup
- **Code standards** enforce Biome formatting, Conventional Commits, and Vitest testing via `.husky/pre-commit` hooks
- **Feature development** follows strict patterns: controllers for API logic, fetcher/hook pairs for frontend data, and i18next keys for localization

## Frequently Asked Questions

### What package manager does the Kaneo project use?

The Kaneo project strictly uses **pnpm** version 10 or higher as specified in the `packageManager` field of [`package.json`](https://github.com/usekaneo/kaneo/blob/main/package.json). Using npm or Yarn will result in lockfile conflicts and is not supported by the monorepo configuration.

### How do I run tests before submitting a pull request?

Execute `pnpm test` to run unit tests with Vitest, or `pnpm test:integration` to run API integration tests that require a PostgreSQL database connection. These commands ensure your changes don't break existing functionality before submission.

### Where are database schema changes defined?

All Drizzle ORM schema definitions live in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts), which uses CUID2 for IDs and includes timestamp utilities. When adding new tables or columns, modify this central schema file and generate migrations according to the conventions outlined in [`CLAUDE.md`](https://github.com/usekaneo/kaneo/blob/main/CLAUDE.md).

### What happens if my code doesn't follow the formatting rules?

The Biome linter automatically enforces code style (spaces, double quotes, semicolons) through the `pnpm run lint` command and the `.husky/pre-commit` hook. If your code fails linting, the commit will be blocked until you fix the issues, either manually or by running the lint command with the `--write` flag.