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.
On this page
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:
- A request arrives and the router resolves it to a content record or a prerendered page.
- The web extension selects the correct template based on content type.
- Page data providers inject navigation, branding, sidebar, and section data into the template context.
- Component renderers expand partials (header, footer, head-assets, scripts).
- 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 Configuration
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.
Footer Navigation
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 overridesservices/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) --
.ttffiles - Headings: Inter (400, 500, 600, 700) --
.woff2files - 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
- Create the file in
storage/files/css/(orjs/). - Add an
AssetDefinitionentry inextensions/web/src/assets.rs. - 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 instorage/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.yamlbyHomepagePrerenderer - Feature pages -- built from
config/features/*.yamlbyFeaturePagePrerenderer - Product pages -- built from
config/products/*.yamlbyProductPagePrerenderer
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.
Related Documentation
- Content Service -- where page content comes from
- Config Service -- how YAML configuration is loaded
- Files Service -- static file serving
- Web Extensions -- building web extensions in Rust
- Page Data Providers -- injecting data into templates
- Component Renderers -- implementing Handlebars partials
- Page Prerenderers -- generating static pages from config
- Asset Declaration -- registering CSS and JS assets