Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Build-time public environment variable for the contact form's reCAPTCHA
# v2 checkbox widget. Not secret — this is the public site key, safe to
# expose client-side. See docs/SECURITY.md.
PUBLIC_RECAPTCHA_SITE_KEY=
2 changes: 2 additions & 0 deletions .github/workflows/preview-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
cache: npm
- run: npm ci
- run: npm run build
env:
PUBLIC_RECAPTCHA_SITE_KEY: ${{ vars.PUBLIC_RECAPTCHA_SITE_KEY }}

# Uploads a new Worker version (not a full deploy — never touches the
# production/Active deployment) with a preview alias tied to this PR
Expand Down
21 changes: 9 additions & 12 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,12 @@ relying on a filter to exclude private rows.
at the point of choosing it, per the project conventions document, with a note to
re-validate the choice when work in that area actually begins.

## Open Items

* The contact form's KV namespace (`RATE_LIMIT`) and reCAPTCHA site/secret
key pair are not yet provisioned. Both require manual setup outside this
repository (Cloudflare KV namespace creation; reCAPTCHA site registration
in Google's admin console) before the contact form is functional in
production. The `EMAIL` send binding and `CONTACT_SENDER`/
`CONTACT_RECIPIENT` secrets are configured in `wrangler.toml`, pointing at
an already-verified Email Routing destination address, so no further
manual provisioning is needed for email delivery itself. The code is
written against these as named bindings/secrets (see `wrangler.toml`,
`src/env.d.ts`) and will fail clearly, not silently, if they are unset.
## Provisioning Status

* The contact form's `RATE_LIMIT` and `SESSION` KV namespaces, the
`CONTACT_SENDER`/`CONTACT_RECIPIENT`/`RECAPTCHA_SECRET` Worker secrets,
and the reCAPTCHA site/secret key pair (registered in Google's admin
console) are all provisioned in production as of 2026-08-13. The `EMAIL`
send binding and `CONTACT_SENDER`/`CONTACT_RECIPIENT` secrets point at an
already-verified Email Routing destination address. The contact form is
fully functional in production.
11 changes: 4 additions & 7 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,10 @@ destination_address = "blindtechmage+jwauthier@gmail.com"
# database_name = "blindtechmage-resources"
# database_id = "REPLACE_WITH_REAL_ID"

# Contact form rate limiting. Requires a real KV namespace, created via
# `wrangler kv namespace create RATE_LIMIT` before this is active — not yet
# provisioned.
#
# [[kv_namespaces]]
# binding = "RATE_LIMIT"
# id = "REPLACE_WITH_REAL_ID"
# Contact form rate limiting.
[[kv_namespaces]]
binding = "RATE_LIMIT"
id = "da26bfe7fe9a4154a77e4e290ada305e"

# Contact form secrets (CONTACT_SENDER, CONTACT_RECIPIENT, RECAPTCHA_SECRET)
# are set via `wrangler secret put <NAME>`, never committed here. Neither
Expand Down
Loading