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.js loads apps/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 as apps/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.qdrant provisions it.

How It Is Wired

  1. Bootstrapapps/cli/src/app.ts creates 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).
  2. Server startapps/cli/src/server.ts calls express() and attaches routes from apps/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).
  3. Execution flowexecuteauthorize (57 callers) → assertAndConsumeProjectAction (52 callers) → fetch (28 callers). The call graph shows cn (defined in apps/rowboat/lib/utils.ts) is referenced from 38 files, making it a secondary blast‑radius node.
  4. External effects Filesystem: executecreate writes via fs.writeFile. Config reads: initidentifygetInstallationId reads fs.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/* uses pnpm/action-setup@v4; replace with a commit SHA.
  • High – Missing Python lockfileapps/python-sdk/pyproject.toml has no lockfile; generate and commit one.
  • High – Wildcard CORSapps/cli/src/server.ts calls cors() with no origin list; restrict to known front‑ends.
  • High – Debug mode enabledapps/experimental/tools_webhook/app.py runs app.run(debug=True); gate behind an env var.
  • Medium – GITHUB_TOKEN permissions.github/workflows/rowboat-build.yml lacks explicit permissions; add least‑privilege scopes.
  • Medium – Dependabot not configured – No .github/dependabot.yml; add to auto‑update 14 manifests.
  • Medium – Docker base image mutableDockerfile.qdrant uses qdrant/qdrant:latest; pin by digest.
  • Medium – No dependency scan – CI lacks a vulnerability review step; integrate dependency-review-action or osv-scanner.
  • Medium – Large binaries in repoapps/docs/docs/videos/intro.mp4 (31 MB) etc.; move to Git LFS or external storage.
  • Medium – Checkout persisting token.github/workflows/electron-build.yml should set persist-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.