A skeptical AI accountability companion that holds you to one commitment and accepts only verified evidence, never self-report, as progress.
Live Demo Β»
Β·
Demo Video
Β·
Report a Bug
Expand
"It doesn't believe you. Yet."
A skeptical accountability companion. Not a cheerleader. One commitment, verified evidence, no self-report. Earn the trust.
Most habit apps take you at your word. Tap a checkbox, keep the streak, lie to yourself for free. Kawan (Malay for "friend") is the opposite: a companion that holds you to one commitment, asks for real evidence, and only believes you once you've shown it.
You commit to a single deliverable with a deadline. Kawan checks in on a schedule, reviews the evidence you submit β a screenshot, a file, or commits in a GitHub repo β and returns a verdict: pass, fail, or unclear. Self-report is never accepted. Trust is earned check-in by check-in.
The catch that makes it work: Kawan can never change the terms of your deal. Your goal, deadline, and how you're verified are yours alone. The AI reads them, reasons about them, and nudges you β but it is structurally incapable of editing them. That guarantee is enforced in the schema, not just the prompt (see The trust boundary).
Built by Team CHJL with π. Read the Pitch Deck and the design direction.
Built for Chutes Hack Malaysia 2026 (Corporate Track), where it placed 1st.
A commitment moves through a single, deterministic lifecycle β from drafting the deal to a verified (or honestly un-verified) outcome.
-
Compose β state the deal.
I will [complete] [a deliverable] by [a deadline].One goal, one deadline. No room to be vague.
-
Plan β set the terms. Choose your evidence source (a GitHub repo to watch, or screenshot/file uploads), optionally name a witness who gets emailed if you miss, and a reminder email. Only you can change these. Kawan reads them but never edits them.
-
Companion β pick who holds you to it. Three personalities, same backbone:
Companion Archetype Tone Kawan Skeptical Concierge Candid, warm, slightly dry. Believes you because you proved it. Adik Gentle Cheerleader Encouraging and kind. Celebrates every step. Cik Maid Playful Taskmaster Brisk, playful, expects results β with a wink.
-
Check in β answer to your companion. Your companion enters the workspace as a live, animated avatar. It gathers context (why, obstacles, time), then checks in on schedule and waits for evidence.
-
Workspace β context, plan & evidence in one place. A focused room around the conversation: captured context, an advisory plan, recent activity, a live countdown to the next check-in, and the Submit final evidence action.
-
Track β overview, progress & terms. Every commitment has a detail page: verified count, check-ins, latest verdict and reasoning, the immutable terms, and a full timeline.
-
Finish β verified, and only then. When the evidence passes, the commitment is closed as done. No participation trophies β a win counts because it was shown.
- One real commitment. A single action + deliverable + deadline. Hard fields you set and only you can change.
- Evidence over self-report. Verdicts come from a GitHub repo's commits, an uploaded file, or a screenshot judged by a vision model. There is no "mark as done" button you can lie to.
- Honest verdicts. Every check-in resolves to
pass/fail/unclear.unclearnever punishes; a flaky or slow model degrades to it instead of guessing. - Three Live2D companions. Kawan, Adik, and Cik Maid, each a stateless preset of tone + animated model + voice + inference model. Switching the companion changes the messenger, never your commitment.
- TEE inference via Chutes. Real check-in lines and evidence judgments run on Chutes' Trusted Execution Environment chutes, with per-persona model routing and an automatic secondary judge on failure. A deterministic offline stub backend runs the whole app with zero keys.
- Reliable delivery. Notifications walk a ladder: live WebSocket β Web Push β persisted in-app timeline, so a check-in is never lost.
- Off-device reminders. Opt-in email (Resend), Web Push (VAPID), and a Telegram check-in channel.
- Stakes & witnesses. Name someone who's emailed if you miss the deadline. That's the whole mechanism.
- Analytics & achievements. A productivity meter, identity titles, and 15 behavioral achievements that reward how you won (verified without a skip-day, finished early, came back after a missβ¦).
- Scheduled & on-demand check-ins. APScheduler drives the cadence; one code path serves both the cron tick and an instant "check now," and rebuilds its jobs from the DB after a restart.
- Guided walkthrough. An optional tour that teaches the commitment flow on real components, not a fake demo.
- Polished UX. Light/dark themes, responsive shell, optional Piper neural TTS with a WebSpeech fallback.
The diagram is drawn with archify from architecture.json.
Kawan is a single-process FastAPI backend plus a React SPA. The frontend is organized in three zones: public pages (Zone 0), the SaaS shell (Zone 1 β home, commitments, analytics, settings), and the full-screen AI workspace (Zone 2 β the compose flow and live companion).
The trust boundary. The core idea is a hard separation between what you own and what the AI can touch β enforced in the data model, not just convention:
- Hard fields (
commitmentstable): action, deliverable, deadline, cadence, evidence type, stake. Written only by GUI handlers and the scheduler/verifier. No AI code path can update them. - Soft context (
soft_contexttable): the why, obstacles, and constraints. The only table the AI is allowed to write. - Proposals: the AI can propose a change to a hard field, but only you can apply it.
- Audit log: every hard-field mutation records an actor β and
'ai'is unrepresentable by a databaseCHECKconstraint. The AI literally cannot be the author of a change to your deal.
That is why the UI can promise "Only you can change these. Kawan reads them but never edits them." and mean it.
The check-in pipeline. One code path (app/pipeline.py) runs for both a scheduled cadence tick and an on-demand check:
- Fetch new evidence through the adapter for the commitment's evidence type (
github/screenshot/file). - Judge it into a
Verdict(pass/fail/unclear) β primary call on a Chutes TEE model, with a bounded timeout that fails fast to a secondary judge rather than hanging. - Persist the evidence, check-in line, and escalation state.
- Deliver down the ladder: WebSocket β Web Push β in-app timeline.
A commitment's status machine (draft β active β verifying β grace β completed / missed, plus lapsed / returned) is the only thing that moves state β derived snapshots feed the AI as read-only prompt context and can never write back.
Project structure.
apps/
βββ backend/ # FastAPI single-process service
β βββ app/
β β βββ main.py # app + lifespan (scheduler, telegram poller)
β β βββ models.py # hard fields / soft context / audit log
β β βββ pipeline.py # check-in + final verify (the one code path)
β β βββ personas.py # Kawan / Adik / Cik Maid presets
β β βββ adapters/ # github Β· screenshot Β· file evidence
β β βββ routes/ # auth Β· commitments Β· push Β· telegram Β· voice Β· ws
β β βββ β¦ # scheduler, chutes client, notify, state machine
β βββ render.yaml # Render deploy
β βββ DEPLOY.md # pooler, secrets and Vercel env notes
β βββ .env.example # backend settings with dev defaults
βββ frontend/ # React + Vite SPA
βββ src/
β βββ shell/ # Zone 1 β SaaS shell + pages
β βββ zone2/ # Zone 2 β workspace, Live2D, new-commitment flow
β βββ timeline/ # analytics, achievements, productivity meter
β βββ β¦ # auth, notifications, ui, share
βββ public/ # Live2D models (Git LFS), banner, icons, service worker
scripts/ # download_voices.sh Β· helpers
docs/ # PRD, TRD, design, ADRs and the pitch deck
βββ readme/ # the images in this README
- Languages: Python and TypeScript.
- Frontend: React 18, Vite, React Router v7, PixiJS v6 with
pixi-live2d-display, Recharts and Lucide, with Live2D Cubism avatars (Haru, Hiyori, LiveroiD). - Backend: FastAPI, SQLAlchemy 2 (async), APScheduler, Pydantic Settings and httpx, delivering over WebSocket and Web Push (VAPID).
- Data: SQLite (dev) and PostgreSQL via the Supabase pooler (prod).
- AI and services: Chutes (OpenAI-compatible TEE inference) with Sign in with Chutes (OAuth2 PKCE), a deterministic stub backend, optional Piper neural TTS, the Telegram Bot API and email through Resend.
- Infrastructure: Backend on Render and frontend on Vercel.
- Tooling:
uv, Biome and pytest.
The app runs fully offline out of the box β the default AI backend is a deterministic stub, so you need no API keys to try it locally.
- Python 3.12+ β runs the FastAPI backend.
- uv β installs and runs the backend.
- Bun β installs and runs the frontend; the frontend lockfile is
bun.lock, and npm or pnpm also work. - Bash β runs the asset scripts, which are bash.
-
Configure the environment. Run each block from the repository root.
cp apps/backend/.env.example apps/backend/.env # sensible dev defaults are pre-filledThe dev defaults use local SQLite, the Vite proxy, and
KAWAN_AI_BACKEND=stub. No secrets required.All settings use the
KAWAN_prefix and load fromapps/backend/.env. See.env.examplefor the full list. The most important knobs:Variable What it does KAWAN_AI_BACKENDstub(deterministic, offline β default) orchutes(real TEE inference)KAWAN_DATABASE_URLSQLite by default; a Supabase pooler URL in prod KAWAN_CHUTES_API_KEYChutes token β enables guest-mode inference KAWAN_SIWC_*Sign in with Chutes (OAuth2 PKCE) client credentials KAWAN_SESSION_SECRET/KAWAN_FERNET_KEYCookie signing + token-at-rest encryption (must be set in prod) KAWAN_VAPID_*Web Push keypair β blank disables push (delivery falls back to the timeline) KAWAN_RESEND_API_KEYStake/reminder email β blank uses a log-only outbox so the miss path still runs KAWAN_TELEGRAM_BOT_TOKENTelegram check-in channel β blank makes every send a no-op KAWAN_PIPER_VOICES_DIRDirectory of Piper voice models β blank returns 204 and the client uses WebSpeech To use real inference, set
KAWAN_AI_BACKEND=chutesand provideKAWAN_CHUTES_API_KEY(and theKAWAN_SIWC_*values for Sign in with Chutes). -
Fetch the Live2D companion models. They are stored in Git LFS; pull them once after clone.
git lfs pull
-
Run the backend. FastAPI on
:8000.cd apps/backend uv sync uv run uvicorn app.main:app --reload -
Run the frontend. Vite on
:5173, proxies/apiand/wsto the backend.cd apps/frontend bun install bun devOpen http://localhost:5173 and choose Continue as guest to start.
Optional β voices: run
./scripts/download_voices.shto fetch the three Piper persona voices. Without them, the frontend falls back to the browser's WebSpeech voice. -
Deployment.
- Backend β Render.
apps/backend/render.yamldefines the web service (uv syncβuvicorn). Secrets and the cross-origin cookie settings (KAWAN_COOKIE_SAMESITE=none,KAWAN_COOKIE_SECURE=true) are set in the Render dashboard. Database notes (Supabase session vs. transaction pooler) live inapps/backend/DEPLOY.md. - Frontend β Vercel.
apps/frontend/vercel.jsonrewrites/api/*to the Render backend and serves the SPA. In production the WebSocket connects directly to Render, which is why prod runsSameSite=None; Securecookies.
- Backend β Render.
-
Run the checks. From the repository root, lint the repository, run the backend tests, then build the frontend.
bun install bun run check
See open issues for a full list of proposed features (and known issues).
Made with contrib.rocks.
See LICENSE for more information.
- Chutes β Trusted Execution Environment inference and Sign in with Chutes.
- Live2D Cubism β the animated companions, with pixi-live2d-display.
- #LiveroiD β the Cik Maid companion model, γ’γγ«εΆδ½οΌε «εζΊζΆ (@yashiro_seika).
- Piper β neural text-to-speech voices.
- archify β architecture diagrams.
- Chutes Hack Malaysia 2026 β the hackathon by Nyala Labs, Chutes and Infinity8, where Kawan entered the Corporate Track; its entry is on Devpost.
- Shields.io
- contrib.rocks





