The Haunted SaaS Skeleton 👻

Inspiration

Every developer has a recurring nightmare: The "Blank Repo."

We wanted to build something for the Skeleton Crew category, but we didn't just want a simple template. We were inspired by the "horror" of modern SaaS development—the terrifying amount of boilerplate required just to get to the "Hello World" of a sellable product. Authentication, billing, feature flags, real-time sockets, AI integration... implementing these from scratch is a graveyard of dead side projects.

Our inspiration was to play Dr. Frankenstein. We wanted to stitch together a "chimera" of the industry's most robust technologies—Go, gRPC, GraphQL, Next.js—and breathe life into them using Kiro. We envisioned a system where the architecture is the "Skeleton," the feature flags are the "Ghosts" (appearing and disappearing on demand), and the AI is the "Medium" speaking to the users.

What it does

Haunted SaaS is a production-ready, microservices-based "Skeleton" that summons a complete startup infrastructure from the void. It provides the six critical organs of a modern platform out of the box:

  1. The Gatekeeper: Full Role-Based Access Control (RBAC) and JWT Authentication.
  2. The Treasurer: A robust Billing Service with Stripe integration (Subscriptions & Webhooks).
  3. The Brain: An AI Gateway that treats prompts as version-controlled code.
  4. The Nervous System: Real-time bi-directional notifications using Socket.IO.
  5. The Shapeshifter: A Feature Flag service to control rollouts without code changes.
  6. The Memory: An abstract Analytics layer to track every user move.

It solves the "N+1" problem of starting a business: it gives you the sophisticated platform on Day 1, so you can focus entirely on your unique product logic.

How we built it

We utilized Kiro not just as a code generator, but as an Architectural Orchestrator. We strictly followed a Spec-Driven Development methodology:

  1. The Seance (Steering): We created a /.kiro/steering.md file to set the laws of our universe: Monorepo structure, strictly typed Go (Golang) backends, and gRPC for internal communication.
  2. The Incantation (Specs): We wrote detailed contracts for our six microservices in /.kiro/specs/. This allowed us to define the "Soul" of the application before the "Body" existed.
  3. The Summoning (Vibe Coding): We commanded Kiro to implement the specs. Kiro read our requirements and generated complex logic—including GORM models, bcrypt hashing, and thread-safe mutexes—with near-perfect accuracy.
  4. The Interface (GraphQL): We stitched the microservices together into a single GraphQL API Gateway using gqlgen, solving the "N+1" problem with Dataloaders.

We maximized performance using this formula: $$ \text{Performance} = \frac{\text{Cache Hits} \times \text{Async Processing}}{\text{Network Latency}} $$ We achieved this by caching Feature Flags in-memory (using the Unleash SDK) and batch-processing Analytics events in background goroutines.

Challenges we ran into

  • The Dependency Hell: Building a monorepo with six Go services led to build-time errors where go.sum files were out of sync inside Docker containers. We had to isolate build contexts in docker-compose.yml and perform rigorous go mod tidy clean-ups.
  • The Circular Logic: We faced a conceptual loop: How does the Billing service give you features if the Feature Flag service checks if you paid? We solved this with an event-driven flow where Billing emits a signal to User Auth, keeping the Feature Flag service a "dumb," fast proxy.
  • The "N+1" Nightmare: Stitching six services into one GraphQL graph risked massive latency. We had to implement the Dataloader pattern in our Gateway to batch gRPC calls.

Accomplishments that we're proud of

  • "Prompt-as-Code" Architecture: We built a hot-reloading engine for the llm-gateway-service. We can edit .yaml prompt files in the /prompts directory, and the running service detects the file change via fsnotify and updates its cache instantly without a restart.
  • Transactional Webhooks: Our Stripe webhook handler is idempotent and transactional. It creates an event record before processing, ensuring we never double-charge a customer or miss a provisioning event.
  • Type Safety from DB to UI: We achieved end-to-end type safety. Our Database schema generates our Go structs, which generate our GraphQL schema, which generates our TypeScript React Hooks.

What we learned

  • Specs are Spells: The more detailed the Spec in Kiro, the more powerful the code generation. "Vibe coding" works best when it has a rigid structure (Requirements/Design/Tasks) to cling to.
  • Abstractions Save Lives: Creating dedicated wrappers (like our StripeClient and UnleashClient) made testing and mocking infinitely easier than calling SDKs directly.
  • The "Flywheel" Effect: By integrating Analytics, AI, and Feature Flags from day one, the application felt "sophisticated" immediately. It wasn't just a CRUD app; it was a platform.

What's next for Haunted SaaS

  • The Frankenstein Demo: We plan to build two distinct "Costume Contest" applications on top of this skeleton to prove its versatility.
  • Open Sourcing the Spells: We want to release our .kiro/specs folder as a public template, allowing any developer to summon this stack in minutes.
  • Android Expansion: Since our API Gateway is platform-agnostic, we plan to extend the "Liveness Layer" (Socket.IO) to a native Android application using Jetpack Compose.

Built With

Share this project:

Updates