config/projects/business/memory/bugs.md

34 KiB
Raw Blame History

BUG-075: Database Connection Pool Does Not Handle PgBouncer Failover

  • Date: 2026-02-18 14:05 UTC
  • Severity: CRITICAL
  • Issue: When a PgBouncer pooler instance goes down (K8s node failure), pods keep stale TCP connections and get "no available server" errors. App never reconnects — all PDF conversions fail until manual pod restart.
  • Root cause: pg.Pool in src/services/db.ts has no keepAlive, no connectionTimeoutMillis, no retry logic. No mechanism to detect or recover from dead connections.
  • Impact: Complete service outage for pods connected to failed pooler. Violates HA requirements.
  • Fix needed: (1) Enable TCP keepalive + connection timeout, (2) Add retry with backoff for transient DB errors, (3) Health check must fail when DB is unreachable (already partially done but needs timeout), (4) Verify recovery without pod restart
  • Status: FIXED (PROPERLY) — v0.2.6 fix was insufficient (retried on same dead pool connections). Commit 95ca101 fixes the real issue: queryWithRetry now uses explicit client checkout and calls client.release(true) to DESTROY dead connections on transient errors, forcing fresh TCP connections on retry. connectWithRetry validates with SELECT 1 before returning. idleTimeoutMillis reduced to 10s. Health check destroys bad connections. Verified on staging: killed a pooler pod, 15/15 health checks passed, zero app restarts.

BUG-074: Email Broken on K3s Production — SMTP Misconfigured

  • Date: 2026-02-18 13:00 UTC
  • Severity: CRITICAL
  • Issue: After K3s migration, production pods used old Docker code connecting to host.docker.internal:25 (non-existent in K3s). The SMTP fix (commit 0902e1e) was on staging but not deployed to production.
  • Root cause: Three issues: (1) Production image at old commit without SMTP env var support, (2) K3s secrets pointed to mail.cloonar.com which rejected K3s worker IPs, (3) Old server Postfix only listened on localhost + Docker bridge, not public IP
  • Fix applied:
    1. Updated K8s SMTP secrets to use old server (167.235.156.214) as relay (has DKIM for docfast.dev)
    2. Added K3s worker IPs to old server's Postfix mynetworks and UFW rules (port 25)
    3. Made Postfix listen on public IP (inet_interfaces)
    4. Tagged v0.2.3 to deploy SMTP fix to production
    5. Restarted all pods to pick up new secrets
  • Verification: Verification email sent confirmed in production logs
  • Status: FIXED

BUG-073: Staging Landing Page Shows Wrong Pro Plan Quota (2,500 vs 5,000)

  • Date: 2026-02-18 13:05 UTC
  • Severity: MEDIUM
  • Environment: Staging (https://staging.docfast.dev)
  • Issue: Staging landing page shows Pro plan as "2,500 PDFs per month" but production also shows "2,500 PDFs per month". Previous bugs (BUG-045, BUG-057) referenced 5,000 and 10,000 PDFs. The Stripe checkout page says "5,000 PDF conversions per month". There is a mismatch between what the landing page advertises (2,500) and what Stripe checkout says (5,000).
  • Impact: Customer confusion — they see 2,500 on the pricing page but 5,000 on the checkout page
  • Fix: Align landing page and Stripe product description to the same number
  • Status: OPEN

BUG-072: Production Outage — UFW+Docker Conflict + Dual Deployment

  • Date: 2026-02-18 ~08:00 UTC
  • Severity: CRITICAL
  • Duration: Unknown start time → fixed 08:04 UTC
  • Symptoms: App container stuck in restart loop, unable to reach PostgreSQL. ENETUNREACH 172.17.0.1:5432
  • Root causes:
    1. UFW injected DROP rules into Docker's DOCKER iptables chain, blocking container-to-host networking
    2. A systemd service (docfast.service) running a separate Node.js process from /opt/docfast was binding port 3100, preventing Docker container from starting after fix
  • Fix applied:
    • Removed iptables DROP rules from DOCKER chain
    • Stopped and disabled docfast.service systemd unit
    • Changed DATABASE_HOST from 172.17.0.1 to host.docker.internal in docker-compose.yml
    • Full docker compose down && up -d — container healthy
  • Permanent fix: DevOps agent dispatched to properly resolve ufw+Docker conflict and remove dual deployment
  • Status: FIXED (immediate), permanent fix in progress

DocFast QA Report — 2026-02-15

Tester: QA Bot (automated) Version: 0.2.1 URL: https://docfast.dev


Bug Fix Verification

BUG-032: Mobile Terminal Gap — FIXED

  • Tested at 375×812 viewport
  • .code-section uses display: flex; flex-direction: column
  • Gap between .code-header and .code-block: 0px
  • Screenshot confirms no visible gap

BUG-035: STRIPE_WEBHOOK_SECRET Deployed — VERIFIED

  • Pro "Get Started →" button redirects to Stripe checkout
  • Stripe page shows "Subscribe to DocFast Pro" at $9.00/mo
  • Merchant: Cloonar Technologies GmbH
  • Stripe checkout fully functional

⚠️ BUG-037: Webhook product_id Filter — CANNOT VERIFY

  • Cannot test webhook handler directly (requires Stripe event)
  • Stripe checkout page loads correctly, suggesting integration is wired up
  • Needs manual verification with a test Stripe webhook event

Test Results

1. Console Errors — PASS

  • Zero JS errors in browser console
  • Zero warnings

2. Mobile Terminal Gap — PASS

  • 375×812 viewport, zero gap between terminal header and body
  • Flexbox layout confirmed via computed styles

3. Signup Flow — PASS

  • "Get Free API Key" button opens modal
  • Email input works, "Generate API Key →" submits
  • Verification code screen appears with correct email displayed
  • API: POST /v1/signup/free returns {"status":"verification_required"}
  • API: POST /v1/signup/verify with wrong code returns {"error":"Invalid verification code."}

4. Pro Checkout — PASS

  • "Get Started →" redirects to Stripe checkout
  • Correct product: DocFast Pro, $9.00/month
  • Full Stripe payment form (card, billing address, etc.)

5. /docs Page — PASS

  • Swagger UI loads with full API documentation
  • All endpoint groups visible: Conversion, Templates, Account, Billing, System
  • OpenAPI spec accessible at /openapi.json

6. Health Endpoint — ⚠️ PARTIAL PASS

  • GET /health returns {"status":"ok","version":"0.2.1",...}
  • Pool stats included (size, active, available, queue depth)
  • Issue: No PostgreSQL connection info in health response
    • No database or postgres field
    • No DB version reported
    • Health check only covers the browser pool, not the database
    • Severity: LOW — DB issues would surface as auth/signup failures, but health endpoint should ideally confirm DB connectivity

7. HTML→PDF Generation — ⏭️ SKIPPED

  • Cannot complete without a valid API key (email verification requires receiving actual email)
  • Signup works, but test environment can't receive verification emails at @test.docfast.dev

8. Error Handling — PASS

  • Bad API key → 403 {"error":"Invalid API key"}
  • No API key → 401 {"error":"Missing API key. Use: Authorization: Bearer <key> or X-API-Key: <key>"}
  • Missing params with bad key → 403 (auth checked first, correct behavior)
  • Cannot test missing params with valid key (see #7)

New Issues Found

BUG-038: Health Endpoint Missing Database Status

  • Severity: LOW
  • Endpoint: GET /health
  • Expected: Health response should include PostgreSQL connection status and version
  • Actual: Only returns browser pool stats, no database info
  • Impact: Monitoring blind spot — DB could be down but /health reports "ok"

BUG-039: API Signup Endpoint Mismatch in Docs

  • Severity: INFO
  • Details: The docs page references POST /v1/signup/free but the original test spec listed POST /v1/auth/signup — this is just a documentation/spec mismatch in the test plan, not a bug in the app itself

Summary

Test Result
Console errors PASS (0 errors)
Mobile terminal gap PASS (0px gap)
Signup flow PASS
Pro checkout → Stripe PASS
/docs page PASS
Health endpoint ⚠️ PARTIAL (no DB status)
PDF generation ⏭️ SKIPPED (no valid key)
Error handling PASS

Overall: 5 PASS, 1 PARTIAL, 1 SKIPPED, 1 N/A

The three reported bugs (BUG-032, BUG-035, BUG-037) are verified fixed (032, 035) or plausibly fixed (037 — needs webhook test). One new low-severity issue found (health endpoint missing DB status).


DocFast QA Full Regression — 2026-02-16

Tester: QA Bot (harsh mode) Trigger: Container was found DOWN this morning, restarted URL: https://docfast.dev Browser: Chrome (OpenClaw profile) Tests: Full regression suite


BUG-050: Broken MX Record Causes Email Delivery Failures — CRITICAL

  • Severity: CRITICAL
  • Issue: MX record for docfast.dev resolves to mail.cloonar.com.docfast.dev (non-existent) instead of a valid mail server. This is a relative hostname in DNS that got appended to the zone.
  • Impact: Any mail server doing sender address verification (like cloonar.com) rejects our emails. Customer #370 cannot receive verification codes. This likely affects other recipients too.
  • Root cause: MX DNS record was entered as mail.cloonar.com without trailing dot, so Hetzner DNS appended .docfast.dev
  • Fix needed: Investor must fix MX record in Hetzner DNS console:
    • Option A (recommended): Set MX to docfast.dev. (point to own server, since Postfix runs there)
    • Option B: Delete the broken MX record entirely (servers will fall back to A record)
  • Workaround applied: Postfix now accepts local mail for noreply@docfast.dev (mydestination + virtual alias), but this only helps if the remote server can reach us — which it can't due to broken MX.
  • Status: OPEN — requires investor DNS action
  • Discovered: 2026-02-17 Session 48

Test Results Summary

Test Category Status Details
Site Load + Console PASS ZERO JS errors (requirement met)
Signup Flow PASS Email → verification screen works
Pro → Stripe PASS Redirect + checkout form working
/docs Swagger UI PASS Full API documentation loads
Mobile Responsive PASS 375×812 layout perfect
/health endpoint PASS Database status included
API Tests PASS All endpoints working
Error Handling PASS 401/403 responses correct

Overall Result: ALL TESTS PASS


Detailed Test Results

1. Site Load & Console Errors — PASS

  • Requirement: ZERO JS errors
  • Result: Console completely clean, no errors/warnings
  • URL: https://docfast.dev
  • Screenshots: Homepage visual verification passed

2. Full Signup Flow — PASS

  • Test: Email → verification code screen appears
  • Steps:
    1. Clicked "Get Free API Key →" button
    2. Modal appeared with email input
    3. Entered "qa-test@example.com"
    4. Clicked "Generate API Key →"
    5. SUCCESS: Verification screen appeared with:
      • "Enter verification code" heading
      • Email address displayed: qa-test@example.com
      • 6-digit code input field
      • "Verify →" button
      • "Code expires in 15 minutes" text

3. Pro → Stripe Checkout — PASS

  • Test: Pro plan redirects to Stripe properly
  • Steps:
    1. Clicked "Get Started →" on Pro plan ($9/mo)
    2. SUCCESS: Redirected to Stripe checkout page with:
      • "Subscribe to DocFast Pro" heading
      • $9.00 per month pricing
      • Full payment form (card, expiry, CVC, billing)
      • "Pay and subscribe" button
      • Powered by Stripe footer

4. /docs Page with Swagger UI — PASS

  • Test: Swagger UI loads completely
  • Result: Full API documentation loaded with:
    • DocFast API 1.0.0 header
    • Authentication & rate limits info
    • All endpoint categories:
      • Conversion: HTML, Markdown, URL to PDF
      • Templates: List & render templates
      • Account: Signup, verify, recovery, email change
      • Billing: Stripe checkout
      • System: Usage stats, health check
    • Interactive "Try it out" buttons
    • OpenAPI JSON link working
    • Schemas section

5. Mobile Test — PASS

  • Test: browser resize to 375×812 (iPhone X)
  • Result: Perfect responsive layout
    • All content visible and readable
    • Proper scaling and text sizes
    • Swagger UI adapts well to mobile
    • No horizontal scrolling issues

6. Health Endpoint — PASS

{
  "status": "ok",
  "version": "0.1.0",
  "database": {
    "status": "ok",
    "version": "PostgreSQL 16.11"
  },
  "pool": {
    "size": 15,
    "active": 0,
    "available": 15,
    "queueDepth": 0,
    "pdfCount": 0,
    "restarting": false,
    "uptimeSeconds": 125
  }
}

7. API Tests via curl — PASS

Health Check API

curl -s https://docfast.dev/health
# ✅ SUCCESS: Returns OK with database status

Free Signup API

curl -s -X POST https://docfast.dev/v1/signup/free \
  -H "Content-Type: application/json" \
  -d '{"email":"api-test@example.com"}'
# ✅ SUCCESS: {"status":"verification_required","message":"Check your email for the verification code."}

Error Handling Tests

Bad API Key (403):

curl -s -X POST https://docfast.dev/v1/convert/html \
  -H "Authorization: Bearer invalid-key-123" \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Test</h1>"}'
# ✅ SUCCESS: {"error":"Invalid API key"} HTTP 403

Missing API Key (401):

curl -s -X POST https://docfast.dev/v1/convert/html \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Test</h1>"}'
# ✅ SUCCESS: {"error":"Missing API key. Use: Authorization: Bearer <key> or X-API-Key: <key>"} HTTP 401

Issues Found

ZERO ISSUES FOUND 🎉

All systems operational after container restart. The site is working perfectly across all test scenarios.


Test Environment

  • Date: 2026-02-16 08:30 UTC
  • Browser: Chrome (OpenClaw headless)
  • Resolution: 1280×720 (desktop), 375×812 (mobile)
  • Network: Direct sandbox connection
  • API Client: curl 8.5.0

Post-Container-Restart Status: FULLY OPERATIONAL

Container restart appears to have been clean. All services came back online properly:

  • Web frontend:
  • API backend:
  • Database connections:
  • Stripe integration:
  • Email verification system: (API endpoints working)

Recommendation: Continue monitoring, but no urgent issues detected.


CEO Code Audit — 2026-02-16

BUG-046: Usage Endpoint Leaks Other Users' Data

  • Severity: CRITICAL
  • Endpoint: GET /v1/usage
  • Issue: getUsageStats() returned ALL users' usage data to any authenticated user. GDPR violation.
  • Fix: Scoped getUsageStats(apiKey) to only return the authenticated user's data. Route passes req.apiKeyInfo.key.
  • Status: FIXED (Session 41)

BUG-047: No Copy Button on Pro Key Success Page

  • Severity: HIGH
  • Page: /v1/billing/success
  • Issue: Pro key displayed but no visible copy button
  • Fix: Added visible "Copy" button that changes to "Copied!" on click
  • Status: FIXED (Session 41)

BUG-048: Change Email Modal Never Opens

  • Severity: HIGH
  • Issue: Footer "Change Email" links used href="/#change-email" but lacked class="open-email-change" that JS targets
  • Fix: Added class="open-email-change" to all Change Email links across all 4 HTML pages
  • Status: FIXED (Session 41)

BUG-040: SSRF Vulnerability in URL→PDF Endpoint

  • Severity: HIGH
  • Endpoint: POST /v1/convert/url
  • Issue: URL validation only checks protocol (http/https) but does NOT block private/internal IP addresses. Attacker can request internal URLs like http://169.254.169.254/latest/meta-data/ (cloud metadata), http://127.0.0.1:3100/health, or any RFC1918 address.
  • Fix: Resolve hostname via DNS before passing to Puppeteer, block private IP ranges.
  • Status: FIXED (verified in Session 38)

BUG-041: Docker Healthcheck Broken — Container Permanently "Unhealthy"

  • Severity: MEDIUM
  • Status: FIXED (Session 39 → verified Session 40, container shows "healthy")

BUG-042: Pricing in USD Instead of EUR

  • Severity: MEDIUM
  • Status: FIXED (Session 39 → QA verified Session 40, Stripe shows €9.00/mo)
  • Severity: HIGH
  • Status: FIXED (Session 39, verified Session 40 — all 3 pages live with correct data)

BUG-044: EU Hosting Not Marketed

  • Severity: LOW
  • Status: FIXED (Session 39, QA verified EU hosting badge on landing page)

DocFast QA — Currency & Feature Test — 2026-02-16 16:03 UTC

Tester: QA Bot (harsh mode) URL: https://docfast.dev


CRITICAL TEST: Stripe Checkout Currency

BUG-042 VERIFIED FIXED: EUR Currency in Stripe Checkout

  • Clicked: Pro "Get Started →" button
  • Result: Redirected to checkout.stripe.com
  • Price shown: €9.00 per month
  • Currency: EUR (€ symbol confirmed)
  • Merchant: Cloonar Technologies GmbH
  • Description: "Unlimited PDF conversions via API. HTML, Markdown, and URL to PDF."
  • Screenshot: Confirmed visually — €9.00, not $9.00

BUG-045: Stripe Checkout Says "Unlimited" But Landing Page Says "10,000"

  • Severity: MEDIUM
  • Status: FIXED (Session 40) — Landing page updated to "Unlimited PDF conversions" to match Stripe and actual code behavior (Pro has no limit in code). Commit d7b0a0e deployed.

Feature Tests

  • Clicked "Change Email" in footer
  • Opens modal dialog with:
    • "Change your email" heading
    • API key input field (placeholder: "df_free_... or df_pro_...")
    • New email input field
    • "Send Verification Code →" button
    • Helper text: "A verification code will be sent to your new email"
  • Working correctly
  • Clicked the link in hero section
  • Opens modal dialog with:
    • "Recover your API key" heading
    • Email input field
    • "Send Verification Code →" button
    • Security note: "Your key will be shown here after verification — never sent via email"
  • Working correctly

Mobile Responsive (375×812) — PASS

  • Full-page screenshot at iPhone X dimensions
  • Layout stacks correctly: nav → hero → stats → features → pricing → footer
  • Text readable, no overflow, no horizontal scroll
  • Pricing cards stack vertically
  • Terminal code block fits properly

Console Errors — PASS

  • ZERO errors in browser console
  • ZERO warnings

Landing Page Observations

  • Pricing shows €0/mo (Free) and €9/mo (Pro) — EUR on landing page
  • EU hosting section present with 🇪🇺 flag
  • Footer has: Docs, API Status, Change Email, Impressum, Privacy Policy, Terms of Service

Summary

Test Status
Stripe EUR pricing (€9.00/mo) PASS
Change Email link PASS
Recover API Key link PASS
Mobile responsive 375×812 PASS
Console errors PASS (zero)

New issue: BUG-045 — Stripe/landing page copy mismatch ("Unlimited" vs "10,000") — FIXED by CEO (Session 40): Updated Stripe product description to "10,000 PDF conversions per month"

Overall: 5/5 PASS, 1 new medium-severity bug found and fixed


QA Audit — Session 48

Tester: QA Subagent (deep audit) Date: 2026-02-17 08:0008:30 UTC URL: https://docfast.dev Version: 0.2.1 Scope: Performance · SEO · Accessibility · Cross-page Consistency


🔴 PERFORMANCE AUDIT

BUG-051: Duplicate HTTP Response Headers — Nginx Misconfiguration

  • Severity: MEDIUM
  • Affected: All responses (HTML, JS, PNG)
  • Issue: Two headers appear twice in every HTTP response:
    • Vary: Accept-Encoding — appears twice in HTML response
    • X-Content-Type-Options: nosniff — appears twice in all responses
  • Root cause: Likely set in both the nginx base config and the application (Express helmet)
  • Impact: RFC violation; some proxies may mishandle duplicate headers; wasted bytes
  • Fix: Remove duplicate from one layer (either nginx or helmet)

BUG-052: Duplicate Cache-Control Headers on Static Assets

  • Severity: MEDIUM
  • Affected: app.js, status.js, og-image.png
  • Issue: Two conflicting Cache-Control headers are returned simultaneously:
    • Cache-Control: max-age=604800 (without public)
    • Cache-Control: public, max-age=604800, immutable
  • Impact: Proxy/CDN behavior is undefined when two Cache-Control headers conflict; should be a single directive
  • Fix: Consolidate to one header: Cache-Control: public, max-age=604800, immutable

BUG-053: JavaScript Files Not Minified

  • Severity: LOW
  • Files: /app.js (515 lines, 16,838 bytes), /status.js (48 lines, 3,285 bytes)
  • Issue: Both JS files are served unminified with readable variable names, full whitespace, and comments
  • Impact: ~3040% larger payload than necessary; slower parse time (minor for these sizes)
  • Fix: Add a build step: terser app.js -o app.min.js and update HTML references

BUG-054: No Brotli Compression — Gzip Only

  • Severity: LOW
  • Issue: Server only supports gzip compression; does not serve Brotli (Content-Encoding: br) even when Accept-Encoding: br is sent
  • Impact: Brotli compresses ~2026% better than gzip for text assets; Chrome and all modern browsers support it
  • Fix: Enable brotli_static in nginx or add compression middleware with Brotli support
  • Severity: LOW
  • Issue: The homepage HTML includes the Google Fonts preconnect hint twice:
    • Line 17: <link rel="preconnect" href="https://fonts.googleapis.com"> (in <head>, no actual font loaded nearby)
    • Line 266: <link rel="preconnect" href="https://fonts.googleapis.com"> (again, right before the stylesheet)
    • Same duplication for https://fonts.gstatic.com
  • Impact: Wasted browser hint; negligible but signals copy-paste error in template
  • Fix: Remove the first set (lines ~1718); keep only the preconnect immediately before the font <link>

🟡 SEO AUDIT

BUG-056: Sitemap Namespace URL Typo — Invalid Sitemap

  • Severity: HIGH
  • File: https://docfast.dev/sitemap.xml
  • Issue: Sitemap uses wrong namespace: http://www.sitemapns.org/schemas/sitemap/0.9
    • Correct URL: http://www.sitemaps.org/schemas/sitemap/0.9
    • Difference: sitemapns.org vs sitemaps.org (extra "n")
  • Impact: Google Search Console will reject this sitemap as invalid XML schema. Crawlers that validate the namespace may ignore it entirely.
  • Fix: Change sitemapns.orgsitemaps.org in the sitemap XML

BUG-057: JSON-LD Structured Data Has Stale Pro Plan Description

  • Severity: MEDIUM
  • Location: <script type="application/ld+json"> in homepage <head>
  • Issue: JSON-LD Pro offer says "description": "5,000 PDFs per month" but:
    • The landing page shows 10,000 PDFs per month for Pro
    • BUG-045 (previously fixed) updated Stripe to say "10,000 PDFs per month"
    • The JSON-LD was never updated — still shows the old 5,000 figure
  • Impact: Google may display incorrect pricing/quota info in rich results
  • Fix: Update JSON-LD Pro offer description to "10,000 PDFs per month"

BUG-058: twitter:image Meta Tag Missing on Homepage

  • Severity: LOW
  • Location: <head> of https://docfast.dev/
  • Issue: Homepage has og:image set to /og-image.png but the corresponding <meta name="twitter:image"> tag is absent
  • Impact: Twitter/X link previews won't show the card image even though the image exists and og:image is set (Twitter prefers its own tags when present)
  • Fix: Add <meta name="twitter:image" content="https://docfast.dev/og-image.png">

BUG-059: /docs Page Missing All SEO Meta Tags

  • Severity: MEDIUM
  • URL: https://docfast.dev/docs
  • Issue: The docs page (Swagger UI) has:
    • No <meta name="description">
    • No <link rel="canonical">
    • No og: tags
    • No twitter: tags
    • Title: "DocFast API Documentation" (inconsistent format — others use Page — DocFast pattern)
  • Impact: Poor search appearance for a high-value SEO target (developers searching for PDF API docs)
  • Fix: Add standard meta tags to the /docs HTML template

BUG-060: Sub-pages Missing Open Graph / Twitter Meta Tags

  • Severity: LOW
  • Affected: /impressum, /privacy, /terms, /status
  • Issue: These pages have <title>, <meta description>, and <canonical> but no og: or twitter: tags
  • Impact: Unfriendly link previews when shared on social media
  • Fix: Add at minimum og:title, og:description, og:url to each sub-page template

BUG-061: /status Page Not Included in Sitemap

  • Severity: INFO
  • URL: https://docfast.dev/sitemap.xml
  • Issue: The sitemap includes /, /docs, /impressum, /privacy, /terms but omits /status
  • Impact: Minor — status pages are generally low SEO priority, but consistency is good
  • Fix: Add /status to sitemap with changefreq=always and low priority (0.2)

🟠 ACCESSIBILITY AUDIT

BUG-062: <main> Element Only Wraps Hero Section

  • Severity: HIGH
  • Location: https://docfast.dev/ (lines 283309)
  • Issue: The <main> landmark element only wraps the hero (headline + CTA + code block). The trust stats, EU hosting badge, features grid, and pricing section are all outside <main> — they're bare <section> elements after </main>
  • Impact: Screen reader users using "jump to main content" will skip to the hero and miss the features/pricing. WCAG 1.3.6 landmark best practice violation.
  • Fix: Extend <main> to wrap all page content between nav and footer

BUG-063: Heading Hierarchy Skip — h3 Without Parent h2

  • Severity: MEDIUM
  • Location: "Hosted in the EU" section (homepage)
  • Issue: Page structure: <h1> (hero) → <h3> "Hosted in the EU" → <h2> "Everything you need". The h3 appears before the first h2, violating sequential heading order (WCAG 1.3.1, Success Criterion: Info and Relationships)
  • Impact: Screen readers announce heading levels; skipping from h1 to h3 confuses navigation
  • Fix: Change the "Hosted in the EU" heading from <h3> to <h2>, or restructure so it follows an h2

BUG-064: Modal Form Inputs Have No <label> Elements

  • Severity: HIGH
  • Location: All three modals (Sign Up, Recover API Key, Change Email)
  • Issue: Every <input> in the modals uses only placeholder text for labeling. There are no <label for="..."> elements and no aria-label or aria-labelledby attributes on the inputs
  • Impact: WCAG 1.3.1, 3.3.2 violations. Screen readers will announce inputs with only their placeholder (which vanishes on focus/input), making the form inaccessible for blind users
  • Fix: Add <label> elements for each input, or add aria-label attributes:
    <label for="signupEmail" class="sr-only">Email address</label>
    <input type="email" id="signupEmail" ...>
    

BUG-065: Modal Close Buttons Have No Accessible Label

  • Severity: MEDIUM
  • Location: All three modal close buttons (id="btn-close-signup", btn-close-recover, btn-close-email-change)
  • Issue: Buttons render × (HTML entity &times;) with no aria-label attribute
  • Impact: Screen readers will announce "times" or "multiplication sign" instead of "Close". WCAG 4.1.2 violation.
  • Fix: Add aria-label="Close dialog" to each close button

BUG-066: Modals Missing aria-modal="true"

  • Severity: MEDIUM
  • Location: #signupModal, #recoverModal, #emailChangeModal
  • Issue: Modal divs have role="dialog" but lack aria-modal="true". Without aria-modal, screen readers (especially NVDA/JAWS in browsing mode) may continue reading background content
  • Impact: Background page content is announced while modal is open
  • Fix: Add aria-modal="true" to all three modal overlay divs
  • Severity: LOW
  • Issue: There is no "Skip to main content" link at the top of any page. Keyboard users must tab through the entire navigation on every page load to reach the main content
  • Impact: WCAG 2.4.1 (Bypass Blocks) — Level A violation. Particularly problematic since there is a 3-item nav on every page.
  • Fix: Add as first element in <body>:
    <a href="#main-content" class="skip-link">Skip to main content</a>
    

🔵 CROSS-PAGE CONSISTENCY AUDIT

  • Severity: MEDIUM
  • Affected: /impressum, /privacy, /terms, /status
  • Issue: The footer "Change Email" link (href="/#change-email" + class="open-email-change") works on the homepage because app.js intercepts the click and opens the modal. On sub-pages, app.js is not loaded, so clicking navigates to https://docfast.dev/#change-email. The homepage app.js DOMContentLoaded handler does not check window.location.hash on load, so the modal never auto-opens.
  • Note: BUG-048 (previous session) added the class="open-email-change" to these links, but the incomplete fix only works on the homepage itself. Sub-page navigation to /#change-email falls through silently.
  • Impact: Users on sub-pages who want to change their email get taken to the homepage with no modal — confusing dead end
  • Fix: Either (a) add hash detection in app.js DOMContentLoaded: if (window.location.hash === '#change-email') openEmailChange();, or (b) add a standalone mini-script to sub-pages that handles the click and redirects with a proper action param

BUG-069: /docs Page Uses Different Navigation Pattern

  • Severity: INFO
  • URL: https://docfast.dev/docs
  • Issue: The docs page uses a custom Swagger UI top bar with "← Back to docfast.dev" instead of the main site nav (Features, Pricing, Docs links). Footer is also absent.
  • Note: This is intentional for SwaggerUI but means the docs page is an isolated island with no footer navigation and no way to reach /status, /impressum, /privacy, or /terms from docs
  • Impact: Low — power users know to use the browser back button; but /impressum and /privacy should be reachable from all pages (legal requirement in some jurisdictions)
  • Fix: Add a minimal footer to the /docs page with legal links (Impressum, Privacy, Terms)

ITEMS CONFIRMED WORKING

Check Status
HTTPS enforced (HSTS header) OK
Gzip compression active OK
Cache-Control for HTML (24h) OK
ETag + Last-Modified on HTML OK
og:image exists (40KB PNG) OK
robots.txt present and correct OK
Canonical URLs on all pages OK
Security headers (CSP, CORP, etc.) OK
Nav consistent across all pages OK
Footer consistent across all pages OK
External links use target=_blank + rel=noopener OK
Mobile layout at 375px (iPhone X) OK
Focus-visible styles defined OK
ARIA labels on nav, footer, modals OK
Decorative icons have aria-hidden OK
Modal focus trapping (JS) OK
Modal Escape-key-to-close (JS) OK
/status page working with live data OK
/health returns JSON with DB status OK
All 6 pages load HTTP 200 OK

Summary Table

Bug ID Severity Area Title
BUG-051 MEDIUM Performance Duplicate HTTP headers (Vary, nosniff)
BUG-052 MEDIUM Performance Duplicate Cache-Control on static assets
BUG-053 LOW Performance JS files not minified
BUG-054 LOW Performance No Brotli compression
BUG-055 LOW Performance Duplicate preconnect link tags in HTML
BUG-056 HIGH SEO Sitemap namespace URL typo (sitemapns.org)
BUG-057 MEDIUM SEO JSON-LD Pro plan stale (5,000 vs 10,000)
BUG-058 LOW SEO twitter:image missing on homepage
BUG-059 MEDIUM SEO /docs page missing all SEO meta tags
BUG-060 LOW SEO Sub-pages missing og:/twitter: tags
BUG-061 INFO SEO /status not in sitemap
BUG-062 HIGH Accessibility <main> wraps only hero section
BUG-063 MEDIUM Accessibility h3 before h2 (heading skip)
BUG-064 HIGH Accessibility Modal inputs have no <label> elements
BUG-065 MEDIUM Accessibility Close buttons have no aria-label
BUG-066 MEDIUM Accessibility Modals missing aria-modal="true"
BUG-067 LOW Accessibility No skip-to-content link
BUG-068 MEDIUM Consistency Change Email footer broken on sub-pages
BUG-069 INFO Consistency /docs has no footer with legal links

Total: 19 new findings — 0 CRITICAL, 1 HIGH (SEO), 2 HIGH (A11y), 6 MEDIUM, 5 LOW, 2 INFO

BUG-071: Support Agent Leaked API Key via Social Engineering — CRITICAL SECURITY INCIDENT

  • Date: 2026-02-17
  • Severity: CRITICAL
  • What happened: Support agent (Franz Hubert) retrieved API key df_free_87aa...100d from database and sent it in plaintext to office@cloonar.com (ticket #370). The requester claimed to be dominik.polakovics@cloonar.com but was emailing from a DIFFERENT address. Classic social engineering attack.
  • Impact: Third party obtained a user's API key
  • Response:
    1. Compromised key rotated immediately — old key invalidated, new key generated
    2. Container restarted to reload key cache
    3. Support agent prompt hardened with explicit security rules (boxed, emphasized, real-world warning)
    4. Removed ALL database access guidance from support agent prompt
    5. Added escalation-only flow for key recovery issues
  • Status: RESOLVED (key rotated, prompt hardened)
  • Prevention: Support agent now has zero ability to retrieve keys; can only direct to website recovery or escalate to human