# InsForge ELI5 Wiki

> InsForge is an open-source backend platform that gives AI coding agents instant access to databases, auth, storage, edge functions, and an AI model gateway — all through a single MCP server or CLI, so the agent can build and ship full-stack apps without a human doing the wiring.

## Context Links

- [Agent index](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/llms.txt)
- [Human interactive wiki](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319)
- [GitHub repository](https://github.com/InsForge/InsForge)

## Repository Metadata

- Repository: InsForge/InsForge

- Generated: 2026-05-22T01:38:05.255Z
- Updated: 2026-05-22T01:41:25.793Z
- Runtime: Claude Code
- Format: Explain Like I'm 5
- Pages: 6

## Page Index

- 01. [Explain It Simply — What Is InsForge?](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/01-explain-it-simply-what-is-insforge.md) - InsForge in plain language: what problem it solves, the simplest useful mental model, and the three ideas a newcomer must remember before reading anything else.
- 02. [Two Doors In — MCP Server vs. CLI + Skills](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/02-two-doors-in-mcp-server-vs.-cli-skills.md) - AI coding agents talk to InsForge through exactly two interfaces: a self-hosted or cloud MCP server (tool calls) and a CLI with bundled Skills (terminal commands). This page explains what each door does, when to use which, and what actually happens when an agent connects.
- 03. [The Seven Backend Primitives — What Each One Does](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/03-the-seven-backend-primitives-what-each-one-does.md) - InsForge exposes seven backend services to agents: Auth, Database (Postgres + PostgREST), Storage (S3-compatible), Model Gateway (OpenAI-compatible AI routing), Edge Functions (Deno serverless), Compute (long-running containers), and Site Deployment. This page explains what each primitive is for and how it is wired in the codebase.
- 04. [Inside the Backend — How Requests Travel from Agent to Database](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/04-inside-the-backend-how-requests-travel-from-agent-to-database.md) - A concrete walk-through of the Express server (server.ts), the three-layer architecture (routes → services → providers/infra), the PostgREST proxy pattern used for database record access, the Socket.IO realtime push, and the database migration system that bootstraps every InsForge instance.
- 05. [Running InsForge — Self-Hosted, Docker, and Cloud](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/05-running-insforge-self-hosted-docker-and-cloud.md) - How to get InsForge running: the Docker Compose stack (backend, Postgres, Deno subhosting), the frontend dashboard (React/Vite, cloud vs. self-host modes), deploy targets (Render, AWS EC2, Azure, GCE), and the environment variables an operator must configure before the server boots.
- 06. [The Short Version — One Mental Model to Keep](https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/06-the-short-version-one-mental-model-to-keep.md) - A plain-English recap of InsForge: the core idea in one sentence, the single analogy worth keeping (InsForge is a universal power strip that plugs an AI coding agent into every backend service at once), the three things that move (requests from agent, config changes to primitives, state events back via realtime), and pointers to where to look next.

## Source File Index

- `.claude/skills/README.md`
- `backend/src/api/routes/ai/index.routes.ts`
- `backend/src/api/routes/auth/index.routes.ts`
- `backend/src/api/routes/compute/services.routes.ts`
- `backend/src/api/routes/database/records.routes.ts`
- `backend/src/api/routes/database/tables.routes.ts`
- `backend/src/api/routes/deployments/index.routes.ts`
- `backend/src/api/routes/functions/index.routes.ts`
- `backend/src/api/routes/storage/index.routes.ts`
- `backend/src/infra/config/app.config.ts`
- `backend/src/infra/database/database.manager.ts`
- `backend/src/infra/database/migrations/000_create-base-tables.sql`
- `backend/src/infra/realtime/realtime.manager.ts`
- `backend/src/infra/socket/socket.manager.ts`
- `backend/src/server.ts`
- `backend/src/services/database/postgrest-proxy.service.ts`
- `CHANGELOG.md`
- `deploy/docker-compose/.env.example`
- `deploy/docker-compose/docker-compose.yml`
- `deploy/Dockerfile.deno`
- `docker-compose.override.yml`
- `Dockerfile`
- `docs/deployment/README.md`
- `docs/insforge-instructions-sdk.md`
- `docs/introduction.mdx`
- `docs/mcp-setup.mdx`
- `docs/quickstart.mdx`
- `docs/vscode-extension.mdx`
- `frontend/src/App.tsx`
- `GITHUB_OAUTH_SETUP.md`
- `package.json`
- `README.md`
- `turbo.json`

---

## 01. Explain It Simply — What Is InsForge?

> InsForge in plain language: what problem it solves, the simplest useful mental model, and the three ideas a newcomer must remember before reading anything else.

- Page Markdown: https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/01-explain-it-simply-what-is-insforge.md
- Generated: 2026-05-22T01:36:56.953Z

### Source Files

- `README.md`
- `docs/introduction.mdx`
- `docs/quickstart.mdx`
- `package.json`
- `turbo.json`

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [README.md](README.md)
- [docs/introduction.mdx](docs/introduction.mdx)
- [docs/quickstart.mdx](docs/quickstart.mdx)
- [docs/insforge-instructions-sdk.md](docs/insforge-instructions-sdk.md)
- [docs/mcp-setup.mdx](docs/mcp-setup.mdx)
- [package.json](package.json)
- [turbo.json](turbo.json)
- [docker-compose.yml](docker-compose.yml)
- [backend/src/services/ai/chat-completion.service.ts](backend/src/services/ai/chat-completion.service.ts)
- [backend/src/services/storage/storage.service.ts](backend/src/services/storage/storage.service.ts)
- [backend/src/api/routes/](backend/src/api/routes/)
- [backend/src/services/](backend/src/services/)
</details>

# Explain It Simply — What Is InsForge?

InsForge is an open-source Backend-as-a-Service (BaaS) platform built specifically for AI coding agents. Instead of requiring a developer to manually set up databases, authentication, storage, and APIs, InsForge exposes all of those backend capabilities as tools that an AI assistant can call directly — the agent configures and operates your backend the same way a backend engineer would.

This page explains what InsForge is in plain terms, the single mental model that makes everything click, and the three ideas you need to hold in your head before you read anything else in the codebase.

---

## The Problem InsForge Solves

When you build a full-stack application with an AI coding agent, the agent is excellent at producing frontend code: React components, UI logic, and API calls. The hard part is the backend. Setting up a PostgreSQL database, wiring up authentication, handling file storage, and deploying serverless functions requires deep expertise and many manual steps. Agents historically could generate backend code but could not actually provision or configure the infrastructure it targets.

InsForge closes that gap. It gives AI agents a live, real backend they can read from, write to, and configure — all through a single programmable interface.

Sources: [docs/introduction.mdx:1-30]()

---

## The One Mental Model: A Backend Your Agent Can Drive

Think of InsForge as a backend workbench with two control panels:

```text
┌─────────────────────────────────────────────────────┐
│                 AI Coding Agent                      │
│          (Cursor, Claude Code, Copilot …)            │
└──────────────┬───────────────────┬───────────────────┘
               │                   │
    ┌──────────▼──────┐   ┌────────▼─────────┐
    │   MCP Server    │   │  CLI + Skills     │
    │ (self-hosted    │   │  (cloud only)     │
    │  or cloud)      │   │                  │
    └──────────┬──────┘   └────────┬─────────┘
               │                   │
               └────────┬──────────┘
                        │
          ┌─────────────▼──────────────────────┐
          │           InsForge Backend          │
          │  ┌──────────┐  ┌──────────────┐   │
          │  │PostgreSQL│  │ Auth (JWT +  │   │
          │  │+ PostgREST  │   OAuth)     │   │
          │  └──────────┘  └──────────────┘   │
          │  ┌──────────┐  ┌──────────────┐   │
          │  │  Storage │  │ Edge Funcs   │   │
          │  │(S3-compat│  │ (Deno-based) │   │
          │  └──────────┘  └──────────────┘   │
          │  ┌──────────┐  ┌──────────────┐   │
          │  │Model     │  │  Realtime    │   │
          │  │Gateway   │  │  (WebSocket) │   │
          │  └──────────┘  └──────────────┘   │
          └────────────────────────────────────┘
```

The agent talks to InsForge, and InsForge talks to the infrastructure. The developer never has to become a DevOps expert.

Sources: [README.md:45-80](), [docker-compose.yml:1-65]()

---

## The Three Ideas You Must Know First

### 1. InsForge is a BaaS, not a framework

InsForge is not a library you import into your app code. It is a running service — a backend server — that your application connects to over HTTP or WebSocket. The service exposes a consistent API surface for every primitive you need: a PostgreSQL database, authentication, file storage, serverless functions, realtime pub/sub, and an AI model gateway.

You connect to it using the TypeScript SDK (`@insforge/sdk`), a REST API, a Swift SDK, or a Kotlin SDK, depending on your platform. The pattern is always the same: create a client with your project URL and anon key, then call methods.

```js
// docs/insforge-instructions-sdk.md — Step 3
import { createClient } from '@insforge/sdk';

const client = createClient({
  baseUrl: 'https://your-app.region.insforge.app',
  anonKey: 'your-anon-key-here'
});
```

Sources: [docs/insforge-instructions-sdk.md:18-40]()

### 2. Agents interact through MCP tools or a CLI — not through your app code

The part of InsForge designed for AI agents is separate from the SDK your app uses. Agents get their own interface: an MCP (Model Context Protocol) server. The MCP server exposes backend operations as named tools — `fetch-docs`, `run-migration`, `deploy-function`, `list-buckets`, and so on — that any MCP-compatible agent can call directly, without writing application code.

There is also a CLI (`@insforge/cli`) paired with "Skills" (agent instruction files) for cloud-hosted projects. Both interfaces let the agent read documentation, inspect running state, apply schema migrations, deploy edge functions, and verify that what it built is actually working.

```bash
# quickstart.mdx — Step 2: link a project
npx @insforge/cli link --project-id <your-project-id>
```

Sources: [README.md:32-50](), [docs/quickstart.mdx:30-50](), [docs/mcp-setup.mdx:1-20]()

### 3. The backend is self-hostable and BYOK (Bring Your Own Keys)

InsForge runs inside a Docker Compose stack. The compose file spins up PostgreSQL (custom image `ghcr.io/insforge/postgres`), PostgREST (which turns your database tables directly into REST endpoints), the InsForge API server, and optionally storage. You can run it locally on port 7130, run multiple isolated projects on the same host by giving them different port offsets, or deploy to Railway, Zeabur, or Sealos with one click.

The AI model gateway routes calls through OpenRouter. You supply your own API keys; InsForge does not proxy traffic through a proprietary model provider. Storage defaults to local disk and optionally switches to any S3-compatible endpoint (AWS, Wasabi, MinIO) you configure.

```yaml
# docker-compose.yml — core service wiring
services:
  postgres:   # ghcr.io/insforge/postgres:v15.13.2
  postgrest:  # postgrest/postgrest:v12.2.12 — database-as-API
  insforge:   # the InsForge API + admin UI on port 7130
```

Sources: [docker-compose.yml:1-80](), [backend/src/services/storage/storage.service.ts:1-24](), [backend/src/services/ai/chat-completion.service.ts:1-15]()

---

## What the Backend Actually Provides

The backend is a TypeScript monorepo (`turbo.json`) with these independently structured service areas, each with its own routes and service layer:

| Capability | What it does | Backed by |
|---|---|---|
| **Database** | PostgreSQL tables exposed as REST endpoints; schema migrations via MCP tools | PostgREST + PostgreSQL |
| **Authentication** | Email/password, OAuth (Google, GitHub), OTP, JWT sessions | Custom auth service + OAuth PKCE |
| **Storage** | S3-compatible file upload/download/listing with signed URLs | Local disk or any S3-compatible endpoint |
| **Edge Functions** | Serverless functions deployed and invoked via the API | Deno-based runtime |
| **Model Gateway** | OpenAI-compatible API routing to multiple LLM providers | OpenRouter |
| **Realtime** | WebSocket pub/sub for database events and client messages | Custom socket layer |
| **Site Deployment** | Build and host frontend apps | Deployment service |
| **Compute** | Long-running container services (private preview) | — |

Sources: [backend/src/api/routes/](backend/src/api/routes/), [backend/src/services/](backend/src/services/), [docs/introduction.mdx:30-55]()

---

## How the Codebase Is Structured

The repository is a Turborepo monorepo with three main workspaces:

```text
insforge/
├── backend/          ← Express API server + all service logic
│   └── src/
│       ├── api/routes/   ← HTTP route handlers (auth, db, storage, AI …)
│       ├── services/     ← Business logic per domain
│       ├── infra/        ← Database connection manager, config
│       └── providers/    ← Storage (local/S3) and AI (OpenRouter) adapters
├── frontend/         ← Admin dashboard UI
└── packages/
    ├── shared-schemas/   ← Zod schemas shared between backend and clients
    ├── ui/               ← Shared React component library
    └── dashboard/        ← Dashboard package
```

Turborepo orchestrates builds so that `shared-schemas` is always compiled before the backend or frontend that depends on it. Development runs both backend and frontend concurrently via `npm run dev`.

Sources: [package.json:5-10](), [turbo.json:1-45]()

---

## Getting Started in 60 Seconds

1. **Cloud path**: Create a free project at `insforge.dev`, copy the Project ID, run `npx @insforge/cli link --project-id <id>`, then send the verification prompt to your agent.
2. **Self-hosted path**: `git clone`, `cp .env.example .env`, `docker compose -f docker-compose.prod.yml up` — the admin UI lands on `http://localhost:7130`.
3. **Connect your agent**: Add the MCP server JSON from your project dashboard to Cursor, Claude Code, or any MCP-compatible assistant. The agent can then call `fetch-docs` to read the current instructions and begin building.

Sources: [docs/quickstart.mdx:1-55](), [README.md:82-130]()

---

## Summary

InsForge is an open-source BaaS built so AI coding agents can fully operate a production-grade backend — not just generate code that targets one. It wraps PostgreSQL, authentication, file storage, serverless functions, a multi-provider AI model gateway, and realtime messaging behind a single MCP interface and CLI that any agent can call. It self-hosts with Docker Compose, is provider-neutral (your own LLM keys, your own S3), and ships SDKs for TypeScript, Swift, Kotlin, and plain REST. The three things to hold onto: InsForge is a running service (not a library), agents drive it through MCP tools rather than app code, and the whole stack is self-hostable and BYOK.

Sources: [README.md:1-50](), [docs/introduction.mdx:1-30](), [backend/src/services/](backend/src/services/)

---

## 02. Two Doors In — MCP Server vs. CLI + Skills

> AI coding agents talk to InsForge through exactly two interfaces: a self-hosted or cloud MCP server (tool calls) and a CLI with bundled Skills (terminal commands). This page explains what each door does, when to use which, and what actually happens when an agent connects.

- Page Markdown: https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/02-two-doors-in-mcp-server-vs.-cli-skills.md
- Generated: 2026-05-22T01:38:05.253Z

### Source Files

- `docs/mcp-setup.mdx`
- `docs/quickstart.mdx`
- `docs/vscode-extension.mdx`
- `.claude/skills/README.md`
- `docs/insforge-instructions-sdk.md`

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

- [docs/mcp-setup.mdx](docs/mcp-setup.mdx)
- [docs/quickstart.mdx](docs/quickstart.mdx)
- [docs/vscode-extension.mdx](docs/vscode-extension.mdx)
- [docs/introduction.mdx](docs/introduction.mdx)
- [docs/insforge-instructions-sdk.md](docs/insforge-instructions-sdk.md)
- [.claude/skills/README.md](.claude/skills/README.md)
- [.claude/skills/insforge-dev/SKILL.md](.claude/skills/insforge-dev/SKILL.md)
- [packages/dashboard/src/features/dashboard/components/connect/mcp/helpers.tsx](packages/dashboard/src/features/dashboard/components/connect/mcp/helpers.tsx)
- [packages/dashboard/src/features/dashboard/components/connect/constants.ts](packages/dashboard/src/features/dashboard/components/connect/constants.ts)
- [backend/src/api/routes/usage/index.routes.ts](backend/src/api/routes/usage/index.routes.ts)
- [backend/src/api/routes/docs/index.routes.ts](backend/src/api/routes/docs/index.routes.ts)
- [backend/src/server.ts](backend/src/server.ts)
</details>

# Two Doors In — MCP Server vs. CLI + Skills

InsForge is an AI-optimized Backend-as-a-Service (BaaS) platform. Its core design philosophy is that AI coding agents — not humans — are the primary builders. To make that work, every interaction point is designed to be machine-readable and agent-friendly. There are exactly two entry points an AI agent uses to work with InsForge: the **MCP server** (tool calls that flow through the Model Context Protocol) and the **CLI plus bundled Skills** (terminal commands that the agent runs directly in your project).

Understanding which door to use, and what happens behind each one, is the key to getting InsForge to "just work" for your agent. This page explains both paths clearly — what they are, when to pick each one, and what actually runs when the agent connects.

---

## The Two Doors at a Glance

```text
┌────────────────────────────────────────────────────────────┐
│                      AI Coding Agent                       │
│  (Cursor, Claude Code, Copilot, Windsurf, Codex, ...)      │
└────────────────┬──────────────────────────┬───────────────┘
                 │                          │
         MCP Tool Calls              CLI Terminal Commands
                 │                          │
  ┌──────────────▼────────────┐  ┌──────────▼──────────────┐
  │  @insforge/mcp  (stdio)   │  │  @insforge/cli  (npx)   │
  │  or mcp.insforge.dev/mcp  │  │  + .claude/skills/      │
  │  (Streamable HTTP)        │  │    SKILL.md files        │
  └──────────────┬────────────┘  └──────────┬──────────────┘
                 │                          │
                 └────────────┬─────────────┘
                              │
               ┌──────────────▼──────────────┐
               │  InsForge Backend API        │
               │  backend/src/server.ts       │
               │  /api/* routes               │
               └──────────────────────────────┘
```

Both doors reach the same backend. The difference is *how* the agent discovers capabilities and *what credentials* flow where.

Sources: [packages/dashboard/src/features/dashboard/components/connect/mcp/helpers.tsx:30-153](), [backend/src/server.ts:204-224]()

---

## Door 1 — The MCP Server

### What it is

The Model Context Protocol (MCP) is an open standard that lets AI coding assistants call named tools with structured arguments, exactly like function calls. InsForge ships an MCP server that exposes every backend capability as a callable tool: fetching documentation, scaffolding a project template, running SQL, creating storage buckets, deploying functions, and more.

There are two flavors of this same server:

| Flavor | Endpoint / Transport | Best for |
|---|---|---|
| **Local (stdio)** | `npx -y @insforge/mcp@latest` | Self-hosted InsForge; works without a public internet URL |
| **Remote (Streamable HTTP)** | `https://mcp.insforge.dev/mcp` | Cloud projects; zero install; shared across machines |

The local server is configured by injecting `API_KEY` and `API_BASE_URL` environment variables into the `npx` process. The remote server uses OAuth (browser-based) so no credentials appear in config files.

Sources: [packages/dashboard/src/features/dashboard/components/connect/mcp/helpers.tsx:120-143](), [docs/mcp-setup.mdx:462-478]()

### How to install it

#### Option A — Remote MCP (fastest)

```bash
npx add-mcp https://mcp.insforge.dev/mcp
```

Supported clients for this one-liner: Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Goose, GitHub Copilot, OpenCode, VS Code, Zed.

After running, open the agent's MCP panel and click **Authenticate** to complete the OAuth flow. Some clients (Antigravity, Cursor) do this automatically.

Sources: [docs/mcp-setup.mdx:464-475]()

#### Option B — Local MCP (self-hosted or manual)

The InsForge dashboard generates a project-specific install command. Copy it from **Settings → Connect → [Select your agent]** and run it in your terminal. For example, the generated command looks like:

```bash
npx @insforge/install --client claude-code \
  --env API_KEY=<your-key> \
  --env API_BASE_URL=https://your-app.region.insforge.app
```

This writes a config file at the appropriate location for your agent. Example for Claude Code:

```json
// .mcp.json (project-local)
{
  "mcpServers": {
    "insforge": {
      "command": "npx",
      "args": ["-y", "@insforge/mcp@latest"],
      "env": {
        "API_KEY": "<your-key>",
        "API_BASE_URL": "https://your-app.region.insforge.app"
      }
    }
  }
}
```

On Windows, the command becomes `cmd /c npx -y @insforge/mcp@latest` to handle shell differences.

Sources: [packages/dashboard/src/features/dashboard/components/connect/mcp/helpers.tsx:121-153](), [docs/mcp-setup.mdx:85-126]()

#### Option C — VS Code Extension (one-click)

The InsForge VS Code extension handles auth (OAuth + PKCE) and runs `npx @insforge/install` automatically once you pick a project and target agent. No terminal required.

Sources: [docs/vscode-extension.mdx:1-68]()

### What the agent can do once connected

The first thing agents are instructed to call is `fetch-docs`. This tool reads documentation directly from the backend's `/api/docs/` route, which serves the `.mdx` files under `docs/` with snippets resolved:

```
GET /api/docs/:docType
GET /api/docs/:feature/:language
```

The canonical verification prompt is:

```
I'm using InsForge as my backend platform, call InsForge MCP's fetch-docs tool to learn about InsForge instructions.
```

A green **MCP Connected** indicator appears in the InsForge dashboard when the MCP server calls `POST /api/usage/mcp` (authenticated via API key) and the backend broadcasts the event to admin clients over Socket.IO.

Sources: [packages/dashboard/src/features/dashboard/components/connect/constants.ts:1-2](), [backend/src/api/routes/usage/index.routes.ts:18-47](), [backend/src/api/routes/docs/index.routes.ts]()

### MCP tool categories

The MCP tools divide cleanly into **infrastructure** operations (things that configure or set up the backend) versus **documentation** fetching. The agent calls infrastructure tools for tasks the SDK cannot handle from application code:

| Category | Example tools | When agents use them |
|---|---|---|
| Project setup | `download-template`, `get-backend-metadata` | Scaffolding a new project |
| Database schema | `run-raw-sql`, `get-table-schema` | Creating or inspecting tables |
| Storage management | `create-bucket`, `list-buckets`, `delete-bucket` | Bucket lifecycle |
| Function deployment | `create-function`, `update-function`, `delete-function` | Serverless deploys |
| Frontend hosting | `create-deployment` | Deploying a Vite/Next.js app |
| Documentation | `fetch-docs`, `fetch-sdk-docs` | Reading SDK and API docs |

Sources: [docs/insforge-instructions-sdk.md:107-127]()

### Supported agents (MCP path)

The `MCP_AGENTS` list in the dashboard defines which agents have first-class MCP support:

Cursor · Claude Code · Trae · Cline · Windsurf · Roo Code · Qoder · GitHub Copilot · Google Antigravity · Codex · Kiro · OpenCode · OpenClaw · (manual MCP JSON)

Sources: [packages/dashboard/src/features/dashboard/components/connect/mcp/helpers.tsx:34-118]()

---

## Door 2 — The CLI + Skills

### What it is

The CLI is an npm package (`@insforge/cli`) that runs as a regular terminal command. Instead of registering tool schemas in the agent's MCP panel, it works as a subprocess the agent invokes through shell commands. Skills are Markdown files (`.claude/skills/<name>/SKILL.md`) placed inside your project that teach the agent *how* to use the CLI and *what conventions to follow*.

Think of Skills as a cheat-sheet for the agent: they describe which commands do what, what patterns to follow, and how to stay within InsForge conventions — all in plain text the agent reads from the filesystem.

### How to install it

```bash
npx @insforge/cli link --project-id <your-project-id>
```

Run this once inside your project directory. It links the local folder to your InsForge project. Skills are automatically discovered by Claude Code (and compatible agents) from `.claude/skills/` — no additional config needed.

Sources: [docs/quickstart.mdx:34-48]()

### How Skills are structured

Each skill lives in a subdirectory. The entry point is `SKILL.md` with YAML frontmatter:

```
.claude/skills/
├── README.md
├── insforge-dev/
│   ├── SKILL.md        # Top-level: route to the right sub-skill
│   ├── backend/SKILL.md
│   ├── dashboard/SKILL.md
│   ├── docs/SKILL.md
│   ├── shared-schemas/SKILL.md
│   └── ui/SKILL.md
└── doc-author/
    ├── SKILL.md        # Vendored from Mintlify upstream
    └── INSFORGE.md     # InsForge-specific overrides
```

The top-level `insforge-dev` skill directs the agent to "use the narrowest package skill that matches the task." This keeps the agent's working context small and focused.

The verification prompt for the CLI path is:

```
I'm using InsForge as my backend platform. Read the current directory,
make sure InsForge skills are installed, and use InsForge CLI for backend tasks.
```

Sources: [.claude/skills/README.md:1-31](), [packages/dashboard/src/features/dashboard/components/connect/constants.ts:4-5]()

### When Skills are updated

The `doc-author/SKILL.md` skill is vendored from Mintlify's upstream repository. To refresh it without hand-editing:

```bash
scripts/update-mintlify-skill.sh
```

The script re-downloads the upstream file, updates the commit SHA attribution, and fails loudly if Mintlify's license has changed from MIT. InsForge-specific overrides live in `doc-author/INSFORGE.md` and are never clobbered by the refresh script.

Sources: [.claude/skills/README.md:20-31]()

---

## Choosing the Right Door

The two interfaces are complementary, not competing. The key distinction is **where the agent is running** and **what kind of task it is doing**:

| Situation | Use | Reason |
|---|---|---|
| Agent inside an IDE (Cursor, Windsurf, etc.) | MCP server | Tool calls are first-class in IDE agents; no shell needed |
| Agent in a terminal (Claude Code CLI) | CLI + Skills | Shell access is direct; Skills provide task-specific instructions |
| Infrastructure operations (schema, buckets, deploys) | MCP tools | These require admin credentials the SDK never holds |
| Application code (auth, CRUD, storage access) | SDK (`@insforge/sdk`) | Runtime code runs in the user's browser/server, not the agent |
| Self-hosted InsForge, no public URL | Local MCP (stdio) or CLI | Remote MCP requires `mcp.insforge.dev` to be reachable |
| Cloud project, quick start | Remote MCP | One command, OAuth, no API key in config |

The `insforge-instructions-sdk.md` file is explicit: use the SDK for all application logic (auth, database CRUD, storage, AI calls) and use MCP tools for infrastructure setup. The CLI + Skills path overlaps with MCP for infrastructure tasks but is especially useful when the agent is running in a terminal environment where typing shell commands is natural.

Sources: [docs/insforge-instructions-sdk.md:100-127]()

---

## What Happens When an Agent Connects

Here is the actual sequence when an agent first connects via MCP:

```mermaid
sequenceDiagram
    participant Agent as AI Agent (e.g. Cursor)
    participant MCP as @insforge/mcp (local)<br/>or mcp.insforge.dev (remote)
    participant Backend as InsForge Backend<br/>backend/src/server.ts
    participant Dashboard as InsForge Dashboard<br/>(WebSocket)

    Agent->>MCP: Reads tool schema list on startup
    Agent->>MCP: Calls fetch-docs("instructions")
    MCP->>Backend: GET /api/docs/instructions
    Backend-->>MCP: Returns docs/insforge-instructions-sdk.md content
    MCP-->>Agent: Documentation text
    Agent->>MCP: Calls get-backend-metadata()
    MCP->>Backend: GET /api/metadata (verifyAdmin)
    Backend-->>MCP: { auth, database, storage, functions, version }
    MCP->>Backend: POST /api/usage/mcp { tool_name: "fetch-docs" }
    Backend->>Dashboard: Socket.IO broadcast MCP_CONNECTED event
    Dashboard-->>User: Shows green "MCP Connected" indicator
```

The `/api/usage/mcp` endpoint requires an API key (`verifyApiKey` middleware). It records the tool call and broadcasts a `MCP_CONNECTED` event via `SocketManager.broadcastToRoom('role:project_admin', ...)` so the dashboard updates in real time.

Sources: [backend/src/api/routes/usage/index.routes.ts:18-47](), [backend/src/api/routes/docs/index.routes.ts](), [backend/src/server.ts:204-224]()

---

## Config File Locations by Agent

The agent's MCP config is written to a well-known location that the agent scans at startup. For the local MCP path:

| Agent | Config file |
|---|---|
| Claude Code | `.mcp.json` (project workspace root) |
| Cursor | `~/.cursor/mcp.json` |
| GitHub Copilot | `.vscode/mcp.json` (project workspace) |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
| Cline | Cline VS Code extension config |
| Codex | `~/.codex/config.toml` |
| Trae | Trae IDE config |
| Qoder / Roo Code | IDE-specific extension config |

For the remote MCP path, the URL `https://mcp.insforge.dev/mcp` replaces the `npx` command + env block.

Sources: [docs/vscode-extension.mdx:54-68](), [docs/mcp-setup.mdx:485-651]()

---

## Summary

InsForge gives AI agents two distinct integration paths. The MCP server (local stdio or remote HTTP) is the richer path: it exposes typed tools for both infrastructure management and documentation fetching, works natively inside IDE agents, and signals connection status to the dashboard in real time. The CLI + Skills path is the lighter-weight path for terminal-based agents: a single `npx link` command + Markdown skill files that teach the agent InsForge conventions without any MCP scaffolding. For most project-level application code, neither path is used directly — the `@insforge/sdk` handles that at runtime. Both paths reach the same Express backend (`backend/src/server.ts`), which routes requests through a layered route → service → provider architecture.

Sources: [docs/introduction.mdx:23-25](), [backend/src/server.ts:65-224]()

---

## 03. The Seven Backend Primitives — What Each One Does

> InsForge exposes seven backend services to agents: Auth, Database (Postgres + PostgREST), Storage (S3-compatible), Model Gateway (OpenAI-compatible AI routing), Edge Functions (Deno serverless), Compute (long-running containers), and Site Deployment. This page explains what each primitive is for and how it is wired in the codebase.

- Page Markdown: https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/03-the-seven-backend-primitives-what-each-one-does.md
- Generated: 2026-05-22T01:37:12.302Z

### Source Files

- `backend/src/server.ts`
- `backend/src/api/routes/auth/index.routes.ts`
- `backend/src/api/routes/database/tables.routes.ts`
- `backend/src/api/routes/storage/index.routes.ts`
- `backend/src/api/routes/ai/index.routes.ts`
- `backend/src/api/routes/functions/index.routes.ts`
- `backend/src/api/routes/compute/services.routes.ts`
- `backend/src/api/routes/deployments/index.routes.ts`

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

- [backend/src/server.ts](backend/src/server.ts)
- [backend/src/api/routes/auth/index.routes.ts](backend/src/api/routes/auth/index.routes.ts)
- [backend/src/api/routes/database/index.routes.ts](backend/src/api/routes/database/index.routes.ts)
- [backend/src/api/routes/database/tables.routes.ts](backend/src/api/routes/database/tables.routes.ts)
- [backend/src/api/routes/storage/index.routes.ts](backend/src/api/routes/storage/index.routes.ts)
- [backend/src/api/routes/ai/index.routes.ts](backend/src/api/routes/ai/index.routes.ts)
- [backend/src/api/routes/functions/index.routes.ts](backend/src/api/routes/functions/index.routes.ts)
- [backend/src/api/routes/compute/services.routes.ts](backend/src/api/routes/compute/services.routes.ts)
- [backend/src/api/routes/deployments/index.routes.ts](backend/src/api/routes/deployments/index.routes.ts)
- [backend/src/services/functions/function.service.ts](backend/src/services/functions/function.service.ts)
- [backend/src/services/compute/services.service.ts](backend/src/services/compute/services.service.ts)
</details>

# The Seven Backend Primitives — What Each One Does

InsForge is a self-hosted application platform that gives agents and developers a set of reusable backend services: identity management, a relational database with a REST query layer, S3-compatible file storage, a model-neutral AI gateway, Deno-based serverless functions, long-running containerized compute, and static site deployment. All seven are wired into a single Express server and exposed under the `/api` prefix.

This page explains what each primitive does, where it lives in the codebase, and what shapes the API it exposes. It is written for developers who are new to the repo and want to understand the system before editing any of it.

---

## How the Primitives Fit Together

At startup the server mounts every primitive as an Express sub-router. The ordering matters in one place: the S3 protocol gateway and the Stripe webhooks are mounted *before* the JSON body-parser so their raw bytes are never consumed.

```text
┌───────────────────── Express app (port 7130) ─────────────────────┐
│  /storage/v1/s3        ← S3 gateway (raw stream, no JSON parse)   │
│  /api/webhooks         ← Stripe webhooks (raw body)               │
│  /api/auth             ← Auth primitive                           │
│  /api/database         ← Database primitive                       │
│  /api/storage          ← Storage primitive                        │
│  /api/ai               ← Model Gateway primitive                  │
│  /api/functions        ← Edge Functions primitive                 │
│  /api/compute/services ← Compute primitive                        │
│  /api/deployments      ← Site Deployment primitive                │
│  /functions/:slug      ← Backwards-compat proxy to Deno runtime   │
└───────────────────────────────────────────────────────────────────┘
```

Sources: [backend/src/server.ts:174-221]()

---

## 1. Auth

**What it does.** Auth manages users, sessions, OAuth, email verification, and password resets. Every other primitive checks tokens minted here before allowing writes.

**How it works.** The service keeps user records in a PostgreSQL database. When someone logs in, it issues a short-lived JWT *access token* and a longer-lived *refresh token*. Web clients receive the refresh token in an `httpOnly` cookie plus a CSRF nonce; mobile, desktop, and server clients receive it in the response body — the code branches explicitly on a `client_type` query parameter.

```
POST /api/auth/sessions           → login  (email + password → access + refresh tokens)
POST /api/auth/users              → registration
POST /api/auth/refresh            → rotate refresh token → new access token
POST /api/auth/logout             → clear cookie (web) / discard token (non-web)
GET  /api/auth/sessions/current   → who am I?
POST /api/auth/email/send-verification → send OTP or magic link
POST /api/auth/email/verify       → redeem code
GET  /api/auth/email/verify-link  → browser click → redirect
POST /api/auth/email/reset-password    → apply new password
POST /api/auth/id-token           → sign in with Google One Tap ID token
GET  /api/auth/oauth/*            → OAuth 2.0 flows (GitHub, Google, custom providers)
```

Admin-only routes (guarded by `verifyAdmin`) let you list, create, or delete users and update auth config, SMTP settings, and email templates.

Sources: [backend/src/api/routes/auth/index.routes.ts:428-465](), [backend/src/api/routes/auth/index.routes.ts:522-619]()

### Token flavors

| Token | Transport | Audience |
|-------|-----------|----------|
| Access JWT | `Authorization: Bearer` header | All clients |
| Refresh token + CSRF nonce | `httpOnly` cookie + `X-CSRF-Token` header | Web |
| Refresh token | Response body | Mobile / desktop / server |
| Anonymous JWT (never expires) | Response body | Public read-only callers |

Sources: [backend/src/api/routes/auth/index.routes.ts:393-423](), [backend/src/api/routes/auth/index.routes.ts:771-783]()

---

## 2. Database (Postgres + PostgREST-style layer)

**What it does.** The Database primitive provides a managed PostgreSQL database with a REST query API. Agents and application code can read and write rows, call stored procedures, and manage schema — all over HTTP, without writing raw SQL.

**How it works.** The backend holds a connection pool (`DatabaseManager`) to the project's Postgres instance. The `/api/database` router has six sub-routers:

| Sub-route | Handles |
|-----------|---------|
| `/tables` | Create / inspect / patch / delete table schemas |
| `/records` | CRUD on individual rows (PostgREST-style query strings) |
| `/rpc` | Call Postgres stored procedures |
| `/advance` | Raw SQL execution for power users |
| `/migrations` | Apply and track migrations |
| `/admin` | Extensions, role grants, etc. |

Top-level paths on the router expose schemas, SQL functions, indexes, RLS policies, and triggers as read endpoints.

```
GET  /api/database/schemas               → list schemas
GET  /api/database/tables?schema=public  → list tables
POST /api/database/tables                → create table
GET  /api/database/tables/:name/schema   → introspect columns
PATCH /api/database/tables/:name/schema  → add/rename/drop columns
POST /api/database/records/:table        → insert row
GET  /api/database/records/:table        → filtered select
POST /api/database/rpc/:function         → call stored proc
```

Every write flushes a `clearColumnTypeCache` call so the type-coercion layer inside `DatabaseManager` picks up schema changes immediately.

Sources: [backend/src/api/routes/database/index.routes.ts:17-23](), [backend/src/api/routes/database/tables.routes.ts:31-67](), [backend/src/api/routes/database/tables.routes.ts:107-126]()

### Row-Level Security

Tables can be created with `rlsEnabled: true`. When a JWT caller queries `/api/database/records`, the backend uses their token's role claim to evaluate RLS policies. API-key callers use the backend's privileged pool and bypass user-level RLS.

Sources: [backend/src/api/routes/database/tables.routes.ts:44-45]()

---

## 3. Storage (S3-compatible)

**What it does.** Storage gives agents a place to put files — images, PDFs, user uploads, build artifacts — organized into named *buckets*. It presents an InsForge-native REST API **and** a full AWS S3 protocol endpoint at `/storage/v1/s3`, so any S3 SDK works out of the box.

**How it works.** Buckets are either public (no auth needed to download) or private. The `conditionalDownloadAuth` middleware checks bucket visibility before deciding whether to require a token on GET requests.

When an external S3 provider is configured, large uploads can bypass the backend entirely: the client calls `POST /upload-strategy` first, receives a presigned URL, uploads directly to S3, then confirms with `POST /confirm-upload`. For local/default storage, the backend streams the bytes itself.

```
POST /api/storage/buckets                           → create bucket
GET  /api/storage/buckets/:name/objects             → list objects
PUT  /api/storage/buckets/:name/objects/*           → upload (specific key)
POST /api/storage/buckets/:name/objects             → upload (server-generated key)
GET  /api/storage/buckets/:name/objects/*           → download (auth conditional)
DELETE /api/storage/buckets/:name/objects/*         → delete object
POST /api/storage/buckets/:name/upload-strategy     → get presigned PUT URL
POST /api/storage/buckets/:name/objects/:key/confirm-upload → confirm presigned upload
POST /api/storage/buckets/:name/objects/:key/download-strategy → get presigned GET URL

GET  /api/storage/s3/config                         → S3 endpoint + region for SDK config
POST /api/storage/s3/access-keys                    → create long-lived S3 credential
GET  /api/storage/s3/access-keys                    → list keys (no secrets)
DELETE /api/storage/s3/access-keys/:id              → revoke key
```

The S3 gateway is mounted before the JSON middleware so chunked `STREAMING-AWS4-HMAC-SHA256-PAYLOAD` requests stream through untouched.

Sources: [backend/src/server.ts:178-179](), [backend/src/api/routes/storage/index.routes.ts:29-46](), [backend/src/api/routes/storage/index.routes.ts:666-680](), [backend/src/api/routes/storage/index.routes.ts:682-710]()

---

## 4. Model Gateway (OpenAI-compatible AI routing)

**What it does.** The Model Gateway lets agents send chat, embedding, and image-generation requests to any supported model. The gateway presents a single stable API surface regardless of which underlying provider is used — today that is OpenRouter, but the provider is isolated behind an interface.

**How it works.** `ChatCompletionService`, `EmbeddingService`, and `ImageGenerationService` are singletons that delegate to provider implementations. For streaming chat completions, the router opens a Server-Sent Events (SSE) response and forwards chunks, token usage, tool calls, and annotations one frame at a time.

```
GET  /api/ai/models                 → list available models (admin)
GET  /api/ai/overview               → key-level usage overview from OpenRouter (admin)
GET  /api/ai/:provider/api-key      → masked API key (admin)
POST /api/ai/chat/completion        → chat (streaming SSE or single JSON)
POST /api/ai/image/generation       → image generation
POST /api/ai/embeddings             → text embeddings
```

`verifyUser` (not `verifyAdmin`) guards the inference endpoints, so any authenticated user in the project can call them.

### Streaming protocol

```
POST /api/ai/chat/completion  { stream: true, messages: [...] }

SSE frames:
  data: {"chunk": "Hello"}
  data: {"chunk": " world"}
  data: {"tokenUsage": {"promptTokens": 10, "completionTokens": 4}}
  data: {"done": true}
```

Sources: [backend/src/api/routes/ai/index.routes.ts:19-20](), [backend/src/api/routes/ai/index.routes.ts:127-170](), [backend/src/api/routes/ai/index.routes.ts:195-264]()

---

## 5. Edge Functions (Deno serverless)

**What it does.** Edge Functions let you deploy short-lived Deno TypeScript code that runs on a request. Think of them as HTTP endpoints you write and deploy without provisioning a server.

**How it works.** `FunctionService` stores function metadata and source code in the `functions.definitions` Postgres table. At runtime, function invocations are proxied to whichever Deno runtime is available:

1. **Deno Subhosting** — if `DENO_SUBHOSTING_ORG_ID` and related vars are set, functions are synced to Deno's managed cloud runtime and invoked there.
2. **Local Deno runtime** — otherwise, the backend proxies to `http://localhost:7133` (configurable via `DENO_RUNTIME_URL`).

The management API lives at `/api/functions`; execution calls reach the function directly at `/functions/:slug` (a backwards-compatible catch-all on the Express root).

```
GET    /api/functions           → list functions + runtime health
GET    /api/functions/:slug     → get function code + metadata
POST   /api/functions           → create function (deploys immediately)
PUT    /api/functions/:slug     → update code (redeploys)
DELETE /api/functions/:slug     → delete + undeploy

ALL    /functions/:slug         → proxy execution to Deno runtime
```

On startup, `functionService.syncDeployment()` runs in the background (non-blocking) to push any locally stored functions to Subhosting if configured.

Sources: [backend/src/server.ts:228-286](), [backend/src/server.ts:339-345](), [backend/src/services/functions/function.service.ts:52-80](), [backend/src/api/routes/functions/index.routes.ts:55-105]()

---

## 6. Compute (long-running containers)

**What it does.** Compute lets you run Docker containers that stay alive between requests — background workers, databases, long-running ML jobs, or any process that doesn't fit in a serverless function's execution window.

**How it works.** `ComputeServicesService` wraps a `ComputeProvider` interface. In self-hosted mode the backing provider is `FlyProvider` (Fly.io machines); in cloud-managed mode `CloudComputeProvider` is used. This abstraction means the route layer stays the same regardless of where containers actually run.

Services are identified by `projectId`, which is read from the JWT claim (`req.projectId`) in cloud mode or from the `PROJECT_ID` environment variable in self-hosted mode.

```
GET    /api/compute/services          → list services
GET    /api/compute/services/:id      → get service details
POST   /api/compute/services          → create + immediately launch container
POST   /api/compute/services/deploy   → prepare (create app, no machine yet)
PATCH  /api/compute/services/:id      → update image / env vars / resources
DELETE /api/compute/services/:id      → delete + destroy container
POST   /api/compute/services/:id/start  → start a stopped service
POST   /api/compute/services/:id/stop   → stop a running service
GET    /api/compute/services/:id/events → lifecycle events (start/stop/exit)
POST   /api/compute/services/:id/deploy-token → issue a Fly deploy token for the CLI
```

The `deploy-token` endpoint exists for the two-step CLI deploy path: the CLI issues `flyctl` commands using a short-lived token minted here rather than requiring the user to hold their own `FLY_API_TOKEN`.

Env vars are stored encrypted in the database. The audit log records only the *keys* that changed, never the values.

Sources: [backend/src/api/routes/compute/services.routes.ts:17-21](), [backend/src/api/routes/compute/services.routes.ts:107-141](), [backend/src/api/routes/compute/services.routes.ts:143-165](), [backend/src/services/compute/services.service.ts:1-10]()

---

## 7. Site Deployment

**What it does.** Site Deployment publishes static frontends (HTML/JS/CSS) to a CDN-backed hosting provider, manages custom domains, and tracks deployment history. The result is a live URL for each deployment.

**How it works.** `DeploymentService` wraps a provider (currently Vercel). Deployments follow one of two flows:

**Legacy zip flow**
1. `POST /api/deployments` — create a deployment record (status: `WAITING`), receive presigned S3 upload info.
2. Upload source zip to S3.
3. `POST /api/deployments/:id/start` — trigger the build.

**Direct-upload flow** (used by the dashboard and CLI)
1. `POST /api/deployments/direct` — create a record with a manifest of files.
2. `PUT /api/deployments/:id/files/:fileId/content` — stream each file's bytes through the backend to Vercel. This endpoint is intentionally not rate-limited because one `POST /direct` call may fan out to many file uploads.
3. `POST /api/deployments/:id/start` — trigger the deployment.

```
POST   /api/deployments                    → create deployment (zip flow)
POST   /api/deployments/direct             → create deployment (direct flow)
PUT    /api/deployments/:id/files/:fileId/content → upload one file
POST   /api/deployments/:id/start          → trigger build
GET    /api/deployments                    → list deployments
GET    /api/deployments/:id                → get deployment
POST   /api/deployments/:id/sync           → pull latest status from Vercel
POST   /api/deployments/:id/cancel         → cancel in-progress build
GET    /api/deployments/metadata           → current live deployment + domain URLs
PUT    /api/deployments/slug               → set/change project slug
GET    /api/deployments/domains            → list custom domains
POST   /api/deployments/domains            → add custom domain
POST   /api/deployments/domains/:domain/verify → verify DNS config
DELETE /api/deployments/domains/:domain    → remove domain
```

Sources: [backend/src/api/routes/deployments/index.routes.ts:30-90](), [backend/src/api/routes/deployments/index.routes.ts:96-150](), [backend/src/api/routes/deployments/index.routes.ts:156-193]()

---

## Putting it All Together

```mermaid
flowchart TD
    Client["Agent / Browser / CLI"]

    subgraph Express["Express Server (port 7130)"]
        Auth["/api/auth\nAuth"]
        DB["/api/database\nDatabase"]
        Stor["/api/storage\nStorage"]
        AI["/api/ai\nModel Gateway"]
        Fn["/api/functions\nEdge Functions"]
        Compute["/api/compute/services\nCompute"]
        Deploy["/api/deployments\nSite Deployment"]
    end

    subgraph Infra["Infrastructure / Providers"]
        PG["PostgreSQL"]
        DenoRT["Deno Runtime\n(local or Subhosting)"]
        Fly["Fly.io / CloudComputeProvider"]
        Vercel["Vercel"]
        S3["S3 / Local filesystem"]
        OR["OpenRouter (AI models)"]
    end

    Client --> Auth
    Client --> DB
    Client --> Stor
    Client --> AI
    Client --> Fn
    Client --> Compute
    Client --> Deploy

    Auth --> PG
    DB --> PG
    Stor --> S3
    AI --> OR
    Fn --> DenoRT
    Compute --> Fly
    Deploy --> Vercel
```

All seven primitives share a single authentication layer — the `verifyToken` / `verifyAdmin` middleware from `backend/src/api/middlewares/auth.ts` — so a token issued by Auth is accepted by every other primitive without additional integration work. Every write operation emits an audit log entry and, for admin-facing resources, a Socket.IO broadcast to update connected dashboards in real time.

Sources: [backend/src/server.ts:204-222](), [backend/src/api/routes/compute/services.routes.ts:23-41]()

---

## 04. Inside the Backend — How Requests Travel from Agent to Database

> A concrete walk-through of the Express server (server.ts), the three-layer architecture (routes → services → providers/infra), the PostgREST proxy pattern used for database record access, the Socket.IO realtime push, and the database migration system that bootstraps every InsForge instance.

- Page Markdown: https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/04-inside-the-backend-how-requests-travel-from-agent-to-database.md
- Generated: 2026-05-22T01:38:01.316Z

### Source Files

- `backend/src/server.ts`
- `backend/src/infra/database/database.manager.ts`
- `backend/src/infra/realtime/realtime.manager.ts`
- `backend/src/infra/socket/socket.manager.ts`
- `backend/src/api/routes/database/records.routes.ts`
- `backend/src/services/database/postgrest-proxy.service.ts`
- `backend/src/infra/database/migrations/000_create-base-tables.sql`
- `backend/src/infra/config/app.config.ts`

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

- [backend/src/server.ts](backend/src/server.ts)
- [backend/src/infra/config/app.config.ts](backend/src/infra/config/app.config.ts)
- [backend/src/infra/database/database.manager.ts](backend/src/infra/database/database.manager.ts)
- [backend/src/infra/realtime/realtime.manager.ts](backend/src/infra/realtime/realtime.manager.ts)
- [backend/src/infra/socket/socket.manager.ts](backend/src/infra/socket/socket.manager.ts)
- [backend/src/api/routes/database/index.routes.ts](backend/src/api/routes/database/index.routes.ts)
- [backend/src/api/routes/database/records.routes.ts](backend/src/api/routes/database/records.routes.ts)
- [backend/src/services/database/postgrest-proxy.service.ts](backend/src/services/database/postgrest-proxy.service.ts)
- [backend/src/services/database/database-migration.service.ts](backend/src/services/database/database-migration.service.ts)
- [backend/src/infra/database/migrations/000_create-base-tables.sql](backend/src/infra/database/migrations/000_create-base-tables.sql)
- [backend/src/infra/database/migrations/017_create-realtime-schema.sql](backend/src/infra/database/migrations/017_create-realtime-schema.sql)
- [backend/src/utils/seed.ts](backend/src/utils/seed.ts)
</details>

# Inside the Backend — How Requests Travel from Agent to Database

InsForge's backend is an Express server that bridges AI agents, frontend clients, and a PostgreSQL database. Understanding how a request enters the server, gets authorized, reaches the right service, touches the database, and optionally triggers a realtime push to connected clients is the key to reading—and modifying—almost any part of the codebase. This page traces that full path with concrete file citations at every step.

---

## Server Bootstrap: What Runs First

Everything starts in `createApp()` and `initializeServer()` in `backend/src/server.ts`.

**Before** Express even accepts a connection, three blocking initialization steps run in order:

1. `DatabaseManager.getInstance().initialize()` — creates the `pg` connection pool to PostgreSQL.
2. `StorageService.getInstance().initialize()` — prepares the storage layer.
3. `LogService.getInstance().initialize()` — connects to CloudWatch (or local logger).

Only then does the Express `app` object get built, middleware is attached, and routes are mounted. After `app.listen()` returns, two more services are layered on top of the same `http.Server` handle:

- **`SocketManager`** — attaches Socket.IO to the HTTP server (`socketService.initialize(server)`).
- **`RealtimeManager`** — opens a dedicated PostgreSQL `LISTEN` connection (`realtimeManager.initialize()`).

```ts
// backend/src/server.ts:327-337
const server = app.listen(PORT, () => { ... });
const socketService = SocketManager.getInstance();
socketService.initialize(server);          // Socket.IO on top of the same port

const realtimeManager = RealtimeManager.getInstance();
await realtimeManager.initialize();        // LISTEN realtime_message
```

This order matters: if `DatabaseManager` fails, the process exits. Socket.IO and the realtime listener come up last, so the HTTP API is already serving by the time WebSocket clients try to connect.

Sources: [backend/src/server.ts:65-355]()

---

## Middleware Stack and Route Mounting

Incoming HTTP requests pass through this ordered middleware chain before any route handler sees them:

| Order | Middleware | Purpose |
|-------|-----------|---------|
| 1 | `cors({ origin: true, credentials: true })` | Allow all origins; expose `Content-Range` and `Preference-Applied` headers |
| 2 | `cookieParser()` | Parse cookies for refresh-token flows |
| 3 | `rateLimit` (3 000 req / 15 min) | Global IP-based rate limiter; skips `/api/health` |
| 4 | Request logger | Instruments `res.send` / `res.json` to capture size and duration |
| 5 | `express.raw()` on `/api/webhooks` | Raw body before JSON parsing so webhook signatures can be verified |
| 6 | S3 gateway on `/storage/v1/s3` | Streams S3 protocol bodies untouched before JSON middleware |
| 7 | `express.json({ limit: '100mb' })` | JSON body parsing for all other routes |

After the shared middleware, the API surface is organised under a single `/api` prefix, then split into named sub-routers:

```
/api/auth          authRouter
/api/database      databaseRouter   ← main focus of this page
/api/storage       storageRouter
/api/realtime      realtimeRouter
/api/functions     functionsRouter
... (14 more routers)
```

Sources: [backend/src/server.ts:86-224]()

---

## Three-Layer Architecture

The backend follows a clean routes → services → infrastructure layering:

```text
┌─────────────────────────────────────────────┐
│  HTTP Route Handler  (api/routes/**)        │  validate, auth, orchestrate
│    e.g. records.routes.ts                   │
└──────────────────┬──────────────────────────┘
                   │ calls
┌──────────────────▼──────────────────────────┐
│  Service Layer   (services/**)              │  business logic, retries
│    e.g. PostgrestProxyService               │
│         DatabaseMigrationService            │
└──────────────────┬──────────────────────────┘
                   │ uses
┌──────────────────▼──────────────────────────┐
│  Infrastructure  (infra/**)                 │  connections, pools, sockets
│    DatabaseManager (pg Pool)                │
│    SocketManager   (Socket.IO)              │
│    RealtimeManager (pg LISTEN)              │
└─────────────────────────────────────────────┘
```

**Routes** are thin: they authenticate the request, validate table names, and delegate to a service. They do not query the database directly.

**Services** own the non-trivial logic: the PostgREST proxy adds retry logic and admin-token injection; `DatabaseMigrationService` validates SQL ASTs before running them.

**Infrastructure** classes are singletons that hold expensive long-lived resources (connection pools, Socket.IO server, a dedicated LISTEN client). Routes and services call them via `getInstance()`.

Sources: [backend/src/api/routes/database/index.routes.ts:1-111](), [backend/src/api/routes/database/records.routes.ts:1-123](), [backend/src/services/database/postgrest-proxy.service.ts:1-170]()

---

## The Database Sub-Router

`/api/database` is itself further split into six sub-routes mounted in `backend/src/api/routes/database/index.routes.ts`:

| Mount path | Router file | Purpose |
|------------|-------------|---------|
| `/tables` | `tables.routes.ts` | DDL: create/alter/drop tables |
| `/records` | `records.routes.ts` | CRUD on user table rows — **proxied to PostgREST** |
| `/rpc` | `rpc.routes.ts` | Call PostgreSQL stored functions |
| `/advance` | `advance.routes.ts` | Raw SQL execution |
| `/migrations` | `migrations.routes.ts` | Apply custom versioned migrations |
| `/admin` | `admin.routes.ts` | Schema introspection (schemas, indexes, policies, triggers) |

Sources: [backend/src/api/routes/database/index.routes.ts:18-23]()

---

## The PostgREST Proxy Pattern for Record Access

The most interesting design decision in the database layer is that **row-level CRUD is not implemented in Express at all**. Instead, a PostgREST sidecar runs on `localhost:5430`, and the Express route simply forwards the request to it.

### Why PostgREST?

PostgREST translates REST requests directly into PostgreSQL queries, honoring Row Level Security (RLS) policies defined in SQL. This means the backend gets filtering, ordering, pagination, and access control essentially for free, without writing custom query-builder logic.

### The Forwarding Path

```
Client
  → GET /api/database/records/orders?status=eq.open
      ↓
  records.routes.ts  (verifyUser middleware)
      ↓
  PostgrestProxyService.forward({ method, path, query, headers, body })
      ↓
  Axios → http://localhost:5430/orders?status=eq.open
      ↓
  PostgREST → PostgreSQL query under the user's JWT role
      ↓
  Response forwarded back to client
```

**Key implementation details in `PostgrestProxyService`:**

1. **Connection pooling** — A dedicated `axios` instance reuses keep-alive HTTP connections to PostgREST (`keepAlive: true`, up to 20 sockets), avoiding handshake overhead on every request.

2. **Admin token injection** — If the caller provides a valid API key, the service replaces the `Authorization` header with an admin-level JWT, granting full table access regardless of RLS.

3. **Retry with exponential backoff** — Network errors (not HTTP-level errors) trigger up to 3 retries. The delay follows `min(200ms × 2.5^(attempt-1), 1000ms)`.

4. **Empty-value filtering** — Before forwarding `POST`/`PATCH`/`PUT` bodies, the route handler consults `DatabaseManager.getColumnTypeMap()` (cached for 5 minutes) and strips empty strings from UUID columns to prevent type coercion errors.

```ts
// backend/src/services/database/postgrest-proxy.service.ts:8
const postgrestUrl = process.env.POSTGREST_BASE_URL || 'http://localhost:5430';

// backend/src/services/database/postgrest-proxy.service.ts:138-155
for (let attempt = 1; attempt <= maxRetries; attempt++) {
  try {
    response = await postgrestAxios(axiosConfig);
    break;
  } catch (error) {
    const shouldRetry = axios.isAxiosError(error) && !error.response && attempt < maxRetries;
    if (shouldRetry) {
      const backoffDelay = Math.min(200 * Math.pow(2.5, attempt - 1), 1000);
      await new Promise((resolve) => setTimeout(resolve, backoffDelay));
    } else { throw error; }
  }
}
```

Sources: [backend/src/services/database/postgrest-proxy.service.ts:1-170](), [backend/src/api/routes/database/records.routes.ts:32-117]()

### Socket Broadcast on Mutations

After a successful `POST` or `DELETE`, the records route fires a real-time side-effect. It broadcasts a `DATA_UPDATE` Socket.IO event to every admin client so the dashboard can refresh:

```ts
// backend/src/api/routes/database/records.routes.ts:100-110
if (['POST', 'DELETE'].includes(method)) {
  const socket = SocketManager.getInstance();
  socket.broadcastToRoom(
    'role:project_admin',
    ServerEvents.DATA_UPDATE,
    { resource: DataUpdateResourceType.DATABASE,
      data: { changes: [{ type: 'records', name: tableName }] } },
    'system'
  );
}
```

Sources: [backend/src/api/routes/database/records.routes.ts:99-113]()

---

## Socket.IO Realtime Push

`SocketManager` wraps Socket.IO and is the single point for all WebSocket communication. It attaches to the same HTTP server as Express (no separate port), so clients use one connection for both REST and WebSocket traffic.

### Authentication

Every Socket.IO connection is authenticated by middleware before any event handler runs. Two auth paths are supported:

- **API key** (`socket.handshake.auth.apiKey`) — verified via `SecretService`. On success, the socket gets a synthetic `api-key-client` user identity.
- **JWT** (`socket.handshake.auth.token`) — verified via `TokenManager.verifyToken()`. The decoded role and user ID are stored on `socket.data.user`.

Sources: [backend/src/infra/socket/socket.manager.ts:73-141]()

### Room Model

When a socket connects, it is automatically placed in two rooms:

- `user:<userId>` — for targeted per-user messages.
- `role:<role>` — for role-based broadcasts (e.g., the `DATA_UPDATE` above goes to `role:project_admin`).

When a client subscribes to a realtime channel (event `REALTIME_SUBSCRIBE`), the server joins the socket to a third room: `realtime:<channelName>`. Presence tracking records who is in each room.

Sources: [backend/src/infra/socket/socket.manager.ts:183-199](), [backend/src/infra/socket/socket.manager.ts:280-350]()

---

## The Realtime Pipeline: From Database Trigger to Browser

This is the most architecturally distinctive feature. InsForge routes realtime messages entirely through PostgreSQL's notification system, not through in-process queues.

### How it works, step by step

```mermaid
sequenceDiagram
    participant DB as PostgreSQL
    participant RM as RealtimeManager<br/>(LISTEN client)
    participant SM as SocketManager
    participant WS as Browser WebSocket

    Note over DB: A trigger calls realtime.publish(...)
    DB->>DB: INSERT INTO realtime.messages
    DB->>DB: trg_message_notify fires pg_notify('realtime_message', message_id)
    DB-->>RM: notification event (message_id only)
    RM->>DB: SELECT * FROM realtime.messages WHERE id = message_id
    RM->>DB: SELECT * FROM realtime.channels WHERE id = channel_id
    RM->>SM: broadcastToRoom('realtime:<channelName>', eventName, payload)
    SM->>WS: Socket.IO emit to subscribed clients
    RM->>DB: UPDATE realtime.messages SET ws_audience_count = N
```

**Why send only a UUID in `pg_notify`?** PostgreSQL imposes an 8 KB size limit on notification payloads. Sending just the `message_id` and fetching the full row afterwards bypasses this limit entirely.

```sql
-- backend/src/infra/database/migrations/017_create-realtime-schema.sql:202-210
CREATE OR REPLACE FUNCTION realtime.notify_on_message_insert()
RETURNS TRIGGER AS $$
BEGIN
  PERFORM pg_notify('realtime_message', NEW.id::text);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;
```

```ts
// backend/src/infra/realtime/realtime.manager.ts:55-56
await this.listenerClient.query('LISTEN realtime_message');
this.listenerClient.on('notification', (msg) => {
  void this.handlePGNotification(msg.payload);   // msg.payload is just the UUID
});
```

Sources: [backend/src/infra/database/migrations/017_create-realtime-schema.sql:197-217](), [backend/src/infra/realtime/realtime.manager.ts:45-120]()

### The `realtime.publish()` SQL function

Developers can publish events from their own database triggers by calling:

```sql
PERFORM realtime.publish(
  'order:' || NEW.id::text,   -- channel name (may match wildcard like 'order:%')
  'order_updated',             -- event name
  jsonb_build_object('id', NEW.id, 'status', NEW.status)
);
```

The function resolves the channel by exact match first, then by `LIKE` pattern, inserts the message, and the `pg_notify` trigger fires automatically. The function is `SECURITY DEFINER` and only executable by the backend role — end users cannot invoke it directly.

Sources: [backend/src/infra/database/migrations/017_create-realtime-schema.sql:149-196]()

### Reconnection Resilience

`RealtimeManager` uses a dedicated `pg.Client` (not the pool) because pooled connections cannot hold a persistent `LISTEN` state. If the connection drops, it retries up to 10 times with exponential backoff starting at 5 seconds.

Sources: [backend/src/infra/realtime/realtime.manager.ts:195-220]()

---

## Migration System: Bootstrapping Every Instance

InsForge ships 44+ numbered SQL migration files (as of this writing) under `backend/src/infra/database/migrations/`. They run sequentially when a new instance is first set up and are tracked in a system table so they are never applied twice.

### What migration 000 creates

The very first migration (`000_create-base-tables.sql`) establishes all the internal system tables that the backend depends on at startup:

| Table | Purpose |
|-------|---------|
| `_config` | Key-value system configuration |
| `_metadata` | Version and creation date |
| `_storage_buckets` / `_storage` | File storage registry |
| `_mcp_usage` | AI tool call tracking |
| `_ai_configs` / `_ai_usage` | AI provider configuration and token usage |
| `_edge_functions` | Edge function code storage |
| `_user` / `_account` | Auth users and OAuth connections |

A shared `update_updated_at_column()` trigger function is also created here and reused by later migrations.

Sources: [backend/src/infra/database/migrations/000_create-base-tables.sql:1-142]()

### Custom migrations via the API

The `DatabaseMigrationService` lets developers apply their own versioned migrations through `/api/database/migrations`. Before running any SQL, it validates the AST with `libpg-query` to block:

- `BEGIN` / `COMMIT` / `ROLLBACK` statements (migrations must not manage their own transactions).
- Writes to managed schemas (`auth`, `realtime`, `storage`).
- Writes to system-internal schemas.

This guardrail prevents migrations from corrupting the tables InsForge itself depends on.

Sources: [backend/src/services/database/database-migration.service.ts:27-62]()

---

## Configuration

All environment-driven settings are typed in `backend/src/infra/config/app.config.ts`. The key variables for the backend request path:

| Variable | Default | Controls |
|----------|---------|---------|
| `PORT` | `7130` | Express listen port |
| `POSTGRES_HOST/DB/USER/PASSWORD` | `localhost/insforge/postgres/postgres` | `pg.Pool` connection |
| `POSTGREST_BASE_URL` | `http://localhost:5430` | PostgREST proxy target |
| `JWT_SECRET` | `your_jwt_secret` | Token signing/verification |
| `ACCESS_API_KEY` | `your_api_key` | API key for admin access |
| `MAX_JSON_BODY_SIZE` | `100mb` | Express JSON body limit |
| `DENO_RUNTIME_URL` | `http://localhost:7133` | Edge function runtime |
| `FLY_API_TOKEN` + `FLY_ORG` | (empty) | Opt-in compute layer |

Sources: [backend/src/infra/config/app.config.ts:30-63](), [backend/src/server.ts:185-190](), [backend/src/services/database/postgrest-proxy.service.ts:8]()

---

## Summary

An HTTP request to InsForge's backend travels from Express middleware (CORS → rate limiter → auth) through a three-layer stack (route handler → service → infrastructure) and ultimately reaches PostgreSQL either directly via the `pg.Pool` or indirectly via the PostgREST sidecar. On write operations, a Socket.IO broadcast notifies admin dashboards immediately. For developer-defined realtime events, the path goes through PostgreSQL itself: a trigger calls `realtime.publish()`, which inserts a message and fires `pg_notify`; the `RealtimeManager`'s dedicated listener picks up the UUID, fetches the full record, and fans it out to subscribed WebSocket clients. The entire schema needed for this to work is bootstrapped by a numbered migration sequence, starting from `000_create-base-tables.sql`, ensuring every self-hosted InsForge instance begins from a consistent, reproducible state.

Sources: [backend/src/server.ts:324-355](), [backend/src/infra/realtime/realtime.manager.ts:86-120]()

---

## 05. Running InsForge — Self-Hosted, Docker, and Cloud

> How to get InsForge running: the Docker Compose stack (backend, Postgres, Deno subhosting), the frontend dashboard (React/Vite, cloud vs. self-host modes), deploy targets (Render, AWS EC2, Azure, GCE), and the environment variables an operator must configure before the server boots.

- Page Markdown: https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/05-running-insforge-self-hosted-docker-and-cloud.md
- Generated: 2026-05-22T01:37:14.109Z

### Source Files

- `docker-compose.override.yml`
- `deploy/docker-compose/docker-compose.yml`
- `Dockerfile`
- `deploy/Dockerfile.deno`
- `deploy/docker-compose/.env.example`
- `frontend/src/App.tsx`
- `docs/deployment/README.md`
- `GITHUB_OAUTH_SETUP.md`

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

- [deploy/docker-compose/docker-compose.yml](deploy/docker-compose/docker-compose.yml)
- [deploy/docker-compose/.env.example](deploy/docker-compose/.env.example)
- [docker-compose.override.yml](docker-compose.override.yml)
- [Dockerfile](Dockerfile)
- [deploy/Dockerfile.deno](deploy/Dockerfile.deno)
- [deploy/zeabur/template.yml](deploy/zeabur/template.yml)
- [deploy/docker-deploy.md](deploy/docker-deploy.md)
- [deploy/zeabur/README.md](deploy/zeabur/README.md)
- [frontend/src/App.tsx](frontend/src/App.tsx)
- [frontend/src/helpers.ts](frontend/src/helpers.ts)
- [docs/deployment/README.md](docs/deployment/README.md)
- [docs/deployment/deploy-to-render.md](docs/deployment/deploy-to-render.md)
- [docs/deployment/deploy-to-aws-ec2.md](docs/deployment/deploy-to-aws-ec2.md)
- [GITHUB_OAUTH_SETUP.md](GITHUB_OAUTH_SETUP.md)
</details>

# Running InsForge — Self-Hosted, Docker, and Cloud

This page explains every way to get InsForge running: the four-container Docker Compose stack that makes up a self-hosted installation, the React/Vite frontend that switches between cloud and self-hosted modes at runtime, the environment variables an operator must supply before the server boots, and the supported deployment targets including Render, AWS EC2, Google Cloud Compute Engine, and Zeabur.

Whether you are spinning up a local development copy, hosting it on a VPS, or pushing it to a managed cloud platform, the entry point is always the same compose file and the same set of environment variables. Understanding how those pieces fit together is the fastest path to a working, production-ready install.

---

## The Four-Service Stack

InsForge runs as four cooperating Docker containers. Each service listens on its own port and depends on the others in a well-defined order.

```text
┌─────────────────────────────────────────────────────┐
│                 insforge-network (bridge)            │
│                                                     │
│  postgres :5432 ──► postgrest :5430                 │
│       │                  │                          │
│       └──────────────────┼──► insforge :7130 / 7131 │
│       │                  │                          │
│       └──────────────────┴──► deno :7133            │
└─────────────────────────────────────────────────────┘
```

| Service | Image | Default port | Role |
|---|---|---|---|
| `postgres` | `ghcr.io/insforge/postgres-all:latest` | `5432` | Persistent PostgreSQL 15 database |
| `postgrest` | `postgrest/postgrest:v12.2.12` | `5430` | Auto-generated REST API from the database schema |
| `insforge` | `ghcr.io/insforge/insforge-oss:v1.5.0` | `7130` (API), `7131` (auth) | Node.js backend + compiled React frontend |
| `deno` | `ghcr.io/insforge/deno-runtime:latest` | `7133` | Deno 2 edge-function runtime |

Sources: [deploy/docker-compose/docker-compose.yml:1-163]()

### postgres

The custom `postgres-all` image bundles the standard Postgres 15 engine with extensions (`pg_cron`, `http`, `pgcrypto`) and runs DB-level initialization on first boot. The JWT secret is written into the database's settings so PostgREST and the application can share it without extra configuration.

A named volume `postgres-data` persists all data across container restarts.

Sources: [deploy/docker-compose/docker-compose.yml:2-18](), [deploy/zeabur/template.yml:147-173]()

### postgrest

PostgREST v12.2.12 generates a full REST API directly from the `public` schema of the InsForge database. The `anon` Postgres role controls unauthenticated access. Schema-reload notifications travel over the `pgrst` channel (`PGRST_DB_CHANNEL`), so table changes are reflected without restarting the container.

PostgREST is reachable inside the network at `http://postgrest:3000` and is exposed on host port `5430` by default.

Sources: [deploy/docker-compose/docker-compose.yml:22-45]()

### insforge (main application)

The `insforge` container starts the Node.js backend on port `7130` and the auth service on port `7131`. On every startup the container runs pending database migrations before starting the server:

```dockerfile
CMD ["sh", "-c", "cd backend && npm run migrate:up && cd .. && exec npm start"]
```

The compiled React frontend is served as static files from inside the same image. The Dockerfile is a five-stage build: it strips the package version for cache stability, installs all dev dependencies, builds all packages, strips down to production dependencies, and finally copies compiled output into a minimal Alpine/Node 20 image running as the unprivileged `node` user.

Sources: [Dockerfile:154](), [Dockerfile:97-154]()

### deno

The Deno service runs a lightweight HTTP server on port `7133`. When an incoming request matches `/:slug`, it fetches the corresponding function's source code from the `functions.definitions` table in Postgres, decrypts any secrets stored in `system.secrets`, and executes the code in an isolated Web Worker. Workers have a configurable timeout (`WORKER_TIMEOUT_MS`, default 60 000 ms) and are terminated after each request.

The health endpoint at `GET /health` returns the Deno version and engine details:

```
GET http://localhost:7133/health
→ { "status": "ok", "runtime": "deno", "version": "2.x.x", ... }
```

Sources: [deploy/zeabur/template.yml:373-615](), [deploy/docker-compose/docker-compose.yml:113-149]()

---

## Startup Sequence and Health Checks

```mermaid
sequenceDiagram
    participant O as Operator
    participant PG as postgres
    participant PGR as postgrest
    participant APP as insforge
    participant DN as deno

    O->>PG: docker compose up -d
    PG-->>PG: init extensions + roles (first boot)
    PG-->>O: healthy (pg_isready)
    PG->>PGR: dependency satisfied
    PGR-->>O: healthy (TCP :3000)
    PG->>APP: dependency satisfied
    PGR->>APP: dependency satisfied
    APP-->>APP: npm run migrate:up
    APP-->>O: healthy (GET /api/health)
    PG->>DN: dependency satisfied
    DN-->>O: healthy (GET /health)
```

All four services join the same `insforge-network` bridge. The compose file enforces health-check conditions (`service_healthy`) for every dependency, so the application will not start until the database and PostgREST are ready.

Sources: [deploy/docker-compose/docker-compose.yml:53-57](), [deploy/docker-compose/docker-compose.yml:118-120]()

---

## Environment Variables

Copy `.env.example` and edit it before starting the stack. The table below covers every variable the compose file reads.

### Required (must change for production)

| Variable | Default | Purpose |
|---|---|---|
| `JWT_SECRET` | `dev-secret-please-change-in-production` | Signs JWTs shared by the backend, PostgREST, and the Deno runtime. Must be ≥ 32 characters. |
| `ADMIN_EMAIL` | `admin@example.com` | Email for the bootstrapped admin account. |
| `ADMIN_PASSWORD` | `change-this-password` | Password for the bootstrapped admin account. |

### Database (optional — defaults shown)

| Variable | Default |
|---|---|
| `POSTGRES_USER` | `postgres` |
| `POSTGRES_PASSWORD` | `postgres` |
| `POSTGRES_DB` | `insforge` |
| `ENCRYPTION_KEY` | Falls back to `JWT_SECRET` |

`ENCRYPTION_KEY` is used for AES-GCM encryption of secrets stored in the `system.secrets` table. If omitted it inherits `JWT_SECRET`.

### Ports (change to avoid conflicts when running multiple instances)

| Variable | Default | Service |
|---|---|---|
| `APP_PORT` | `7130` | InsForge backend + frontend |
| `AUTH_PORT` | `7131` | Auth sub-service |
| `POSTGREST_PORT` | `5430` | PostgREST REST API |
| `POSTGRES_PORT` | `5432` | PostgreSQL |
| `DENO_PORT` | `7133` | Deno edge functions |

### URL configuration (required when behind a reverse proxy)

| Variable | Purpose |
|---|---|
| `API_BASE_URL` | Server-side base URL; tells the backend what public URL it is reachable at. |
| `VITE_API_BASE_URL` | Baked into the frontend bundle at compile time; tells the React app where to make API calls. |

**Note:** `VITE_API_BASE_URL` is a Vite build argument. For the pre-built Docker image it must be set before the image is built, or set at runtime (the compose file passes it through).

### Integrations (all optional)

| Variable | Purpose |
|---|---|
| `OPENROUTER_API_KEY` | LLM routing for AI agent features |
| `STRIPE_TEST_SECRET_KEY` / `STRIPE_LIVE_SECRET_KEY` | Payment catalog sync |
| `VERCEL_TOKEN` / `VERCEL_TEAM_ID` / `VERCEL_PROJECT_ID` | Self-hosted site deployments to Vercel |
| `WORKER_TIMEOUT_MS` | Maximum time (ms) for a Deno edge function (default `60000`) |

### OAuth providers (all optional)

Each supported provider needs a client ID and secret. The callback URL pattern is `http://<your-domain>/api/auth/oauth/<provider>/callback`.

| Provider | Variables |
|---|---|
| Google | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` |
| GitHub | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` |
| Microsoft | `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET` |
| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` |
| LinkedIn | `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET` |
| X (Twitter) | `X_CLIENT_ID`, `X_CLIENT_SECRET` |
| Apple | `APPLE_CLIENT_ID`, `APPLE_CLIENT_SECRET` |

For GitHub OAuth the authorization callback URL must be set in the GitHub developer settings and match the backend route exactly. See [GITHUB_OAUTH_SETUP.md]() for the full walkthrough.

Sources: [deploy/docker-compose/.env.example:1-73](), [deploy/docker-compose/docker-compose.yml:61-109]()

---

## Quick-Start: Local Docker Compose

```bash
# 1. Fetch the compose file and example env
wget https://raw.githubusercontent.com/insforge/insforge/main/deploy/docker-compose/docker-compose.yml
wget https://raw.githubusercontent.com/insforge/insforge/main/deploy/docker-compose/.env.example
mv .env.example .env

# 2. Edit at minimum JWT_SECRET, ADMIN_EMAIL, ADMIN_PASSWORD
$EDITOR .env

# 3. Start all four services
docker compose up -d

# 4. Open the dashboard
open http://localhost:7130
```

After a few seconds the app is accessible at `http://localhost:7130`. The migration step runs automatically inside the container before the server starts.

Sources: [deploy/docker-deploy.md:1-30]()

### Running multiple instances on the same host

Each project needs its own env file, unique ports, and a unique compose project name:

```bash
cp .env .env.project2
# Edit .env.project2: change APP_PORT, AUTH_PORT, DENO_PORT, POSTGREST_PORT, POSTGRES_PORT, JWT_SECRET
docker compose --env-file .env.project2 -p project2 up -d
```

The `-p` flag gives each instance completely isolated containers, volumes, and networks. Each instance's data is fully independent.

Sources: [deploy/docker-deploy.md:31-89]()

---

## The Application Image: Multi-Stage Build

The production `Dockerfile` has five stages to keep image size small and build cache efficient:

| Stage | Base | What it does |
|---|---|---|
| `package-prep` | `node:20-alpine` | Strips `version` from `package.json` so downstream layers are cache-stable across releases |
| `deps` | `package-prep` | Runs `npm ci` with both dev and prod dependencies |
| `build` | `deps` | Compiles all packages (`shared-schemas` → `backend` → `frontend`) |
| `prod-deps` | `node:20-alpine` | Runs `npm ci --omit=dev` for a minimal dependency tree |
| `runner` | `node:20-alpine` | Copies compiled output and prod `node_modules`; runs as non-root `node` user with `tini` as PID 1 |

A `dev` stage (used by the override compose file) skips the build steps and mounts source code as volumes instead.

Sources: [Dockerfile:1-181]()

---

## Frontend: Cloud vs. Self-Hosted Mode

The React app checks the browser's origin at runtime and renders a different root component depending on whether it is running in the cloud or self-hosted:

```tsx
// frontend/src/App.tsx
function App() {
  if (isCloudHosting()) {
    return <CloudHostingDashboard />;
  }
  return <SelfHostingDashboard />;
}
```

`isCloudHosting()` returns `true` when `window.location.origin` ends with `.insforge.app`:

```ts
// frontend/src/helpers.ts
export function isCloudHosting(): boolean {
  return window.location.origin.endsWith('.insforge.app');
}
```

This means the identical built image serves the cloud-managed dashboard when deployed to `*.insforge.app` and the full self-hosted dashboard everywhere else. No feature flags or build-time switches are needed.

Sources: [frontend/src/App.tsx:1-13](), [frontend/src/helpers.ts:1-7]()

---

## Deploy Targets

### Zeabur (one-click)

The `deploy/zeabur/template.yml` defines all four services as a Zeabur template. After a one-click deploy you configure `PUBLIC_DOMAIN`, `ADMIN_EMAIL`, `ADMIN_PASSWORD`, and optionally `OPENROUTER_API_KEY` and Stripe keys in the Zeabur dashboard. All secrets are wired automatically between services via template variable references (`${JWT_SECRET}`, `${PGPASSWORD}`, etc.).

```bash
npx zeabur@latest template deploy -f deploy/zeabur/template.yml
```

Sources: [deploy/zeabur/template.yml:1-50](), [deploy/zeabur/README.md:1-27]()

### Render

Render requires a bit more manual setup: create a managed PostgreSQL database, run the initialization SQL scripts against it, then deploy the `insforge` container as a Web Service. The recommended spec is at least the Starter plan ($7/month for the database, free tier for testing). Render auto-provisions TLS and can auto-deploy from commits.

Sources: [docs/deployment/deploy-to-render.md:1-80]()

### AWS EC2

Run the Docker Compose stack on an EC2 instance (Ubuntu 24.04, `t3.medium` minimum — 2 vCPU, 4 GB RAM). Open inbound TCP ports 80, 443, 7130, and 7131 in the security group. An Elastic IP is recommended so the address survives reboots. Place Nginx or Caddy in front of the containers for TLS termination.

Sources: [docs/deployment/deploy-to-aws-ec2.md:1-60]()

### Google Cloud Compute Engine

The same Docker Compose approach applies on a GCE VM. The deployment guide mirrors the EC2 steps: provision a VM, install Docker, copy the compose file and env, run `docker compose up -d`, and add a reverse proxy for HTTPS.

Sources: [docs/deployment/README.md:29-33]()

### Azure

An Azure VM guide is listed as "Coming Soon" in the deployment index.

Sources: [docs/deployment/README.md:37-45]()

---

## Minimum Infrastructure Requirements

| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 2 GB | 4 GB |
| Disk | 20 GB | 30 GB |
| PostgreSQL | 15+ | 15+ |
| Docker Engine | Any recent | 24+ |

Sources: [docs/deployment/README.md:67-76]()

---

## Summary

InsForge self-hosting always starts with the same compose file (`deploy/docker-compose/docker-compose.yml`). Before the server boots, an operator must set `JWT_SECRET`, `ADMIN_EMAIL`, and `ADMIN_PASSWORD` — everything else has safe defaults for local use. The main app container runs database migrations on every startup and then serves the backend API alongside the compiled React frontend, which automatically detects whether it is running on `*.insforge.app` (cloud mode) or elsewhere (self-hosted mode) based solely on the browser origin. For cloud deployment, both Zeabur (one-click template) and Render, EC2, and GCE (Docker Compose on a VM) are documented paths; all of them ultimately run the same four-container stack with the same environment variables.

Sources: [deploy/docker-compose/docker-compose.yml:49-111](), [frontend/src/helpers.ts:1-7]()

---

## 06. The Short Version — One Mental Model to Keep

> A plain-English recap of InsForge: the core idea in one sentence, the single analogy worth keeping (InsForge is a universal power strip that plugs an AI coding agent into every backend service at once), the three things that move (requests from agent, config changes to primitives, state events back via realtime), and pointers to where to look next.

- Page Markdown: https://grok-wiki.com/public/wiki/insforge-insforge-357039661319/pages/06-the-short-version-one-mental-model-to-keep.md
- Generated: 2026-05-22T01:36:42.622Z

### Source Files

- `README.md`
- `docs/introduction.mdx`
- `docs/quickstart.mdx`
- `CHANGELOG.md`

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

- [README.md](README.md)
- [docs/introduction.mdx](docs/introduction.mdx)
- [docs/quickstart.mdx](docs/quickstart.mdx)
- [docs/mcp-setup.mdx](docs/mcp-setup.mdx)
- [docs/core-concepts/realtime/architecture.mdx](docs/core-concepts/realtime/architecture.mdx)
- [docs/core-concepts/database/architecture.mdx](docs/core-concepts/database/architecture.mdx)
- [docs/core-concepts/functions/architecture.mdx](docs/core-concepts/functions/architecture.mdx)
- [docs/sdks/typescript/overview.mdx](docs/sdks/typescript/overview.mdx)
- [CHANGELOG.md](CHANGELOG.md)
</details>

# The Short Version — One Mental Model to Keep

This page distills InsForge down to its single core idea. If you read no other page in this wiki, read this one. Everything else — database migrations, edge functions, realtime channels, the MCP server — is just a consequence of this idea working in practice.

Whether you are evaluating InsForge for a new project or trying to explain it to a teammate in one sentence, the mental model here is the one that sticks.

---

## The One Sentence

> **InsForge is the all-in-one, open-source backend platform for agentic coding.** It gives your AI coding agent a single connection point for database, auth, storage, compute, hosting, and AI — so the agent can build and operate full-stack apps end-to-end.

Sources: [README.md:42]()

---

## The Analogy Worth Keeping

Think of a **universal power strip** that sits between your AI coding agent and every backend service your app needs. Without the power strip, the agent has to find, wire up, and manage each appliance individually — database credentials here, storage keys there, auth config somewhere else. With the power strip, the agent plugs in once and every socket is immediately live.

InsForge is that power strip. The agent connects to InsForge via one protocol (MCP or CLI), and from that moment it can reach PostgreSQL, JWT auth, S3-compatible storage, serverless edge functions, a model gateway, compute containers, and site deployment — all through consistent, predictable tool calls.

The analogy maps directly to the code:

```text
AI Coding Agent
      │
      │  (MCP tools / CLI skills)
      ▼
   InsForge
      │
      ├── Authentication
      ├── Database (PostgreSQL + PostgREST)
      ├── Storage (S3-compatible)
      ├── Edge Functions (Deno)
      ├── Model Gateway (OpenAI-compatible)
      ├── Compute (long-running containers)
      └── Site Deployment
```

Sources: [README.md:58-89](), [docs/introduction.mdx:23-25]()

---

## The Two Interfaces

An AI agent reaches InsForge through exactly two doors:

| Interface | Where it runs | How the agent uses it |
|-----------|---------------|----------------------|
| **MCP Server** | Self-hosted (`localhost:7130`) or cloud (`mcp.insforge.dev/mcp`) | Agent calls named MCP tools (e.g. `fetch-docs`) from any MCP-compatible assistant (Cursor, Claude Code, GitHub Copilot, Windsurf, Codex, and more) |
| **CLI + Skills** | Cloud only | Agent runs `npx @insforge/cli` commands from the terminal, after linking a project with `npx @insforge/cli link --project-id <id>` |

Both interfaces let the agent act like a backend engineer: read documentation and schema, deploy functions, run migrations, configure auth providers, inspect storage, and more — without a human in the loop.

Sources: [README.md:48-56](), [docs/mcp-setup.mdx:7-9](), [docs/quickstart.mdx:34-46]()

---

## The Three Things That Move

Everything in InsForge reduces to three flows. Once you see them, the whole system makes sense.

### 1. Requests from the agent → InsForge primitives

The agent sends tool calls (via MCP) or CLI commands. InsForge translates those into backend operations: SQL migrations, function deployments, bucket creation, auth provider configuration. The agent never touches infrastructure directly; it describes *what* to do and InsForge executes it.

```
Agent: "run this migration"
  → MCP tool call / CLI command
  → InsForge API
  → PostgreSQL ALTER TABLE
```

Sources: [README.md:53-56](), [docs/core-concepts/database/architecture.mdx:49-60]()

### 2. Config changes to primitives

When the agent deploys a function, creates a storage bucket, or registers an OAuth provider, InsForge updates its internal primitives: PostgREST auto-generates new REST endpoints for any newly created table, Deno Subhosting receives the function code and makes it live at a URL, S3-compatible storage activates the bucket. State changes are durable and immediately usable by the app.

```
Agent deploys edge function
  → InsForge pushes to Deno Subhosting
  → Live URL: {appKey}.functions.insforge.app/{slug}
```

Sources: [docs/core-concepts/functions/architecture.mdx:1-10](), [docs/core-concepts/database/architecture.mdx:50-60]()

### 3. State events back via Realtime

When data changes in PostgreSQL — an order is created, a record is updated — database triggers fire `realtime.publish()`. That function writes to `realtime.messages`, emits a `pg_notify`, and the Realtime Manager fans the event out to connected WebSocket clients via Socket.IO and to configured webhook URLs via HTTP POST.

```
INSERT into orders
  → Trigger fires realtime.publish('order:123', 'INSERT_order', {...})
  → pg_notify → Realtime Manager
  → Socket.IO → SDK clients  (low latency, ~10–50 ms)
  → HTTP POST → webhook endpoints  (external services)
```

This loop means the app's UI and external integrations stay in sync with the database automatically, without polling.

Sources: [docs/core-concepts/realtime/architecture.mdx:12-41](), [docs/core-concepts/realtime/architecture.mdx:151-171]()

---

## A Sequence View of All Three Flows Together

```mermaid
sequenceDiagram
    participant Agent as AI Coding Agent
    participant IF as InsForge (MCP / CLI)
    participant PG as PostgreSQL
    participant RT as Realtime Manager
    participant App as Client App (SDK)

    Agent->>IF: deploy migration / create function / configure auth
    IF->>PG: ALTER TABLE / INSERT config / register provider
    PG-->>IF: ack
    IF-->>Agent: success + metadata

    Note over App,PG: Later, app writes data...
    App->>IF: INSERT into orders
    IF->>PG: SQL INSERT
    PG->>RT: pg_notify (realtime.publish trigger)
    RT->>App: Socket.IO event "INSERT_order"
    RT->>App: HTTP POST webhook (if configured)
```

Sources: [docs/core-concepts/realtime/architecture.mdx:153-172](), [docs/core-concepts/database/architecture.mdx:12-60]()

---

## What Each Primitive Does (in one line)

| Primitive | What it is | What an agent can do with it |
|-----------|-----------|------------------------------|
| **Database** | PostgreSQL 15 + PostgREST auto-API | Create tables, run migrations, query records, set RLS policies |
| **Authentication** | JWT-based user management + OAuth | Configure providers, manage users, read auth state |
| **Storage** | S3-compatible file storage | Create buckets, upload/download files |
| **Edge Functions** | Deno-based serverless compute | Deploy JS/TS functions, invoke them via URL |
| **Model Gateway** | OpenAI-compatible multi-provider API | Route LLM calls across providers |
| **Compute** | Long-running container services (private preview) | Run persistent workloads |
| **Site Deployment** | Build + host static/server sites | Deploy frontends alongside the backend |
| **Realtime** | PostgreSQL-trigger → WebSocket pub/sub | Subscribe to channels, receive DB-change events |

Sources: [README.md:91-99](), [docs/introduction.mdx:39-63]()

---

## Where to Look Next

| Question | File to read |
|----------|-------------|
| How do I connect my agent to InsForge? | [`docs/mcp-setup.mdx`](docs/mcp-setup.mdx) (remote MCP) or [`docs/quickstart.mdx`](docs/quickstart.mdx) (CLI) |
| How does the database API work? | [`docs/core-concepts/database/architecture.mdx`](docs/core-concepts/database/architecture.mdx) |
| How does realtime event delivery work? | [`docs/core-concepts/realtime/architecture.mdx`](docs/core-concepts/realtime/architecture.mdx) |
| How do edge functions deploy? | [`docs/core-concepts/functions/architecture.mdx`](docs/core-concepts/functions/architecture.mdx) |
| How do I use the TypeScript client? | [`docs/sdks/typescript/overview.mdx`](docs/sdks/typescript/overview.mdx) |
| What changed recently? | [`CHANGELOG.md`](CHANGELOG.md) |

---

The mental model holds at any scale: InsForge is the single plug that connects an AI coding agent to a complete backend. The agent sends requests, config changes propagate to live primitives, and state events flow back in realtime. Everything else in the codebase is the implementation of those three arrows.

Sources: [README.md:41-43](), [docs/introduction.mdx:23-28]()

---
