|
| 1 | +# Algolia Env Split Design |
| 2 | + |
| 3 | +Date: 2026-04-25 |
| 4 | + |
| 5 | +## Summary |
| 6 | + |
| 7 | +This change keeps the site on its current pre-rendered Cloudflare Pages setup and splits Algolia configuration into: |
| 8 | + |
| 9 | +- public build-time variables for browser search |
| 10 | +- private publish-only variables for search index uploads |
| 11 | + |
| 12 | +Fork PRs and any build without the public Algolia variables should still build successfully, but the UI must clearly show that search is unavailable. The search indexing script should continue to skip cleanly when its private publish variables are missing. |
| 13 | + |
| 14 | +## Goals |
| 15 | + |
| 16 | +- Keep the existing static React Router + Cloudflare Pages architecture. |
| 17 | +- Do not adopt `@react-router/cloudflare`. |
| 18 | +- Make browser search depend only on `VITE_` variables that are safe to bundle. |
| 19 | +- Keep Algolia admin credentials out of the browser bundle and out of fork PR builds. |
| 20 | +- Make preview and production index names deterministic via CI-computed `dev_` and `prod_` prefixes. |
| 21 | +- Make disabled search obvious in the UI and in local/build logs. |
| 22 | + |
| 23 | +## Non-Goals |
| 24 | + |
| 25 | +- Changing the site from prerendered Pages to a Cloudflare Workers runtime app. |
| 26 | +- Solving team-wide local secret distribution in this PR. |
| 27 | +- Enabling Algolia-backed search for fork preview builds. |
| 28 | +- Introducing GitHub Environments. This design uses repo-level variables and secrets only. |
| 29 | + |
| 30 | +## Current Constraints |
| 31 | + |
| 32 | +- The site is prerendered with `ssr: false` and uploaded to Cloudflare Pages as static output. |
| 33 | +- Frontend values must be present at build time because Vite bakes `VITE_*` values into the client bundle. |
| 34 | +- The deploy workflow builds in GitHub Actions before uploading to Cloudflare Pages, so Cloudflare dashboard vars are not the primary source for these build-time values. |
| 35 | +- Fork PR workflows must not receive Algolia secrets or public search variables. |
| 36 | + |
| 37 | +## Environment Contract |
| 38 | + |
| 39 | +### GitHub repo variables and secrets |
| 40 | + |
| 41 | +Public build-time values: |
| 42 | + |
| 43 | +- `ALGOLIA_APP_ID` |
| 44 | +- `ALGOLIA_INDEX_BASENAME` |
| 45 | +- `ALGOLIA_SEARCH_API_KEY_DEV` |
| 46 | +- `ALGOLIA_SEARCH_API_KEY_PROD` |
| 47 | + |
| 48 | +Private publish-only secrets: |
| 49 | + |
| 50 | +- `ALGOLIA_ADMIN_API_KEY_DEV` |
| 51 | +- `ALGOLIA_ADMIN_API_KEY_PROD` |
| 52 | + |
| 53 | +### Computed values in CI |
| 54 | + |
| 55 | +The workflow computes the full index name from the base name: |
| 56 | + |
| 57 | +- pull request builds with Algolia config: `dev_${ALGOLIA_INDEX_BASENAME}` |
| 58 | +- preview / non-fork PR deploys: `dev_${ALGOLIA_INDEX_BASENAME}` |
| 59 | +- production deploys from `master`: `prod_${ALGOLIA_INDEX_BASENAME}` |
| 60 | + |
| 61 | +### Variables exposed to the browser build |
| 62 | + |
| 63 | +The build step exports: |
| 64 | + |
| 65 | +- `VITE_ALGOLIA_APP_ID` |
| 66 | +- `VITE_ALGOLIA_INDEX_NAME` |
| 67 | +- `VITE_ALGOLIA_SEARCH_API_KEY` |
| 68 | + |
| 69 | +These are the only Algolia values used by the frontend. |
| 70 | + |
| 71 | +### Variables used by the indexing script |
| 72 | + |
| 73 | +The indexing script receives: |
| 74 | + |
| 75 | +- `ALGOLIA_APP_ID` |
| 76 | +- `ALGOLIA_INDEX_NAME` |
| 77 | +- `ALGOLIA_ADMIN_API_KEY` |
| 78 | + |
| 79 | +These values remain private and are never read from `import.meta.env`. |
| 80 | + |
| 81 | +## Workflow Behavior |
| 82 | + |
| 83 | +### `deploy.yml` |
| 84 | + |
| 85 | +- `pull_request` for non-fork branches uses the `DEV` public key and `DEV` admin key. |
| 86 | +- `pull_request` builds target the Algolia `dev_` index prefix, never the `prod_` index prefix. |
| 87 | +- `push` to `master` uses the `PROD` public key and `PROD` admin key. |
| 88 | +- `workflow_dispatch` keeps the existing `environment` input and maps it to either `DEV` or `PROD`. |
| 89 | +- The workflow computes the index name with the matching prefix before running the build. |
| 90 | +- The workflow exports both the public `VITE_*` variables and the private indexing variables for the build pipeline. |
| 91 | + |
| 92 | +### `deploy-fork-preview.yml` |
| 93 | + |
| 94 | +- Do not inject any Algolia variables or secrets. |
| 95 | +- The build must still succeed. |
| 96 | +- Search should render in its disabled state for these builds. |
| 97 | +- The indexing step should skip because its private variables are absent. |
| 98 | +- Even for pull requests, fork preview builds do not use the Algolia dev index because they receive no Algolia configuration. |
| 99 | + |
| 100 | +### `pull-request.yml` |
| 101 | + |
| 102 | +- Do not inject Algolia variables by default. |
| 103 | +- Validation should continue to pass with search disabled. |
| 104 | +- Only revisit this if a test later requires Algolia-backed search specifically. |
| 105 | + |
| 106 | +## Application Behavior |
| 107 | + |
| 108 | +### Frontend search availability |
| 109 | + |
| 110 | +Search is enabled only when all of these values are present and non-empty: |
| 111 | + |
| 112 | +- `VITE_ALGOLIA_APP_ID` |
| 113 | +- `VITE_ALGOLIA_INDEX_NAME` |
| 114 | +- `VITE_ALGOLIA_SEARCH_API_KEY` |
| 115 | + |
| 116 | +If any one is missing, the app treats search as unavailable. |
| 117 | + |
| 118 | +### Disabled UI |
| 119 | + |
| 120 | +When search is unavailable: |
| 121 | + |
| 122 | +- Do not mount the Algolia DocSearch modal. |
| 123 | +- Do not wire the search trigger to open the modal. |
| 124 | +- Render a visibly disabled search affordance instead of a normal active trigger. |
| 125 | +- Include explicit text such as `Search unavailable`. |
| 126 | +- Include accessible labeling that explains search is disabled for this build. |
| 127 | + |
| 128 | +The result should be obvious to users rather than silently removing the feature or showing a broken interaction. |
| 129 | + |
| 130 | +### Logging |
| 131 | + |
| 132 | +When the public browser variables are incomplete: |
| 133 | + |
| 134 | +- log a clear message during local development and build startup |
| 135 | +- include the missing variable names in the message |
| 136 | + |
| 137 | +Example shape: |
| 138 | + |
| 139 | +`Algolia search disabled: missing VITE_ALGOLIA_APP_ID, VITE_ALGOLIA_INDEX_NAME` |
| 140 | + |
| 141 | +When the indexing variables are incomplete: |
| 142 | + |
| 143 | +- keep the current graceful skip behavior |
| 144 | +- log which private variable is missing |
| 145 | + |
| 146 | +## Local Development |
| 147 | + |
| 148 | +- Remove tracked Algolia values from `.env`. |
| 149 | +- Developers can opt into local search with untracked local environment files such as `.env.local`. |
| 150 | +- If local Algolia values are absent, the site should still run normally with disabled search UI and a console warning. |
| 151 | + |
| 152 | +This keeps local development usable without requiring every contributor to have Algolia credentials. |
| 153 | + |
| 154 | +## Naming Decision |
| 155 | + |
| 156 | +This design uses one Algolia application and separate indices for preview and production: |
| 157 | + |
| 158 | +- `dev_<base>` |
| 159 | +- `prod_<base>` |
| 160 | + |
| 161 | +Index naming alone does not create a special Algolia environment. It only scopes records into separate indices. This is sufficient for this PR and avoids introducing a second Algolia application. |
| 162 | + |
| 163 | +## Verification Plan |
| 164 | + |
| 165 | +- Confirm non-fork preview deploys receive `dev_` index names and the `DEV` keys. |
| 166 | +- Confirm production deploys receive `prod_` index names and the `PROD` keys. |
| 167 | +- Confirm fork preview builds complete without Algolia configuration. |
| 168 | +- Confirm the UI renders the disabled search state when public vars are absent. |
| 169 | +- Confirm the build/dev logs clearly state why search is disabled. |
| 170 | +- Confirm the indexing script skips cleanly when its private variables are absent. |
| 171 | + |
| 172 | +## Open Questions |
| 173 | + |
| 174 | +None for this design. Team distribution of local development variables is intentionally deferred. |
0 commit comments