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/            # Feature landing page configurations
│   └── products/            # Product page configurations
└── templates/
    ├── 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
    ├── product-page.html     # Product page template
    ├── legal-post.html       # Legal page template
    ├── platform-page.html    # Platform documentation 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

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
product-page.html Product pages
legal-post.html Legal / policy pages
platform-page.html Platform documentation

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 four categories: legal, contact, features, and resources. 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. There is also a separate platform_sidebar for platform 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 declared in config and loaded via @font-face rules generated by the head-assets partial:

  • Body: OpenSans (400, 500, 600, 700) -- .ttf files
  • Headings: Inter (400, 500, 600, 700) -- .woff2 files
  • Brand: Inter SemiBold (600) -- .woff2

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, which calls web_assets() in extensions/web/src/assets.rs:

AssetDefinition::css(
    storage_css.join("core/variables.css"),
    "css/core/variables.css",
),
AssetDefinition::js(
    storage_js.join("analytics.js"),
    "js/analytics.js",
),

The first 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 AssetDefinition entry in extensions/web/src/assets.rs.
  3. Build and copy assets:
    just build
    systemprompt infra jobs run copy_extension_assets
    

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.css, components/footer.css, components/mobile-menu.css Shared UI components
Page-level homepage.css, homepage-hero.css, homepage-sections.css, homepage-pricing.css, homepage-demo.css Homepage-specific styles
Content blog.css, blog-code.css, blog-layout.css, blog-typography.css, docs.css, paper.css Content type styles
Features feature-base.css, feature-rust.css, feature-cli.css, feature-memory.css, feature-agentic-mesh.css Feature page styles
Animations animation-cli-remote.css, animation-memory-loop.css CSS animations for feature pages
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, LegalPageDataProvider 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, ProductPagePrerenderer 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
  • Product pages -- built from config/products/*.yaml by ProductPagePrerenderer

Run prerendering with:

systemprompt infra jobs run content_prerender

Background Jobs

The web extension registers jobs for content and asset management. Key jobs: content_ingestion (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 templates only
systemprompt web templates

# Validate assets
systemprompt web assets

# Validate sitemap
systemprompt web sitemap

CLI Reference

Command Description
systemprompt web content-types Manage content types
systemprompt web templates List and inspect templates
systemprompt web assets List registered assets
systemprompt web sitemap Sitemap operations
systemprompt web validate Validate configuration, templates, and assets

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/src/assets.rs. Rebuild with just build and run systemprompt infra jobs run copy_extension_assets.

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.