Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 16 additions & 10 deletions .agents/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Pod — Documentation Index

> **pod · v0.0.82** · Bun + Next.js 16 + open-sse (local JS fork) + SQLite · port **20128** · [pod.lazuardy.tech](https://pod.lazuardy.tech)
> **pod · v0.0.82** · Bun + Next.js 16 + open-sse (typed local fork) + SQLite · port **20128** · [pod.lazuardy.tech](https://pod.lazuardy.tech)
> Self-hosted AI gateway unifying 50+ LLM providers behind one OpenAI-compatible endpoint.

> **Last reviewed**: 2026-07-13.
> **Freshness note**: As of this review, `/api/monitoring/health` and `/api/monitoring/health/stream` are **public reads** (no API key required), on par with `/api/health`. Do not trust older architecture or knowledge docs that claim these endpoints require authentication.
> **Last reviewed**: 2026-08-07.
> **Freshness notes**:
>
> - `open-sse/` is TypeScript and included in root `tsc`; source paths in docs should use `.ts` even though imports keep `.js` suffixes for ESM/bundler resolution.
> - `/api/monitoring/health` and `/api/monitoring/health/stream` are **public reads** (no API key), on par with `/api/health`. Ignore older docs that claim auth.
> - Service worker (`public/sw.js`): **network-first** navigation + offline fallback; never reject `respondWith` / never `Response.error()` on images; no blind `controllerchange` reload. See gotcha §34 (`knowledge/04-gotchas.md`).

---

Expand All @@ -24,7 +28,7 @@
| File | Covers |
| ------------------------------------------------------------ | -------------------------------------------------------------- |
| [architecture/00-engine.md](architecture/00-engine.md) | open-sse engine: routing, translation, streaming, crash guards |
| [architecture/01-app.md](architecture/01-app.md) | Next.js pages, API routes, middleware, PWA, stores |
| [architecture/01-app.md](architecture/01-app.md) | Next.js pages, API routes, routeAuth, PWA, stores |
| [architecture/02-providers.md](architecture/02-providers.md) | Provider config, auth types, executors, translators, retry |
| [architecture/03-data.md](architecture/03-data.md) | SQLite, Redis, offline cache, mutation queue |
| [architecture/04-infra.md](architecture/04-infra.md) | Docker, Zeabur, Cloudflare, networking |
Expand All @@ -51,18 +55,20 @@
| [knowledge/01-overview.md](knowledge/01-overview.md) | Quick facts, repo layout, three-layer architecture |
| [knowledge/02-conventions.md](knowledge/02-conventions.md) | Coding, naming, body parsing, modal rules |
| [knowledge/03-dev-workflow.md](knowledge/03-dev-workflow.md) | Commands, pre-push verification, Zeabur deploy |
| [knowledge/04-gotchas.md](knowledge/04-gotchas.md) | Common traps (parser quirks, Turbopack, abort) |
| [knowledge/04-gotchas.md](knowledge/04-gotchas.md) | Common traps (parser, Turbopack, abort, SW §34) |
| [knowledge/05-open-issues.md](knowledge/05-open-issues.md) | Active watchlist |

---

## Other Directories

| Path | Purpose |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| issues/ | Historical audit and security analysis — verify against live code |
| reports/ | Release rollups and verification reports by version |
| plan/ | Draft plans: [js-to-ts-migration.md](plan/js-to-ts-migration.md), [openai-compat-fixes.md](plan/openai-compat-fixes.md), [optimizing-pod-for-multiple-instance.md](plan/optimizing-pod-for-multiple-instance.md), [voidzero-adoption.md](plan/voidzero-adoption.md) |
| Path | Purpose |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| skills/ | Cursor agent skills — ponytail suite from [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) (`/ponytail`, `/ponytail-review`, `/ponytail-audit`, `/ponytail-debt`, `/ponytail-gain`, `/ponytail-help`) |
| issues/ | Historical audits — start at [issues/INDEX.md](issues/INDEX.md); verify against live code |
| reports/ | Release rollups and verification reports by version |
| plan/ | [js-to-ts-migration.md](plan/js-to-ts-migration.md) (completed), [openai-compat-fixes.md](plan/openai-compat-fixes.md), [optimizing-pod-for-multiple-instance.md](plan/optimizing-pod-for-multiple-instance.md), [voidzero-adoption.md](plan/voidzero-adoption.md) (completed) |
| tests/ | SW seams: [../tests/SW-TEST-SEAM.md](../tests/SW-TEST-SEAM.md); unit `tests/unit/swShellCache.test.ts` |

---

Expand Down
19 changes: 13 additions & 6 deletions .agents/PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ Pod is a self-hosted AI gateway that unifies 50+ LLM providers behind a single O

### Offline and PWA

- Service worker for offline reads (offlineJsonCache via IndexedDB)
- Offline mutation queue for safe idempotent writes
- Installable PWA with web app manifest
- Service worker (`public/sw.js`): network-first navigation with offline `/offline` fallback; no `Response.error()` on images; deploy-hash cache namespaces via `/sw-version.json`
- Offline reads via `offlineJsonCache` (IndexedDB); mutation queue for safe idempotent writes
- Installable PWA with web app manifest; registration-only lifecycle (no self-update UX)

## Non-Goals

Expand All @@ -106,10 +106,17 @@ Pod is a self-hosted AI gateway that unifies 50+ LLM providers behind a single O
- Not a multi-tenant SaaS (self-hosted single-tenant)
- Not a replacement for provider-native SDKs

## Deployment & Branches

- `canary` = active development; `main` = stable (promote via PR only)
- Zeabur: `pod` → `pod.lazuardy.tech` (port 20140); `pod-canary` → `pod-canary.zeabur.app`
- Compatibility gate: [compatibility-matrix.md](compatibility-matrix.md)
- Health: `/api/health` and `/api/monitoring/health*` are public reads

## Product Constraints

- **Bun-only** — never npm/pnpm
- **Local open-sse fork** — never replace with npm version, frozen as JS
- **Local open-sse fork** — never replace with npm version; TypeScript, included in root `tsc`
- **SQLite primary store** — optional Redis for rate limiting
- **Dark-only UI** — no light mode
- **Defensive by default** — sanitized errors, safe streaming, crash guards
Expand All @@ -120,7 +127,7 @@ Pod is a self-hosted AI gateway that unifies 50+ LLM providers behind a single O
- **Chunked body reading**: Large request bodies (5MB+) are stream-read in chunks to prevent 9-15s stalls. `readBodyTextStream()` enforces the size cap mid-stream and returns `413` on overflow.
- **Configurable body cap**: All mutation routes enforce a 50MB default body cap (env-tunable). `413` returned on overflow; no silent memory spikes.
- **Compatibility first**: OpenAI/Anthropic error shapes, auth headers, streaming format, and tool calling match official specs. Any regression is a release blocker.
- **Offline-first dashboard**: Reads degrade via `offlineJsonCache`; writes queue via mutation stack; only safe idempotent mutations queued.
- **Offline-capable dashboard**: SW network-first for documents; reads degrade via `offlineJsonCache`; writes queue via mutation stack; only safe idempotent mutations queued.

## Key Numbers

Expand All @@ -133,6 +140,6 @@ Pod is a self-hosted AI gateway that unifies 50+ LLM providers behind a single O
| SSE idle timeout | 5 minutes |
| Body cap | 50MB default (env: POD_MAX_REQUEST_BODY_BYTES, POD_MAX_CHAT_BODY_BYTES) |
| Providers supported | 50+ |
| Executors | 19 (provider executors; `base.js` is a base class, `index.js` is a barrel) |
| Executors | 19 (provider executors; `base.ts` is a base class, `index.ts` is a barrel) |
| API route groups | 26 |
| Dashboard pages | 15 (top-level, no /dashboard prefix) |
24 changes: 12 additions & 12 deletions .agents/architecture/00-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ The open-sse engine is a local fork (never the npm package) that handles provide
```
open-sse/
config/ Provider definitions, model catalogs, runtime constants
executors/ Provider-specific HTTP clients (19 executors; base.js is a base class, index.js is a barrel)
executors/ Provider-specific HTTP clients (19 executors; base.ts is a base class, index.ts is a barrel)
handlers/ Core chat handler: streaming and non-streaming paths
services/ Model resolution, provider metadata, credential management, token refresh
transformer/ Response transformation utilities
translator/ Request/response format translation (OpenAI ↔ Claude ↔ Gemini)
utils/ Stream processing, error handling, proxy fetch patch, RTK
rtk/ Real Talk tool_result compression subsystem
index.js Public API surface — re-exports for src/sse/ consumers
index.ts Public API surface — re-exports for src/sse/ consumers
```

## Executor Types
Expand All @@ -38,7 +38,7 @@ Each provider gets its own executor in `open-sse/executors/`. They share a commo
| ------------ | ----------------------------------------------------- |
| `request/` | Client request → provider-native format |
| `response/` | Provider-native response → OpenAI-compatible format |
| `formats.js` | Format constants (`openai`, `claude`, `gemini`, etc.) |
| `formats.ts` | Format constants (`openai`, `claude`, `gemini`, etc.) |
| `helpers/` | Shared translation utilities |

### Claude-to-OpenAI Thinking Fix
Expand All @@ -61,15 +61,15 @@ Each streaming response chunk passes through a TransformStream that applies form

## Invariants

| Rule | Where enforced |
| ------------------------------------------------------------ | ------------------------------- |
| SSE connection cap: 100 concurrent | `src/sse/handlers/chat.js` |
| SSE idle timeout: 5 minutes | `src/sse/handlers/chat.js` |
| Crash guard around stream processing | `open-sse/utils/stream.js` |
| Crash guard around chat core | `open-sse/handlers/chatCore.js` |
| Guarded peek-reader (inspect first chunk without consuming) | `open-sse/handlers/chatCore.js` |
| Transactional connection locking (`modelLockCount_${model}`) | `open-sse/handlers/chat.js` |
| Guarded fallback loop | `src/sse/handlers/chat.js` |
| Rule | Where enforced |
| ------------------------------------------------------------ | ----------------------------------------------- |
| SSE connection cap: 100 concurrent | `src/sse/handlers/chat.ts` |
| SSE stream stall timeout: 5 minutes | `open-sse/utils/stream.ts` (`STALL_TIMEOUT_MS`) |
| Crash guard around stream processing | `open-sse/utils/stream.ts` |
| Crash guard around chat core | `open-sse/handlers/chatCore.ts` |
| Guarded peek-reader (inspect first chunk without consuming) | `open-sse/handlers/chatCore.ts` |
| Transactional connection locking (`modelLockCount_${model}`) | `open-sse/services/accountFallback.ts` |
| Guarded fallback loop | `src/sse/handlers/chat.ts` |

These guards are non-negotiable. Removing or weakening any of them risks process crashes or stream corruption.

Expand Down
2 changes: 1 addition & 1 deletion .agents/architecture/01-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,5 @@ This layer sits between the API route and `open-sse/`. It manages the 100-connec

- **Thin API routes**: Routes call into `lib/` services; no business logic in route handlers
- **Zustand per domain**: Each domain (auth, providers, theme, notifications, header) gets its own store
- **PWA**: Service worker is registration-only (no auto-updates); offline reads via `offlineJsonCache`; writes queue via mutation stack
- **PWA**: SW registration-only (no auto-update UX). `public/sw.js` is **network-first** for navigations (offline `/offline` fallback); never reject `respondWith` / never `Response.error()` on images; `ServiceWorkerRegistrar` must not blind-reload on `controllerchange`. Offline reads via `offlineJsonCache`; writes via mutation queue. See gotcha §34.
- **Header actions**: Route through `headerActionStore`
38 changes: 19 additions & 19 deletions .agents/architecture/02-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,31 @@ Provider definitions live in `src/shared/constants/providers.ts`. Model catalogs
| Service account | GCP IAM | Vertex AI |
| Free | No credentials needed | Kiro, Qwen Code, Gemini CLI, iFlow |

Token refresh logic lives in `open-sse/services/tokenRefresh.js` with provider-specific refreshers for Claude, Codex, Copilot, GitHub, Google, iFlow, and Qwen.
Token refresh logic lives in `open-sse/services/tokenRefresh.ts` with provider-specific refreshers for Claude, Codex, Copilot, GitHub, Google, iFlow, and Qwen.

## Executor Routing

Executors live in `open-sse/executors/`. Each implements the same interface for generic dispatch.

| Executor | Provider(s) | Notable behavior |
| -------------------------------- | ---------------------- | ------------------------------------------ |
| `default.js` | Most OpenAI-compatible | Standard passthrough |
| `vertex.js` | Vertex AI | GCP auth + strips `stream` field from body |
| `kiro.js` | Kiro AI | Transient overload body-gating for retry |
| `codex.js` | OpenAI Codex | Reasoning token budget normalization |
| `ollama-local.js` | Ollama | Local endpoint handling |
| `antigravity.js` | Antigravity | OAuth-based |
| `cursor.js` | Cursor IDE | OAuth-based |
| `github.js` | GitHub Copilot | OAuth token refresh |
| `grok-web.js` | xAI Grok (web) | Cookie-based |
| `perplexity-web.js` | Perplexity (web) | Cookie-based, x-pod-skip-reasoning |
| `iflow.js` | iFlow AI | Free access |
| `qoder.js` | Qoder | OAuth-based |
| `qwen.js` | Qwen Code | Free access |
| `opencode.js` / `opencode-go.js` | OpenCode | Free access |
| `commandcode.js` | Command Code | OAuth-based |
| `gemini-cli.js` | Gemini CLI | Free access |
| `azure.js` | Azure OpenAI | API key |
| `default.ts` | Most OpenAI-compatible | Standard passthrough |
| `vertex.ts` | Vertex AI | GCP auth + strips `stream` field from body |
| `kiro.ts` | Kiro AI | Transient overload body-gating for retry |
| `codex.ts` | OpenAI Codex | Reasoning token budget normalization |
| `ollama-local.ts` | Ollama | Local endpoint handling |
| `antigravity.ts` | Antigravity | OAuth-based |
| `cursor.ts` | Cursor IDE | OAuth-based |
| `github.ts` | GitHub Copilot | OAuth token refresh |
| `grok-web.ts` | xAI Grok (web) | Cookie-based |
| `perplexity-web.ts` | Perplexity (web) | Cookie-based, x-pod-skip-reasoning |
| `iflow.ts` | iFlow AI | Free access |
| `qoder.ts` | Qoder | OAuth-based |
| `qwen.ts` | Qwen Code | Free access |
| `opencode.ts` / `opencode-go.ts` | OpenCode | Free access |
| `commandcode.ts` | Command Code | OAuth-based |
| `gemini-cli.ts` | Gemini CLI | Free access |
| `azure.ts` | Azure OpenAI | API key |

## Format Translation

Expand Down Expand Up @@ -91,4 +91,4 @@ When a provider returns rate-limit or overload errors:
3. Lockout status visible on `/health` page
4. Connection-level lockdown with exponential cooldown (v0.0.75+)

Account fallback logic lives in `open-sse/services/accountFallback.js`.
Account fallback logic lives in `open-sse/services/accountFallback.ts`.
2 changes: 1 addition & 1 deletion .agents/architecture/03-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Pod uses a local-first storage model:

- Browser-side read cache for dashboard data
- Tag-based invalidation after safe mutations
- Service worker integration
- Complements SW shell caching (`public/sw.js` network-first navigations; see gotcha §34)

## Rate Limiting (`src/lib/rateLimit/`)

Expand Down
Loading
Loading