Skip to main content

Web Service

Reference for the web service that renders pages, manages templates, serves assets, and controls the entire presentation layer of a systemprompt.io application.

TL;DR: The web service is the presentation layer of systemprompt.io. It takes content from the database, selects a Handlebars template, injects navigation and theme data, and returns fully rendered HTML. All visual configuration -- branding, colors, fonts, navigation, homepage sections -- lives in YAML files under services/web/. CSS source files live in storage/files/css/ and are registered in the web extension.

What It Does and Why It Matters

Every page a visitor sees passes through the web service. Blog posts, documentation, feature pages, product pages, and the homepage all follow the same pipeline:

  1. A request arrives and the router resolves it to a content record or a prerendered page.
  2. The web extension selects the correct template based on content type.
  3. Page data providers inject navigation, branding, sidebar, and section data into the template context.
  4. Component renderers expand partials (header, footer, head-assets, scripts).
  5. The Handlebars engine renders the final HTML and returns it to the browser.

This means you change how things look by editing YAML and HTML -- not Rust code. The Rust extension wires everything together, but the day-to-day configuration is declarative.

Directory Structure

services/web/
├── config.yaml              # Branding, fonts, colors, layout tokens, paths
├── metadata.yaml            # SEO defaults and site-wide metadata
├── config/
│   ├── homepage.yaml        # Homepage sections (hero, pricing, FAQ, etc.)
│   ├── navigation.yaml      # Header, footer, docs sidebar, platform sidebar
│   ├── theme.yaml           # Design tokens (colors, typography, spacing)
│   └── features/            # Optional feature landing page configurations
└── templates/
    ├── templates.yaml        # Template registry
    ├── homepage.html         # Homepage template
    ├── blog-post.html        # Blog article template
    ├── blog-list.html        # Blog listing template
    ├── docs-page.html        # Documentation page template
    ├── feature.html          # Feature page template
    ├── feature-page.html     # Feature landing page template
    ├── legal-post.html       # Legal page template
    └── partials/             # Reusable components

Template System (Handlebars)

Templates use Handlebars syntax. The web extension registers component renderers that expand partials and page data providers that populate the template context.

Basic Template Structure

<!DOCTYPE html>
<html lang="en">
<head>
  <title>{{TITLE}}</title>
  {{> head-assets}}
</head>
<body>
  {{> header}}
  <main>{{{CONTENT}}}</main>
  {{> footer}}
  {{> scripts}}
</body>
</html>

Template Variables

Variable Source Notes
{{TITLE}} Content frontmatter Page title
{{DESCRIPTION}} Content frontmatter Meta description
{{{CONTENT}}} Rendered Markdown Triple braces output unescaped HTML
{{SLUG}} Content frontmatter URL path
{{> partial-name}} Partials directory Includes a reusable component

Registered Partials

Partial File Purpose
head-assets partials/head-assets.html CSS links, meta tags, font preloads
header partials/header.html Site header with navigation and CTA
footer partials/footer.html Site footer with links and social icons
scripts partials/scripts.html JavaScript includes
content-card partials/content-card.html Reusable content card component
animation-* partials/animation-*.html SVG/CSS animations for feature pages

Note: The head-assets, header, footer, and scripts partials are compiled into the binary via include_str!. Editing them requires a rebuild (just build) and a server restart before just publish takes effect.

Template Types

Template Content Type
homepage.html Landing page (prerendered from homepage.yaml)
docs-page.html Documentation articles
blog-post.html Blog articles
blog-list.html Blog listing / index
feature.html Feature detail pages
feature-page.html Feature landing pages
legal-post.html Legal / policy pages

Navigation is defined in services/web/config/navigation.yaml and injected into every page by the NavigationPageDataProvider.

Header Navigation

header:
  items:
    - id: features
      label: "Features"
      href: "/features/dashboard"
      dropdown: true
      sections:
        - title: "Platform"
          links:
            - label: "Dashboard"
              href: "/features/dashboard"
    - id: blog
      label: "Blog"
      href: "/blog"
  cta:
    label: "Login"
    href: "/admin/login"

Each header item can be a simple link or a dropdown with grouped sections. The cta field renders a call-to-action button on the right side of the header.

The footer key groups links into categories: infrastructure, capabilities, integrations, resources, legal, and contact. Each is a list of path/label or href/label pairs.

Documentation Sidebar

The docs_sidebar key defines the left-hand navigation tree on documentation pages.

docs_sidebar:
  - title: "Getting Started"
    links:
      - label: "Overview"
        href: "/documentation/"
      - label: "Installation"
        href: "/documentation/installation"
  - title: "Services"
    links:
      - label: "Web"
        href: "/documentation/services/web"

Theming and Branding

Theme tokens are split across two files that are merged at startup:

  • services/web/config.yaml -- branding identity, fonts, colors, layout, and mobile overrides
  • services/web/config/theme.yaml -- runtime design tokens (same structure, used for theme overrides)

Branding

branding:
  name: "systemprompt.io"
  title: "AI Agent Infrastructure | systemprompt.io"
  description: "Production-ready AI infrastructure..."
  themeColor: "#f79938"
  logo:
    primary:
      svg: "/files/images/logo.svg"
      png: "/files/images/logo.png"
    dark:
      png: "/files/images/logo.png"
  favicon: "/files/images/favicon.ico"

Design Tokens

The theme system provides a full token set consumed by CSS variables. Key groups include colors (light/dark palettes), typography (sizes xs-xxl, weights 400-700), spacing (xs-xxl), radius, shadows, animation durations, zIndex layers, layout geometry, and mobile overrides. See services/web/config/theme.yaml for the complete token reference.

Fonts

systemprompt.io uses self-hosted web fonts. Families are declared in the fonts section of services/web/config.yaml, the @font-face rules live in storage/files/css/core/fonts.css, and the head-assets partial preloads the primary .woff2 files:

  • Body: OpenSans
  • Headings and brand: Inter

Font files live in storage/files/fonts/.

Static Assets Management

Assets (CSS, JavaScript, images) follow a strict source-to-output pipeline.

Where Files Live

storage/files/css/           <- CSS SOURCE (create and edit files here)
storage/files/js/            <- JS SOURCE
storage/files/fonts/         <- Font files
storage/files/images/        <- Image files
web/dist/                    <- OUTPUT (generated, never edit directly)

How Assets Are Registered

Every asset must be declared in the web extension's required_assets() method (in extensions/web/src/extension_impl.rs), which calls web_assets() from extensions/web/site/src/assets/:

macro_rules! css {
    ($p:expr, $name:literal) => {
        AssetDefinition::css($p.join($name), concat!("css/", $name))
    };
}

css!(p, "core/variables.css"),

The first AssetDefinition argument is the source path under storage/files/. The second is the output path under web/dist/.

Adding a New Asset

  1. Create the file in storage/files/css/ (or js/).
  2. Add an entry in extensions/web/site/src/assets/ (css.rs, js_pages.rs, or js_services.rs).
  3. Build and publish:
    just build
    just publish
    

CSS Architecture

CSS is organized into layers inside storage/files/css/:

Layer Files Purpose
Core core/variables.css, core/fonts.css, core/reset.css CSS custom properties, font-face rules, browser reset
Components components/header-core.css, components/header-dropdown.css, components/footer.css, components/mobile-menu.css, components/cta-buttons.css Shared UI components
Page-level homepage-hero.css, homepage-features.css, homepage-sections-*.css, homepage-demo-*.css Homepage-specific styles
Content blog-base.css, blog-code.css, blog-*.css, docs-layout.css, docs-*.css Content type styles
Features feature-base-*.css, feature-page-*.css Feature page styles
Admin admin-bundle.css, admin/*.css Admin dashboard styles

Important: Never put CSS files in extensions/*/assets/css/. All CSS source files belong in storage/files/css/.

Server-Side Rendering Pipeline

The web extension implements several traits from the systemprompt library that together form the SSR pipeline:

Trait Implementation Role
PageDataProvider NavigationPageDataProvider, HomepagePageDataProvider, DocsPageDataProvider, BlogListPageDataProvider, BlogPostPageDataProvider Inject data into the template context for each page type
ContentDataProvider DocsContentDataProvider Provide additional content data (e.g., child docs, related pages)
PagePrerenderer HomepagePrerenderer, FeaturePagePrerenderer Generate static pages from YAML config at build time
ComponentRenderer HeadAssetsPartialRenderer, HeaderPartialRenderer, FooterPartialRenderer, ScriptsPartialRenderer, animation renderers Expand Handlebars partials with runtime data

Prerendered Pages

Some pages do not come from Markdown content. Instead, they are prerendered from YAML configuration:

  • Homepage -- built from config/homepage.yaml by HomepagePrerenderer
  • Feature pages -- built from config/features/*.yaml by FeaturePagePrerenderer (the directory is optional)

Run prerendering with:

systemprompt infra jobs run content_prerender

Background Jobs

The web extension registers jobs for content and asset management. Key jobs: blog_content_ingestion and content_sync (ingest Markdown into the database), copy_extension_assets (copy assets to web/dist/), content_prerender (generate static pages), sitemap_generation, robots_txt_generation, llms_txt_generation, and publish_pipeline (run the full publish workflow). Execute any job with:

systemprompt infra jobs run <job_name>

Validation

# Validate all web configuration
systemprompt web validate

# Validate a single category
systemprompt web validate --only templates
systemprompt web validate --only assets
systemprompt web validate --only sitemap

CLI Reference

Command Description
systemprompt web content-types Manage content types
systemprompt web templates List, show, create, edit, and delete templates
systemprompt web assets List and inspect registered assets
systemprompt web sitemap Show sitemap configuration and generate sitemap.xml
systemprompt web validate Validate configuration, templates, assets, and sitemap

Run systemprompt web <command> --help for detailed options.

Troubleshooting

Styles not applying -- Verify the CSS file exists in storage/files/css/ and is registered in extensions/web/site/src/assets/. Rebuild with just build and run just publish.

Navigation not updating -- Check YAML syntax in services/web/config/navigation.yaml. The navigation config is loaded once at startup via OnceLock, so an application restart is required.

Template rendering errors -- Look for missing Handlebars variables or unclosed tags. Run systemprompt infra logs view --level error --since 1h to see the template name and line number.

Prerendered pages stale -- Run systemprompt infra jobs run content_prerender to regenerate homepage, feature, and product pages from their YAML configs.

Fonts not loading -- Confirm font files exist in storage/files/fonts/ and are declared in the fonts section of services/web/config.yaml.