< Back to projects

Desktop app · case study

Notecom Desktop

Desktop build of Notecom that generates lessons with the user's own Claude Code CLI, with no API key stored.

Notecom desktop app preview
My role

Sole developer

Type

Desktop application

Stack

Tauri, Rust, Next.js, TypeScript

Why I built it

The desktop build of Notecom exists for the one thing the website can't do: generate notes.It runs the user's own Claude Code CLI on their own subscription, so no API key is stored anywhere.I built it as part of Notecom.

The problem

  • Generating notes needs a signed-in Claude Code CLI, which a hosted website doesn't have.

  • Installing it should take one download, not a clone and a terminal.

Features

  • Generates lessons and quizzes from uploaded slides/PDFs
  • Runs generation in the background, up to 3 at once
  • Auto-refreshes the tree when a file lands
  • Only this build (or your dev machine) can generate - hosted web can’t

Architecture

Architecture diagramA Rust Tauri shell picks a free port and starts a bundled Node sidecar. The sidecar runs the Next.js standalone server on 127.0.0.1, and a native window opens on it. Generation spawns the user's own Claude Code CLI, which writes lessons to a local vault folder. The app imports those files into the same Supabase project as the web app, under the same RLS.YOUR MACHINESUPABASETauri shellRust, splash, free portkills process tree on exitNative webview127.0.0.1:<port>/vaultNode sidecarNext standalone serverloopback onlyclaude CLIuser's own subscription/lect, /quiz, max 3 jobsvault/lesson HTMLindex.jsonSupabase Authsame session as webPostgres + RLSno service-role keyspawnsopensHTTPspawns jobimport (read)writessign-inanon key + JWT
Dev · applicationSec · security controlOps · infrastructureExternal · third partyScroll sideways to see all of it.
  • Tauri shell to Node sidecar: spawns
  • Tauri shell to Native webview: opens
  • Native webview to Node sidecar: HTTP
  • Node sidecar to claude CLI: spawns job
  • Node sidecar to vault/: import (read)
  • claude CLI to vault/: writes
  • Node sidecar to Supabase Auth: sign-in
  • Node sidecar to Postgres + RLS: anon key + JWT

In words

A Rust Tauri shell picks a free port and starts a bundled Node sidecar.The sidecar runs the Next.js standalone server on 127.0.0.1, and a native window opens on it.Generation spawns the user's own Claude Code CLI, which writes lessons to a local vault folder.The app imports those files into the same Supabase project as the web app, under the same RLS.

Pipeline

push → main

  1. GitHub Actions

    type-check only, fast on purpose

tag v*.*.*

  1. OS matrix

    Windows and Linux; macOS job failing

  2. Bundle

    Next standalone + Node sidecar

  3. tauri build

    Rust shell, per-OS installers

  4. GitHub Release

    .msi .exe .AppImage .deb

Decisions

Use the user's own Claude Code CLI, not an API key.

No per-token charges on top of the user's subscription.No shared key serving several people.

A per-user project folder, resolved at launch.

An installer built on one machine works on any other.

Challenges

Stopping every process on exit.

On Windows, taskkill ends the whole process tree.A plain kill leaves children running.

Two generations writing to the same folder at once.

Jobs queue per folder, with up to three running at once.Lesson numbers never collide.

Testing

  • Every version tag builds installers for Windows, Linux and macOS.

  • Not yet: the macOS job fails at the Rust build.v0.1.0 shipped for Windows and Linux only.

Outcome

  • v0.1.0 released for Windows (.exe, .msi) and Linux (.AppImage, .deb).

Next

  • Fix the macOS universal build

Stack

Tauri

Packages the app as a native window and manages startup.

Rust

Runs the native shell beneath Tauri.

Next.js

Runs the UI and the background job queue.

TypeScript

Types the note generation pipeline.

Security

Authentication

The web app's Supabase session.The shell holds no credentials of its own.

Input validation

Generation runs only on files the user attaches.Its rules live in the command files, not the app.

Protections

No API key stored anywhere.Generation uses the user's own signed-in Claude CLI.