[Story] Newsletter signup (serverless) #15
No Reviewers
Labels
Clear labels
agent/analyst-drafted
agent/analyst-drafted
needs/human-decision
needs/human-decision
needs/security-review
needs/security-review
tier/t0
tier/t1
tier/t2
tier/t3
kind
bug
kind
bug
kind
epic
kind
epic
kind
initiative
EPPP programme initiative
kind
story
kind
story
kind
task
EPPP engineering card/task decomposed from a story
kind
toil
kind
toil
loop
1
loop
1
loop
2
loop
2
loop
3
loop
3
risk
agent-full
risk
agent-full
risk
human-gated
risk
human-gated
risk
human-only
risk
human-only
size
l
size
l
size
m
size
m
size
s
size
s
status
blocked
status
blocked
status
done
Workflow: Done
status
in-progress
status
in-progress
status
proposed
status
proposed
status
ready
status
ready
status
review
status
review
stream
checkout
stream
checkout
stream
onboarding
stream
onboarding
stream
platform
stream
platform
trivial — implementer only, auto-merge
standard — implementer + reviewer + tester
complex — security if triggered, human merge
critical — full chain + security, human merge
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Fabrika/PersonalBlog#15
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #14
Summary
Delivers issue #14 — a newsletter signup page (
newsletter.html) whose form POSTs the visitor's email to a configured https-only serverless endpoint. No credential is ever sent or shipped: the serverless function reads its API token from platform env/secrets at deploy time (SEC-14-R1 option (a), per the reworked acceptance criteria in the issue). No self-hosted email backend, subscriber list management, spam filtering, or captcha (all out of scope).This PR reworks the earlier loop-1 design (which carried a deploy-time token in client-served config) — the client now ships only the non-secret endpoint URL.
What changed
newsletter.html— new page: an email field and Subscribe button, a liverole="status"/aria-live="polite"region for the result message, nav marks itaria-current="page".js/newsletter-config.js— single config point:NEWSLETTER_ENDPOINTonly (non-secret). ShipsisHttpsUrl()/validateEndpoint()and asserts https-only at config load time, mirroring the protocol allowlist pattern injs/reading-list.js(tightened to https). No token export exists.js/newsletter.js— signup logic: POSTs{ email }as JSON with no Authorization header and no credential in the request; shows a confirmation on success; maps invalid email, non-2xx responses, redirects, and network failures to one fixed user-safe error — the server-held token, the endpoint, the status code, and any raw response body are never surfaced (status region usestextContent, never HTML).css/style.css— signup form and status (success/error) styles..gitea/workflows/ci.yml— newgitleaksjob (gitleaks detect --source . --no-git --redact) that fails on any secret hit.README.md— documents the endpoint-only config, server-side-only auth, residual signup-abuse risk (rate limiting / origin allowlist on the function), and the authoritative server-side validation follow-up.tests/newsletter.test.js— component + unit tests for the reworked criteria.Criterion → test mapping
tests/newsletter.test.js→ "newsletter page renders an email field and a submit button"; "the email field is required and the page loads the wiring module"; "every site page links to the newsletter page, and it marks itself current"js/reading-list.jsgitleaksjob fails on any hit{ email }field with a pinnedContent-Type: application/json.Out of scope (as specified in the issue)
Verification
npm teston the head commit (32c81d8): 53/53 green (node v22.23.2, Node built-in test runner).gitleaks detect --source . --no-git --redacton the head tree: no leaks found (exit 0) (verified locally with the same patterns the CI job runs)..gitea/workflows/ci.ymlrunsnpm testplus a gitleaks secret scan on pull requests and pushes tomain. This Gitea instance has no Actions runner (same situation as PRs #6, #9, and #11), so runs stay "Waiting to run" (pending) — there are no failing checks, and the suite is verified green locally.Rollback