Hyperion
A shared Rust/Axum backend monolith powering several product apps over one Postgres and four API contract documents.
Hardening · 6 of 6 shippedRoadmap ↓
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
- Shipped: Single Cargo workspace with one deployable per app
- Shipped: Shared auth, session, DB-pool, and role-based authorization base crates
- Shipped: minimal-app: a startup app with the base crates already wired
- Shipped: AI integrations in active use across the app crates
- Shipped: Compiler-enforced domain crate boundaries
- Shipped: Versioned, browsable crate and architecture docs
Hardening · 6 of 6 shipped
After Hardening
- Planned: Domain-based generic rate limiting
- Planned: Ship the subscription crate to a real consumer
- Planned: General production-readiness hardening
- Planned: Contract-first shift: OpenAPI spec to code, not code to spec
- 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.