core · Current reference
SEO and discovery
Apply Core's search, metadata, structured-data, and discovery contracts.
Applies to v1.1.2
Component pins for v1.1.2
| Component | Role | Version |
|---|---|---|
| core | runtime | v1.1.2 |
| theme-shield | theme | v0.1.15 |
| docs | reference | v0.1.6 |
| distribution | tooling | v0.3.19 |
Foundry now emits shared SEO from partial "foundry/seo/render.html" ..
Downstream themes should call that partial from baseof.html, not from a site overrideable head-end.html file. This keeps canonical tags, alternates, metadata, and schema from drifting between core, theme, and project repos.
What core renders
- Canonical URL, including paginator-aware URLs after list page one
- Meta description
- Robots and googlebot tags when
robots,noindex, orprivateare set - Open Graph and Twitter metadata
- Source-visible generator markers:
LikeStyle Foundry Core <version> (Hugo <version>)- a dedicated
foundry-core-versionmeta value
hreflangalternate links for multilingual pages- JSON-LD for:
WebSiteplus the home route’sWebPageWebPageorCollectionPageMedicalWebPagefor medical treatment/article pages when the site declares a medical local businessBreadcrumbListBlogPostingServicePersonFAQPage- optional
Organization/LocalBusiness
Theme responsibility
Theme head-end.html files should only add theme or brand assets such as:
- dark-mode anti-flash scripts
- theme CSS bundles
- favicons
- theme color
- project-specific preconnects
Shared SEO should not be reimplemented there.
Foundry attribution
Core owns a required bottom-of-page attribution hook at
layouts/_partials/hooks/body-end/foundry-attribution.html. It renders the
linked wording Built by Likestyle with Foundry, pointing to
https://likestyle.com/foundry/. The link uses a self-contained dark badge with white
text so site and theme footer colors cannot make the wording unreadable.
Sites may opt into the public
Foundry Attribution Control Runtime. The browser can then
consume an exact show or legacy hide decision from Foundry Control, but that
decision cannot hide the required identity link, wrapper, or local version
identity. Missing configuration, service failure, timeout, and invalid response
also remain fail-visible; source-visible generator and version markers are
never removed.
The SEO partial emits generator metadata, dedicated version meta tags, and an
HTML comment naming LikeStyle Foundry Core. The attribution wrapper includes
data-foundry-core-version plus resolved data-foundry-site-version and
data-foundry-version values, so the deployed site release, owner-facing
Foundry bundle, and technical Core component remain distinguishable in
generated source. See Foundry Attribution
for resolution precedence and the optional visible footer line.
Core resolves the version from the pinned
github.com/jerrybroughton/likestyle-foundry-core entry exposed by
hugo.Deps. Sites may set params.foundry.core_version to an explicit value
such as dev+59a7b8f when a local replacement or release materializer needs to
override the dependency version. If neither source provides a version, Core
reports dev instead of claiming a release.
Custom shells that do not automatically collect Foundry body-end hooks must
call partial "hooks/body-end/foundry-attribution.html" . before </body>.
Site config fields
Supported site params:
params:
description: "Site-wide fallback description"
og_image: "/images/og-default.jpg"
social:
instagram: "https://instagram.com/example"
facebook: "https://facebook.com/example"
contact:
phone: "(555) 555-5555"
email: "info@example.com"
hours: "By appointment only"
seo:
author: "Site Author"
reviewed_by_default: "author-key"
twitter_handle: "@example"
og_image_default: "/images/og-default.jpg"
organization:
name: "Example Company"
logo: "/images/logo.png"
same_as:
- "https://linkedin.com/company/example"
local_business:
types:
- "LocalBusiness"
name: "Example Company"
price_range: "$$"
address:
street_address: "123 Main St"
city: "Anywhere"
region: "CA"
postal_code: "90210"
country: "US"
Supported page params:
canonical: "https://example.com/custom-url/"
robots: "noindex,nofollow"
noindex: true
author: "author-key"
author_name: "Author Name"
author_role: "Medical Reviewer"
reviewed_by: "reviewer-key"
reviewed_on: 2026-03-04
seo:
schema_title: "Visible page heading"
image: "/images/page-social-card.jpg"
image_alt: "Concise description of the social image"
image_width: 1200
image_height: 630
image_type: "image/jpeg"
person:
key: "author-key"
service:
type: "Service"
service_type: "Primary service label"
seo.schema_title is the semantic title used by WebPage, article, and
Service JSON-LD. Set it when the editorial <h1> intentionally differs from
the SEO-oriented front matter title; it should exactly match the visible
heading. Open Graph and Twitter titles continue to use the page title.
For a custom paginated list, initialize the exact paginator before calling the
SEO partial and store its URL in the page scratch as
foundry_seo_canonical. Foundry uses that value for canonical, Open Graph, and
schema URLs. This avoids Core instantiating a second paginator with a different
collection or page size.
Foundry emits article schema only for leaf content pages. Section, taxonomy,
archive, and other list pages keep their WebPage/CollectionPage schema even
when they live under an article section.
Editorial articles resolve every valid contributor through data/authors and
emit the complete typed author array. contributor_type: person becomes a
schema Person; contributor_type: organization becomes an Organization.
Visible bylines, Pagefind author names and keys, social author metadata, and
JSON-LD use the same canonical result. Unknown slug-like contributor keys fail
closed instead of becoming invented public identity. A valid reviewer stays a
separate visible and machine-readable role.
The shared Blog, author, and taxonomy collection templates initialize their
exact paginator before SEO renders. Page two and later therefore use their own
paginator URL for canonical, Open Graph, and schema identity. Section and term
RSS outputs are advertised with <link rel="alternate" type="application/rss+xml">.
See Editorial Constellation.
The rendered discoverability validator verifies every advertised RSS 2.0 or Atom 1.0 feed from the static output tree. Advertised targets must exist on the canonical site, parse as the declared format, use unique absolute entry URLs, and avoid missing rendered targets. A noindex entry is reported for review because its intent is site-owned rather than intrinsically invalid. Feeds remain optional; the contract applies once a page advertises one.
Rendered JSON-LD validation also checks absolute and unique top-level @id
values when present, BreadcrumbList item structure and positions, article
date ordering and future dates, author type/name/URLs, and
mainEntityOfPage agreement with the page canonical. These checks prove
internal consistency only; valid structured data does not guarantee a search
feature or ranking.
seo.image is the canonical page-level SEO/social image field advertised by
the page manifest. og_image remains a compatibility alias. The resolved
precedence is seo.image, og_image, the shared discovery image, the
site-level params.og_image, and then params.seo.og_image_default.
Image width, height, and MIME metadata are emitted only when the page supplies
the real values above. Core no longer claims 1200x630 for every image.
reviewed_on is the canonical editorial review-date field used by the shared
article shell and JSON-LD resolver. It emits lastReviewed on the page schema,
and the date must remain visible in rendered content. reviewed_at, reviewed,
and last_reviewed remain compatibility aliases.
The discovery model infers search.kind: service for the services section
and search.kind: treatment for the treatments section. Explicit
search.kind remains preferred when a service lives elsewhere. Both service
and treatment kinds can emit Service JSON-LD; seo.service can supply the
visible service type and other supported service facts.
Robots
Set enableRobotsTXT: true in each downstream site config. Shared and site-owned theme repos can expose layouts/robots.txt wrappers that call the shared Foundry robots partial.
Do not add a static robots.txt containing an example-domain sitemap. Static
files override the site-aware template and can silently publish the wrong host.
Projects with their own custom base layout and no theme layouts/robots.txt wrapper should add a local layouts/robots.txt file that calls:
{{ partial "foundry/seo/robots.txt" . }}
The wildcard contract remains backward compatible:
params:
seo:
robots_txt:
disallow:
- /private/
allow:
- /private/public-guide/
extra: |-
Crawl-delay: 2
Foundry also supports opt-in purpose-aware crawler profiles and per-crawler overrides:
params:
seo:
robots_txt:
profile: search-visible-no-training
crawlers:
gptbot: inherit
perplexitybot: disallow
Crawler override keys come from
data/foundry/crawler-policy-registry.json; valid values are inherit,
allow, and disallow. An allow rule preserves the wildcard path rules for
that crawler rather than bypassing site-wide private paths. A disallow rule
blocks / for that crawler. Unknown profiles safely fall back to inherit,
and unknown crawler keys produce no group.
See AI Discovery for profile intent, crawler-purpose boundaries, user-fetch limitations, and the rendered discoverability validator.