{"id":"6a4fb7a1bb026db852baf4ce","title":"2026 07 09 Build 760","path":"how-to/committee/release-notes/2026-07-09-build-760","contentMarkdown":"# 09-Jul-2026 — Simpler site-hosting configuration behind the scenes [#304](https://github.com/nbarrett/ngx-ramblers/issues/304)\n\n## [build 760](https://github.com/nbarrett/ngx-ramblers/actions/runs/29027037678) — [commit 4c8294e](https://github.com/nbarrett/ngx-ramblers/commit/4c8294e9683dd95b9c9340f0beb281423693a060)\n\n_____\n\n### **fly**: collapse per-environment Fly tokens to a single apiKey ([#304](https://github.com/nbarrett/ngx-ramblers/issues/304))\n\nEach environment carried two Fly token fields: an app-scoped deploy apiKey and a separate org-scoped metricsToken, with the metrics code falling back through metricsToken || apiKey. That split was never a requirement - one org token per environment does everything (deploy, restart and read metrics).\n\nRemove metricsToken entirely and resolve a single flyio.apiKey:\n- FlyioConfig / FlyRuntimeConfig / FlySecureConfig drop metricsToken; createDefaultFlyioConfig and the FLY_METRICS_TOKEN env var go too.\n- fly-runtime-config resolves the one token (env -> encrypted FLY_CONFIG bundle -> staging environments-config fallback); the FLY_CONFIG bundle built at deploy carries only apiToken + appName.\n- fly-metrics uses the single token directly for Prometheus.\n- Per-env settings UI drops the \"Metrics Token\" field and relabels the remaining one \"API Token\", noting it must be org-scoped.\n\nIncidental fix: isIntegrationWorkerAvailable() previously required a metricsToken the worker never had, so it always returned false; it now checks apiToken && appName.\n\nThe staging config.environments doc has been consolidated to match: metricsToken removed from all 16 environments and, for the five that were split (ashford, canterbury, ekwg, medway, stag-walkers), the org token promoted metricsToken -> apiKey. Deployed apps keep working and adopt the single-token shape on their next normal deploy.\n\n### perf+fix(server+admin): stop area GeoJSON loads pinning 512MB machines, disable clear-all-forwards at zero ([#304](https://github.com/nbarrett/ngx-ramblers/issues/304), [#278](https://github.com/nbarrett/ngx-ramblers/issues/278))\n\n## What this fixes\n\nVisiting System Settings for anything at all (staging-lite's case: adding OS maps API keys) instantiated every tab component on page load, so the area-map sync component's ngOnInit fired its preview-districts call, which parsed the 13.9MB bundled areas.geojson into ~170MB of cached heap (raw copy + full-resolution WGS84 district index) for 10 idle minutes. On a 512MB machine that pushed memory to the cap during an unrelated email session. Sites that never use area maps paid the full cost on every visit.\n\n## What you'll see\n\n- Opening System Settings no longer loads any area GeoJSON; the MAPS tab component is created only when that tab is first opened, and stays alive afterwards so switching tabs behaves exactly as before\n- On sites that do use area maps, opening the MAPS tab now settles at single-digit MB retained instead of ~170MB\n- Committee Settings: the Clear all forwards button is disabled when there are no live Cloudflare forwarding rules for the domain, so the confirmation panel can no longer offer to Delete 0 forwards\n\n## Technical notes\n\n- preview-districts only ever needed district names and match counts, so it now works entirely off the district index and never touches the raw GeoJSON cache\n- The district index is built from a transient direct file read (the raw parse is GC-eligible as soon as the index exists) and each boundary is simplified at 10m tolerance before the BNG-to-WGS84 transform, shrinking the retained index from ~85MB to single-digit MB while keeping point-in-polygon group-to-district matching accurate\n- featureCountForDistricts matches either the current LAD24NM name or the older LAD23NM name that group configs may store\n- The bundledGeoJson idle cache remains only for endpoints that serve raw geometry: the areas endpoint's no-S3-map fallback and uploadDefaultAreaMap","contentHtml":"<h1>09-Jul-2026 — Simpler site-hosting configuration behind the scenes <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/304\">#304</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/29027037678\">build 760</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/4c8294e9683dd95b9c9340f0beb281423693a060\">commit 4c8294e</a></h2>\n<hr>\n<h3><strong>fly</strong>: collapse per-environment Fly tokens to a single apiKey (<a href=\"https://github.com/nbarrett/ngx-ramblers/issues/304\">#304</a>)</h3>\n<p>Each environment carried two Fly token fields: an app-scoped deploy apiKey and a separate org-scoped metricsToken, with the metrics code falling back through metricsToken || apiKey. That split was never a requirement - one org token per environment does everything (deploy, restart and read metrics).</p>\n<p>Remove metricsToken entirely and resolve a single flyio.apiKey:</p>\n<ul>\n<li>FlyioConfig / FlyRuntimeConfig / FlySecureConfig drop metricsToken; createDefaultFlyioConfig and the FLY_METRICS_TOKEN env var go too.</li>\n<li>fly-runtime-config resolves the one token (env -&gt; encrypted FLY_CONFIG bundle -&gt; staging environments-config fallback); the FLY_CONFIG bundle built at deploy carries only apiToken + appName.</li>\n<li>fly-metrics uses the single token directly for Prometheus.</li>\n<li>Per-env settings UI drops the &quot;Metrics Token&quot; field and relabels the remaining one &quot;API Token&quot;, noting it must be org-scoped.</li>\n</ul>\n<p>Incidental fix: isIntegrationWorkerAvailable() previously required a metricsToken the worker never had, so it always returned false; it now checks apiToken &amp;&amp; appName.</p>\n<p>The staging config.environments doc has been consolidated to match: metricsToken removed from all 16 environments and, for the five that were split (ashford, canterbury, ekwg, medway, stag-walkers), the org token promoted metricsToken -&gt; apiKey. Deployed apps keep working and adopt the single-token shape on their next normal deploy.</p>\n<h3>perf+fix(server+admin): stop area GeoJSON loads pinning 512MB machines, disable clear-all-forwards at zero (<a href=\"https://github.com/nbarrett/ngx-ramblers/issues/304\">#304</a>, <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/278\">#278</a>)</h3>\n<h2>What this fixes</h2>\n<p>Visiting System Settings for anything at all (staging-lite&#39;s case: adding OS maps API keys) instantiated every tab component on page load, so the area-map sync component&#39;s ngOnInit fired its preview-districts call, which parsed the 13.9MB bundled areas.geojson into ~170MB of cached heap (raw copy + full-resolution WGS84 district index) for 10 idle minutes. On a 512MB machine that pushed memory to the cap during an unrelated email session. Sites that never use area maps paid the full cost on every visit.</p>\n<h2>What you&#39;ll see</h2>\n<ul>\n<li>Opening System Settings no longer loads any area GeoJSON; the MAPS tab component is created only when that tab is first opened, and stays alive afterwards so switching tabs behaves exactly as before</li>\n<li>On sites that do use area maps, opening the MAPS tab now settles at single-digit MB retained instead of ~170MB</li>\n<li>Committee Settings: the Clear all forwards button is disabled when there are no live Cloudflare forwarding rules for the domain, so the confirmation panel can no longer offer to Delete 0 forwards</li>\n</ul>\n<h2>Technical notes</h2>\n<ul>\n<li>preview-districts only ever needed district names and match counts, so it now works entirely off the district index and never touches the raw GeoJSON cache</li>\n<li>The district index is built from a transient direct file read (the raw parse is GC-eligible as soon as the index exists) and each boundary is simplified at 10m tolerance before the BNG-to-WGS84 transform, shrinking the retained index from ~85MB to single-digit MB while keeping point-in-polygon group-to-district matching accurate</li>\n<li>featureCountForDistricts matches either the current LAD24NM name or the older LAD23NM name that group configs may store</li>\n<li>The bundledGeoJson idle cache remains only for endpoints that serve raw geometry: the areas endpoint&#39;s no-S3-map fallback and uploadDefaultAreaMap</li>\n</ul>\n"}