[Unreleased]
Added
- byanalytics counts the AI crawlers that read its own public pages when
BYANALYTICS_DOGFOOD_SITE_KEYis set, next to its own tracker. - The operator is emailed about every new account (
BYANALYTICS_NOTIFY_SIGNUPS, on by default), whether it signed up with a password or with Google. - Sites overview: each site card shows how many visitors it has right now, under its live badge.
- Keyword cannibalization on the Growth tab: queries for which Google alternates between two or more pages of the site (Search Console query × page, last 28 days).
- Index coverage on the Search tab: sitemap URLs against Google's indexing status, with the reasons for URLs that are not indexed. URL Inspection now spends leftover daily quota on sitemap URLs.
- AI crawler activity per page on the AI search tab, reported from the customer's server through
POST /api/b: WordPress plugin 1.1.0, a Laravel middleware (TrackAiCrawlers) and a Cloudflare Worker. - White-label PDF reports (Business): the agency's name and logo replace byanalytics on the report.
- Download your data: a JSON export of everything held about the signed-in person, from the profile page.
- Nightly database backups with rotation (
php artisan byanalytics:backup) and a backup check inbyanalytics:doctor. - Operator alerts by email (
BYANALYTICS_OPS_EMAIL): failed jobs, production errors and failing health checks, at most one per problem and hour. scripts/deploy.shto update an installation; tracking, the events API and Stripe webhooks keep working in maintenance mode./llms.txt: a Markdown map of the product pages and documentation, in both languages, for AI assistants.- Optional Google reCAPTCHA v3 on sign in, sign up, password recovery, password reset and contact (
RECAPTCHA_SITE_KEY,RECAPTCHA_SECRET_KEY). - Legal texts can point to the contact form when the owner publishes no email address, and name the country in Spanish.
php artisan byanalytics:stripe-setupcreates the Stripe products and prices fromconfig/plans.phpand prints the configuration lines.- Stripe Checkout collects the billing address and tax number; optional Stripe Tax (
STRIPE_AUTOMATIC_TAX). - Failed payments: the plan stays active while Stripe retries, the owner is emailed and the billing page shows a notice. Trial-ending reminder three days before the no-card trial ends.
- Non-destructive downgrades: sites beyond the new plan's limit are paused (no tracking or monitoring, data kept) and the previous data retention applies for 14 more days.
- Branded, translated error pages (401, 403, 404, 419, 429, 500, 503).
- CI runs the test suite on MariaDB 10.6 and MySQL 8.4, audits dependencies, and Dependabot is enabled.
Fixed
- Long syncs (PageSpeed, URL Inspection, crawls) were handed to a second worker after 90 seconds, so they ran twice or were marked as failed while still working: the database queue now waits longer than the longest job.
- URL Inspection: a URL Google refuses no longer aborts the whole run, the Search Console account is used (the one that owns the property), and the error says to connect Search Console first when it is missing.
- Site dashboard: the tab bar no longer jumps back to the first tab after choosing one; it keeps its position and centres the open tab.
- PageSpeed & CrUX: the two APIs are attempted independently, so a key that is not allowed to use CrUX no longer discards the PageSpeed scores, and the error shows Google's own explanation instead of a bare HTTP code.
- Site connections: when the list of Google properties cannot be loaded the form now says why (for example an API switched off in Google Cloud) instead of silently asking to type the property, and the failure is no longer cached for ten minutes.
- The public live demo answered 404: shared dashboards only accepted 40-character tokens and the demo uses a readable one.
- Demo sites are left out of real uptime checks, daily checks, crawls and connector syncs: their invented domains were being requested and the generated demo data overwritten with failures.
- The demo account no longer receives alert or weekly report emails: its address is not a mailbox, so each one bounced and raised an operator alert.
- Starting a subscription failed at Stripe Checkout for new customers ("could not find a valid address") while tax number collection was on.
- Public changelog page failing with a server error on installations made from a release archive, which left
CHANGELOG.mdout. byanalytics:doctorno longer fails (and emails the operator every hour) over a missing GeoLite2 database when no MaxMind account is configured; it is a warning.- Migrations failed on MariaDB before 10.10 (
Invalid default valueon non-null timestamp columns), which blocked installing on most shared hosting. - Changing plan with another currency, a declined card or during a Stripe outage ended in a server error instead of a message.
- The queue cron line documented for shared hosting only processed the
defaultqueue. favicon.icowas an empty file./sitemap.xmlreturned a 500 on servers with PHPshort_open_tagenabled.- Production now redirects plain http and other hostnames (www) to the address in
APP_URL. - Migrations failed on servers whose default storage engine is MyISAM (
Specified key was too long); tables are now always created as InnoDB andbyanalytics:doctorfails if any table is not.
Changed
- Contact page: the "Prefer email?" line under the form is gone; the form is the only way in.
- Site connections: choosing the Search Console property also switches on URL Inspection and PageSpeed & CrUX, and sources with nothing to fill in connect with one click instead of opening an empty form.
- Compiled front-end assets (
public/build) are committed, so a deploy is a singlegit pull;scripts/deploy.shno longer builds on the server. - Site connections: Google properties are offered in one list grouped by Google account; picking a property also selects its account, so they can no longer be mismatched.
- One tag for every platform: the install screen shows a single tag instead of per-platform packages and plugins that customers could not obtain, plus copy-and-paste code (PHP or Cloudflare Worker) to report AI crawlers. Public pages and docs no longer advertise a Laravel package, an Astro component or a WordPress plugin.
- Listed prices now include tax (
plans.tax.behavior): the customer pays 19, not 19 plus VAT.byanalytics:stripe-setupreplaces prices created with the other tax treatment and archives the old ones. - The billing currency is set by the visitor's country (euro countries pay in EUR, everyone else in USD) and can no longer be chosen; a live subscription keeps its currency.
- The sitemap lists every language version as its own entry (each with all its alternates), as Google recommends for translated pages.
- The configuration file can have another name and live anywhere outside the project:
BYANALYTICS_CONFIG_FILE, or an optionalbootstrap/config-location.phppointer for shared hosting where environment variables cannot be set. - Legal texts take the hosting and email providers from the configuration, mention Anthropic only while the AI assistant is enabled, and describe failed payments, downgrades, backups and the EU right of withdrawal as implemented.
- Superadmin CRUD: users (create, edit, verify, reset password, clear lockout, disable 2FA, delete), workspaces (create, edit, members, ownership transfer, plan override, trial, delete), sites (create, edit, rotate key, delete) and editable plan limits/features (
/app/admin/plans). - Growth tab and decision-oriented reports: brand vs non-brand search, winners and losers, declining content, click opportunities sized in estimated clicks, what converts by source and page, and GEO results (AI landing pages, AI vs organic, monthly trend). Reports open with three findings and three actions.
- One unified PDF report: executive analysis in prose, three findings and three actions, then every section with charts and a note that interprets each table (acquisition, pages, audience, conversions, AI assistants, Search Console with zero-click queries, opportunities, movers, SEO and GEO). Brand terms are configurable per site.
- Site icon upload (create, replace, remove) and optional owner details that head the PDF report.
- AI-written executive analysis on the Growth tab; when present, the PDF report of that range opens with it.
- Dashboard tabs can be reordered by drag and drop (or Alt + arrow keys); the order is saved automatically per user.
- Access blocks for the superadmin: block an account, an email, an email domain or an IP / CIDR range (
/app/admin/blocks), plusphp artisan byanalytics:unblock. - PDF report per site and date range ("Export PDF" in the dashboard toolbar).
- GEO (generative engine optimization) audit and "AI search" tab: AI crawler access from robots.txt,
llms.txt, per-page quotability signals, a 0–100 readiness score with prioritized recommendations, and referral traffic from AI assistants. - AI assistant tab per site, powered by the Claude API, with a monthly question quota per plan (
ai_messages_per_month). - Laravel 13 scaffold with Breeze (Blade + Alpine CSP build), Tailwind CSS 4, Vite, Pest, Pint and Larastan (level 6).
- Configuration loaded from
byanalytics.confoutside the project (BYANALYTICS_CONFIG_DIR), withbyanalytics.conf.example. php artisan byanalytics:doctorhealth command (config, DB, cron, queue, GeoLite2, Google, Stripe, MaxMind, mail).- English (default) and Spanish UI, localized public routes (
/and/es), hreflang tags and translation-parity tests. - Security headers middleware (nonce-based CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy).
- Design system: tokens, dark mode by default, reusable Blade components.
- GitHub Actions CI (Pint, Larastan, Pest).
- Workspaces with owner/admin/viewer roles, email invitations (hashed tokens), workspace switcher, settings and deletion.
- Sites: CRUD, normalized domains, allowed domains/subdomains, internal search parameters, excluded paths, rotating site keys and an install wizard with live first-event verification.
- Two-factor authentication (TOTP + recovery codes), Google sign-in, account lockout, new-device email alerts and an audit log.
- Plan resolution (override → Stripe → trial → free) with site and seat limits.
- Cookie-less tracker
t.js(2.3 KB): pageviews, engagement time, UTM, internal search, outbound links, downloads,data-ba-eventand JS API, SPA + Astro View Transitions. - Ingestion endpoint
POST /api/ewith origin validation, per-IP and per-site rate limits, bot filtering, quota enforcement and daily-salted anonymous visitor hashes. - Local GeoLite2 geolocation (
byanalytics:geoip-update), source/channel classification (search, social, AI assistants, email, paid). - Hourly and daily rollups with pre-computed filter scopes, realtime presence, salt rotation and plan-based raw event retention.
- Dashboard: "All sites" overview with sparklines and workspace KPIs; per-site tabs (overview, realtime, pages, sources, geo, devices, internal search, events) with date presets, comparison, clickable filters, interactive world map, CSV export and partial navigation.
- Cmd/Ctrl+K command palette, realtime view with 10-second polling, accessible data tables for every chart.
- Public shareable dashboards with optional password (Business plan).
- Demo seeder: three sites with 90 days of realistic traffic.
- Connectors framework with per-integration isolation: Google Search Console (16-month import), URL Inspection (quota aware), PageSpeed Insights + CrUX, Bing Webmaster Tools, Cloudflare GraphQL Analytics, GA4 Data API and Open PageRank; Google account connection with automatic token refresh.
- Search tab (Google & Bing performance, striking-distance keywords, indexing status, backlinks, crawl issues) and Performance tab (Core Web Vitals field data, Lighthouse scores, Cloudflare edge metrics).
- Uptime monitoring with incidents, SSL certificate and domain (RDAP) expiry, robots.txt and sitemap change watch.
- SEO crawler (robots-aware, byanalyticsBot) with 20 issue types and an SEO Health Score.
- SSRF-hardened HTTP client for every customer-supplied URL.
- Cross-source insights engine (traffic drops/spikes, low CTR, striking-distance and dropping keywords, deindexed pages, slow popular pages, failing CWV, expiring SSL/domains, broken pages, robots blocking, disconnected sources).
- Alerts by email (per-user language, opt-in, per-site toggles and thresholds, cooldown) and signed webhooks (Business).
- Weekly email report.
- Usage & billing page: Stripe Checkout, plan swaps, Customer Portal, invoices, monthly/yearly and USD/EUR; 14-day Pro trial without card.
- Quota emails at 80% and 100% of monthly events (once per month).
- Superadmin panel: platform stats, system health, connector status, failed jobs, users, workspaces with plan override, audit log and audited impersonation.
- Public website in English and Spanish: landing page with animated product preview, features, integrations, pricing with plan comparison, documentation, changelog, contact form and live demo.
- Legal page templates (Privacy, Terms, Cookies, DPA, Legal Notice, Acceptable Use) filled from configuration.
- Technical SEO: multi-language sitemap with hreflang alternates, dynamic robots.txt, Open Graph image, favicon and schema.org data.
- Events API (
POST /api/v1/events) with workspace API tokens (Business plan) and a token management page. - Integrations: HTML snippet, Astro component,
byanalytics/laravelpackage (Blade component + server-side client) and WordPress/WooCommerce plugin. - Conversions tab: event and page goals (with
*wildcards) with conversion rate and revenue, and ordered funnels with drop-off. - Chart annotations to mark launches and campaigns on the trend chart.
Changed
- Deleting a site, workspace or account now also removes what database cascades cannot reach: icon files, billing rows, sessions, password resets, invitations and the audit trail.
- The superadmin panel moved from
/adminto/app/adminand is fully responsive. - Accessibility pass: WCAG AA contrast in light and dark themes, landmarks and headings, keyboard-scrollable tables (axe-core clean on audited pages).
- Query budget tests to prevent N+1 regressions; fewer queries on the all-sites overview.
Security
- Deleting an account transfers shared workspaces to an admin instead of deleting teammates' data; API tokens are revoked with their workspace.
- Contact form protected by a honeypot and rate limiting.
- Stripe webhooks are rejected unless a signing secret is configured.