Kaneo Project Structure: A Complete Guide to the Monorepo Architecture

Kaneo is organized as a pnpm monorepo built with TurboRepo, splitting into three main sections: runnable applications in apps/, shared libraries in packages/, and Kubernetes deployment charts in charts/.

This article breaks down exactly how the usekaneo/kaneo repository is structured. Whether you're contributing code, deploying to production, or exploring the codebase for patterns to borrow, understanding this layout is essential.

Monorepo Layout Overview

Kaneo follows a standard TurboRepo-managed pnpm monorepo pattern with clear separation of concerns:

Section Path Purpose
Apps apps/ Runnable applications
Packages packages/ Shared internal libraries
Charts charts/ Helm charts for Kubernetes deployment

This structure keeps dependencies isolated while enabling efficient code sharing and parallel builds.

The Apps Directory: apps/

The apps/ folder contains three distinct applications that form the complete Kaneo platform.

API Backend (apps/api/)

The backend is a Hono-based HTTP server with PostgreSQL persistence.

Key architectural decisions:

  • Framework: Hono for lightweight, Edge-compatible routing
  • ORM: Drizzle ORM with schema defined in apps/api/src/database/schema.ts
  • Authentication: Better Auth for session management
  • Validation: Valibot schemas enforced via hono-openapi decorators
  • Documentation: Self-documenting OpenAPI specs via decorators

Feature routes follow a strict convention. Each domain lives in apps/api/src/{feature}/ with this internal structure:


apps/api/src/label/
├── index.ts          # Hono router with route definitions

└── controllers/
    ├── get-label.ts  # Business logic implementation

    └── ...

The entry point at apps/api/src/index.ts wires together CORS, authentication, and feature routers.

Web Frontend (apps/web/)

The frontend is a React 19+ SPA with modern data-fetching patterns.

Technology stack:

  • Build tool: Vite for fast HMR and optimized builds
  • Routing: TanStack Router with file-based routing in apps/web/src/routes/
  • Data fetching: TanStack Query with fetchers in apps/web/src/fetchers/{feature}/
  • State management: Zustand for client-side state
  • UI components: Radix UI primitives styled with Tailwind CSS v4

Route components import from fetchers, following this pattern:

// apps/web/src/fetchers/label/get-label.ts
import { apiClient } from "@/lib/api-client";

export async function getLabel(labelId: string) {
  const resp = await apiClient.get(`/label/${labelId}`);
  return resp.json();
}

The corresponding hook wraps this fetcher with caching:

// apps/web/src/hooks/queries/label/use-label.ts
import { useQuery } from "@tanstack/react-query";
import { getLabel } from "@/fetchers/label/get-label";

export function useLabel(labelId: string) {
  return useQuery(["label", labelId], () => getLabel(labelId));
}

Documentation Site (apps/docs/)

Powered by Next.js, this hosts the public documentation and guides.

The Packages Directory: packages/

Shared code lives in packages/ and is consumed by multiple apps via pnpm workspaces.

Package Path Contents
email packages/email/ Email-sending utilities and templates
libs packages/libs/ Reusable code including shared TypeScript types and utility functions; exposed via packages/libs/src/index.ts
typescript-config packages/typescript-config/ Centralized tsconfig.json presets for consistent compiler settings across the monorepo

This approach eliminates duplication while maintaining strict versioning boundaries.

The Charts Directory: charts/

Production deployment assets include Helm charts for Kubernetes. The charts/kaneo/Chart.yaml file defines chart metadata for deploying the full Kaneo stack.

Database Schema Architecture

The Drizzle ORM schema in apps/api/src/database/schema.ts enforces consistent patterns:

// apps/api/src/database/schema.ts
export const exampleTable = pgTable("example", {
  id: text("id").$defaultFn(() => createId()).primaryKey(),
  projectId: text("project_id")
    .notNull()
    .references(() => projectTable.id, { onDelete: "cascade", onUpdate: "cascade" }),
  title: text("title").notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" })
    .defaultNow()
    .$onUpdate(() => new Date())
    .notNull(),
}, (t) => [index("example_projectId_idx").on(t.projectId)]);

Key conventions:

  • Primary keys: CUID2 via createId() for collision-resistant identifiers
  • Timestamps: Every table includes createdAt and updatedAt
  • Foreign keys: Explicit onDelete and onUpdate cascade rules
  • Indexing: Named indexes for query performance
  • Relations: Declared separately in apps/api/src/database/relations.ts

Running the Kaneo Monorepo

Local development uses standard pnpm commands:


# Install all dependencies

pnpm install

# Start both API and Web in dev mode (via TurboRepo)

pnpm dev

TurboRepo orchestrates the dev servers, rebuilding only what changed.

Critical Files to Know

File Significance
apps/api/src/database/schema.ts Source of truth for all database tables
apps/api/src/index.ts Backend entry point—routers, middleware, and CORS configuration
apps/web/src/App.tsx Frontend root component with provider setup
packages/libs/src/index.ts Shared utilities exported for cross-app use
charts/kaneo/Chart.yaml Deployment metadata for Kubernetes

Summary

  • Kaneo uses a pnpm monorepo with TurboRepo for build orchestration
  • Three apps in apps/: API (Hono/PostgreSQL), Web (React/Vite), and Docs (Next.js)
  • Shared utilities in packages/ including email, common libraries, and TypeScript configs
  • Kubernetes deployment via Helm charts in charts/
  • Consistent patterns: feature-based folders, Valibot validation, file-based routing, and CUID2 primary keys

Frequently Asked Questions

What build system does Kaneo use?

Kaneo uses pnpm workspaces combined with TurboRepo. This enables efficient task running across the monorepo with intelligent caching and parallel execution. The pnpm dev command starts both API and web servers simultaneously.

Why does Kaneo use Hono instead of Express?

According to the Kaneo source code, Hono provides Edge-compatible routing with TypeScript-first design and minimal overhead. It integrates cleanly with hono-openapi for automatic documentation and runs efficiently in serverless environments.

How are database changes managed in Kaneo?

Database schema changes are handled through Drizzle ORM with migrations generated from apps/api/src/database/schema.ts. The schema uses CUID2 for IDs, enforces cascade rules on relations, and includes performance indexes. Migration commands would run via Drizzle Kit (standard for Drizzle projects).

Where should new API endpoints be added?

New endpoints belong in apps/api/src/{feature}/ following the existing pattern: create a index.ts router with hono-openapi decorators, place business logic in controllers/, and validate inputs with Valibot schemas. Register the router in apps/api/src/index.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →