npm package: @spicez21/moss
Description: Drop-in RAG-powered AI chat for Next.js — Claude + Pinecone + PostgreSQL
Latest version: 0.1.2

# moss

> Drop-in RAG-powered AI chat for Next.js. Claude + Pinecone + PostgreSQL. Zero boilerplate.

```bash
npm install @spicez21/moss
npx moss init
```

## What you get

- **Floating chat widget** — customer-facing, streams responses via Server-Sent Events
- **Admin panel** — upload docs, watch them index in real time, manage your knowledge base
- **RAG pipeline** — embed queries, search Pinecone, inject context into Claude
- **Anonymous sessions** — cookie-based, no auth required for customers
- **Localhost-only admin** — the admin panel is completely blocked in production

## Requirements

- Next.js 14+ (App Router)
- PostgreSQL database
- Pinecone account (free tier works)
- Anthropic API key ([anthropic.com](https://anthropic.com))
- Voyage AI API key for embeddings ([voyageai.com](https://voyageai.com) — free tier available)

## Quick start

### 1. Install and initialize

```bash
npm install @spicez21/moss @anthropic-ai/sdk @pinecone-database/pinecone pg
npx moss init
```

The init wizard will ask for your API keys and write:
- `.env.local` — all your environment variables
- `lib/moss.ts` — your moss configuration
- `app/api/moss/[...route]/route.ts` — all API routes, wired up
- `middleware.ts` — admin route guard

### 2. Create your Pinecone index

In the Pinecone dashboard, create an index with:
- **Dimension**: `1024` (required for voyage-3)
- **Metric**: `cosine`

### 3. Add the chat widget to your layout

```tsx
// app/layout.tsx
import { ChatWidget } from '@spicez21/moss/client'
import { mossContext } from '@/lib/moss'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <ChatWidget theme={mossContext.config.theme} />
      </body>
    </html>
  )
}
```

### 4. Add the admin panel

```tsx
// app/admin/page.tsx
import { AdminPanel } from '@spicez21/moss/client'

export default function AdminPage() {
  return <AdminPanel />
}
```

### 5. Run and upload your docs

```bash
npm run dev
# Visit http://localhost:3000/admin
# Upload your FAQs, docs, or any text — they'll be chunked, embedded, and indexed
# Then visit http://localhost:3000 to see the chat widget in action
```

## Configuration

Edit `lib/moss.ts` to customize everything:

```ts
import { createMossConfig } from '@spicez21/moss'

export const mossContext = createMossConfig({
  // AI provider
  provider: 'claude',
  apiKey: process.env.ANTHROPIC_API_KEY!,
  voyageApiKey: process.env.VOYAGE_API_KEY!,  // for embeddings

  // Vector store
  pineconeApiKey: process.env.PINECONE_API_KEY!,
  pineconeIndex: process.env.PINECONE_INDEX!,

  // Database
  db: 'postgres',
  dbConnectionString: process.env.DATABASE_URL!,

  // Optional customization
  systemPrompt: 'You are Acme Support. Be concise and helpful.',
  topK: 5,            // number of context chunks to retrieve
  chunkSize: 2000,    // chars per chunk (~500 tokens)
  chunkOverlap: 200,  // overlap between chunks

  // Widget theme
  theme: {
    primaryColor: '#0f172a',
    accentColor: '#6366f1',
    position: 'bottom-right',
    botName: 'Acme Support',
    welcomeMessage: 'Hi! How can I help you today?',
  },
})
```

## API routes

All routes are served from `app/api/moss/[...route]/route.ts`:

| Method | Path | Access | Description |
|--------|------|--------|-------------|
| `POST` | `/api/moss/chat` | Public | Streaming chat (SSE) |
| `GET` | `/api/moss/me/history` | Public | Session message history |
| `POST` | `/api/moss/admin/ingest` | Localhost only | Upload and index a document |
| `GET` | `/api/moss/admin/documents` | Localhost only | List all documents |
| `DELETE` | `/api/moss/admin/documents/:id` | Localhost only | Delete a document |

## AG-UI events

The chat endpoint streams typed events (Server-Sent Events). Use the `useAgui` hook to consume them in your own components:

```tsx
import { useAgui } from '@spicez21/moss/client'
import type { MossAGUIEvent } from '@spicez21/moss/client'

function MyCustomChat() {
  const { send, isStreaming } = useAgui({
    url: '/api/moss/chat',
    onEvent: (event: MossAGUIEvent) => {
      if (event.type === 'TEXT_DELTA') {
        // append event.delta to your UI
      }
    },
  })

  return <button onClick={() => send('Hello!')}>Ask</button>
}
```

## Security

The admin panel and all `/api/moss/admin/*` routes are **completely inaccessible in production**. This is enforced at two layers:

1. **Middleware** (`middleware.ts`) — blocks requests before they reach your handlers
2. **Handler guard** — `isLocalhost()` check inside each admin handler (belt-and-suspenders)

There is no login, no password, no token. Localhost = trusted. Production = 403.

## Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `ANTHROPIC_API_KEY` | Yes | Anthropic API key |
| `VOYAGE_API_KEY` | Yes (with Claude) | Voyage AI key for embeddings |
| `PINECONE_API_KEY` | Yes | Pinecone API key |
| `PINECONE_INDEX` | Yes | Pinecone index name |
| `DATABASE_URL` | Yes | PostgreSQL connection string |

## Database

The schema is applied automatically on first startup. To apply manually:

```bash
psql $DATABASE_URL -f node_modules/@spicez21/moss/schema.sql
```

## Supported file types

| Type | MIME type | Notes |
|------|-----------|-------|
| Plain text | `text/plain` | Direct UTF-8 read |
| Markdown | `text/markdown` | Symbols preserved |
| PDF | `application/pdf` | Text-based PDFs only (no OCR) |

## License

MIT