Monorepo Overview

Understanding the monorepo structure and how apps are organized.

What is a Monorepo?

A monorepo (monolithic repository) is a single repository that contains multiple projects, applications, and shared code. Instead of having separate repositories for each app, everything lives together in one place.

Benefits for Vibe Coders

  • Shared code: Common utilities, types, and configurations are shared across all apps
  • Consistent patterns: All apps follow the same structure and conventions
  • Single source of truth: One CLAUDE.md file contains all the guidelines
  • Easier collaboration: Claude can understand the entire codebase context

Repository Structure

vibe-coding-platform/
├── apps/                    # All applications live here
│   ├── checkin-board/       # Example app
│   ├── breakfast-tracker/   # Another app
│   └── your-new-app/        # Your app goes here
├── shared/                  # Shared resources
│   ├── database/            # Database schema (Prisma)
│   ├── auth/                # Authentication services
│   ├── storage/             # Object storage helpers (Railway buckets)
│   ├── api/                 # Shared API contracts
│   └── types/               # Shared TypeScript types
├── templates/               # App templates for new projects
├── CLAUDE.md                # AI development guidelines
└── package.json             # Root workspace configuration

Platform Architecture

At a glance, the platform centralizes authentication, keeps app data separate from core booking data (the dual-database model), and connects to algoritmi-core for booking, property, and room data. Individual apps under apps/ are omitted here because they change over time.

vibe-coding-platformalgoritmi-core
User (Browser / Tablet)
AI Agents (Claude / Cursor / custom)
Apps (apps/*)
Each app · auth via middleware · mints JWT for service calls
Auth
Auth UI ⇄ Auth API · magic link / passkey · RS256 JWT / JWKS
MCP (algoritmi-vibe)
Read-only views · Bearer auth
algoritmi-vibe-coding DB · DATABASE_URL
Auth tables · app state (reference IDs, no PII) · pricing / recruit / HR
Core-API
Authenticated REST API
Core DB · CORE_DATABASE_URL_RO
properties · rooms · bookings · room_offers_daily · reception_notifications
MCP (core)
Read-only PMS views
booking-automation-sync
Syncs Booking.com → Core DB
Booking.com API
External source
Other services: check-in-api · reception-notification · slack-notifications · keycafe-open · sync-http-cron
Sign in
MCP (Bearer token)
read / write (pg)
session / users
read-only
Call Core-API · JWT (Bearer)
CORE_DATABASE_URL_RO · read-only · no PII
read
read
write
bookings
Write / HTTPRead-onlyAuth flowExternal service
How the vibe-coding-platform fits together: authentication, the dual-database model, and its relationship to algoritmi-core.

Apps Spanning vibe-coding-platform and algoritmi-core

Several apps deliberately span both repos: they read live booking data from algoritmi-core while keeping their own state in the vibe database. There are two patterns.

Pattern A — Read core data read-only (dual database)

These apps open a read-only connection to the core booking database (CORE_DATABASE_URL_RO) for booking/room/property context, and write only to their own tables in the vibe database. No guest PII is stored.

Pattern A · Read core data read-only, write to your own DB
reception
reads: bookings · rooms · properties
writes: reception_*
receptionist-dashboard
reads: bookings · rooms · properties
writes: receptionist_dashboard_*
package-tracker
reads: bookings (guest search)
writes: package_tracker_*
guest-fulfillment
reads: bookings · properties · rooms
writes: guest_fulfillment_*
staff-app (/cleaning)
reads: bookings · rooms · properties
writes: staff_app_* · staff_onboarding_*
dynamic-pricing
reads: bookings (occupancy, no PII)
writes: dynamic_pricing_*
monthly-shift
reads: bookings (arrival/departure counts)
writes: monthly_*
daily-shift-v2
reads: bookings (checkouts per property)
writes: daily_shift_*
laundry-app
reads: bookings (checkouts per property)
writes: laundry_*
CORE_DATABASE_URL_RO · read-only · no PII
algoritmi-core · PMS DB (CORE_DATABASE_URL_RO)
bookings · rooms · properties · room_offers_daily
reads core (read-only)writes own vibe DB
Pattern A — apps that read core booking data read-only (CORE_DATABASE_URL_RO) and write only to their own vibe database.

Pattern B — Check-in: register & view ID photos via check-in-api

The check-in apps register guests through algoritmi-core's check-in-api. The check-in-tablet uploads passport and ID photos (POST /v1/register-check-in), which check-in-api stores in an S3 bucket and records in check_in_terminal_input. reception then lists check-ins and streams the images back (GET /v1/check-ins, GET /v1/objects), and a webhook from the tablet pushes a real-time refresh to reception over SSE. (Authentication via short-lived JWT is omitted here for clarity.)

check-in-tablet
guest kiosk — capture passport + ID photos (base64), proxied via its backend
voice-notify
TTS / speaker announcement
reception
staff console — list check-ins & view ID/passport images (server proxy, no-store)
keycafe
unlock key cabinet
check-in-api (algoritmi-core)
register-check-in (write) · check-ins (list) · objects (read)
S3 bucket (object storage)
uuid/passport.jpg · uuid/id.jpg
core PMS DB
check_in_terminal_input — name · location · image keys
POST /v1/register-check-in · passport + ID
putObject
insert row
GET /v1/check-ins
GET /v1/objects?key
getObject
webhook → SSE (auto-refresh)
Help → TTS
unlock
also: register-incomplete (call-staff) · additional photos — same upload route
write / requestread (image bytes)notify / unlock
Check-in in detail — registering a guest with ID/passport photos and showing them to reception (authentication omitted for clarity).

Pattern C — AI agent via the core MCP

The external dynamic-pricing-agent (a separate repo) reads the core MCP with a Bearer token and posts pricing proposals back to the dynamic-pricing app.

dynamic-pricing-agent
external AI agent (separate repo)
core MCP (algoritmi-core)
read-only PMS / pricing views
dynamic-pricing (vibe app)
stores proposals & decisions
Bearer — occupancy / competitor / pricing
Bearer — POST proposals
dynamic-pricing also reads core DB directly (Pattern A)
Pattern C — the external dynamic-pricing-agent reads the core MCP and posts pricing proposals back to the dynamic-pricing app.

The apps/ Directory

Each app in the apps/ directory is a standalone Next.js application with its own:

  • package.json - Dependencies and scripts
  • app/ directory - Next.js App Router pages
  • deploy.config.yml - Railway deployment configuration

App Naming Convention

Apps use kebab-case (lowercase with hyphens):

✅ Correct❌ Incorrect
checkin-boardCheckinBoard
breakfast-trackerbreakfast_tracker
my-new-appmyNewApp

The shared/ Directory

Shared resources that multiple apps can use:

DirectoryPurposeWho Can Edit
shared/database/Prisma schema for all tablesVibe coders (with review)
shared/auth/Centralized authentication serviceDevelopers only
shared/storage/Object storage helpers (S3-compatible)Vibe coders (with review)
shared/api/Shared API contractsDevelopers only
shared/types/Shared TypeScript typesVibe coders (with review)

Deeper guides: Database, Auth & Access, File Storage.

Database Access

Apps use the pg library (not Prisma client) for runtime database queries. Use the getDb() helper from the template and always use parameterized queries ($1, $2, …) to prevent SQL injection:

Example database querytypescript
import { getDb } from '@/app/_lib/db';

const db = getDb();
const result = await db.query(
  'SELECT * FROM my_app_users WHERE id = $1',
  [userId]
);
const users = result.rows;

Note: Database queries only execute in deployed environments (PR preview or production). See Database for the full schema and migration workflow.

Key Files to Know

CLAUDE.md (Root)

The most important file for AI development. Contains:

  • Coding standards and conventions
  • Git workflow rules
  • Database guidelines
  • Deployment instructions
Ask Claude to explain CLAUDE.md
Claude prompt
Read the CLAUDE.md file and summarize the key rules I need to follow as a vibe coder.

App-Specific Files

Each app may have its own documentation:

FilePurpose
README.mdApp overview and setup instructions
CLAUDE.mdApp-specific AI guidelines
PLAN.mdDevelopment progress and planned work

Working with the Monorepo

Running Commands

Always run commands from the repository root. You don't run apps locally — everything runs on staging and production after you open a PR (see Deployment). The one command you do run locally is the build, to reproduce the CI check before committing:

Running app commands from rootbash
# Verify the build passes before committing (same check CI runs)
pnpm build:your-app-name

Installing Dependencies

Never run pnpm install inside an app directory:

Installing dependenciesbash
# ✅ Correct: Run from repository root
pnpm install

# ❌ Wrong: Don't run inside app directory
cd apps/my-app && pnpm install

Adding a New Dependency

Ask Claude to add a dependency
Claude prompt
Add the "date-fns" package as a dependency to my-app-name.
Or run it yourselfbash
# Add to a specific app
pnpm --filter my-app-name add date-fns

# Add as dev dependency
pnpm --filter my-app-name add -D @types/some-package

Quiz

Quiz

Where should you run pnpm install?

Next Steps