# Technical Orientation

> What Cabinet is, its two-process runtime (Next.js on port 4000 + daemon on port 4100), the file-on-disk data model, the npx CLI entry points (cabinetai / create-cabinet), the full tech stack (Next.js 16, TypeScript, Tiptap, Zustand, xterm.js, node-cron, better-sqlite3, ws), and how the rest of this reference is organized.

- Repository: hilash/cabinet
- GitHub: https://github.com/hilash/cabinet
- Human wiki: https://grok-wiki.com/public/wiki/hilash-cabinet-73c70f449a59
- Complete Markdown: https://grok-wiki.com/public/wiki/hilash-cabinet-73c70f449a59/llms-full.txt

## Source Files

- `README.md`
- `package.json`
- `next.config.ts`
- `server/cabinet-daemon.ts`
- `src/lib/runtime/runtime-config.ts`
- `cabinetai/README.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [README.md](README.md)
- [package.json](package.json)
- [next.config.ts](next.config.ts)
- [server/cabinet-daemon.ts](server/cabinet-daemon.ts)
- [server/db.ts](server/db.ts)
- [src/lib/runtime/runtime-config.ts](src/lib/runtime/runtime-config.ts)
- [src/lib/storage/path-utils.ts](src/lib/storage/path-utils.ts)
- [src/lib/agents/adapters/registry.ts](src/lib/agents/adapters/registry.ts)
- [cabinetai/src/index.ts](cabinetai/src/index.ts)
- [cabinetai/package.json](cabinetai/package.json)
- [cabinetai/README.md](cabinetai/README.md)
- [cli/README.md](cli/README.md)
</details>

# Technical Orientation

Cabinet is a self-hosted, AI-first knowledge base and startup operating system. All content lives as plain Markdown files on disk — no cloud database, no vendor lock-in. An onboarding wizard builds a custom AI team in five questions; from that point agents write documents, file reports, scout feeds, and schedule recurring work while the owner interacts through a rich browser UI backed entirely by the local file system.

This page explains what Cabinet is, how its two-process runtime fits together, how the data model is laid out on disk, the CLI entry points that users interact with, and the full technology stack. It also acts as a navigation aid for the rest of this reference.

---

## What Cabinet Is

Cabinet combines three things that have historically been separate products:

| Capability | How Cabinet delivers it |
|---|---|
| Knowledge base / wiki | WYSIWYG Markdown editor with git-backed history |
| AI agent orchestration | Per-agent personas, scheduled cron jobs, live task transcripts |
| Developer workspace | Embedded web terminal, HTML app hosting, file-based everything |

All state is Markdown on disk. There is no remote service requirement beyond the AI CLI tools you bring (Claude Code, Codex, Gemini, etc.). The git history is Cabinet's undo stack and audit log.

Sources: [README.md](README.md) (philosophy section), [cabinetai/README.md](cabinetai/README.md)

---

## Two-Process Runtime

Cabinet runs as two cooperating Node.js processes:

```text
┌─────────────────────────────────────┐   ┌──────────────────────────────────────┐
│         Next.js App Server          │   │         Cabinet Daemon                │
│         port 4000 (default)         │   │         port 4100 (default)           │
│                                     │   │                                       │
│  • React UI (pages, editor, agents) │   │  • PTY sessions (xterm.js backend)    │
│  • Next.js API routes (/api/*)      │   │  • Structured adapter execution        │
│  • SSR / RSC                        │◄──►  • node-cron job scheduler             │
│  • Reads/writes DATA_DIR files      │   │  • WebSocket event bus                 │
│                                     │   │  • better-sqlite3 DB (WAL mode)        │
│  CABINET_APP_PORT env override      │   │  • Full-text search index             │
└─────────────────────────────────────┘   │  • chokidar file watcher               │
                                          │                                        │
                                          │  CABINET_DAEMON_PORT env override      │
                                          └──────────────────────────────────────┘
```

Starting both together is one command:

```bash
npm run dev:all        # development
npm run start          # production  (next start + tsx server/cabinet-daemon.ts)
```

Sources: [README.md](README.md) (Commands section), [src/lib/runtime/runtime-config.ts:88-107](src/lib/runtime/runtime-config.ts)

### Port resolution

Both ports have a three-tier resolution order, implemented in `runtime-config.ts`:

1. Environment variable (`CABINET_APP_PORT` / `CABINET_DAEMON_PORT`)
2. Value persisted in `DATA_DIR/.cabinet-state/runtime-ports.json`
3. Hard-coded default (4000 / 4100)

```ts
// src/lib/runtime/runtime-config.ts
export function getAppPort(): number {
  const runtimePort = readRuntimePorts().app?.port;
  return parsePort(
    process.env.CABINET_APP_PORT || process.env.PORT,
    typeof runtimePort === "number" && Number.isFinite(runtimePort)
      ? runtimePort : 4000
  );
}
```

Sources: [src/lib/runtime/runtime-config.ts:88-107](src/lib/runtime/runtime-config.ts)

### Daemon responsibilities in detail

The daemon (`server/cabinet-daemon.ts`) is the unified background server. On startup it:

1. Checks that `better-sqlite3` was compiled against the running Node ABI (`ensureBetterSqlite3`).
2. Opens the SQLite database at `DATA_DIR/.cabinet.db` (WAL mode, foreign-keys on).
3. Initializes the full-text search index by walking `DATA_DIR`.
4. Starts a `chokidar` watcher for incremental index updates.
5. Loads all `.agents/*/persona.md` and `.jobs/*.yaml` files to schedule cron jobs via `node-cron`.
6. Cleans up any conversations still marked `running` from a crashed previous session.
7. Listens for WebSocket connections on `/pty` (PTY sessions) and `/events` (browser event bus).
8. Exposes an HTTP API for session output polling, health checks, and detached session creation.

Sources: [server/cabinet-daemon.ts:1-80](server/cabinet-daemon.ts), [server/db.ts:1-50](server/db.ts)

#### Session types

The daemon manages two concrete session kinds in a shared `sessions: Map<string, ActiveSession>`:

| Kind | Type tag | Execution engine | When used |
|---|---|---|---|
| `PtySession` | `"pty"` | `legacy_pty_cli` — spawns a PTY via `node-pty` | Interactive terminal, legacy Claude/Codex PTY flow |
| `StructuredSession` | `"structured"` | Adapter's `execute()` function (subprocess, no raw TTY) | `claude_local`, `codex_local`, and all modern adapters |

Sources: [server/cabinet-daemon.ts:280-310](server/cabinet-daemon.ts)

---

## File-on-Disk Data Model

Cabinet has no application database for user content. Everything is plain files:

```text
~/my-startup/                    ← your cabinet (set via CABINET_DATA_DIR or .cabinet-install.json)
  .cabinet                       ← YAML manifest that marks this as a cabinet root
  .cabinet-state/
    runtime-ports.json           ← persisted port assignments
    install.json                 ← installed app version metadata
    update-status.json           ← update checker state
  .cabinet.db                    ← SQLite (conversations, search metadata, telemetry queue)
  .agents/
    ceo/
      persona.md                 ← frontmatter: active, heartbeat cron, skills, ...
      tasks/                     ← per-task conversation files
  .jobs/
    *.yaml                       ← cron job definitions (schedule, prompt, ownerAgent, ...)
  index.md                       ← entry page (Markdown / YAML frontmatter)
  [your pages and folders]/
    *.md
    index.html                   ← optional embedded HTML app (rendered as iframe)
```

`DATA_DIR` is resolved once at startup by `getManagedDataDir()`:

1. `CABINET_DATA_DIR` env var
2. `.cabinet-install.json` → `dataDir` field in the project root
3. Electron default (`~/Documents/Cabinet` on macOS/Windows, `~/Cabinet` on Linux)
4. `<project-root>/data` (source/dev mode)

Sources: [src/lib/storage/path-utils.ts](src/lib/storage/path-utils.ts), [src/lib/runtime/runtime-config.ts:55-82](src/lib/runtime/runtime-config.ts), [cabinetai/README.md](cabinetai/README.md)

### SQLite layer

SQLite is used exclusively for structured metadata that is impractical in flat files: conversation transcripts, full-text search records, telemetry queue, and token-usage summaries. The database file lives at `DATA_DIR/.cabinet.db` and is opened in WAL mode for safe concurrent reads from Next.js API routes and the daemon.

```ts
// server/db.ts
const DB_PATH = path.join(DATA_DIR, ".cabinet.db");
_db.pragma("journal_mode = WAL");
_db.pragma("foreign_keys = ON");
```

Sources: [server/db.ts:11-44](server/db.ts)

---

## CLI Entry Points

Cabinet ships two npm packages that users interact with via `npx`:

### `cabinetai` (primary CLI)

Published as the `cabinetai` npm package. The `bin` entry point is `dist/index.js` (esbuild bundle). Built with `commander`:

```ts
// cabinetai/src/index.ts
const program = new Command();
program.name("cabinetai").description("Cabinet CLI — AI-first self-hosted knowledge base");
registerCreate(program);   registerRun(program);      registerDoctor(program);
registerUpdate(program);   registerImport(program);   registerList(program);
registerUninstall(program);registerResetConfig(program);
```

| Command | What it does |
|---|---|
| `create [name]` | Scaffold a cabinet directory |
| `run` | Download app if needed, start both servers, open browser |
| `import <template>` | Pull a pre-made cabinet from the GitHub template registry |
| `list` | List all cabinets in the current directory |
| `doctor [--fix]` | Health checks: Node version, cabinet structure, port availability |
| `update` | Pull a newer app version from GitHub releases |
| `uninstall [--all] [--yes]` | Remove cached app versions (and optionally telemetry data) |
| `reset-config` | Reset the local install config |

On first `run`, the CLI downloads the versioned Next.js app into `~/.cabinet/app/v{version}/` and installs its dependencies there. The cabinet directory itself is just content files.

Sources: [cabinetai/src/index.ts](cabinetai/src/index.ts), [cabinetai/package.json](cabinetai/package.json), [cabinetai/README.md](cabinetai/README.md)

### `create-cabinet` (thin wrapper)

Published separately as the `create-cabinet` npm package (`cli/` directory). Exists solely to match the `npx create-*` convention. It delegates immediately to `cabinetai create + run`:

```bash
npx create-cabinet@latest my-startup
# equivalent to: npx cabinetai create my-startup && cd my-startup && npx cabinetai run
```

Sources: [cli/README.md](cli/README.md)

---

## Technology Stack

| Layer | Technology | Version / Notes |
|---|---|---|
| Web framework | Next.js | 16.2.1 — standalone output, `serverExternalPackages` for native modules |
| Language | TypeScript | 5.x throughout (app + daemon + CLI) |
| UI library | React | 19.2.4 |
| Styling | Tailwind CSS v4 + shadcn/ui | |
| Rich-text editor | Tiptap | 3.x — full extension suite (tables, code blocks, math, mentions, YouTube, task lists) |
| Client state | Zustand | 5.x |
| Web terminal | xterm.js (`@xterm/xterm`) | 6.x with fit, unicode11, and web-links addons |
| PTY (server-side) | `node-pty` | 1.1.x — listed in `serverExternalPackages` |
| Job scheduler | `node-cron` | 4.x |
| Database | `better-sqlite3` | 12.x — WAL mode, rebuilt on ABI mismatch at daemon boot |
| WebSockets | `ws` | 8.x — PTY relay + event bus |
| File watcher | `chokidar` | 5.x (no FSEvents; per-directory `fs.watch`) |
| Git integration | `simple-git` | 3.x — auto-commit on save, diff viewer, restore |
| Search | `flexsearch` | 0.8.x — in-process full-text index |
| Markdown parse | `remark` + `unified` + `gray-matter` | Frontmatter + GFM |
| Diagrams | `mermaid` | 11.x |
| i18n | `i18next` + `react-i18next` | Browser language detection |
| Electron (optional) | Electron Forge | 36.x — desktop packaging path |

Sources: [package.json](package.json), [next.config.ts](next.config.ts)

### Provider / adapter layer

Cabinet is BYOAI ("Bring Your Own AI"). An `AgentAdapterRegistry` maps adapter type strings to execution adapters. At startup the registry registers both **structured** adapters (subprocess-based, no raw PTY) and **legacy PTY** adapters for backward compatibility:

| Adapter type | Engine | Provider |
|---|---|---|
| `claude_local` | structured | Claude Code CLI |
| `codex_local` | structured | OpenAI Codex CLI |
| `gemini_local` | structured | Gemini CLI |
| `cursor_local` | structured | Cursor |
| `opencode_local` | structured | OpenCode |
| `pi_local` | structured | Pi |
| `grok_local` | structured | Grok CLI |
| `copilot_local` | structured | GitHub Copilot CLI |
| `claude_code_legacy` | `legacy_pty_cli` | Claude Code (PTY) |
| `codex_cli_legacy` | `legacy_pty_cli` | Codex CLI (PTY) |

Per-run overrides can select provider, model, and reasoning effort without touching the agent persona. External adapters can be registered at runtime via `agentAdapterRegistry.registerExternal()`.

Sources: [src/lib/agents/adapters/registry.ts:1-200](src/lib/agents/adapters/registry.ts)

---

## Repository Layout

```text
cabinet/
  cabinetai/           npx cabinetai CLI package (esbuild, commander)
  cli/                 npx create-cabinet thin wrapper
  server/
    cabinet-daemon.ts  Unified daemon: HTTP + WS + PTY + cron + search
    db.ts              SQLite singleton + migration runner
    migrations/        SQL migration files
    pty/               PTY manager, Claude lifecycle, ANSI helpers
    search/            Index builder, search service, file watcher
  src/
    app/               Next.js App Router pages and API routes
    app/api/           REST API routes called by both the UI and daemon
    components/        React components (editor, sidebar, agents, terminal, ...)
    stores/            Zustand state stores
    lib/
      agents/          Personas, conversation runner, adapter registry, skills
      runtime/         runtime-config.ts, cabinet-env loader
      storage/         path-utils, file I/O helpers
      git/             simple-git wrappers
      jobs/            Job normalization and scheduling helpers
      system/          SQLite preflight, file/SQL migration runners
      telemetry/       Anonymous telemetry (opt-out via env or Settings UI)
  data/                Default cabinet created for dev (not committed)
  docs/                CABINETAI.md CLI reference, other guides
  test/                Unit tests (tsx --test)
  scripts/             Dev helpers, release manifest generator, i18n tools
```

Sources: [README.md](README.md) (Architecture section), directory inspection

---

## How the Rest of This Reference Is Organized

The wiki is structured around the major subsystems of the runtime:

- **Data Model** — deeper dive into the `.cabinet` manifest, agent persona frontmatter, and job YAML schema.
- **Daemon Internals** — PTY lifecycle, structured adapter execution, cron scheduling, and WebSocket event bus channels.
- **Editor and UI** — Tiptap extension set, slash commands, embedded HTML apps, and xterm.js terminal integration.
- **Agent System** — how personas, skills, heartbeats, missions, and the conversation store fit together.
- **CLI Reference** — full flag documentation for all `cabinetai` commands (see also `docs/CABINETAI.md`).
- **Configuration** — environment variables, `.cabinet.env`, multi-cabinet setups, and Electron packaging.
- **Contributing** — dev setup, linting, test runner, release flow, and the update system.

---

## Summary

Cabinet's design is intentionally simple at the boundaries: two processes, one of which is stock Next.js, files on disk as the source of truth, and a pluggable adapter layer so any local AI CLI can do the work. The daemon owns all long-running concerns — PTY processes, WebSocket relay, cron jobs, the SQLite database, and the search index — while the Next.js app owns the UI and the REST API surface that the browser calls. The two communicate over HTTP and the daemon's WebSocket event bus. Everything a user produces is a plain file they can inspect, version-control, and take with them.

Sources: [server/cabinet-daemon.ts:1-25](server/cabinet-daemon.ts), [src/lib/runtime/runtime-config.ts](src/lib/runtime/runtime-config.ts)
