InfraGuard

Public landing, demo requests & analytics tags

The surfaces that open without login (landing, demo form, user manual), the GA4/GTM tags that measure their visitors, and the admin screen that works the resulting demo requests.

Overview — what it covers

Besides the console behind login, InfraGuard has a public surface.

Path Screen Access
/ marketing landing guests (an authenticated session redirects to /console)
/demo demo request form guests
/manual user manual guests and logged-in users alike
/login · /lang/{locale} login · language switch guests

The console home lives at /console. That move handed / to the public surface without breaking the route name (overview) or existing bookmarks.

The landing is built as a separate bundle (front.*). Putting it in the console bundle would make every visitor download Vue, ECharts, CodeMirror, Konva and xterm (measured: roughly 1,283KB console versus 24KB front). For a marketing page, first impression is loading speed. Only the minimal shared UI (icon buttons, language switcher) lives in _shared-ui so both sides use the same definition — copy-pasting it would let one side drift.

The public manual

/manual opens without login. Someone evaluating the product should be able to read how it is actually operated, and public documentation is itself a marketing and SEO asset. The documents contain no secrets and no live data.

Demo requests

The public form (/demo)

This is a request intake, not a one-click demo. Only a name and a work email are required; locale, referral source and IP are recorded alongside.

This form is the only guest write path in the application, so it is defended four ways.

  1. Honeypot — if filled, nothing is stored but the response still looks like success (never announce detection).
  2. throttle:8,1 — the ninth request in a minute gets a 429.
  3. CSRF token.
  4. Server validation with a whitelist — status and note are admin-only, so the public form cannot set them.

The admin screen (Administration ▸ Demo requests)

/admin/demo-requests runs the inbox: modal CRUD plus search, status filters with counts, and pagination.

Status Meaning
new received, untouched
contacted reached out
qualified qualified opportunity
closed finished

Search escapes LIKE wildcards (%, _, \). Without that, typing a single % would match every row.

Analytics tags (GA4 · GTM · Search Console)

Put a measurement ID in .env and the tag appears on the public surface only. When the value is empty no <script> is rendered at all, so a default deployment makes zero outbound requests.

Key Value Purpose
GA_MEASUREMENT_ID G-XXXXXXXXXX gtag.js (GA4)
GTM_CONTAINER_ID GTM-XXXXXXX Google Tag Manager (can run alongside)
GOOGLE_SITE_VERIFICATION meta token Search Console ownership
GA_PRODUCTION_ONLY true (default) fire only in production

Never add these to the console (layouts/app). Console URLs and titles carry host names, IPs, instance aliases and incident titles. Sending those to a third-party analytics property contradicts the self-hosting and data-sovereignty promise the landing page itself makes, and leaks customer infrastructure detail.

GA4 does not give you search keywords. It reports the channel (organic search) and stops there; the queries live only in Search Console. That is why the ownership meta tag is emitted independently of GA4 — you can verify ownership without enabling GA4 at all. For per-site reporting see Visitor & search insights.

Copy policy

Trust figures on the landing use only what can be counted in code, and they are counted at render time rather than hardcoded (ruleset count, check count, mail/SMS adapter counts, and so on).

This policy caught a real error — the check count was 36, not 37. Conversely, observed values are never used: distinct signature counts run 43 over 24h, 67 over 7d and 79 overall, so they would change between visits and undermine trust rather than build it. Being a public page, it also never touches the DB — a query per visit is unnecessary, and the marketing surface should survive a wobbling database.

Data sources

Target Location Role
infr_demo_request default (000770) demo request inbox (status, note, locale, source, ip)
config/analytics.php ← .env config measurement IDs and gating SOT (App\Support\Analytics)
public/brand/ static files logo and OG images

public/brand/ must be committed. Most of public/ is generated output (build, hot, storage are gitignored) so it is easy to miss, but brand assets are not Vite output — nothing builds them, and without the commit the logo and OG image are 404 in production. The OG image must be PNG: many social crawlers will not render an SVG preview.

Permissions

Troubleshooting · FAQ

Related

The public landing page
The public landing page