Upleveler
Maintainer verifiedShared by Kayra @kayra61b
Upleveler is a local-first work log for software developers: it compares what you log with your company career ladder, shows the gaps, prepares 1:1s from your notes and writes your promotion document with a local LLM through Ollama.
- Language
- Rust
- Hosting
- Cloudflare Workers
- License
- AGPL-3.0
- Running for
- 1 year
- Team
- Solo
Why this architecture
Upleveler keeps every log, note and goal as plain local files and routes all AI work through a local Ollama model in small map-reduce prompts, so one Rust binary can serve the CLI, a Ratatui app and a 127.0.0.1 Axum dashboard without an account or cloud.
Tech stack by layer
9 technologies · audited Oct 8, 2026- RatatuiInteractive terminal app with an inline viewport: finished output goes to scrollback, a live area holds the spinner, popups and tui-textarea input.
- MaudCompile-time HTML templates for the local dashboard views and the static landing page, which render the same view functions.
- ClapDerive-based CLI: every feature is also a plain subcommand such as upleveler log, gap, prep or export.
- RustSingle Cargo workspace: the upleveler crate (CLI, TUI, core library, dashboard) and the upleveler-site generator; Rust 1.82 minimum for the app.
- Axumupleveler web serves the dashboard on 127.0.0.1 behind a random token, Host-header checks and security headers; behind the optional server feature.
- TokioRuntime for the dashboard server only; file and model work runs in spawn_blocking so the synchronous core library stays unchanged.
- ureqBlocking HTTP client for the minimal chat client that talks to Ollama /api/chat or any OpenAI-compatible /chat/completions endpoint.
- JSONL fileslogs.jsonl and notes.jsonl under ~/.upleveler hold one JSON object per line; rewrites go through a temp file and a process-wide write lock.
- YAML and TOML filesladder.yaml, people.yaml and goals.yaml via serde_norway; config.toml stores the model, language and levels.
- calamineReads xlsx, xls, ods, csv and tsv for import; the model only maps columns and rows are converted deterministically.
- rust_xlsxwriterExports the log to xlsx next to the md, csv and jsonl text formats.
- Cloudflare WorkersStatic-assets Worker that serves the generated landing page at upleveler.dev with no script and no user data.
- GitHub ActionsCI (fmt, clippy, tests on Ubuntu and macOS, install on Rust 1.82), five-target release builds, the site deploy and a gitleaks history scan.
- resvgRenders the OG image and app icons to PNG from the design tokens, the mark and a bundled Manrope TTF.
- cargo-aboutGenerates THIRD-PARTY-LICENSES.txt for every release archive alongside the Manrope OFL notice.
- OllamaDefault local model provider; the request sets num_ctx and temperature, and JSON mode is used for structured answers.
- OpenAI-compatible APIOptional provider for LM Studio, vLLM or a company gateway, chosen in setup after confirming the endpoint is approved.
- Prompt templatesEleven Markdown prompts (route, tag_entries, gap_item, brag_item, prep, summary, ask, imports) embedded at build time.
- CLAUDE.mdRules for AI assistants: never commit secrets or personal data, run fmt, clippy and tests before a commit, ask before publishing.
Upleveler architecture diagram
Open SVGDiagram as text
- CLI and TUI (clap · Ratatui) → Core library (session · analyze)
- Local dashboard (Maud HTML) → upleveler web (Axum · 127.0.0.1): token cookie
- upleveler web (Axum · 127.0.0.1) → Core library (session · analyze)
- Core library (session · analyze) → ~/.upleveler (JSONL · YAML · TOML): read / write
- Core library (session · analyze) → Local model (Ollama): /api/chat
- upleveler.dev (Cloudflare Workers) → Local dashboard (Maud HTML): same views
Key architectural decisions
7 decisions- 01
Plain local files instead of a database
crates/upleveler/src/config.rs resolves every path under ~/.upleveler (or $UPLEVELER_HOME): logs.jsonl, notes.jsonl, ladder.yaml, people.yaml, goals.yaml and config.toml. store.rs appends one JSON object per line and rewrites through a .tmp file, so users can read, edit or delete their data with any editor.
- 02
Map-reduce prompts sized for a small local model
crates/upleveler/src/analyze.rs maps entries to ladder expectations in batches of at most 8 that fit the configured context budget, then reduces them into gap, brag and summary reports. Each entry stores the ladder hash it was tagged with, so only new or re-laddered entries go back to the model.
- 03
One library, three front ends
crates/upleveler/src/session.rs holds everything a front end does with the data without printing or prompting. main.rs (clap subcommands), the Ratatui app in src/tui/ and the Axum dashboard in src/web/ all call it, and background jobs report progress through the same cancellable callback.
- 04
A local dashboard locked to 127.0.0.1 with a token link
crates/upleveler/src/web/server.rs binds only 127.0.0.1 (port 4747 by default), trades a random ?token= for an HttpOnly SameSite=Strict cookie, rejects foreign Host headers against DNS rebinding and serves no third-party assets, following the Jupyter model.
- 05
The landing page renders the real dashboard views
site/src/main.rs depends on the upleveler crate with default features off, renders the same Maud views with made-up data from src/web/demo.rs and writes static files that Cloudflare serves through site/wrangler.jsonc. Tokens in src/web/tokens.rs are the only source of colour for both surfaces.
- 06
Design rules enforced by cargo test
crates/upleveler/tests/design_lint.rs fails the build on banned patterns in src/web/ and site/src/, and the test suite also checks WCAG AA colour contrast, so the design system is checked by the same command contributors already run.
- 07
Prebuilt binaries with checksums instead of crates.io
The crate sets publish = false. .github/workflows/release.yml builds macOS (arm64, x86_64), static musl Linux (x86_64, arm64) and Windows binaries, writes SHA256SUMS and takes release notes from CHANGELOG.md; site/install.sh verifies the checksum before installing to ~/.local/bin.
How Upleveler is built
How Upleveler is structured
Upleveler is a Cargo workspace with two members. crates/upleveler is the whole product: the CLI, the interactive terminal app, the core library and the local web dashboard. site is a separate binary, upleveler-site, that generates the static landing page at upleveler.dev from the same view code. The app crate sets publish = false; it ships as prebuilt release binaries and through cargo install --path crates/upleveler.
Inside the app crate, src/lib.rs lists the modules: store, ladder, people, goals, intent, analyze, llm, prompts, import, export, session, tui and web. src/session.rs is the seam: it does everything a front end does with the data "without printing or prompting", and the clap subcommands in src/main.rs, the Rust TUI and the dashboard all call into it. The dashboard server is behind a cargo feature named server (default on), which pulls in Axum, Tokio, getrandom and webbrowser; the site builds the crate with that feature off.
Frontend
There are three front ends over one library.
| Surface | Code | Notes |
|---|---|---|
| Scriptable CLI | src/main.rs |
clap derive; upleveler log, gap, brag, prep, export -f xlsx |
| Terminal app | src/tui/ |
Ratatui inline viewport, tui-textarea input, @file and @person completion |
| Browser dashboard | src/web/views/ |
Maud templates: overview, logs, people, goals, ladder, reports |
The terminal app in src/tui/mod.rs is modelled on agent CLIs: Ratatui runs with Viewport::Inline, finished output is written into the terminal's own scrollback with insert_before, and only a small live area holds the spinner, popups and the input box. Free text is routed by src/intent.rs: obvious questions skip the model, a leading "dün" or "yesterday" is split into a date, and the model decides between a log entry, a note about a person or a goal check-in. A note or check-in aimed at someone not in people.yaml comes back as unclear instead of guessed. src/tui/jobs.rs runs every AI call on its own thread and streams tokens back over a channel so drawing never blocks.
The dashboard is server-rendered HTML. src/web/assets/app.js is a short script for copy buttons and nothing else; styles come from assets/style.css plus CSS custom properties rendered by src/web/tokens.rs, and the Manrope font is bundled as woff2 so the page loads nothing from the internet.
Backend & APIs
src/web/server.rs is the only network listener. It binds 127.0.0.1 on port 4747 when free (or any free port), prints a link with a random token, and on the first request trades ?token= for an HttpOnly, SameSite=Strict cookie named per port. Requests whose Host header is not the server are refused to block DNS rebinding, and a middleware adds security headers to every response. Handlers do their file and model work in tokio::task::spawn_blocking, so the core library stays synchronous. Analyses started from the browser go through src/web/runs.rs: one at a time, on their own thread, with progress the pages read and a cancel flag.
The model client in src/llm.rs is deliberately small. A Llm trait has complete and stream; HttpLlm uses ureq to call Ollama's native /api/chat (so it can set num_ctx) or an OpenAI-compatible /chat/completions. JSON answers use temperature 0.1 and Ollama's format: json or response_format: json_object, and complete_json gives the model one retry to fix invalid JSON. Prompts are Markdown files in src/prompts/ embedded at build time.
Data & persistence
There is no database. src/config.rs resolves ~/.upleveler (or $UPLEVELER_HOME) to config.toml, ladder.yaml, logs.jsonl, people.yaml, notes.jsonl and goals.yaml. src/store.rs gives each entry an id from the date and a SHA-256 of the normalised text, appends new lines, and rewrites the file through a .tmp swap under a process-wide mutex, because the TUI keeps logging while analyses update tags in the background. A test checks that concurrent appends survive those rewrites.
src/analyze.rs explains the AI design: "Everything is map-reduce over small prompts so that a 7B local model with an 8K context can handle years of logs." map_entries tags entries against ladder expectations in batches of up to eight that fit the context budget, stores the ladder hash on each entry, and skips entries already tagged with the current hash. Gap analysis, the brag document and summaries then reduce over those tags. People notes are passed to the model only for a 1:1 prep or a question about that person, never into gap, brag or summary prompts. Import uses calamine for spreadsheets (the model only picks column meanings) and a block-by-block free-text importer that keeps anything the model fails on verbatim.
Build, test & deploy
.github/workflows/ci.yml runs cargo fmt --check, clippy with all features and again with --no-default-features (the site's build), tests on Ubuntu and macOS, and an install job on Rust 1.82 that runs the exact README command. crates/upleveler/tests/design_lint.rs fails on banned design patterns in src/web/ and site/src/, and the suite checks WCAG AA contrast. secrets.yml scans the full git history with gitleaks on every push.
release.yml runs on version tags: it builds five targets including static musl Linux binaries, generates third-party notices with cargo-about, checks the tag against Cargo.toml, writes SHA256SUMS and publishes a GitHub release with notes cut from CHANGELOG.md. site.yml runs cargo run -p upleveler-site, which also renders the OG image and icons with resvg, and deploys site/dist with Wrangler to a static-assets Cloudflare Workers route on upleveler.dev, or uploads a preview version for pull requests.
What to copy (and what not to)
Copy the session layer that keeps CLI, TUI and web as thin shells, the content-hash cache that keeps a slow local model from redoing work, and the 127.0.0.1 token-cookie pattern for any local dashboard. Rendering the marketing site from the product's own views keeps the demo honest. Plain files suit a single-user tool; a multi-device or team version would need a real store and a sync story the repository does not have.
Sources & repo audit
- Workspace Cargo.toml
- crates/upleveler/Cargo.toml (server feature)
- crates/upleveler/src/analyze.rs (map-reduce analyses)
- crates/upleveler/src/llm.rs (Ollama and OpenAI-compatible client)
- crates/upleveler/src/web/server.rs (local dashboard)
- site/README.md (landing page on Cloudflare)
- .github/workflows/release.yml
Maintained by the project's owner. It started from our independent analysis of github.com/k61b/upleveler (Oct 8, 2026).
Show this stack on your README or website
[](https://stackitfast.com/project/upleveler)Paste it into your README. It shows your current stack and links to this page.
Scaffold it with your agent
Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with Upleveler's architecture.
- 1Copy the promptThe full markdown spec, with every layer and decision.
- 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
- 3Paste and scaffoldUse it as the first instruction; review before you ship.
# MISSION: Scaffold "Upleveler" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **Upleveler**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Upleveler
- **What It Does**: Upleveler is a local-first work log for software developers: it compares what you log with your company career ladder, shows the gaps, prepares 1:1s from your notes and writes your promotion document with a local LLM through Ollama.
- **Domain & Category**: Developer Work Log & Promotion Coach
- **Production Scale**: Solo
- **Development Mode**: HYBRID
- **Architectural Rationale**: Upleveler keeps every log, note and goal as plain local files and routes all AI work through a local Ollama model in small map-reduce prompts, so one Rust binary can serve the CLI, a Ratatui app and a 127.0.0.1 Axum dashboard without an account or cloud.
- **Live Website Reference**: https://upleveler.dev
- **Source Repository**: https://github.com/k61b/upleveler
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: Rust, Axum, Tokio, Ratatui, Maud, Clap, Ollama, Cloudflare Workers, GitHub Actions
- **Primary Language(s)**: Rust, CSS, Shell, JavaScript
- **License of the reference repo**: AGPL-3.0
- **Frontend**: Ratatui — Interactive terminal app with an inline viewport: finished output goes to scrollback, a live area holds the spinner, popups and tui-textarea input.; Maud — Compile-time HTML templates for the local dashboard views and the static landing page, which render the same view functions.; Clap — Derive-based CLI: every feature is also a plain subcommand such as upleveler log, gap, prep or export.
- **Backend & APIs**: Rust — Single Cargo workspace: the upleveler crate (CLI, TUI, core library, dashboard) and the upleveler-site generator; Rust 1.82 minimum for the app.; Axum — upleveler web serves the dashboard on 127.0.0.1 behind a random token, Host-header checks and security headers; behind the optional server feature.; Tokio — Runtime for the dashboard server only; file and model work runs in spawn_blocking so the synchronous core library stays unchanged.; ureq — Blocking HTTP client for the minimal chat client that talks to Ollama /api/chat or any OpenAI-compatible /chat/completions endpoint.
- **Data & persistence**: JSONL files — logs.jsonl and notes.jsonl under ~/.upleveler hold one JSON object per line; rewrites go through a temp file and a process-wide write lock.; YAML and TOML files — ladder.yaml, people.yaml and goals.yaml via serde_norway; config.toml stores the model, language and levels.; calamine — Reads xlsx, xls, ods, csv and tsv for import; the model only maps columns and rows are converted deterministically.; rust_xlsxwriter — Exports the log to xlsx next to the md, csv and jsonl text formats.
- **Infrastructure & deploy**: Cloudflare Workers — Static-assets Worker that serves the generated landing page at upleveler.dev with no script and no user data.; GitHub Actions — CI (fmt, clippy, tests on Ubuntu and macOS, install on Rust 1.82), five-target release builds, the site deploy and a gitleaks history scan.; resvg — Renders the OG image and app icons to PNG from the design tokens, the mark and a bundled Manrope TTF.; cargo-about — Generates THIRD-PARTY-LICENSES.txt for every release archive alongside the Manrope OFL notice.
- **Tooling, testing & ops**: Ollama — Default local model provider; the request sets num_ctx and temperature, and JSON mode is used for structured answers.; OpenAI-compatible API — Optional provider for LM Studio, vLLM or a company gateway, chosen in setup after confirming the endpoint is approved.; Prompt templates — Eleven Markdown prompts (route, tag_entries, gap_item, brag_item, prep, summary, ask, imports) embedded at build time.; CLAUDE.md — Rules for AI assistants: never commit secrets or personal data, run fmt, clippy and tests before a commit, ask before publishing.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/k61b/upleveler @ 8b8ad56)
1. **Plain local files instead of a database**: crates/upleveler/src/config.rs resolves every path under ~/.upleveler (or $UPLEVELER_HOME): logs.jsonl, notes.jsonl, ladder.yaml, people.yaml, goals.yaml and config.toml. store.rs appends one JSON object per line and rewrites through a .tmp file, so users can read, edit or delete their data with any editor.
2. **Map-reduce prompts sized for a small local model**: crates/upleveler/src/analyze.rs maps entries to ladder expectations in batches of at most 8 that fit the configured context budget, then reduces them into gap, brag and summary reports. Each entry stores the ladder hash it was tagged with, so only new or re-laddered entries go back to the model.
3. **One library, three front ends**: crates/upleveler/src/session.rs holds everything a front end does with the data without printing or prompting. main.rs (clap subcommands), the Ratatui app in src/tui/ and the Axum dashboard in src/web/ all call it, and background jobs report progress through the same cancellable callback.
4. **A local dashboard locked to 127.0.0.1 with a token link**: crates/upleveler/src/web/server.rs binds only 127.0.0.1 (port 4747 by default), trades a random ?token= for an HttpOnly SameSite=Strict cookie, rejects foreign Host headers against DNS rebinding and serves no third-party assets, following the Jupyter model.
5. **The landing page renders the real dashboard views**: site/src/main.rs depends on the upleveler crate with default features off, renders the same Maud views with made-up data from src/web/demo.rs and writes static files that Cloudflare serves through site/wrangler.jsonc. Tokens in src/web/tokens.rs are the only source of colour for both surfaces.
6. **Design rules enforced by cargo test**: crates/upleveler/tests/design_lint.rs fails the build on banned patterns in src/web/ and site/src/, and the test suite also checks WCAG AA colour contrast, so the design system is checked by the same command contributors already run.
7. **Prebuilt binaries with checksums instead of crates.io**: The crate sets publish = false. .github/workflows/release.yml builds macOS (arm64, x86_64), static musl Linux (x86_64, arm64) and Windows binaries, writes SHA256SUMS and takes release notes from CHANGELOG.md; site/install.sh verifies the checksum before installing to ~/.local/bin.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
- Structure as a Turborepo monorepo with strict package boundaries:
- `apps/web`: Application UI, routing, layouts, and server endpoints.
- `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
- `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
- `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
- Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
- Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
- Isolate interactive UI state to leaf components. Keep core pages lightweight and performant.
4. **Design System & Aesthetics**:
- Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
- Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
- Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
- Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (JSONL files, YAML and TOML files, calamine, rust_xlsxwriter): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.Copied 1 time · 1 of 1 reported launches succeeded (100%)
Frequently asked about Upleveler
What is Upleveler built with?
Upleveler is a single Rust binary. The CLI uses clap, the interactive terminal app uses Ratatui, the optional browser dashboard is an Axum server with Maud templates, and AI features call a local Ollama model over HTTP with ureq. Data is stored as JSONL, YAML and TOML files in ~/.upleveler.
Does Upleveler send my work log to the cloud?
No. Logs, notes, goals and reports stay as plain files in ~/.upleveler, the model runs locally in Ollama by default, and the browser dashboard only listens on 127.0.0.1. An OpenAI-compatible endpoint is used only if the user picks it in setup and confirms it is approved.
Which model does Upleveler need?
The README recommends Ollama with gemma4:12b and about 16 GB of RAM. The analyses are split into small map-reduce prompts so that a 7B model with an 8K context window can still process years of log entries.
How is the upleveler.dev website built?
A small Rust program in site/ renders the landing page to static HTML with Maud, reusing the real dashboard views and design tokens from the app crate, and a static-assets Cloudflare Worker serves the files. A GitHub Actions workflow deploys it on pushes to main.
Is Upleveler written in Rust?
Yes. The app crate, the dashboard server and the website generator are all Rust in one Cargo workspace; the only other code is a stylesheet, a short copy-button script and the shell install script.
One email a month: new deep dives and stack trends
New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.
Similar architectures
- AxumClassicRust Web Framework · 1M+ MAUShares Rust · Axum · Tokio
- DioxusClassicCross-Platform Rust App Framework · 6-20 peopleShares Rust · Axum · Tokio
- LeptosClassicFull-Stack Rust Web Framework · 6-20 peopleShares Rust · Axum · Tokio
- Actix WebClassicRust Web Framework · 1M+ MAUShares Rust · Tokio · GitHub Actions
- crates.ioClassicPackage Registry · 1M+ MAUShares Rust · Axum · Tokio
- DenoClassicSecure JavaScript & TypeScript Runtime · 20+ peopleShares Rust · Tokio