PHPackages                             globetrotters-ai/symfony-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. globetrotters-ai/symfony-bundle

ActiveSymfony-bundle

globetrotters-ai/symfony-bundle
===============================

Serves the Globetrotters Official AI Presence at a Symfony site's apex: llms.txt, ai.json, schema.json and the .well-known artefact set, with scheduled refresh, stale-serve, server-rendered homepage JSON-LD and agent-traffic reporting.

v0.2.0(today)03↑2900%MITPHPPHP &gt;=8.2CI passing

Since Jul 23Pushed todayCompare

[ Source](https://github.com/globetrotters-ai/gt-symfony-bundle)[ Packagist](https://packagist.org/packages/globetrotters-ai/symfony-bundle)[ Docs](https://globetrotters.ai)[ RSS](/packages/globetrotters-ai-symfony-bundle/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (20)Versions (6)Used By (0)

Globetrotters AI Presence — Symfony Bundle
==========================================

[](#globetrotters-ai-presence--symfony-bundle)

[![Latest Version](https://camo.githubusercontent.com/4564eb26bc2d8622430a4eff510eb655afa04042757a29214f0ada53a6b0020b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f676c6f626574726f74746572732d61692f73796d666f6e792d62756e646c652e737667)](https://packagist.org/packages/globetrotters-ai/symfony-bundle)[![Downloads](https://camo.githubusercontent.com/3ee5b9c6093860ca2a3318f8607990d88100b130dda160a3c6f33aa926df9b96/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f676c6f626574726f74746572732d61692f73796d666f6e792d62756e646c652e737667)](https://packagist.org/packages/globetrotters-ai/symfony-bundle)[![CI](https://github.com/globetrotters-ai/gt-symfony-bundle/actions/workflows/ci.yml/badge.svg)](https://github.com/globetrotters-ai/gt-symfony-bundle/actions/workflows/ci.yml)[![PHP Version](https://camo.githubusercontent.com/c87a34e5979d817e5f3c341d8ba17986def0854d2f56945e6c923454a4d19477/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f676c6f626574726f74746572732d61692f73796d666f6e792d62756e646c652e737667)](composer.json)[![License: MIT](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)

Serves your [Globetrotters](https://globetrotters.ai) Official AI Presence at your site's apex domain. The bundle pulls the published artefact set from your Globetrotters subdomain on a schedule, caches it, and serves it from a `kernel.request` subscriber that runs **before routing** — so it works even when a reverse proxy, security bundle, or catch-all controller would otherwise intercept `/.well-known/*`, and serving needs no filesystem write access.

What your apex serves once installed:

PathContent-Type`/llms.txt``text/plain; charset=utf-8``/ai.json``application/json; charset=utf-8``/schema.json``application/ld+json; charset=utf-8``/.well-known/mcp.json``application/json; charset=utf-8``/.well-known/agent-card.json``application/json; charset=utf-8``/.well-known/globetrotters-apex-version.json``application/json; charset=utf-8` (version/drift marker)Every artefact response carries `X-Content-Type-Options: nosniff`, `Cache-Control: no-store, private`, `Surrogate-Control: no-store` and `Access-Control-Allow-Origin: *` (every artefact is public, unauthenticated, read-only metadata, so it's readable cross-origin by browser-context agent clients). Heavy files (`llms-full.txt`, `content.md`) are intentionally not served locally — they are linked back to Globetrotters by absolute URL.

On top of the routes, the bundle:

- **reports agent traffic** to those six paths back to Globetrotters, so an apex install still shows up in Presence Analytics (see [Reporting agent traffic](#reporting-agent-traffic));
- injects a **server-rendered, breakout-safe JSON-LD** `` (built from the cached `schema.json`) into your homepage HTML, so crawlers see it in the raw markup without executing JavaScript;
- decorates `/robots.txt` with the AI-crawler allow-list and a `Sitemap:` directive pointing at your Globetrotters-hosted sitemap (or serves a generated `robots.txt` when your app has none);
- **stale-serves**: the cached bundle is only ever replaced by a fully successful pull, so an unreachable Globetrotters leaves the last known good version serving.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Symfony 6.4 LTS or 7.x

Install
-------

[](#install)

```
composer require globetrotters-ai/symfony-bundle
```

If your app doesn't use Symfony Flex, register the bundle manually in `config/bundles.php`:

```
Globetrotters\AiPresenceBundle\GlobetrottersAiPresenceBundle::class => ['all' => true],
```

Configure
---------

[](#configure)

```
# config/packages/globetrotters_ai_presence.yaml
globetrotters_ai_presence:
    website_url: 'https://your-site.globetrotters.ai'  # the published GT subdomain to pull from
    refresh_interval: 'daily'                          # daily | weekly
    cache_pool: 'cache.app'                            # optional: which PSR-6 pool to use
    homepage_path: '/'                                 # optional: where the JSON-LD head injection applies

    reporting:                                         # optional: see "Reporting agent traffic"
        endpoint: '%env(GLOBETROTTERS_INGEST_ENDPOINT)%'
        ingest_token: '%env(GLOBETROTTERS_INGEST_TOKEN)%'
```

Then trigger the first pull and verify:

```
bin/console gt:refresh --force   # pull now
bin/console gt:status            # installed vs latest version, last refresh, last error
curl -I https://your-domain.example/llms.txt
curl -s https://your-domain.example/.well-known/globetrotters-apex-version.json
```

Keeping it fresh
----------------

[](#keeping-it-fresh)

Two supported wirings — pick one.

### 1. Cron (default, zero-worker deployments)

[](#1-cron-default-zero-worker-deployments)

Schedule `gt:refresh` from system cron or a systemd timer. The command respects `refresh_interval` internally (it no-ops when a refresh isn't due yet), so running it hourly is safe:

```
0 * * * * cd /srv/app && bin/console gt:refresh >/dev/null 2>&1
```

### 2. symfony/scheduler (if you already run Messenger workers)

[](#2-symfonyscheduler-if-you-already-run-messenger-workers)

With `symfony/scheduler` and `symfony/messenger` installed, the bundle auto-registers a schedule named `gt` that dispatches a refresh at the configured cadence. Run it with:

```
bin/console messenger:consume scheduler_gt
```

Reporting agent traffic
-----------------------

[](#reporting-agent-traffic)

An apex install is **pull-and-cache, not proxy**. A request to `https://your-domain.example/llms.txt` is served by this bundle and terminates inside your application — it never touches a Globetrotters edge, so without this it is invisible and your apex looks like it gets no agent traffic at all.

Turn it on by pasting the two values Studio issues together on the apex install screen (the token is shown **exactly once**):

```
# .env.local, or better, the Secrets vault: bin/console secrets:set GLOBETROTTERS_INGEST_TOKEN
GLOBETROTTERS_INGEST_ENDPOINT=https://api.globetrotters.ai/presence/analytics/server-log
GLOBETROTTERS_INGEST_TOKEN=…
```

Both are required; until both are set nothing is captured and nothing is written to disk. What is sent, per served artefact request: a UUID, a UTC timestamp, the canonical path, the User-Agent, the client IP, the referer, the status and the byte size. No cookies, no per-visitor identifiers. The backend uses the IP transiently to verify the agent against published vendor ranges and to resolve a country, then drops it — it is never stored.

### Scheduling the flush

[](#scheduling-the-flush)

Events are buffered locally and flushed at most every 15 minutes. Three lanes, all sharing one interval, so whichever you have wins and the others stay dormant.

**1. Cron (recommended).** The command enforces the 15-minute cadence itself, so running it more often is safe:

```
*/5 * * * * cd /srv/app && bin/console gt:presence:flush >/dev/null 2>&1
```

**2. symfony/scheduler.** With `symfony/scheduler` and `symfony/messenger` installed the `gt` schedule dispatches a flush every 15 minutes, alongside the artefact refresh (`bin/console messenger:consume scheduler_gt`).

**3. `kernel.terminate` fallback (on by default).** For a shared host with no cron and no worker: after a response has been sent, an artefact request triggers at most one flush per 15 minutes. Because it runs post-response it costs the visitor nothing. Set `reporting.opportunistic_flush: false` to disable it.

`bin/console gt:status` reports which lane last flushed, how many events are buffered, how many were dropped, and whether client-IP resolution looks trustworthy.

### Behind a proxy or CDN

[](#behind-a-proxy-or-cdn)

The client IP is what lets the backend confirm that a claimed ClaudeBot hit really came from Anthropic. Resolution uses Symfony's own [`framework.trusted_proxies`](https://symfony.com/doc/current/deployment/proxies.html) — **without it every hit reports your proxy's address and every row is recorded unverified.** `gt:status` flags this.

Behind Cloudflare, also set `reporting.trust_cloudflare_header: true` to read `CF-Connecting-IP`. It is off by default and only honoured for requests arriving from a declared trusted proxy: the header is forgeable, and trusting it blindly would let anyone claim a vendor IP.

### Full reporting options

[](#full-reporting-options)

```
globetrotters_ai_presence:
    reporting:
        enabled: true                                        # kill switch for a configured install
        endpoint: '%env(GLOBETROTTERS_INGEST_ENDPOINT)%'
        ingest_token: '%env(GLOBETROTTERS_INGEST_TOKEN)%'
        buffer_dir: '%kernel.project_dir%/var/globetrotters-ai-presence'
        opportunistic_flush: true                            # the kernel.terminate lane
        trust_cloudflare_header: false                       # read CF-Connecting-IP
```

Twig alternative for the head injection
---------------------------------------

[](#twig-alternative-for-the-head-injection)

The JSON-LD injection is automatic on `homepage_path`. If you'd rather place the tag explicitly, use the Twig function in your base template — the automatic injector detects the rendered tag and won't duplicate it:

```
{{ gt_ai_presence_head() }}
```

Caveats
-------

[](#caveats)

- **Static files shadow the kernel.** If a real file exists in `public/` for one of the artefact paths (or `public/robots.txt`), your web server serves it directly and the bundle never sees the request. Delete the static copies when migrating from the file-drop lane.
- **`cache:clear` empties `cache.app`.** The artefacts then fall through to your app until the next `gt:refresh`, and the reporting lane forgets when it last flushed successfully — buffered events themselves live in `buffer_dir` and survive. For durability across deploys, point `cache_pool` at a pool that survives cache clears (e.g. a Redis-backed pool).
- **Don't use a per-process pool.** `cache_pool` must be shared between CLI and web (filesystem, Redis, shared APCu) — with an in-memory pool, CLI refreshes would be invisible to web requests.
- The configured `website_url` is fetched with an SSRF guard (private/reserved IPs are rejected), a 5-second timeout, and a 1 MiB per-file size cap.
- **Reporting needs a writable `buffer_dir`**, shared by the web user and whoever runs the flush — the rest of the bundle needs no filesystem write access, and an install that doesn't report never creates the directory. It holds at most 5000 events or 512KB; past that the oldest are dropped and counted, and the count is reported so the gap is visible rather than silent. `gt:status` shows both.
- **An accepted flush is not proof the token is right.** The ingest endpoint answers `202` to a bad token, an unknown install and a malformed body alike, deliberately revealing nothing about which tokens exist. `gt:status` distinguishes "configured but never accepted" from "reporting normally", but confirm the numbers in Studio.

Development
-----------

[](#development)

```
make install   # composer update
make ci        # php-cs-fixer + phpstan + phpunit (unit + integration)
```

The integration suite boots a real `HttpKernel` with a catch-all "antagonist" controller and a network-free fake fetcher, proving route pre-emption, robots decoration, raw-HTML JSON-LD, and stale-serve end to end. It also covers the reporting lane: capture through a live kernel, the console and `kernel.terminate` flush lanes, the buffer under forked concurrent writers, and the no-store headers surviving Symfony's own `HttpCache`.

License
-------

[](#license)

Released under the [MIT License](LICENSE). © 2026 Globetrotters.ai

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/394356?v=4)[Benoit Hediard](/maintainers/benorama)[@benorama](https://github.com/benorama)

---

Top Contributors

[![benorama](https://avatars.githubusercontent.com/u/394356?v=4)](https://github.com/benorama "benorama (6 commits)")

---

Tags

agenticaijson-ldllms-txtsymfonysymfony-bundlesymfonybundleaiJSON-LDagenticllms.txtglobetrotters

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/globetrotters-ai-symfony-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/globetrotters-ai-symfony-bundle/health.svg)](https://phpackages.com/packages/globetrotters-ai-symfony-bundle)
```

###  Alternatives

[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

595.8M684](/packages/shopware-core)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k6.0M780](/packages/sylius-sylius)[shopware/platform

The Shopware e-commerce core

3.4k1.5M3](/packages/shopware-platform)[contao/core-bundle

Contao Open Source CMS

1301.7M3.1k](/packages/contao-core-bundle)[symfony/security-bundle

Provides a tight integration of the Security component into the Symfony full-stack framework

2.5k190.0M2.6k](/packages/symfony-security-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M236](/packages/sulu-sulu)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
