ultimo
CLAUDE.md802 starsChanged 53 days agoMITView on GitHub
Ultimo is a modern Rust web framework: REST + JSON-RPC in one app, automatic TypeScript client generation from Rust API definitions, and RFC 6455 WebSocket support with built-in pub/sub. Built on Hyper 1.0 + Tokio. Ultimo ships two crates to crates.io that real users depend on: ultimo (the library) and ultimo-cli (the binary; depends on ultimo). Every change is a potential breaking change. The public API contract — changing any of it requires a deliberate version bump: release-plz opens a…
- Installs packages
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- ship-featureSkill
- ship-featureSkill
The file
We split this CLAUDE.md into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 192
Use Makefile targets, not raw cargo, where one exists.
- Very specific: line 194
Run cargo fmt --all and cargo clippy before committing (.githooks/ may enforce this).
- Very specific: line 198
Every new page on ultimo.dev MUST include:
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Ultimo — Developer Reference (for Claude Code / coding agents)
Ultimo is a modern Rust web framework: REST + JSON-RPC in one app, automatic TypeScript client generation from Rust API definitions, and RFC 6455 WebSocket support with built-in pub/sub. Built on Hyper 1.0 + Tokio.
- Homepage: https://ultimo.dev · Docs: https://docs.ultimo.dev · Roadmap: https://docs.ultimo.dev/roadmap
- Repo: https://github.com/ultimo-rs/ultimo · Issues: https://github.com/ultimo-rs/ultimo/issues · Board: https://github.com/orgs/ultimo-rs/projects/1
- Current version: 0.9.4 · Edition 2021 · MSRV 1.86.0 · License MIT
⚠️ THESE ARE PUBLISHED CRATES — read before changing any public code
Ultimo ships two crates to crates.io that real users depend on: ultimo
(the library) and ultimo-cli (the binary; depends on ultimo). Every
change is a potential breaking change. The public API contract — changing
any of it requires a deliberate version bump:
- Any
pubitem (types, fns, traits, enum variants, fields) and theprelude - Cargo feature names (
websocket,testing,session,csrf,jwt,oidc,api-key,static-files,compression,client-gen,database,sqlx-*,diesel-*,test-helpers) — renaming/removing breaksfeatures = [...]downstream - MSRV (
rust-version) — raising it is breaking (we're at 1.86.0) - Direct dependency floors — raising one (e.g.
bytes,diesel) constrains downstream resolution; a dep type re-exported in our API (e.g.bytes::Bytesin WebSocket messages) makes that dep's version part of our API - CLI subcommands / flags of
ultimo-cli
On EVERY change
- Decide SemVer impact. Pre-1.0 (0.x): breaking → bump minor (0.3.x → 0.4.0); additive/fix → bump patch. CI's
semver-checksenforces this against the published crate — respect its verdict. - Don't hand-edit
CHANGELOG.md— release-plz regenerates it from your conventional commits. Write a good commit message; that is the changelog entry. - Keep
README.mdaccurate — it's the crates.io landing page. - Don't break docs.rs — public items need doc comments; doctests must compile (
cargo test --doc).
On a RELEASE (automated by release-plz — rarely done by hand)
release-plz opens a release PR that bumps version in root Cargo.toml
([workspace.package], shared by both crates) and writes the per-crate
ultimo/CHANGELOG.md / ultimo-cli/CHANGELOG.md from commits. Merging it
publishes ultimo then ultimo-cli to crates.io and tags a release. A
published vuln/serious bug → cargo yank it and ship a fixed patch.
Version must be in sync EVERYWHERE — one source of truth (Cargo.toml). The
version-sync CI gate (scripts/check-versions.sh) fails a PR if any surface
drifts: website/package.json, docs-site/package.json, the website hero badge,
the ultimo = "x.y" install snippets across README + docs, CLAUDE.md/AGENTS.md,
the blog comparison post, and the root CHANGELOG.md + docs-site/.../changelog.mdx
(plus the git tag + GitHub release, done by release-plz). release-plz only bumps
Cargo.toml + the per-crate changelogs, so the release workflow auto-runs
scripts/sync-versions.sh on the release PR to propagate the rest (it mirrors the
new changelog section from whichever crate — library or CLI — was released). If
version-sync ever fails on a release PR, run bash scripts/sync-versions.sh and
commit — don't hand-edit the surfaces one by one.
Workspace layout
Cargo workspace (resolver = "2"). Members:
| Crate / path | Role |
ultimo/ | The framework. Core library — everything below is a module here. |
ultimo-cli/ | CLI binary (clap). Subcommands: generate (TS client), new (scaffold), dev (hot-reload server), build. |
coverage-tool/ | Custom coverage runner (ultimo-coverage), invoked by make coverage. |
examples/* | Runnable example apps. Cargo.toml members is the source of truth — some dirs (react-app, benchmark, database-with-openapi) exist on disk but aren't members. |
ultimo/src modules (lib.rs is the map)
app.rs(Ultimobuilder),context.rs(Context),router.rs,handler.rs,middleware.rsrpc.rs— JSON-RPC registry + TypeScript client codegen (RpcRegistry)response.rs,error.rs(UltimoError/Result),validation.rs(validate),openapi.rs(+openapi/docs.rs)cookie.rs(core),session/(session),csrf.rs(csrf),testing/(testing)database/=sqlx.rs+diesel.rs(behinddatabase);websocket/(behindwebsocket)
Public API surface (keep stable): Ultimo, Context, Result, UltimoError,
RpcRegistry, RpcRequest, RpcResponse, validate, plus ultimo::prelude::*.
The crate is #![forbid(unsafe_code)] — 100% safe Rust, no unsafe.
Cargo features (ultimo/Cargo.toml)
default = [] — everything is opt-in. When adding code under a feature, gate
it with #[cfg(feature = "...")]. Features: websocket, testing
(TestClient/assertions/fixtures), session, csrf, jwt, oidc (implies
jwt), api-key, static-files, compression, client-gen (ts-rs type
derivation), test-helpers (exposes websocket::test_helpers for integration
tests), database (enabled transitively by sqlx-postgres|mysql|sqlite,
diesel-postgres|mysql|sqlite).
⚠️ Test invocation gotchas (READ BEFORE TOUCHING TESTS)
- Integration tests need feature flags. The
tests/*.rsfiles use feature-gated modules, so a barecargo test(no features) fails to compile them — that's expected, not a regression. Enable the combo:cargo test -p ultimo --features "websocket,test-helpers,testing,session,csrf" --all-featuresneeds system libs.diesel-mysql/sqlx-mysqllinklibmysqlclient; postgres backends needlibpq. Without them--all-features/make checkfails at the linker — an env gap, not code rot. Install them (brew install mysql-client libpq) or scope features.
Verified-good invocations:
cargo fmt --all --check
cargo test -p ultimo --lib
cargo test -p ultimo --features "websocket,test-helpers,testing,session,csrf"
cargo test -p ultimo --features "testing,sqlx-sqlite,diesel-sqlite" # DB-backed testsCommands (Makefile is the canonical entry point)
make help # list targets
make build # cargo build --release
make test # cargo test --lib --no-fail-fast (UNIT ONLY — see gotchas)
make check # clippy --all-targets --all-features + fmt --check (needs system libs)
make fmt # cargo fmt --all
make coverage # cargo run --release -p ultimo-coverage → target/coverage/html
make doc # cargo doc --no-deps --open
make ci # check + test + coverageCLI usage (the product surface):
cargo run -p ultimo-cli -- new <name> --template <basic|fullstack|api-only|rpc|production>
cargo run -p ultimo-cli -- generate --path <dir> --output <dir> [--watch] # TS client gen
cargo run -p ultimo-cli -- dev --port <n>
cargo run -p ultimo-cli -- build --profile <debug|release>Benchmarks
ultimo/benches/ (criterion, harness = false): websocket_bench.rs,
websocket_pubsub_bench.rs. Perf claims (158k+ req/s) live or die by these —
guard against regressions before changing hot paths.
CI / repo state
ci.yml(push + PR tomain):fmt + clippy,test(ubuntu + macOS: lib + feature-gated integration + CLI + benches),test-db(sqlite DB tests),MSRV(readsrust-versionfrom Cargo.toml),semver-checks(public-API breakage vs published crate),cargo-audit(RUSTSEC),cargo-deny(advisories/bans/sources),version-sync.release-plz.ymlautomates releases (opt-in per crate); CodeQL + Vercel (docs-site + website) run as checks.Cargo.lockis committed.mainbranch protection requires 1 review → solo merges use--adminoverride.
Shipping changes — use the ship-feature skill
.claude/skills/ship-feature/SKILL.md encodes the standard loop:
branch → TDD → docs surfaces → verification gate → PR → CI green →
gh pr merge --squash --admin --delete-branch → sync. Conventional commits
throughout. Invoke it for any change to this repo.
Roadmap
Full detail: docs-site/docs/pages/roadmap.mdx (canonical — keep this summary
in sync with it, not the other way around). Its phases (### Phase N: ...)
group features thematically and don't track the published crate's SemVer —
small additive/fix PRs bump the real version independently of which phase
their feature belongs to.
- Phases 1–7: shipped. Core, WebSocket, sessions/testing, security &
performance (JWT/API-key/OIDC auth, authz guards, security headers, CSRF,
rate-limit, IP allow/deny, benchmark suite), static files + SPA +
compression,
client-genTypeScript type derivation, hot reload. - Phase 8 (Codegen + Ship-Readiness): mostly shipped. Still open: Rust
client generation (
ultimo generate --target rust). - Phase 9 (Real-Time, Auth & Production Gaps): mostly shipped. Typed
extractors + validation, OIDC/JWKS, streaming responses, SSE transport,
request timeouts + HTTP graceful shutdown are done. Still open: OAuth2 and
observability (
request_id()shipped; OpenTelemetry + Prometheus remain). - Phase 10 (Distribution & DX): started. Middleware pack shipped
(request-id, cache-control/ETag, server-timing, basic-auth, trailing-slash).
Still open: Redis integration (sessions/cache/rate-limit), WebSocket
compression,
#[derive(UltimoType)]. - Phase 11 (Stabilization → 1.0): end-to-end typed errors, public-API audit + SemVer freeze, documentation completeness, production battle-testing.
- Post-1.0 / demand-driven: typed RPC subscriptions (derived SSE event types), S3-compatible storage, background tasks, Python client generation.
Documentation surfaces (MUST keep current, in the same PR as the change)
The ship-feature skill walks these — summary of the rules:
README.md— GitHub + crates.io landing page. Move shipped items "Coming Soon" → "Available Now"; keep install snippet (ultimo = "0.x") + badges accurate. Never advertise a shipped feature as coming-soon (or vice-versa).docs-site/docs/pages/api-reference.mdx— canonical public-API list. Update on any new/renamed/removedpubmethod/type, new middleware, newContext/Ultimo/Requestmethod, or changed Cargo feature.docs-site/docs/pages/<feature>.mdx+docs-site/vocs.config.tssidebar — any user-facing feature needs a docs-site page (Vocs → docs.ultimo.dev, auto-deploys on merge) AND a sidebar entry. Top-leveldocs/is internal notes only; writing there does NOT surface a feature to users.examples/— if usable from a frontend (routes/RPC, cookies, sessions, WebSocket, auth, SSE, file upload…), create/update a runnable example demonstrating it from a client and add it to workspacemembersso CI builds it. Pattern:examples/session-auth(Rust backend serving HTML+JS,cargo run -p <example>). Same PR — don't defer.- Keep
docs-site/docs/pages/roadmap.mdxhonest — when a feature ships, flip its Feature Status row to✅ Availablewith the real shipped version, and check off (or move to shipped) its bullet in the matching phase section. Phase names (### Phase N: ...) are thematic, not version numbers — don't reintroduce version-shaped headings.
Conventions
- Published crates — see the PUBLISHED CRATES section before changing public code, features, MSRV, or dep floors.
- Use Makefile targets, not raw cargo, where one exists.
- Breaking public-API changes must be deliberate and version-bumped;
cargo-semver-checksruns in CI — respect its verdict. - Run
cargo fmt --allandcargo clippybefore committing (.githooks/may enforce this).
Website SEO conventions (website/)
Every new page on ultimo.dev MUST include:
- Canonical URL —
alternates: { canonical: "https://ultimo.dev/<path>" }in the page'smetadataexport. - Structured data (JSON-LD) — pick the appropriate schema:
- Blog post →
BlogPosting+BreadcrumbList(already automatic viaapp/blog/[slug]/page.tsx) - Tutorial/guide → add
HowTowith steps - Feature/product page →
FAQPageif it has Q&A, otherwiseWebPage - Landing page →
Organization/SoftwareApplication(already in root layout)
- Blog post →
- Open Graph + Twitter meta — title, description,
og:type, image. Use themetadataexport. - Internal links — link to at least 2-3 other pages on the site from new content.
- External links — link to authoritative sources (docs.rs, rust-lang.org, tokio.rs, etc.) where relevant.
- Sitemap entry — new routes are auto-included via
app/sitemap.ts; blog posts viagetAllPosts(). Static pages must be added manually to the sitemap array.
Validate schemas after deploy: https://search.google.com/test/rich-results
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

