Back to Blog

From Travel Website to Agentic Storefront

How alpina.travel uses Astro, GitHub, and an RDF Knowledge Graph

Tourism is entering a new discovery phase.

For twenty years, travel operators optimized for two audiences: search engines and human visitors. They built websites, landing pages, booking flows, galleries, destination guides, and blog posts. The best sites were fast, beautiful, mobile-friendly, and ranked well on Google.

That still matters. But the next visitor to a travel website may not be a person browsing a menu. It may be an AI agent asking:

We’re taking the kids to the Austrian Alps this winter and want an apartment near a gentle ski area, with a lake, some easy hikes for the off-days, and good food within reach. Which places are actually worth it, and how do a few compare?

That single shift changes the architecture of travel websites. A traditional CMS is designed around humans publishing pages. An agentic storefront is designed around entities, relationships, evidence, and machine-readable actions.

This is the idea behind alpina.travel, a small, family-run alpine travel project we are building with Astro, GitHub, AI agents, and WordLift’s RDF Knowledge Graph. It stays a simple travel website for people. For agents, it now exposes a full, standards-based surface they can discover, read, and query.

Joost Got Here First: Astro as an Agent-Ready SEO Stack

Joost de Valk recently published a definitive guide to Astro SEO, and his argument lines up with where the market is heading: Astro gives us a cleaner, faster, more controllable foundation than a traditional CMS. As he puts it, “the baseline is already fast because there’s nothing slow to do.”

That matters more than it used to, because performance is no longer only a human UX concern. AI agents also operate under latency, retrieval, and inference-cost constraints. Fast, clean, static or hybrid HTML is easier to crawl, parse, cache, and reason over.

Joost is also right about structured data. The problem on most sites is that their markup is a single isolated object, one Article or WebPage node that never describes how things actually connect: who wrote the article, what site it lives on, what organization stands behind it. His answer is a linked @graph wired together with @id references so an agent can walk the relationships, packaged in @jdevalk/seo-graph. His recipes even include a VacationRental with a RentAction — the exact shape a project like ours needs.

This is where WordLift extends the pattern. Joost’s Astro stack produces a strong page-level graph. WordLift adds the missing infrastructure underneath it: a persistent RDF Knowledge Graph as the single source of truth, where that VacationRental is not re-declared page by page but exists once, with a stable identity, connected to everything around it. And that is the deeper shift: out of the page paradigm altogether. The human-centered web is organized around pages; a Knowledge Graph is organized around a contextual graph of entities, relationships, and evidence. You stop composing pages and start modeling the context and evidence an AI needs to reason over your content and act on it.

The Knowledge Graph Comes First

alpina.travel is not a website where structured data is bolted on after the page is written. It is the opposite. The Knowledge Graph defines the world first, and the website is generated from it.

The graph holds the entities that matter to a travel operator: Organization, WebSite, VacationRental, LodgingBusiness, Place, TouristDestination, TouristAttraction, SkiResort, LakeBodyOfWater, Restaurant, SportsActivityLocation, GeoCoordinates, Article, ImageObject, VideoObject.

These are not isolated schema snippets. They are connected entities. A vacation rental is located in a place. A place belongs to a destination. A destination contains attractions. Attractions have coordinates. Images depict places and experiences. Articles explain what the entities mean. The organization publishes and validates the whole thing.

That is the difference between a travel website and a travel Knowledge Graph. A CMS stores pages. A Knowledge Graph stores the meaning behind the offering.

The Real Architecture: GitHub → WordLift KG → Astro → Cloudflare

alpina.travel is a KG-first, agent-ready stack. The RDF Knowledge Graph is the semantic source of truth, Astro is the rendering layer, GitHub is the governed workspace where an AI agent manages data safely, and Cloudflare is the edge.

AI agent working in GitHub
        ↓
Structured YAML, Markdown, media metadata, validation scripts
        ↓
Sync to the WordLift RDF Knowledge Graph
        ↓
WordLift KG becomes the canonical semantic layer
        ↓
Astro fetches KG data and renders fast static pages + agent endpoints
        ↓
Cloudflare delivers the human site and the agent-facing surface

The distinction that matters: we do not add schema markup after publishing pages. We model the travel world first, then render the website from that model.

GitHub is the editorial and data-staging layer; not the source of truth. It holds source content, structured YAML/JSON, Markdown, image metadata, validation scripts, and build logic. Changes move through commits and pull requests, which gives the AI agent a controlled environment instead of letting it mutate production data directly. The repo makes this explicit: entities.yaml is described as an editorial staging representation, with WordLift as the semantic source of truth once synced.

WordLift is the canonical semantic layer. It owns the entities, relationships, schema.org mappings, sameAs links, structured data, and KG summaries. The repo is deliberate about not standing up a parallel, hard-coded schema layer except as a temporary fallback.

Astro is the rendering layer. It produces static pages, JSON endpoints, llms.txt, agent-facing Markdown, and, as we’ll see, the discovery metadata and well-known entrypoints that make the site legible to agents.

Cloudflare is the edge layer. The project is configured for static Astro output with the Cloudflare adapter, MDX, and sitemap integration.

Astro does not own the data. Astro renders the experience. The Knowledge Graph owns the meaning, and it exports that meaning to language models: enhanced entity pages rooted in the linked-data stack, and tools an agent can call to traverse the graph itself.

What the AI Agent Actually Manages

The AI agent does not simply write copy. It manages structured travel knowledge under human supervision.

For Lungau, the canonical entity registry already includes Alpina.travel, Mountain Nests Rentals, Lungau, Mariapfarr, Pichl, Samspitze 2, Prebersee, Fanningberg, Grosseck-Speiereck, Katschberg, Obertauern, Mauterndorf, Tamsweg, Zederhaus, and Gambswirt, along with itineraries, guides, and travel need-states. Each lives under a stable identifier in the WordLift dataset wl01855; the village of Mariapfarr, for example, is https://data.wordlift.io/wl01855/mariapfarr.

The apartment itself, Samspitze 2, is modeled as both VacationRental and LodgingBusiness, with coordinates, a booking URL, guest capacity, bedrooms, bathrooms, media, and a verified date. The property page carries the facts a human or an agent would actually ask for: it sleeps up to six guests, has two bedrooms, two bathrooms and 68 m², Wi-Fi, a balcony, a kitchen, parking, and a shared sauna, and it suits families, ski weekends, summer hiking, and longer alpine stays.

The relationships file turns those facts into graph structure:

Samspitze 2          → locatedIn          → Mariapfarr
Samspitze 2          → provider           → Mountain Nests Rentals
Mariapfarr           → partOf             → Lungau
Samspitze 2          → bestFor            → FamilyHoliday
Samspitze 2          → supportsItinerary  → 3 Days with Kids
Samspitze 2          → supportsItinerary  → Winter Weekend
Prebersee            → suitableFor        → QuietNature
Winter Weekend       → includesPlace      → Fanningberg
Summer Lake Weekend  → includesPlace      → Prebersee

These are not menu labels. They are machine-traversable relationships.

How Data Moves into the Knowledge Graph

A sync script converts the staged YAML into an RDF graph and pushes it to WordLift through the WordLift client, carrying schema.org types, geo coordinates, sameAs links, canonical URLs, media, videos, and relationships.

The script maps local predicates onto schema.org properties:

locatedIn / partOf     → schema:containedInPlace
bestFor / suitableFor  → schema:audience
includesPlace          → schema:containsPlace
hasBase                → schema:location
supportsItinerary      → schema:subjectOf

That mapping is the bridge between editorial staging and semantic infrastructure. The graph is then serialized as Turtle and sent to WordLift via create_or_update_entities.

A manual GitHub Action handles the sync. It currently defaults to dry-run: it installs Python, runs the sync script, exports a KG snapshot, and uploads it as an artifact.

How Astro Consumes the Knowledge Graph

At build time, Astro calls the WordLift Data API for each page URL:

https://api.wordlift.io/data/https/{host}{path}

If the KG returns JSON-LD, Astro injects it into the initial HTML. If the KG has no data for that URL yet, the site falls back to a locally generated graph, so the build stays resilient. The code comment is blunt about the intent: WordLift stays the source of truth once populated. On top of the inline JSON-LD, every page also emits the discovery links described in the next section, so an agent can find the graph even without parsing the body.

The Agentic Storefront: A Standards-Based Surface for AI Agents

The agentic storefront is a sidecar, not the whole website: the same Knowledge Graph, exposed to agents through dedicated interfaces. What changed recently is that this surface is no longer a handful of files. It is built on the open standards that are becoming the agent web’s equivalent of robots.txt and sitemap.xml: RFC 8288 Web Linking, RFC 9727 API Catalog, the Model Context Protocol (MCP), and the Agent2Agent (A2A) agent card.

Think of it as five moves an agent makes, in order.

1. Discovery: how an agent finds the door

Two mechanisms, both standards-based, so nothing here is proprietary to us.

First, every response carries HTTP Link headers (RFC 8288), configured in _headers, advertising the machine surface before an agent parses a single byte of HTML. This is the actual header returned by /lungau/:

link: </llms.txt>; rel="llms-txt"; type="text/plain",
      </.well-known/api-catalog>; rel="api-catalog"; type="application/linkset+json",
      </.well-known/agent-card.json>; rel="service-desc"; type="application/json",
      </.well-known/mcp/server-card.json>; rel="service-desc"; type="application/json",
      </.well-known/ucp>; rel="service-desc"; type="application/json",
      </acp/feed>; rel="describedby"; type="application/json",
      </okf/index.md>; rel="describedby"; type="text/markdown",
      </sitemap-index.xml>; rel="sitemap"; type="application/xml"

In one header an agent learns the whole surface: the plain-text llms.txt, the API catalog, the MCP server card, the A2A agent card, a commerce profile (UCP), an agent-commerce feed (ACP), a Markdown knowledge feed (OKF), and the sitemap.

Second, the RFC 9727 catalog at /.well-known/api-catalog returns a single application/linkset+json document listing every API the site exposes: the machine-readable index of the whole surface. Each entry has an anchor (the resource), optional service-desc links (machine descriptions), service-doc links (human docs), and item links (the concrete data or endpoints). Excerpted from the live catalog:

{
  "linkset": [
    {
      "anchor": "https://alpina.travel/lungau/data/",
      "service-doc": [
        { "href": "https://alpina.travel/lungau/agent-guide.md", "type": "text/markdown" },
        { "href": "https://alpina.travel/llms.txt", "type": "text/plain" }
      ],
      "item": [
        { "href": "https://alpina.travel/lungau/data/apartments.json",  "type": "application/json" },
        { "href": "https://alpina.travel/lungau/data/places.json",      "type": "application/json" },
        { "href": "https://alpina.travel/lungau/data/itineraries.json", "type": "application/json" },
        { "href": "https://alpina.travel/lungau/data/guides.json",      "type": "application/json" }
      ]
    },
    {
      "anchor": "https://alpina.travel/",
      "service-desc": [
        { "href": "https://alpina.travel/.well-known/ucp", "type": "application/json" }
      ],
      "item": [
        { "href": "https://alpina.travel/acp/feed",     "type": "application/json" },
        { "href": "https://alpina.travel/ask",          "type": "application/json" },
        { "href": "https://alpina.travel/okf/index.md", "type": "text/markdown" }
      ]
    }
    // …plus anchors for /mcp/sse (→ the MCP server card) and the A2A agent card
  ]
}

One fetch, and an agent has the destination’s data files, its query endpoint, its feeds, and its transport: no HTML scraping, no guessing paths.

2. Description: every page tied back to the graph

Discovery finds the site; description tells an agent what each page is. Every page emits two links, wired in BaseLayout.astro via a helper in normalize.ts:

<link rel="about" href="https://data.wordlift.io/wl01855/lungau">
<link rel="alternate" type="application/ld+json"
      href="https://api.wordlift.io/data/https/alpina.travel/lungau/"
      title="WordLift Data API — structured data">

The rel="about" link declares, by stable identifier, which entity the page represents — the same wl01855 node the graph and the /ask endpoint use. The rel="alternate" link gives the page’s structured data a canonical, fetchable address on the WordLift Data API, instead of only inlining it in the <head>.

This is the small detail that does the heavy lifting. An agent that lands on /lungau/ doesn’t have to scrape the page — it follows one link to a live, canonical JSON-LD graph, and the about identifier tells it exactly which entity that graph is centered on. The HTML is for people; the alternate is for machines; both point at the same source of truth.

3. Query: an agent can ask, not just read

POST /ask runs natural-language search over the Knowledge Graph and returns grounded results. It is public. The real request and response:

curl -s -X POST https://alpina.travel/ask \
  -H 'Content-Type: application/json' \
  -d '{"query":{"text":"family apartment in Mariapfarr with ski access"}}'
{
  "_meta": {
    "selected_intent": "PRODUCT_SEARCH",
    "selected_backend": "wordlift_neural_search",
    "tenant_id": "alpina",
    "processing_time_ms": 554
  },
  "results": [{
    "@context": "https://schema.org",
    "@type": "ItemList",
    "name": "family apartment in Mariapfarr with ski access",
    "numberOfItems": 5,
    "itemListElement": [
      { "@type": "ListItem", "position": 1, "item": {
          "@type": "Product", "name": "Mariapfarr",
          "description": "One of Austria's sunniest villages, home of the Silent Night lyrics and the Samspitze 2 base apartment.",
          "@id": "https://data.wordlift.io/wl01855/mariapfarr" }},
      { "@type": "ListItem", "position": 4, "item": {
          "@type": "Product", "name": "Pichl",
          "description": "Quiet locality of Mariapfarr where Samspitze 2 is located.",
          "@id": "https://data.wordlift.io/wl01855/pichl" }}
    ]
  }]
}

Notice what comes back. The query is classified (selected_intent: PRODUCT_SEARCH), routed to WordLift neural search over the graph, and answered as a schema.org ItemList of real entities — the village, the region, the neighbouring locality where the apartment sits — each carrying its canonical data.wordlift.io/wl01855 identifier. This is not free-text guessing; it is connected evidence an agent can cite and dereference. The human planner (plan.astro) takes the same @ids and maps them back to the site’s editorial URLs, so people get clickable pages while agents get entities.

4. Transport: an agent can connect over its own protocol

For agents that speak MCP rather than plain HTTP, the same graph is available as a tool server:

GET https://alpina.travel/mcp/sse          # public MCP over Server-Sent Events
      https://alpina.travel/mcp/alpina/*    # tenant / protected MCP

Alongside these sit the commerce and open-feed routes the Link header advertises — UCP at /.well-known/ucp, the ACP feed at /acp/feed, the OKF Markdown feed at /okf/index.md — proxied from the commerce layer, so an agent that wants to move from reading toward transacting has a defined path. The point is not the number of protocols. It is that every one of them dereferences to the same WordLift KG.

5. Policy: explicit, machine-readable AI-usage terms

Finally, the operator states what agents may do, in a form they can read. robots.txt (generated by robots.txt.ts) carries a Content-Signal line:

Content-Signal: search=yes, ai-input=yes, ai-train=yes

Indexing, use as grounding input, use for training — declared explicitly, per site, on the operator’s terms. Control is part of the offer, not an afterthought.

And a bridge back to humans

The same query surface powers people, too. /lungau/plan/ now carries an “Ask Lungau anything” box wired to /ask; it maps WordLift entity IDs back to internal editorial URLs, so a human gets clickable pages while an agent gets entities. And /for-agents/ documents the whole thing — discovery, /ask, MCP, feeds, with curl examples — a front door for any developer building against the site.

The endpoint is not the product. The graph is the product. Every rung of this ladder — the Link header target, the catalog entry, the per-page alternate, the /ask result, the MCP tool — resolves to the same canonical wl01855 entities. Plural protocols, single source of truth. That is the part an Astro-SEO framing does not reach, because it starts from the page.

The Unfair Advantage: Borrow the Region’s Graph

There is a second reason this works especially well here, and it is close to unfair.

A property in Salzburgerland does not have to describe its world from scratch. The region has spent years building its own semantic data layer — an authoritative, linked-data description of its places, valleys, lakes, lifts, trails, events, and towns. As a local operator, you can partner with the region and pull that linked data straight into your own site, interlinking your entities with theirs.

That is the entire point of linked data: it is built to connect. When your apartment’s Place links to Salzburgerland’s canonical entity for Mariapfarr, for Lungau, for a particular ski area, you are not copying facts — you are joining an existing graph. Your small site inherits the region’s context and authority, and an agent traversing your data can walk straight out into the region’s graph, and back. You get the depth of a destination portal without maintaining one.

A personal note. I have spent more than a decade working on exactly this kind of core infrastructure — the semantic, linked-data groundwork that lets regions and brands describe themselves in a way machines can actually use. So there is a particular honour, on a small family project like alpina.travel, in finally standing on the other side of it: not building the data layer this time, but becoming a consumer of it. The infrastructure I helped lay down is now the ground my own family’s alpine holiday sits on.

Why This Matters for Tourism Operators

Every tourism operator sits on hidden knowledge. A hotel knows which rooms work best for families. A destination board knows which hikes are right in spring. A ski resort knows which slopes suit beginners. An apartment owner knows which restaurants are genuinely worth recommending. A regional portal knows how places, seasons, events, and activities connect.

Today most of that knowledge is trapped in pages, PDFs, galleries, booking engines, or someone’s head. Agents need something different: structured, connected, verifiable evidence. They need to know that a rental is in Pichl, that Pichl is a locality of Mariapfarr, that Mariapfarr is in Lungau, that Lungau is in Salzburgerland, that specific hikes, lakes, restaurants, and ski areas are nearby — and that the recommendation is backed by real content, media, and coordinates.

That is not a keyword problem. It is a graph problem.

And here is the part that matters if you run a hotel, a rental, or a destination portal in the region: you don’t rebuild your website to get this. You model your offering once, in the graph, and a thin endpoint layer speaks the standards on top of it — the same Link headers, well-known URIs, MCP, and content signals an agent already encounters everywhere else on the web. An agent can then find your world, read it without scraping, query it in natural language, and cite it with confidence — while you keep explicit control over what it’s allowed to do, and while you refuse to fake what you can’t yet prove, like live availability. A machine-readable twin of your property, sitting beside the site you already have.

From Family Emotion to Structured Evidence

alpina.travel starts from lived experience: family holidays, snow, hikes, lakes, restaurants, morning walks, the emotional memory of a place. The hard part is translating that experience into structured data without losing its soul.

A memory becomes an entity. A hike becomes a route or an attraction. A lake becomes a seasonal experience. A photo becomes evidence. A restaurant becomes a local recommendation. A destination becomes a connected node. A family story becomes something an AI agent can understand, cite, and reuse.

The goal is not to make travel content colder. It is to make authentic experience legible to machines while keeping it meaningful for people.

Conclusion

Astro gives tourism operators a fast, modern, controllable frontend. Joost showed how it becomes an SEO- and agent-ready publishing stack — an important step toward the machine-readable web. WordLift adds the next layer beneath it: a persistent RDF Knowledge Graph as the single source of truth.

In alpina.travel, an AI agent manages data in GitHub, that data is validated and pushed into the Knowledge Graph, and Astro renders it — to fast pages at the Cloudflare edge, and to a standards-based agent surface built on RFC 8288, RFC 9727, MCP, and A2A. For humans, it stays a simple travel website. For agents, it becomes a structured environment they can discover, read, query, and act on. For search engines, it becomes a clearer semantic graph. For the operator, it becomes reusable infrastructure across SEO, AI discovery, automation, recommendations, and booking.

The website is only the visible layer. The real asset is the Knowledge Graph behind it — a semantic memory of places, experiences, media, emotions, and offers that both humans and AI agents can use.

The next traveler may arrive through an agent. The question is whether the agent can understand your world.

Thank You

alpina.travel is a piece of infrastructure and a love letter in equal measure. The last word belongs to the people behind it.

To my wife, and to my beautiful daughters, who share this love for remote alpine places. To Martin and Susanne, my dear Austrian friends, who somehow turned an Italian family into hard-core Lungauers. To Rainer, Hannah, and the entire Salzburgerland team. And to everyone who has been part of this journey: thank you!

The graph holds the data. You are the reason any of it means something.