The prior commit (4a506e1c) was assembled from a sandbox bash mount that
returned a truncated view of two files. Net effect:
- functions.php was cut at line 235, dropping the closing of
peptide_starter_inline_dark_mode_script() and its add_action
registration. wp_head dark-mode script was effectively removed.
- ARCHITECTURE.md was cut mid-paragraph and lost its Version section.
Both files are now rebuilt from main + minimum-delta patches:
- functions.php: +4 minimal additions (perf-asset-policy @see, version
bump, require_once, add_action). Dark-mode script preserved.
- ARCHITECTURE.md: +3 lines (perf-asset-policy in inc/ tree, version
banner bumped). Version section preserved.
- style.css, CHANGELOG.md, CONVENTIONS.md: also rebuilt from main +
targeted patches as a safety measure (no functional change vs the
prior commit, just guaranteed-untruncated).
Verified: every file ends with a trailing newline; functions.php contains
the dark-mode add_action; ARCHITECTURE.md contains the Version section.
Co-authored-by: Engineer Theme (in-session subagent) <engineer-theme@peptiderepo.users.noreply.github.com>
274 lines
15 KiB
Markdown
274 lines
15 KiB
Markdown
# Peptide Starter Theme — Architecture
|
|
|
|
> **Cross-app context:** decisions that affect multiple plugins (Cloudflare AI Gateway routing, OpenRouter account sharing, the interface pattern, image-generation stack, social distributor choice) are recorded in `Peptide Repo CTO/docs/engineering/decisions/`. The incident runbook for cross-app failure modes is at `Peptide Repo CTO/docs/engineering/INCIDENT-RUNBOOK.md`. Read both before making decisions that cross plugin boundaries.
|
|
|
|
## Overview
|
|
|
|
Peptide Starter (slug: `peptide-starter`) is a premium classic WordPress
|
|
theme for peptiderepo.com — a scientific peptide reference database. It
|
|
assembles a modern, accessible frontend from a monolithic stylesheet, a
|
|
handful of vanilla-JS modules, and a collection of PHP modules under
|
|
`inc/`. The theme integrates with six companion plugins through
|
|
shortcodes (graceful degradation via `shortcode_exists`), coordinates
|
|
dark mode via CSS custom properties + a `data-theme` attribute, and — as
|
|
of v1.5.1 — runs a self-contained abuse-control layer (rate limiting,
|
|
honeypots, email verification) over its frontend auth surface.
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
peptide-starter/
|
|
├── style.css # All theme CSS. Section 26 = v1.5.1 additions.
|
|
├── functions.php # Theme setup entry point (<250 lines).
|
|
│ # - Constants, feature module loading (order matters)
|
|
│ # - Theme support + nav menus
|
|
│ # - Script/style enqueue (conditional per template)
|
|
│ # - Newsletter signup POST handler
|
|
│ # - Inline dark mode flash preventer in wp_head
|
|
│
|
|
├── inc/
|
|
│ ├── config.php # [v1.5.1] Security config — single source of truth.
|
|
│ │ # Every threshold lives here; filterable.
|
|
│ ├── helpers.php # Nav walker + menu fallback, logo, getters,
|
|
│ │ # pagination, honeypot renderer, csv_safe,
|
|
│ │ # require_login gate, safe referer.
|
|
│ ├── cloudflare-ips.php # [v1.5.2] CF edge range snapshot +
|
|
│ │ # CIDR matcher (v4 and v6).
|
|
│ ├── rate-limiter.php # Peptide_Starter_Rate_Limiter +
|
|
│ │ # peptide_starter_get_client_ip (peer-validated,
|
|
│ │ # PSEC-007).
|
|
│ ├── email-verification.php # [v1.5.1] Token send + verify route + resend
|
|
│ │ # endpoint + user_is_verified().
|
|
│ ├── customizer.php # Customizer sections/settings/controls.
|
|
│ ├── auth-handlers.php # AJAX login + register — unified errors,
|
|
│ │ # honeypot, rate limit, no auto-login.
|
|
│ ├── contact-handler.php # AJAX contact — header-injection guard,
|
|
│ │ # honeypot, rate limit, request IDs in mail.
|
|
│ ├── newsletter-admin.php # Subscriber viewer + CSV-safe export +
|
|
│ │ # /newsletter-unsubscribe route.
|
|
│ ├── mail-diagnostic.php # [v1.5.1] Tools → Mail Test — wp_mail probe.
|
|
│ ├── perf-asset-policy.php # [v1.6.0] Mobile perf: dequeue + font slim
|
|
│ │ # + preconnect + defer.
|
|
│ └── page-setup.php # Auto-create pages on activation +
|
|
│ # v1.5.0 user migration (enrol into verify).
|
|
│
|
|
├── header.php # Site header, nav, icons, search overlay.
|
|
├── footer.php # Widget grid + copyright + settings panel.
|
|
├── front-page.php # Hero → module cards → plugin feeds.
|
|
│
|
|
├── page-calculator.php # OPEN — reconstitution calculator (no PII).
|
|
├── page-protocol-builder.php # GATED — require_login (builds saved protocols).
|
|
├── page-tracker.php # GATED — require_login (per-user tracking).
|
|
├── page-subject-log.php # GATED — require_login (user lab data).
|
|
├── page-directory.php # Peptide directory.
|
|
├── page-documentation.php # Two-column docs with ToC sidebar.
|
|
├── page-science-feed.php # Science feed + newsletter signup.
|
|
├── page-profile.php # Profile + verify-required banner + resend.
|
|
├── page-auth.php # Sign In / Register forms (honeypotted).
|
|
├── page.php, index.php, 404.php # Standard fallbacks.
|
|
├── single-peptide.php # Peptide CPT detail.
|
|
├── archive-peptide.php # Peptide CPT archive.
|
|
│
|
|
├── template-parts/
|
|
│ ├── content.php, content-*.php # Loop / CPT / empty state.
|
|
│ ├── module-cards.php # Front-page 6-card grid.
|
|
│ ├── newsletter-signup.php # [v1.5.1] Signup + consent + honeypot.
|
|
│ └── settings-panel.php # Slide-out support/contact panel + honeypot.
|
|
│
|
|
├── assets/js/
|
|
│ ├── navigation.js # Mobile menu + body-overflow save/restore.
|
|
│ ├── theme.js # Dark mode + search overlay + plugin API.
|
|
│ ├── documentation.js # ToC builder (text-slug IDs) + scroll spy.
|
|
│ ├── auth.js # Form toggle + AJAX + XSS-safe status.
|
|
│ └── settings-panel.js # Open/close + focus trap + AJAX contact.
|
|
│
|
|
├── tests/
|
|
│ ├── bootstrap.php # PHPUnit bootstrap.
|
|
│ ├── test-functions.php # Legacy helper tests.
|
|
│ ├── test-theme-setup.php # Theme setup tests.
|
|
│ ├── test-rate-limiter.php # [v1.5.1] Rate limiter lifecycle + IP resolver.
|
|
│ ├── test-auth-handlers.php # [v1.5.1] Login + register unified errors.
|
|
│ ├── test-email-verification.php# [v1.5.1] Token send/verify/reject.
|
|
│ ├── test-contact-handler.php # [v1.5.1] Header injection + rate limit + honeypot.
|
|
│ ├── test-newsletter.php # [v1.5.1] Signup + consent + CSV-safe.
|
|
│ └── test-auth-gate.php # [v1.5.1] require_login redirects.
|
|
│
|
|
├── .phpcs.xml.dist # PHPCS config (WordPress-Core).
|
|
├── phpunit.xml # PHPUnit config (theme suite).
|
|
├── composer.json # Dev-only dependencies.
|
|
├── CHANGELOG.md, CONVENTIONS.md, README.md, CLAUDE.md
|
|
└── screenshot.png
|
|
```
|
|
|
|
## Navigation Structure
|
|
|
|
```
|
|
Home | Tools ▾ | My Data ▾ | Resources ▾ | [Sign In / Username ▾] ⚙ 🌐 🔍 🌙
|
|
|
|
Tools: Calculator (open) · Protocol Builder · Tracker
|
|
My Data: Peptides · Subject Log
|
|
Resources: Documentation · Science Feed
|
|
User menu: My Profile · Tracker · Subject Log · Sign Out
|
|
```
|
|
|
|
## Page Template Map
|
|
|
|
| Page Title | Slug | Template | Gated | Shortcode |
|
|
|-------------------|------------------|-----------------------------|-----------|-------------------------------------|
|
|
| Calculator | calculator | page-calculator.php | open | [peptide_tools_calculator] |
|
|
| Protocol Builder | protocol-builder | page-protocol-builder.php | login+verify | [peptide_tools_protocol_builder] |
|
|
| Tracker | tracker | page-tracker.php | login+verify | [peptide_tracker] |
|
|
| Subject Log | subject-log | page-subject-log.php | login+verify | [peptide_tracker_subject_log] |
|
|
| Documentation | documentation | page-documentation.php | open | — |
|
|
| Peptide Directory | peptides | page-directory.php | open | [peptide_directory] |
|
|
| Science Feed | news | page-science-feed.php | open | [peptide_news] |
|
|
| Profile | profile | page-profile.php | login | [peptide_community_profile] |
|
|
| Sign In | auth | page-auth.php | open | (custom forms) |
|
|
|
|
Pages auto-created via `inc/page-setup.php` on theme activation.
|
|
"Gated" templates call `peptide_starter_require_login()` before `get_header()`.
|
|
|
|
## Data Flow
|
|
|
|
### Authentication (v1.5.1)
|
|
|
|
```
|
|
page-auth.php
|
|
├─ Sign In form → auth.js AJAX → inc/auth-handlers.php::peptide_starter_ajax_login
|
|
│ ├─ Nonce check
|
|
│ ├─ Honeypot check (fake success on trip)
|
|
│ ├─ Rate-limit check (login bucket, IP+email hash)
|
|
│ ├─ wp_signon (all failures → unified message)
|
|
│ └─ Success → reset limiter → redirect
|
|
│ (unverified users → /profile?verify_required=1)
|
|
│
|
|
└─ Register form → auth.js AJAX → inc/auth-handlers.php::peptide_starter_ajax_register
|
|
├─ Nonce check
|
|
├─ Honeypot check (fake success on trip)
|
|
├─ Rate-limit check (register bucket, IP only)
|
|
├─ Validation (all failures → unified message)
|
|
├─ wp_create_user
|
|
├─ peptide_starter_send_verification_email
|
|
│ ├─ Generate 43-char token
|
|
│ ├─ Set ps_pending_verification=1,
|
|
│ │ ps_verify_token, ps_verify_expires
|
|
│ └─ wp_mail with /verify?uid=&token= link
|
|
└─ Respond with "check your inbox" (no redirect, no auto-login)
|
|
|
|
/verify?uid=&token=
|
|
→ inc/email-verification.php::peptide_starter_handle_verify_request
|
|
├─ hash_equals() token match + TTL check
|
|
├─ Clear verify meta on success
|
|
├─ wp_set_auth_cookie
|
|
└─ Redirect /profile?verified=1 (failure → /auth?verify_error=1)
|
|
```
|
|
|
|
### Abuse controls (v1.5.1)
|
|
|
|
```
|
|
Every public form submit:
|
|
1. Nonce verify → 403-equivalent security error if missing/invalid
|
|
2. Honeypot check → Fake-success drop on non-empty hidden field
|
|
3. Peptide_Starter_Rate_Limiter → Silent generic error on budget exhaustion
|
|
4. Business logic → Unified error on any validation failure
|
|
|
|
Rate-limit state:
|
|
Storage: wp_options transients, key ps_rl_{action}_{hash}
|
|
Hash: substr( wp_hash( ip . '|' . identifier ), 0, 16 )
|
|
Never stores raw IPs or identifiers.
|
|
```
|
|
|
|
### Front page assembly
|
|
|
|
```
|
|
front-page.php
|
|
├─ Hero (title, subtitle, search CTA)
|
|
│ └─ [peptide_search] (Peptide Search AI)
|
|
├─ Research Modules grid (template-parts/module-cards.php)
|
|
├─ [prautoblogger_posts] (PRAutoBlogger)
|
|
└─ [peptide_news] (Peptide News)
|
|
```
|
|
|
|
### Dark mode coordination
|
|
|
|
```
|
|
wp_head inline script sets html[data-theme] from localStorage → system pref → customizer default
|
|
CSS :root vs html[data-theme="dark"] colour tokens swap
|
|
theme.js toggle writes localStorage, dispatches 'themechange' DOM event
|
|
```
|
|
|
|
## External Integrations
|
|
|
|
Shortcodes (plugins), all wrapped in `shortcode_exists()`:
|
|
|
|
1. **Peptide Search AI** (`[peptide_search]`) — `.psa-*`
|
|
2. **Peptide News** (`[peptide_news]`) — `.pn-*`
|
|
3. **PRAutoBlogger** (`[prautoblogger_posts]`) — `.prab-*`
|
|
4. **Peptide Tools** (`[peptide_tools_calculator]`, `[peptide_tools_protocol_builder]`)
|
|
5. **Peptide Tracker** (`[peptide_tracker]`, `[peptide_tracker_subject_log]`)
|
|
6. **Peptide Community** (`[peptide_community_profile]`, `[peptide_directory]`)
|
|
|
|
## Key Architectural Decisions
|
|
|
|
1. **Classic theme (not FSE)** — stability, plugin compatibility.
|
|
2. **Monolithic CSS** — no build step, CSS custom properties for tokens.
|
|
3. **Vanilla JavaScript** — no bundler; `node --check` in CI.
|
|
4. **Shortcode integration** — plugins decoupled; theme degrades gracefully.
|
|
5. **Split `functions.php` into `inc/` modules** — every file < 300 lines.
|
|
6. **Self-contained auth hardening (v1.5.1 / ADR-0001)** — rate limiter,
|
|
honeypots, email verification all live in the theme. No plugin
|
|
dependencies, no paid services. Swappable via two small interfaces
|
|
(`Peptide_Starter_Rate_Limiter`, `peptide_starter_require_login`).
|
|
7. **Transient-backed rate limits** — auto-expire, no custom table, no
|
|
uninstall cleanup. If the site ever goes multi-server, swap to the
|
|
shared object cache without caller changes.
|
|
|
|
## Security Summary
|
|
|
|
- CSRF nonces on every form (auth, newsletter, contact, mail-test, unsubscribe).
|
|
- Unified error messages on login + registration (no enumeration).
|
|
- Registration validation does **not** short-circuit — all checks
|
|
evaluate unconditionally so response timing can't distinguish which
|
|
one failed (PSEC-009, v1.5.2).
|
|
- Rate limiting on login / register / contact / newsletter / verify-resend.
|
|
- Honeypots on the four public forms (login, register, contact, newsletter).
|
|
- `require_login()` gate on every template that owns user data.
|
|
- Email verification required for write access to user-data tools.
|
|
- Existing accounts (created before v1.5.2) are grandfathered as
|
|
verified. Verification enforces only on registrations from v1.5.2
|
|
onward (PSEC-008, v1.5.2).
|
|
- CSV export formula-injection safe.
|
|
- All input sanitized at the boundary; all output escaped.
|
|
- Login redirect validated with `wp_validate_redirect`.
|
|
- Rate-limit storage keys are hashed — no raw IPs persisted.
|
|
- Contact sender names reject header-injection characters before `wp_mail`.
|
|
|
|
### Client-IP trust model (PSEC-007, v1.5.2)
|
|
|
|
peptiderepo.com sits behind Cloudflare. The rate limiter keys on client
|
|
IP, so the source of that IP matters for correctness of every abuse
|
|
control in the theme.
|
|
|
|
`peptide_starter_get_client_ip()` trusts in this priority order:
|
|
|
|
1. **`HTTP_CF_CONNECTING_IP`** — only when `REMOTE_ADDR` is itself
|
|
inside a published Cloudflare edge range (see `inc/cloudflare-ips.php`).
|
|
Direct-to-origin connections that bypass Cloudflare cannot forge this
|
|
header because their peer IP is not a CF edge.
|
|
2. **`HTTP_X_FORWARDED_FOR`** — ignored by default. Opt in via
|
|
`add_filter( 'peptide_starter_trust_xff', '__return_true' )` only
|
|
when a known, trusted non-CF proxy fronts the origin.
|
|
3. **`REMOTE_ADDR`** — always trusted as the final fallback. Cannot be
|
|
client-controlled at the TCP layer.
|
|
|
|
`inc/cloudflare-ips.php` holds a static snapshot of CF ranges. Refresh
|
|
quarterly. Override via `peptide_starter_cloudflare_ip_ranges` filter
|
|
for out-of-band updates.
|
|
|
|
## Version
|
|
|
|
**Current:** v1.6.0 (2026-04-23)
|
|
|
|
Bump `style.css` header line 7 and `PEPTIDE_STARTER_VERSION` in
|
|
`functions.php` together on release.
|