KanvaFlow — Remixable Team Task Boards on Dodil
KanvaFlow is a clean, remixable team task board built natively on the Dodil cloud (DataK3, appid, Ignite, and Dodil Models), operable seamlessly from both a drag-and-drop web UI and Claude / ChatGPT over MCP.
Built for the Dodil Boards Challenge (dodil-challenge, dodil-challenge-boards) under the MIT Licence.
1. Remix into Your Own Dodil Organization in One Command
dodil update # CLI 0.1.89 or later
dodil git remix dachdigitalwerksorg/kanvaflow --to-org <your-org> --name kanvaflow
Name budget note: Keep
--nameto 18 characters or less (e.g.kanvaflow,team-boards) so the provisioned DataK3 bucket<repo>-datastays within K3's 25-character limit.
What the Remix Provisions Automatically (.dodil/remix.yaml)
- DataK3 Bucket (
<repo>-data) initialized withschema.sql(boards,memberships,board_lists,cards,card_dependencies,activity— natural primary keys only). - AppID User Pool (
<repo>-users) fronting the Ignite gateway so end-users sign in without custom auth code. - Service Account (
<repo>-app) grantedk3.editoron<repo>-dataandignite.model-userfor Dodil Models (kimi-k2.6+jina-embeddings-v4), wired into CI secrets automatically. - Dodil Git CI/CD (
.github/workflows/ci.yml+.dodil/deploy.yaml) runningpytest, building the container image, and deploying live to Dodil Ignite.
Post-Remix Sign-In Step (Required Once After Remix)
Dodil Git CD (>= 1.6.11) registers https://<your-app-host>/.dodil/auth/callback on <repo>-users automatically during deploy:
- Check your deploy log (or
dodil ignite app get <repo>): if the callback line reports"not registered", run the one-linedodil appidcommand printed in the deploy log to addhttps://<public_url>/.dodil/auth/callbackto the pool's allowlist. - Open your live Ignite URL in a browser and sign up / sign in (new pools allow signup by default; if you set
--allow-signup=false, invite teammates viadodil appid user create <repo>-users <email> --roles member). - Connecting Claude.ai or ChatGPT via MCP: Add
https://<public_url>/mcpas a custom MCP connector, complete the browser sign-in prompt, and approve the dynamically registered OAuth client once:dodil appid client list <repo>-users dodil appid client approve <repo>-users <client-id>
2. Features & Capabilities
Core Capabilities
- People & Strict RBAC (
owner,member,viewer): Owners invite/remove members and viewers. Viewers have strictly enforced read-only access across both Web and MCP. Non-members cannot discover or inspect boards they are not on (404 Not Found). - Boards → Custom Lists → Rich Cards: Every board starts with
To do,Doing, andDoneand supports custom columns (Review,QA, etc.). Cards carrytitle,description,assignee,due_date,labels,checklist, andposition, with HTML5 Drag-and-Drop on the web UI. - Full Audit Activity Log: Every board, list, membership, dependency, and field-level card change records
actor,event_at, andaction(viewable per board and per card). - Claude & ChatGPT MCP Door (
/mcp): 20 high-level action tools (never raw table access) sharing the exact same use cases and gateway identity as the web UI:- "What's on my plate this week?" →
my_agenda - "Add a card 'Send invoice' to Doing on the Ops board" →
add_card(acceptsboard_idor exact board title!) - "Move card 9a3f to Done" →
move_card(supports full ID or unambiguous short prefix + case-insensitive list matching) - "What's overdue?" →
list_overdue
- "What's on my plate this week?" →
Scored Extras Included
- Dodil Models AI (
kimi-k2.6+jina-embeddings-v4):- "Turn this card into a checklist" →
suggest_checklist(preview orapply=Truestraight onto the card). - "Find cards like this one" →
search_similar_cards(hybrid semantic cosine similarity viajina-embeddings-v4+ lexical fallback, strictly scoped to boards the caller can view). - "Give me a daily summary of this board" →
summarize_board(concise daily standup highlighting momentum, overdue items, and blockers).
- "Turn this card into a checklist" →
- Card Dependencies & Cycle Detection:
- Link
"blocked by"cards (add_blocker/remove_blocker) with DFS cycle prevention in the domain model, and ask "What is blocking the launch?" (what_is_blocking) to trace both direct and transitive unfinished upstream blockers.
- Link
- Automatic Overdue Flagging:
- Any card with
due_date < todaynot in aDonelist is flagged automatically in API payloads, MCP responses, and with redOVERDUEbadges + one-click filtering in the web UI.
- Any card with
3. Architecture & Main Design Decisions
KanvaFlow uses Light Domain-Driven Design (Hexagonal Ports & Adapters):
app/boards/
domain/model.py Pure Python entities & invariants (Board, Card, Role, cycle DFS, similarity)
application/ports.py Repository & AI Assistant protocols
application/use_cases.py Single source of truth for all actions (Boards service)
infrastructure/dodil.py Service-account token cache, DataK3 pg-wire, Dodil Models (chat + embeddings)
infrastructure/repository.py Idempotent natural-key SQL upserts on DataK3
doors/identity.py Gateway X-Dodil-User parser (DEV_USER fallback for local dev only)
doors/web.py FastAPI JSON routes + responsive Drag-and-Drop Kanban SPA + POST / self-check
doors/mcp.py Streamable-HTTP MCP action tools calling application/use_cases.py
main.py FastAPI lifespan, RFC 9728 OAuth resource metadata, /healthz, /mcp gate
tests/test_boards.py Fast in-memory pytest suite covering domain rules & both doors
Key Design Decisions:
- Two Thin Doors, One Application Core: Neither
doors/web.pynordoors/mcp.pytouches SQL or checks roles directly — both translate HTTP/MCP calls intoBoardsuse cases, making it structurally impossible for an MCP tool call to bypass an RBAC check. - Zero Custom Auth Code: Authentication relies 100% on the Dodil gateway's
appidpool andX-Dodil-Userheader, plus RFC 9728/.well-known/oauth-protected-resourcediscovery so Claude.ai and ChatGPT trigger user sign-in automatically. - Fault-Tolerant AI Enrichment: Card creation computes semantic embeddings via
jina-embeddings-v4opportunistically; if the model call ever times out, core task persistence still succeeds andsearch_similar_cardsblends vector and lexical similarity gracefully. - Ignite
POST /Self-Check: In addition to/healthz,POST /executes a live DataK3 table count check using the app's own service-account credential sododil ignite invoke <app>verifies real database connectivity immediately.
4. Run & Test Locally
cd app
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt pytest
python -m pytest -q ../tests
To run locally against a remixed DataK3 bucket:
export BUCKET=<your-repo>-data
export DODIL_SERVICE_ACCOUNT_ID=<client-id>
export DODIL_SERVICE_ACCOUNT_SECRET=<client-secret>
export DEV_USER=you@example.com
uvicorn boards.main:app --port 8080
What We Would Improve Next
- Native K3 pgvector KNN (
data_vsearch/vectorcolumn): Move embeddings from JSON-in-TEXT to a typed DataK3 vector column once boards scale past thousands of cards per workspace. - Real-Time Multi-User Presence: Add Server-Sent Events (SSE) notifications so concurrent card moves animate live across teammates' browsers without refreshing.
- Board Templates & Webhooks: One-click sprint/launch board presets and outbound webhook triggers on list transitions.
5. Licence
MIT — see LICENSE.