Adding a New Practice Website¶
Step-by-step guide for adding a new dental practice to the Helios platform.
Estimated time: 2-3 hours (infrastructure + configuration) + content creation time.
Phase B (2026-07-10): identity is the
code, domain is optional. A practice is created, previewed, and served by itsinternal_code— the production.frdomain is a cutover-time attribute, blank until go-live. You can stand up a new site and preview it at{code}.{env}.groupe-suffren.comwith no domain set at all, then fill the domain in at cutover.Phase C (2026-07-11): previews are DERIVED — onboarding a preview is ONE step. A preview host is
{code}.{env}.groupe-suffren.com(dotted env), derived by nginx (per-env regexserver_name) and by Helios (codeFromHost), and already covered by the per-env wildcard cert + wildcard DNS. Creating the AletheiaSiteConfigrow is the whole job — no DNS record, no nginx edit, no cert, no monitoring, no Helios code deploy. Sections 5 and 6 below apply only at.frcutover, not to stand up a preview.
Prerequisites¶
- Practice exists in Aletheia (Practice model with address, phone, hours, team)
- Practice internal code chosen (the identity key; e.g.,
cda,vsm,pds— URL-safe: letters/digits/dashes) - Production domain decided (optional until cutover; e.g.,
cabinet-dentaire-aubagne.fr) - Brand colors chosen (primary hue/chroma, accent hue/chroma)
1. Aletheia — Create SiteConfig¶
In Aletheia's /websites/config/:
- Create SiteConfig for the practice:
- Domain: leave blank until cutover (Phase B) — set the production domain (e.g.,
nouveau-cabinet.fr) only when the.frgoes live. The site is keyed by the practicecodemeanwhile. - City name: for SEO URL generation
- Primary hue/chroma + accent hue/chroma
- Enabled locales (default:
["fr"]) - Enabled services (e.g.,
["implantologie", "esthetique", "parodontologie"]) -
Meta title template + default description
-
Add core model data (if not already in Aletheia):
- Dentist photos (
photofield) - Dentist slugs (auto-generated)
- Dentist display_order on DentistContract
- Practice logo
- WhatsApp number (if applicable)
2. Aletheia — Create Pages & Content¶
In Aletheia's /websites/pages/:
Required pages (minimum for launch)¶
| Page | Template | Slug | Notes |
|---|---|---|---|
| Homepage | homepage |
accueil |
Hero + text + stats blocks minimum |
| Contact | contact |
contact |
Hero + contact_form block |
| Mentions légales | legal |
mentions-legales |
Legal text |
| Politique de confidentialité | legal |
politique-confidentialite |
Privacy policy |
Service pages (per enabled service)¶
| Page | Template | Slug pattern |
|---|---|---|
| Service hub (L1) | service_hub |
{service}-{ville}/ (e.g., implant-dentaire-aubagne) |
| Service detail (L2) | service_detail |
{service}-{ville}/{treatment}/ |
Recommended pages¶
| Page | Template | Slug |
|---|---|---|
| Notre philosophie | cabinet |
cabinet/notre-philosophie |
| Nos technologies | cabinet |
cabinet/nos-technologies |
| Tarifs | cabinet |
cabinet/tarifs |
| Accès & informations | cabinet |
cabinet/acces-informations |
| Résultats | results |
resultats |
"Votre Besoin" pages¶
Create PatientNeed entries and corresponding pages with M2M links to service pages.
3. Aletheia — Create Navigation¶
The navigation tree is auto-generated from:
- ServiceCategory entries (for "Nos Soins" dropdown)
- PatientNeed entries (for "Votre Besoin" dropdown)
- Published pages with specific templates (for "Le Cabinet" dropdown)
Verify navigation at: GET /api/v1/websites/sites/{code}/nav/
4. Helios — Add Domain Mapping¶
src/lib/practices.ts — Single source of truth¶
Add one entry to the PRACTICES array:
// `domain` is optional (Phase B) — omit it until cutover; `{ code: "nvc" }` alone
// is enough to preview at nvc[-staging|-dev].groupe-suffren.com.
export const PRACTICES: PracticeConfig[] = [
// ...existing...
{ code: "nvc", domain: "nouveau-cabinet.fr" },
];
This automatically registers the practice in src/proxy.ts (Host → /_sites/[practice]/… rewrite keyed on the code + redirects), the [practice] route segment (generateStaticParams prerenders the tenant once its Aletheia API is live), allowedDevOrigins (next.config.ts), and the /api/contact proxy (src/app/api/contact/route.ts), which only forwards submissions for a code present in PRACTICES. All hostname variants (nvc.groupe-suffren.com, nvc-staging, nvc-dev, nvc.local, and www.nouveau-cabinet.fr once a domain is set) are derived from the code (and domain, when present).
Unknown hosts 404. A Host header not in the derived map gets a plain 404 from the proxy (Sentry-alerted in prod). There is no default-tenant fallback — a misconfigured domain fails loud instead of serving Aubagne's content.
Note: the contact form POSTs same-origin to
/api/contact, which forwards to Aletheia server-side — there is no CORS config to set on Aletheia and no per-practice browser-facing URL. Adding the entry here is all that's needed for the form to work.
Indexing gate — no per-practice code change¶
There is no per-practice domain whitelist. Indexing is gated by
shouldIndexSite() in src/lib/seo.ts (consumed by both
src/app/[practice]/robots.txt/route.ts and the <meta name="robots"> in
src/app/[practice]/layout.tsx):
- Environment: only a deployment with
ENVIRONMENT=prodallows indexing — staging/dev/local always emitnoindex+ a blocking robots.txt. - Per practice: go-live is controlled in Aletheia — flip
SiteConfig.is_activefor the practice. While inactive, its API endpoints 404 and Helios never serves the site at all.
5. Infrastructure — DNS (cutover only — previews need none)¶
OVH DNS (groupe-suffren.com zone)¶
No action for a preview (Phase C): the per-env wildcards
*.prod. / *.staging. / *.dev.groupe-suffren.com already resolve every
{code}.{env} host to the VPS. This section is only the production .fr
registrar step at cutover (below).
Production domain DNS (at domain registrar)¶
When ready to go live:
| Type | Name | Value |
|---|---|---|
| A | @ |
54.36.99.184 |
| CNAME | www |
nouveau-cabinet.fr |
6. Infrastructure — Nginx + cert (cutover only — previews need none)¶
No action for a preview (Phase C): the prod/staging/dev vhosts already accept
any {code}.{env}.groupe-suffren.com via their per-env regex server_name, and the
per-env wildcard cert already covers it. Nothing below is touched to stand up a
preview.
At .fr cutover — add the real domain only¶
In Aether nginx/conf.d/ (/opt/docker/aether/repo/), edit helios-prod.conf.full:
add nouveau-cabinet.fr (+ www.) to the prod server_name and to the
$helios_preview_realm map as off (so the public domain isn't password-gated).
Do not add any *.groupe-suffren.com preview host — those are derived.
Then mint the real domain's cert — a separate per-domain HTTP-01 cert, NOT the
helios wildcard (do not pass --cert-name helios — it would overwrite the wildcard):
docker exec certbot certbot certonly --webroot -w /var/www/certbot \
--non-interactive --agree-tos --email admin@groupe-suffren.com \
-d nouveau-cabinet.fr -d www.nouveau-cabinet.fr
7. Deploy¶
# Commit Helios changes
cd ~/code/helios
git add -A && git commit -m "Add [practice name] practice" && git push
# Deploy to server
# Dev first:
ssh server "cd /opt/docker/helios/repo && git pull && ./deploy.sh dev"
# Then staging, then prod
8. Verify¶
Pre-launch checklist¶
- [ ] Homepage loads at
https://nvc-dev.groupe-suffren.com/ - [ ] All nav links work (no 404s)
- [ ] Contact form submits successfully
- [ ] Team page shows correct practitioners
- [ ] Service pages have content
- [ ] Footer shows correct hours, address, phone
- [ ] SEO: meta titles correct on all pages
- [ ] SEO:
robots.txtallows crawling on production domain - [ ] SEO:
sitemap.xmllists all published pages - [ ] Schema.org: test with Google Rich Results Test
- [ ] Mobile: nav works, bottom dock shows, responsive layout
- [ ] Performance: Lighthouse > 90
DNS cutover (go live)¶
- [ ] Point production domain DNS to server
- [ ] SSL cert covers production domain
- [ ] Add production domain to nginx
server_name - [ ] Set
SiteConfig.is_active = Truein Aletheia admin (per-practice go-live;ENVIRONMENT=prodin the env file is the deployment-wide indexing gate — seesrc/lib/seo.ts) - [ ] Verify
robots.txtallows crawling - [ ] Submit sitemap to Google Search Console
- [ ] Set up 301 redirects from old site URLs (if migrating)
- [ ] Monitor for 2 weeks post-launch
Quick Reference — Files to Edit¶
| File | What to add |
|---|---|
src/lib/practices.ts |
Practice entry (code, domain) — drives the proxy rewrite, static params, allowedDevOrigins, and the /api/contact allowlist |
| Aletheia admin | SiteConfig.is_active = True when going live (indexing itself is env-gated — ENVIRONMENT=prod, src/lib/seo.ts; no Helios code change) |
Aether nginx/conf.d/helios-*.conf.full |
server_name domains |