FamilyNexus Site Guide
Everything you need to know about how this landing page is built, configured and deployed.
Architecture
The landing page is a pure static HTML site hosted on Azure Static Web Apps (Free tier). There is no build step, no framework, no Node runtime — just index.html, staticwebapp.config.json, and optional assets.
familynexus-landing · Resource group: rg-familynexus · Region: West EuropeDefault URL:
black-plant-03dabae03.7.azurestaticapps.netCustom domains:
familynexus.co.uk · www.familynexus.co.uk
The two apps (Budget and Schedule) are separate Azure App Services running Node.js, each with their own custom subdomain:
| App | URL | Azure resource |
|---|---|---|
| Pocket Budget | budget.familynexus.co.uk | pocket-budget-v3-sutton |
| Pocket Schedule | scheduler.familynexus.co.uk | pocket-schedule-v2-sutton |
File structure
landing/
├── index.html ← Landing page (edit this to change content)
├── guide.html ← This page
├── admin.html ← Settings admin UI
├── settings.json ← Site settings (announcement, etc.)
├── favicon.svg ← Site icon (FN gradient logo)
└── staticwebapp.config.json ← Azure routing + security headers
.github/
└── workflows/
└── deploy-landing.yml ← GitHub Actions deploy workflow
To update the site content, edit index.html and either push to GitHub (auto-deploys) or use the SWA CLI directly.
staticwebapp.config.json
This file controls how Azure Static Web Apps handles routing, headers and error pages. It sits in the landing/ folder alongside index.html.
{
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["*.{css,js,png,gif,ico,jpg,svg,webp,woff,woff2,ttf}"]
},
"globalHeaders": { ... security headers ... },
"routes": [],
"responseOverrides": {
"404": { "rewrite": "/index.html", "statusCode": 200 }
}
}
Security headers
All applied via globalHeaders so they cover every response:
| Header | Purpose |
|---|---|
| Strict-Transport-Security | Forces HTTPS for 2 years, including subdomains. Prevents protocol downgrade attacks. |
| X-Content-Type-Options: nosniff | Stops browsers guessing MIME types. Prevents content-type sniffing attacks. |
| X-Frame-Options: DENY | Blocks the page being embedded in an iframe. Prevents clickjacking. |
| X-XSS-Protection | Legacy XSS filter for older browsers. |
| Referrer-Policy | Only sends referrer to same origin. Reduces information leakage to third parties. |
| Permissions-Policy | Disables camera, microphone, geolocation and interest-cohort (FLoC) APIs. |
| Content-Security-Policy | Restricts what resources the page can load. Inline styles allowed; external connect blocked. |
| cache-control | 5-minute cache, 1-hour stale-while-revalidate. Balances freshness vs CDN performance. |
Routing notes
The routes array is intentionally empty. Previously a /* wildcard route was here for cache headers, but it caused a validation conflict with the navigationFallback.exclude patterns (Azure treats exclude entries as implied routes). Cache headers are now in globalHeaders instead.
Important: if you add routes, put more specific routes before any wildcard (/*) otherwise they are unreachable and deployment fails.
settings.json
A lightweight JSON file the landing page fetches on load. Used to control the announcement banner without redeploying the HTML. Edit via the Admin UI or directly in the file.
{
"announcement": "",
"announcementStyle": "info"
}
| Field | Values | Effect |
|---|---|---|
| announcement | Any string, or empty | If non-empty, shows a banner bar at the top of the page |
| announcementStyle | info · success · warning | Controls banner colour (blue · green · amber) |
Deploying with SWA CLI
The fastest way to push changes without going through GitHub Actions.
# One-off deploy (uses the deployment token)
npx @azure/static-web-apps-cli deploy ./landing \
--deployment-token "$(az staticwebapp secrets list \
--name familynexus-landing \
--resource-group rg-familynexus \
--query 'properties.apiKey' -o tsv)" \
--env production
To get the token separately and reuse it:
az staticwebapp secrets list \
--name familynexus-landing \
--resource-group rg-familynexus \
--query "properties.apiKey" -o tsv
The token is long-lived but can be rotated in the Azure portal under Static Web Apps → familynexus-landing → Manage deployment token. If you rotate it you must update the GitHub secret too.
GitHub Actions auto-deploy
Any push to main that touches a file inside landing/ triggers the deploy workflow automatically.
# Workflow file
.github/workflows/deploy-landing.yml
# Triggers on:
push → main → paths: landing/**
workflow_dispatch (manual trigger from Actions tab)
GitHub secret
The workflow authenticates using a secret stored in the repo:
| Secret name | Value |
|---|---|
| AZURE_STATIC_WEB_APPS_TOKEN | Deployment token from Azure (see above) |
To update it: gh secret set AZURE_STATIC_WEB_APPS_TOKEN --body "TOKEN" --repo AS-199-FamNexus/PocketBudget
Common failure causes
- "No matching static web app" — token is stale or wrong. Fetch a fresh one from Azure and update the secret.
- "Route covered by wildcard" — a specific route in
staticwebapp.config.jsonappears after a/*wildcard. Delete or move it before the wildcard. - Node.js deprecation warning — set
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: truein workflow env (already done).
DNS records (GoDaddy)
All DNS is managed at GoDaddy for familynexus.co.uk. Records needed:
| Type | Name | Points to | Purpose |
|---|---|---|---|
| ALIAS/CNAME | @ | black-plant-03dabae03.7.azurestaticapps.net | Apex → landing page |
| CNAME | www | black-plant-03dabae03.7.azurestaticapps.net | www → landing page |
| CNAME | budget | pocket-budget-v3-sutton.azurewebsites.net | budget subdomain → Budget app |
| CNAME | scheduler | pocket-schedule-v2-sutton.azurewebsites.net | scheduler subdomain → Schedule app |
| TXT | asuid.budget | (Azure verification ID) | Proves domain ownership for Budget |
| TXT | asuid.scheduler | (Azure verification ID) | Proves domain ownership for Schedule |
To get verification IDs: az webapp show --name pocket-budget-v3-sutton --resource-group rg-familynexus --query customDomainVerificationId -o tsv
SSL certificates
All certificates are free Azure-managed certificates — they auto-renew and require no action.
familynexus.co.ukandwww.familynexus.co.uk— managed by Azure Static Web Apps automatically when custom domains are bound.budget.familynexus.co.ukandscheduler.familynexus.co.uk— managed certificates via App Service, provisioned by Terraform after DNS propagates.
If a certificate shows as pending, check the DNS records are correct and wait up to 15 minutes for Azure to verify ownership.
Pocket Budget
pocket-budget-v3-suttonURL:
budget.familynexus.co.ukAdmin:
budget.familynexus.co.uk/adminData: JSON files in
DATA_DIR on the App Service filesystemPricing: configurable in Admin → Settings (served via
/api/config publicly)
The budget app's admin panel controls pricing, maintenance mode, registration, Stripe keys and announcements. Changes take effect immediately without redeploying.
Pocket Schedule
pocket-schedule-v2-suttonURL:
scheduler.familynexus.co.ukAdmin:
scheduler.familynexus.co.uk/adminData: JSON files in
DATA_DIR on the App Service filesystemPricing: configurable in Admin → Settings (served via
/api/config publicly)