Architecture

One Bun monorepo: four apps, three packages, and nothing on top of Bun workspaces.

Repository map

---
config:
  look: handDrawn
  theme: neo-dark
---
flowchart TD
    subgraph Apps["apps/"]
        API["<b>api</b><br>Hono + Better Auth<br>REST, /nasi, /widget"]
        WEB["<b>web</b><br>TanStack Start<br>landing + admin portal"]
        TUI["<b>tui</b><br>Ink terminal client<br>+ Telegram bot"]
        WID["<b>api/widgets</b><br>embeddable browser bundle"]
        SBX["<b>sandbox</b><br>stdio MCP servers<br>for cloud turns"]
    end
    subgraph Pkgs["packages/"]
        NASI["<b>nasi</b><br>the agent brain"]
        SCH["<b>schema</b><br>Zod contracts"]
        SHR["<b>shared</b>"]
    end
    DB[("PostgreSQL")]
    SQL[("SQLite")]

    NASI --> API
    NASI --> TUI
    SCH --> API
    SCH --> WEB
    SCH --> TUI
    SCH --> NASI
    SHR --> WEB
    SHR --> TUI
    WID --> API
    API --> DB
    TUI --> SQL
    WEB -->|HTTP| API
    TUI -->|"HTTP (cloud mode)"| API
    API -->|"MCP over HTTP"| SBX
    SCH --> SBX
    SHR --> SBX
Workspace What it is
apps/api Hono REST API: auth, admin config, the cloud agent (/nasi), widget serving, emails
apps/api/widgets the embeddable browser chat bundle, built as part of the API
apps/web TanStack Start: the public landing site and the signed-in web app
apps/tui the terminal client, Telegram bot, local config and storage
apps/sandbox the MCP sandbox: runs stdio MCP servers (a headless Chrome and more) for cloud turns, one per user, behind an egress proxy that only reaches public addresses. Anyone can run one, and it dials the API’s WebSocket
packages/nasi the agent brain: loop, tools, store interface
packages/schema every Zod schema, in role-based subpaths
packages/shared small pure utilities, including the Telegram plumbing both bots share

The two databases are on the Database page.

Design ideas

One brain, many hosts. The agent loop lives in packages/nasi and knows nothing about terminals, HTTP or databases. Every front door builds it with a store and a model client and drives the same loop. Adding a front door means writing a host, not another agent.

Offline-first is a real option. Local mode has no account, no server and no telemetry. Config is plain TOML, state is one SQLite file, and with Ollama or llama.cpp it needs no internet.

The cloud is the same product, minus your machine. It exists so someone can try Kaja without an API key. Same loop, personas and memory, without the tools that would reach into a server’s filesystem.

Deliberate non-goals: no mobile app (terminal, Telegram and the widget cover it), no ORM (raw parameterized SQL and hand-written row mappers), and no open publishing (the marketplace is a curated folder in this repo).


Next:

Local setup