Whitelabel orgs
Enterprise-tier clubs (and federations who want a branded surface) can host a
rebranded copy of the Scouting Network at /wl/<slug> — custom logo, accent
color, footer text, and optional custom domain (e.g. scouts.fcbarcelona.com).
Schema
Migration 0048_whitelabel.sql adds:
whitelabel_orgs— `id, owner_user_id, name, slug (unique), custom_domain
(unique), logo_url, accent_color (default #c79015), footer_text, active`.
whitelabel_members—(org_id, user_id)withrole ∈ {owner,admin,member}.
Pins and leads shown on /wl/<slug> are scoped to whitelabel_members.user_id
joined against network_pins.scout_id / network_leads.agent_user_id. The
public landing page does NOT show anything from non-members — even if those
users are also on the public Network. This makes the org a real silo.
API
| Method | Path | Role |
|--------|-------------------------------------------------------|-------------|
| GET | /api/network/whitelabel | any member |
| POST | /api/network/whitelabel | enterprise |
| GET | /api/network/whitelabel/[id] | any member |
| PATCH | /api/network/whitelabel/[id] | owner |
| DELETE | /api/network/whitelabel/[id] | owner |
| GET | /api/network/whitelabel/[id]/members | any member |
| POST | /api/network/whitelabel/[id]/members {email,role} | owner/admin |
| DELETE | /api/network/whitelabel/[id]/members?user_id=… | owner/admin |
| GET | /api/whitelabel/resolve?host=… | public |
POST/PATCH/DELETE write to admin_audit_log (action whitelabel.*).
POST /members looks up auth_users_view by email. The target user must
already have an IAMScouting account; we don't currently send invite emails.
Custom-domain DNS setup
For the pilot we only resolve the canonical path /wl/<slug> — the custom
domain field is stored but not yet wired to nginx auto-rewriting. To enable a
custom subdomain manually:
- Owner sets
custom_domainin the org settings UI (e.g.
scouts.fcbarcelona.com).
- Owner adds a DNS CNAME pointing
scouts.fcbarcelona.com→
iamscouting.com (or the canonical app host).
- Operator adds nginx server block on the VPS that maps that Host to a
rewrite of / → /wl/<slug>. Example snippet:
```nginx
server {
listen 443 ssl;
server_name scouts.fcbarcelona.com;
ssl_certificate /etc/letsencrypt/live/scouts.fcbarcelona.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/scouts.fcbarcelona.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000/wl/fcb-scouts$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host scouts.fcbarcelona.com;
}
}
```
- Operator issues a Let's Encrypt cert with `certbot --nginx -d
scouts.fcbarcelona.com`.
- Operator confirms by hitting
https://scouts.fcbarcelona.comand
verifying it loads the /wl/<slug> page with the org's branding.
The /api/whitelabel/resolve endpoint exists so a future edge / nginx Lua
rewriter can auto-derive <slug> from the Host header without operator
intervention. For now it's documentation + a forward-compat shim.
Middleware
/wl, /network/whitelabel, /api/network/whitelabel, and
/api/whitelabel/resolve are whitelisted in NETWORK_PUBLIC_PREFIXES so
non-admin users can reach them. /wl/<slug> is also added to
ONBOARDING_SKIP_PREFIXES so anon traffic doesn't loop through the legacy
onboarding redirect.
Tier gate
POST /api/network/whitelabel returns 403 with enterprise_tier_required
unless x-iams-tier === 'enterprise' (or the caller is admin). Existing
orgs continue to function if the owner's tier later drops; the gate is on
create only.