The Problem
Developers and knowledge workers spend disproportionate time re‑entering context—emails, meeting notes, decisions—into AI tools. The lack of a persistent, searchable knowledge graph forces repeated prompting and limits the usefulness of LLM assistants.
What This Does
rowboat is a local‑first AI coworker that builds an Obsidian‑compatible Markdown vault, enriches it with backlinks, and uses that graph to provide context‑aware assistance. Core functionality lives in the CLI (apps/cli/src) and the web UI (apps/rowboat/app).
- The CLI entry point
apps/cli/bin/app.jsloadsapps/cli/src/app.ts, which wires the DI container (apps/cli/src/di/container.ts) and starts the HTTP server (apps/cli/src/server.ts). - The web UI is a Next.js app under
apps/rowboat/app; its API routes (e.g.,apps/rowboat/app/api/v1/[projectId]/chat/route.ts) call use‑case classes such asapps/rowboat/src/application/use-cases/api-keys/create-api-key.use-case.ts. - Persistent storage is a vector store (Qdrant) built from the Markdown vault; the Dockerfile
Dockerfile.qdrantprovisions it.
How It Is Wired
- Bootstrap –
apps/cli/src/app.tscreates the DI container (apps/cli/src/di/container.ts). The container registers core services (apps/x/packages/core/src/di/container.ts) and the config module (apps/x/packages/core/src/config/config.ts), the latter imported by 52 other modules (the biggest hub). - Server start –
apps/cli/src/server.tscallsexpress()and attaches routes fromapps/cli/src/application/assistant/skills/*. Each route eventually invokes the execute function found in multiple use‑case files (e.g.,apps/rowboat/src/application/use-cases/api-keys/create-api-key.use-case.ts). - Execution flow –
execute→authorize(57 callers) →assertAndConsumeProjectAction(52 callers) →fetch(28 callers). The call graph showscn(defined inapps/rowboat/lib/utils.ts) is referenced from 38 files, making it a secondary blast‑radius node. - External effects – Filesystem:
execute→createwrites viafs.writeFile. Config reads:init→identify→getInstallationIdreadsfs.readFileSync. Network:execute(in API‑key use case) performs an outbound request to the licensing server and generates a cryptographic secret. Database: the simulation runner (apps/experimental/simulation_runner) reads/writes a SQLite DB, but is isolated from the main CLI/Web flow.
Cycles – 32 modules form circular imports; the most problematic are the DI containers (apps/x/packages/core/src/di/container.ts and its CLI counterpart) and shared defaults (apps/x/packages/core/src/models/defaults.ts). Breaking these cycles would reduce build‑time coupling and improve testability.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/rowboat
cd rowboat
# Install Node dependencies (pnpm is declared)
pnpm install
# Build Docker image for Qdrant (vector store)
docker build -f Dockerfile.qdrant -t rowboat-qdrant:sha256-<digest> .
# Start the vector store
docker run -d -p 6333:6333 rowboat-qdrant:sha256-<digest>
# Launch the CLI server (development)
pnpm --filter ./apps/cli dev # runs apps/cli/src/server.ts
# Or start the Next.js UI
pnpm --filter ./apps/rowboat dev # runs Next dev server
Configuration lives in .env.example (copy to .env) and per‑user JSON files under ~/.rowboat/config/ (e.g., deepgram.json, elevenlabs.json). The CLI reads apps/cli/src/config/config.ts; the web UI reads the same module via the container.
Real‑World Use
A product manager runs rowboat locally, connects Gmail/Calendar via the Google setup guide, and invokes the CLI command rowboat generate deck --topic "Q3 roadmap". The CLI routes the request to apps/rowboat/src/application/use-cases/api-keys/create-api-key.use-case.ts, which fetches relevant Markdown notes from the vault, enriches them with embeddings stored in Qdrant, and streams a PDF back to the user’s terminal. No external API keys are transmitted beyond the local machine.
Code Health & Issues
- High – Pin GitHub Actions –
.github/workflows/*usespnpm/action-setup@v4; replace with a commit SHA. - High – Missing Python lockfile –
apps/python-sdk/pyproject.tomlhas no lockfile; generate and commit one. - High – Wildcard CORS –
apps/cli/src/server.tscallscors()with no origin list; restrict to known front‑ends. - High – Debug mode enabled –
apps/experimental/tools_webhook/app.pyrunsapp.run(debug=True); gate behind an env var. - Medium – GITHUB_TOKEN permissions –
.github/workflows/rowboat-build.ymllacks explicitpermissions; add least‑privilege scopes. - Medium – Dependabot not configured – No
.github/dependabot.yml; add to auto‑update 14 manifests. - Medium – Docker base image mutable –
Dockerfile.qdrantusesqdrant/qdrant:latest; pin by digest. - Medium – No dependency scan – CI lacks a vulnerability review step; integrate
dependency-review-actionorosv-scanner. - Medium – Large binaries in repo –
apps/docs/docs/videos/intro.mp4(31 MB) etc.; move to Git LFS or external storage. - Medium – Checkout persisting token –
.github/workflows/electron-build.ymlshould setpersist-credentials: false.
No critical findings; overall test and CI presence is satisfactory.
The Bottom Line
rowboat delivers a concrete local‑first AI coworker with a well‑structured monorepo, clear entry points, and a functional vector‑store pipeline. The biggest risks are tight module coupling (import cycles) and several production‑hardening gaps (CORS, debug mode, action pinning). Teams comfortable managing Docker and Node/PNPM environments can adopt it quickly, but should address the highlighted health issues before a production rollout.