← All posts

Five sub-sites in six months — when a student council utility becomes an ecosystem

5 sub-site ใน 6 เดือน — เมื่อ utility สโมสรเริ่มกลายเป็น ecosystem

— build-in-public, case-study, cuvetsmo, ecosystem, vet-tech, indie-builder, thailand

Six months ago, WebCUVETSMO was a 6-table Postgres schema with an approval workflow for student-council projects. Today, cuvetsmo.com is the anchor of a 5-vertical product family: an AI assistant, a DICOM viewer with AI overlays, a Web3 playground, an internship-experience archive, and a developer docs site. None of them were planned at Day 1. This is the story of how that happened — and why each one became a separate subdomain instead of a tab inside one app.

The starting point

I’m Anuthin “Palm” Danoi, a fourth-year vet student at Chulalongkorn University and VP for Planning & Quality of Student Life on the CUVETSMO board for 2026. WebCUVETSMO started as a single-product idea: a digital approval workflow for student-council projects, because the existing paper-based process was a mess of LINE chats, PDFs in Drive folders, and verbal sign-offs that didn’t survive board handover.

That first product shipped Phase 1 in early May: 6-table Postgres schema, RLS policies, 11 routes, a 16-state approval-status state machine, Chula email domain auth trigger, MD export for archival.

If the story ended there, this wouldn’t be a post.

Why it didn’t end there

Three weeks after Phase 1 shipped, three separate but related things happened:

  1. A vet-student friend asked me to recommend an LLM for clinical questions in Thai. She wanted citations, didn’t want a login, and didn’t want her queries logged.
  2. The DI Unit faculty advisor mentioned that students struggle with DICOM viewers — most tools require download, none have AI overlays for Norberg angle or VHS measurement.
  3. The CUVETSMO board started talking about Web3 — not as a product, but as a curriculum gap. Students wanted to “learn the thing without losing money on it.”

Three problems. Three different audiences. Three different solutions. None of which would naturally fit inside the approval-workflow app.

Within ten days I had three new subdomains live:

  • ai.cuvetsmo.com — free Thai vet AI assistant. No login. No retention. 250M papers indexed. Always cited.
  • imaging.cuvetsmo.com — in-browser DICOM viewer with AI overlays (Norberg, VHS, image occlusion).
  • web3.cuvetsmo.com — Web3 playground with four pillars: Learn, Play, Build, The Lab. Eleven testnet contracts shipped at $0 EVM cost via Pimlico + ERC-4337 sponsored deploy on Base Sepolia.

Then two more in the following weeks:

  • vettobe.cuvetsmo.com — internship-experience archive for the Vet to be program. Search past internships, review departments, plan new rotation rounds.
  • docs.cuvetsmo.com — developer docs + succession guide so Vet 87, 88, 89 maintainers don’t reinvent everything when the 2026 board hands over.

Six sites under one domain. None of them are tabs inside the anchor app.

Why subdomain-per-product instead of tab-per-feature

I made the subdomain choice deliberately. Three reasons:

1. Different stacks, different deploy cadences

The anchor app is React + Vite + Supabase + RLS. The AI assistant is Next.js with an LLM proxy and a citation pipeline. The DICOM viewer ships its own WASM. The Web3 playground bundles ethers and Pimlico SDK. The docs site is Astro + Starlight.

Forcing one stack would mean compromising at every layer. Subdomain-per-product lets each one optimize for its specific job.

2. Different threat models

The approval workflow needs strict auth (Chula email only, RLS-enforced row ownership, audit trails). The AI assistant intentionally has none — no login, no retention, that’s the value prop. The DICOM viewer needs to run entirely client-side because PHI never leaves the browser. The Web3 playground talks to public testnets.

Mixing those in one auth context would be either over-restrictive (bad UX) or under-restrictive (security risk). Subdomain isolation is the easiest answer.

3. Different audiences within the same broader community

The approval workflow is for the eight student-council departments. The AI assistant is for all 800+ Chula vet students. The DICOM viewer is for clinical-rotation students (Year 4-5). The Web3 playground is for curious students who want to play. The internship archive is for Year 5/6 students planning rotations.

Same university, same faculty, same broader community — but different jobs-to-be-done. Subdomain-per-audience makes that legible from the URL itself.

What an “ecosystem” actually means here

I’ve been using the word “ecosystem” but I want to be precise. There are three things going on:

Shared identity (the human) — every sub-site has me as primary maintainer. That’s not architectural, that’s just contingent. Eventually each should have its own maintainer (and docs.cuvetsmo.com exists specifically to make that handoff smoother).

Shared brand (the umbrella) — every sub-site uses the same logo, same cuvetsmo brand mark, same footer attribution. When you land on imaging.cuvetsmo.com, you should feel “this is CUVETSMO, that’s the student council I trust.” Visual consistency = trust transfer.

Shared infra (the Schema.org cross-link) — every sub-site declares the same Organization in its Schema.org publisher field and links back to cuvetsmo.com in the footer. Google sees one entity with six properties; not six unrelated sites.

The first one (shared identity) is a bug that will become a feature once succession lands. The other two are deliberate.

What I’d do differently

In hindsight:

  • Build docs.cuvetsmo.com FIRST, not last. The succession concern was visible from Phase 1; I just deferred it. Should have started writing onboarding docs from week one.
  • Pick a more constrained Web3 scope earlier. “Web3 playground” is too broad — it took three iterations to land on the four-pillar (Learn/Play/Build/The Lab) framing. A single “wallet-less testnet sandbox for student experiments” would have shipped 2 weeks faster.
  • Reuse the auth layer across ai and vettobe. They both eventually want logged-in features (saved chats, saved rotation plans). I duplicated the auth setup. One shared Supabase auth project would have been cleaner.

Where this is going

Three more subdomain ideas in the backlog, not committed yet:

  • data.cuvetsmo.com — open data portal for de-identified clinical and educational datasets the community uses for projects
  • events.cuvetsmo.com — event archive (Meet the Dean, SVF, mental health workshops) with photo galleries and post-event reports
  • learn.cuvetsmo.com — student-authored study notes, exam wrap-ups, and clinical pearls — adjacent to but distinct from VetMock (which is exam Q-bank-focused)

Each one is the same question: is this a separate vertical (subdomain), a tab inside an existing site (route), or not a product yet (no need to ship anything)?

The rule I’ve been using: if it has a distinct audience, a distinct interaction pattern, or a distinct trust boundary, it’s a subdomain. Otherwise it’s a tab.

So far that rule has held.


This is part of an ongoing build-in-public series. See also: Building My Online Identity in 7 Days. For the live ecosystem map, see the elsewhere page.