NodeBook
by and1truong
README.md
# NodeBook
Issue-native wiki, planning, reminders, attachments, and MCP workspace — built natively on Cloudflare Workers.
Canonical PRD: https://github.com/and1truong/wiki/issues/229
## What it is
NodeBook is a single-owner workspace where every issue is a first-class node in a wiki graph:
- **Issues** — all PRD types (`task`, `bug`, `epic`, `story`, `decision`, `finding`, `incident`, `learning`, `wiki`, `note`) with open/closed state, labels, priorities, Markdown bodies, durable audit history, and a paginated list with selectable 20/50/100-row pages (`ISSUES_DEFAULT_LIMIT`, default **20**).
- **Graph** — parent/child hierarchy, typed relationships (`related`, `depends_on`, `blocks`, `supersedes`, `duplicates`), and `#123` references that resolve even when the target is created later.
- **Wiki** — hierarchy tree navigation, breadcrumbs, backlinks, and related-content panels.
- **Search** — FTS5 full-text search over titles, bodies, comments, labels, and attachment metadata, with type/state/label filters and PRD `search_knowledge` semantics.
- **Planning** — Inbox / Today / Calendar / Overdue views in the owner's timezone; recurring tasks (RFC 5545 rules) record occurrences and advance planning dates instead of closing. The Calendar workspace (`/calendar`, day/week/month views) opens in a deployment-configurable default view (`CALENDAR_DEFAULT_VIEW`, default **week**) and shows due dates and scheduled instants in the viewer's timezone via a bounded range API; entries are created from a date/time popup by clicking month/week dates or day timeline slots, rescheduled by dragging between dates, and the day timeline sets scheduled times in 15-minute increments (with **Move date…** shortcuts for Today, Tomorrow, Next week, and Next month plus a custom picker as a fallback). The week start is deployment-configurable too (`WEEK_START_DAY`, default **sunday**): it rotates Calendar views, the date pickers, the inbox **Plan** shortcuts, and the day-view **Move date…** shortcuts — **Next week** schedules for the first day of the following week and **Next month** for the first day of the following month. `/upcoming` redirects to `/calendar` for compatibility.
- **Reminders & notifications** — absolute, before-due, and recurring reminders delivered to an in-app notification inbox by a one-minute Cron Trigger, with idempotent delivery and expiring claim locks.
- **Attachments** — private R2 blobs with checksum deduplication, inline previews or forced downloads, range support, soft deletion, and daily garbage collection.
- **Theming** — light, dark, and system themes (Tailwind CSS v4 + CSS-variable tokens on shadcn/ui components) with a topbar switcher, localStorage persistence, no flash-of-wrong-theme on load, and live following of the OS preference in system mode. The palette is derived from TabTerm's warm parchment/brown/gold theme (light `#f5f0e8`/`#fffcf6`/`#7a5c00`, dark `#1a1200`/`#251a00`/`#ffd000`), with light-mode text tokens darkened to stay ≥ 4.5:1 (WCAG AA).
- **MCP** — a Streamable HTTP MCP server on `/mcp` exposing 19 scoped read/write tools that share the exact same services, validation, and audit trail as the web UI.
## Architecture
```
Browser (React SPA) ── Cloudflare Access ──▶ Worker ──▶ D1 (domain data + FTS5)
│ ├── R2 (private blobs)
MCP clients ── PAT (nbk_…) ──▶ /mcp ──▶ Durable Object (session state)
│
Cron Triggers (1 min / daily) ───────────────▶ scheduled handlers
```
One TypeScript project, one deployable Worker. No Node.js runtime (`nodejs_compat` is not required). See [docs/architecture.md](docs/architecture.md), [docs/deployment.md](docs/deployment.md), and [docs/mvp-scope.md](docs/mvp-scope.md).
## Quick start
```bash
npm ci
cp .dev.vars.example .dev.vars # local identity (owner@nodebook.local)
# Terminal 1 — the Worker (API + MCP + a fresh UI build on :8787)
npm run db:migrate:local
npm run dev:worker
# Terminal 2 — the Vite dev server with API proxy (recommended for client
# development and hot reload; open http://localhost:5173)
npm run dev:web
```
## Quality gates
```bash
npm run lint # ESLint (source + tests)
npm run typecheck # TypeScript strict across client, Worker, services, MCP
npm test # unit tests (recurrence, timezones, refs, auth, search utils)
npm run test:integration # integration tests under the Workers runtime (D1/R2/DO)
npm run test:e2e # Playwright acceptance flow against a local Worker
npm run build # production client bundle
npx wrangler d1 migrations apply nodebook --local # migrations prove clean
npx wrangler deploy --dry-run # packaging + bindings check
```
## CI/CD
- **CI:** `.github/workflows/ci.yml` runs every gate above (plus e2e) on every
pull request and every push to `main` — one job on `ubuntu-latest`, failing
fast on any red step. A red check blocks merge.
- **CD:** production deploys via Cloudflare's Git integration (Workers
Builds): a push to `main` makes Cloudflare run `npm ci && npm run build`
and `npx wrangler deploy` against this repo. CI never deploys.
- **Manual/staging:** `npm run deploy` runs the same build + deploy from your
machine and remains the staging path.
The one-time Cloudflare dashboard setup (connect the repo, D1 `database_id`,
secrets) is documented in [docs/deployment.md](docs/deployment.md) §5.
## MCP
NodeBook speaks Streamable HTTP MCP at `/mcp` with two credential types:
**OAuth 2.1 (recommended for ChatGPT and other OAuth-capable clients).** NodeBook is its own authorization server — no tunnel or third-party identity provider needed:
```
URL: https://<your-worker>/mcp
Auth: OAuth (authorization-code flow with PKCE) — select OAuth in the client
```
Clients discover the authorization server from the `WWW-Authenticate: Bearer resource_metadata=…` challenge (or `/.well-known/oauth-authorization-server`), register dynamically, and redirect you to the Cloudflare Access login + NodeBook consent page. Access tokens are short-lived (10 min); refresh tokens rotate on every use; scopes never expand beyond what you approved. Manage connections under **Settings → MCP tokens → OAuth connections**.
**Personal access tokens (any MCP client).** Create a scoped token in **Settings → MCP tokens**, then point the client at:
```
URL: https://<your-worker>/mcp
Auth: Authorization: Bearer nbk_…
```
Tokens are stored as SHA-256 hashes with display prefixes, support expiration, and revoke immediately. Every tool call is re-checked against the database on each request. `get_today`/`get_upcoming` accept an optional `timezone` argument (IANA); `get_upcoming` is retained unchanged for compatibility with the retired Upcoming browser page.
Issue edits use optimistic locking across the browser and MCP. Read the current issue first, then pass its `version` as `expected_version` to `update_issue`. A `-32009` conflict means another editor changed the issue; refetch and deliberately reapply the intended changes rather than retrying the stale payload.
### API/MCP migration notes
- Optimistic locking is a **breaking contract change**: `PATCH /api/issues/:ref` and MCP `update_issue` require `expected_version` and at least one field to change. Existing MCP clients must reconnect or refresh their cached tool schema, then read an issue before updating it. Missing arguments fail with HTTP `400` or MCP `-32602`; stale versions fail with HTTP `409` or MCP `-32009`.
- `GET /api/issues` preserves its historical default limit of 100 for compatibility. The web UI sends its deployment-configured `ISSUES_DEFAULT_LIMIT` explicitly; API consumers can paginate with `limit`, `offset`, and the response's `total`.
## Production notes
- The web/API hostname **must** be protected with Cloudflare Access (`ACCESS_TEAM` + `ACCESS_AUD`); `/mcp`, the OAuth discovery/registration/token endpoints bypass Access, and `/oauth/authorize` (the consent page) stays behind it. Every request without valid credentials is still rejected.
- Set `OAUTH_ISSUER` to the stable custom domain (e.g. `https://nodebook.example.com`) — never `workers.dev`.
- Disable `workers.dev` access or keep `AUTH_DEV_EMAIL` unset in production.
- Back up D1 before applying migrations (`wrangler d1 export`), and deploy migrations to staging first.
- See [docs/deployment.md](docs/deployment.md) for the full runbook and ChatGPT setup.
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues