Auth, admin UI, jobs, search, multi-tenancy — 28 things you'd otherwise wire by hand, already there and already following one architecture. You write the part that's actually your product.
This is the whole thing, unedited: new project, one sentence describing a module, schema and tests on screen, migrated, running. If you've ever spent day one of a project on setup, this is day one with Wagoe — every line below is what the CLI printed.
$ wagoe new my-app ✓ Project created: my-app/ $ cd my-app $ bb setup --database sqlite --ai-provider replicate ✓ Generated resources/conf/dev/config.edn $ bb quickstart [4/8] Scaffolding sample module [8/8] Verifying project structure ━━━ Quickstart Complete ━━━━━━━━━━━━━━━ $ bb scaffold ai "product module with name, price, stock" --yes ✓ Successfully generated module: product $ clojure -M:test --focus my_app.product.core.product-test 0 failures. $ bb scaffold integrate product $ bb migrate up $ bb repl user=> (go)
Recorded in one take against the released v1.0.0-beta-8 —
CI walks the same path on every commit.
You know the list: auth, roles, admin screens, background jobs, search, email, file storage, audit logs. Each one is a library here — built the same way, tested the same way, swappable with a config key. Pick the ones you need this sprint; add the rest when you need them.
No mockups. Define a user schema and this admin, this form and these validation errors exist — you didn't write any of them. Real output from the current beta.
Same missing JWT secret. Left: what Clojure tells you. Right: what you actually needed — where it broke in your code, the framework frames folded away, the one line that fixes it, and a (fix!) if you'd rather not type it.
Execution error (ExceptionInfo) at shop.checkout.core/issue-session-token (core.clj:6). JWT_SECRET not configured Full report at: /var/folders/cw/hbtpm.../clojure-5181972888.edn
━━━ BND-103: Missing JWT Secret ━━━━━━━━━━━━━ JWT_SECRET is not set but the user module is active. ── Your code ──────────────────────────────── shop.checkout.core/issue-session-token (core.clj:6) shop.main/sign-in (main.clj:8) shop.main/-main (main.clj:12) ── Framework (12 frames) ──────────────────── (expand with (explain *e :verbose)) Fix: export JWT_SECRET="your-secret-at-least-32-characters" Auto-fix: (fix!) — Generate and set dev JWT_SECRET Dashboard: http://localhost:9999/dashboard/errors Docs: bb guide error BND-103 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Start it with (go), then ask it what's running, which routes exist and what it wants you to do next. No restart, no digging through system maps, no "which component didn't come up this time."
user=> (status) ┌─ Wagoe Dev ─────────────────────────────────────────┐ │ System: running (43 components, 0 errors) │ │ Web: http://localhost:3000 │ │ Admin: http://localhost:3000/web/admin │ │ nREPL: port 7888 │ │ │ │ Modules: admin, ai, invite, payment, search (+... │ │ Guidance: full (set :guidance-level :minimal to... │ │ │ │ Try: (status) | (routes) | (modules) | (commands) │ └─────────────────────────────────────────────────────┘
Every team decides business logic goes here and I/O goes there. Six months later someone's in a hurry and it doesn't. Wagoe turns that agreement into a directory layout and a lint rule: core/ can't import I/O, and the build fails if it tries. Not a style guide — a check.
shell/ports.cljcore/Enforced by directory layout + clj-kondo rules · Every library follows the same structure
Each library is a standard deps.edn dependency. Already have Integrant, Reitit and next.jdbc in place? They stay. Add wagoe-user for auth this week and nothing else — that's a valid way in.
Four things you'll do in every project — generate a module, model a state machine, go from dev to prod, add auth and audit to a route — as they look in Wagoe.
# Natural language module generation $ bb scaffold ai "order module with customer, total, status" ✓ Schema defined schema.clj ✓ Validation rules core/validation.clj ✓ Persistence layer shell/persistence.clj ✓ HTTP routes shell/routes.clj ✓ Service layer shell/service.clj ✓ Tests generated test/core_test.clj Generated 6 files in libs/order/ All tests passing: 12/12
(ns wagoe.order.schema (:require [malli.core :as m])) (def Order [:map [:id :uuid] [:customer-id :uuid] [:total [:and :decimal [:> 0]]] [:status [:enum :pending :paid :shipped :delivered]] [:created-at :instant]])
(defworkflow order-workflow {:initial-state :pending :states #{:pending :paid :shipped :delivered :cancelled} :transitions [{:from :pending :to :paid :required-permissions [:finance]} {:from :paid :to :shipped :guard :payment-confirmed} {:from :shipped :to :delivered}]}) ; Pure core logic — no I/O, no mocks needed
The entire state machine lives in core/ — no I/O, no side effects. Test every transition without a database or mock.
Every state change is recorded with who, when, and from/to state. Query with plain SQL.
Role-based guards and custom guard functions. The workflow rejects invalid transitions at the transition layer.
;;; Development — zero setup, zero config {:wagoe/db-context {:adapter :sqlite :db "dev.db"} :wagoe/cache {:adapter :in-memory} :wagoe/observability {:log-adapter :stdout :err-adapter :no-op}} ;;; Production — one key change each. Zero code changes. {:wagoe/db-context {:adapter :postgresql :host #env DB_HOST :pool-size 10} :wagoe/cache {:adapter :redis :uri #env REDIS_URL} :wagoe/observability {:log-adapter :datadog :err-adapter :sentry :dsn #env SENTRY_DSN}}
;;; Cross-cutting concerns declared alongside the route. {:path "/api/admin/orders" :methods {:post {:handler 'handlers/create-order :interceptors ['auth/require-admin 'audit/log-action 'rate-limit/admin 'metrics/record-latency]}}} ;;; Service layer — one macro call, all telemetry automatic (defn create-order [this order-data] (service/execute :create-order {:order order-data} (fn [{:keys [params]}] (order-core/prepare (:order params))))) ; Automatically instruments: structured logs, metrics, ; Sentry breadcrumbs, PII redaction. No telemetry code needed.
Kit and Biff are toolkits: small by design, you wire everything. Wagoe makes those choices for you — FC/IS layout, admin UI, multi-tenancy, jobs, search, reports — and asks you to follow its conventions in return. If your team would rather spend that time on the product, this is for you. It's beta-8; Django and Rails have twenty years on it.
The same libraries and the same patterns, whether you're two people or two hundred — but what they solve depends on where you sit.
wagoe command.wagoe add payments.wagoe new already generated .env with a real JWT_SECRET.Star it, read the code, or ask on Clojurians Slack. It's beta — your first project will find things we haven't, and we'd rather hear it now.