Hyperion

A shared Rust/Axum backend monolith powering several product apps over one Postgres and four API contract documents.

Hardening · 6 of 6 shippedRoadmap ↓

rusttokioaxumpostgres

Capabilities

  • Auth, sessions, role-based authorization, email, user identity, and AI integrations are written once and shared across every app, not copied per app. They are not fully decoupled: the AI layer depends directly on user identity, and user identity depends directly on email.

  • Spin up a new product app from `minimal-app` with auth, session, DB pool, email, and AI already wired - day one is domain logic, not plumbing.

  • Domain crate boundaries are enforced by the compiler, not by convention: a crate can only reach what it declares as a dependency, and Cargo rejects a dependency cycle outright, so an architecture-violating import fails the build.

Roadmap

  1. Shipped: Single Cargo workspace with one deployable per app
  2. Shipped: Shared auth, session, DB-pool, and role-based authorization base crates
  3. Shipped: minimal-app: a startup app with the base crates already wired
  4. Shipped: AI integrations in active use across the app crates
  5. Shipped: Compiler-enforced domain crate boundaries
  6. Shipped: Versioned, browsable crate and architecture docs

Hardening · 6 of 6 shipped

After Hardening

  1. Planned: Domain-based generic rate limiting
  2. Planned: Ship the subscription crate to a real consumer
  3. Planned: General production-readiness hardening
  4. Planned: Contract-first shift: OpenAPI spec to code, not code to spec
  5. Planned: Deep-dive write-up of the monorepo migration

Hyperion started as a set of Rust microservices - a deliberate exercise in distributed system design, not a product.

That shape was fine for learning. It let me experiment with observability, tracing, and the other quirks of a distributed ecosystem. But the operational cost (Kafka wiring, per-service deploys, duplicated auth/DB-pool code) wasn't worth it once the goal changed to something real and maintainable long-term.

So I moved everything into one Cargo workspace: one deployable per app, and a shared base - auth, sessions, DB pool, email, AI - written once instead of copied per service. The boundaries between domain crates are enforced by the compiler rather than by convention: a crate can only reach what it declares as a dependency, and Cargo rejects a dependency cycle outright. An import that would violate the architecture fails the build.

Moving to a monorepo didn't mean giving up modularity - it's still the whole point. Domain crates stay decoupled from each other, so any of them could be pulled back out into its own service later if a real scaling need shows up.

The docs live under the same rule. A Next.js app renders the crate and architecture docs (through the same diagram pipeline used here) as one browsable book, versioned with the code it describes - so a crate rename breaks the doc build instead of quietly rotting in a wiki.

A deep-dive post on this is coming. Stay tuned.