WEB SERVER & PUBLISHER. ONE BINARY, ONE PORT, ONE AUDIT SURFACE.
One Rust process serves your website, blog, and documentation, indexes content in Postgres full-text, records campaign clicks, and runs the RBAC middleware that governs every AI tool call.
One Process, One Port
A marketing CMS, a Hugo documentation site, and an AI governance gateway running alongside them is three vendors, three deploy pipelines, and three attack surfaces. Every incident review asks which one served the request. Every compliance review asks which one holds the log. systemprompt.io collapses them into one Rust process on one port.
The same binary that runs the RBAC check in front of an MCP tool call accepts the HTTP request, attaches security headers, writes a trace_id to the request, and dispatches to a server-rendered template. No reverse proxy in front. No static site generator behind. A CTO paying Contentful plus Vercel plus an AI gateway pays three bills and runs three SSO integrations to serve one company's content and one company's agents.
Templates render server-side, so the first byte the browser receives is HTML. No hydration flash, no JavaScript framework supply chain to audit, no build-step toolchain matrix. Marketing writes a page in YAML, the publish command reloads configuration, the page is live. The binary serving this paragraph is the binary described by this paragraph.
- One port, not three services — The web server is a module in the same Rust binary as the RBAC gate. No second process, no reverse proxy to audit, no sidecar to bypass. One set of logs, one TLS endpoint, one process to restart.
- Server-rendered HTML — Templates render before the response body is written. Search engines and screen readers read complete HTML. Marketing inherits no React version matrix or build toolchain.
- Self-hosted proof — This website, this page, the documentation, and the blog are served by the same binary in the template repository. If the product binary serves this site under real traffic, it serves yours.
- server/builder.rs HTTP server binding one port, attaching security headers and trace IDs to every request.
- server/routes.rs Route table mapping API, content, and feature-page URLs to handlers in this binary.
- registry.rs Template registry that renders Handlebars server-side. Browsers receive complete HTML on first byte.
- builder.rs Registry builder composing template providers, page-data providers, and prerenderers at startup.
- core_provider.rs Filesystem template loader. Templates live on disk next to the binary, changed via git.
- server/discovery.rs Well-known endpoint discovery for robots, sitemap, and favicons, served by the same binary.
Markdown In Your Repo
A marketing lead editing a landing page should not touch the AI governance configuration. A staff engineer reviewing a blog post should not need a CMS login to comment. Both open a pull request against a Markdown file. systemprompt.io treats every piece of content (blog, documentation, guide, feature page, legal) as Markdown with YAML frontmatter in the repository, the same format governance policies and skill definitions use.
The ingestion service walks configured source directories, parses frontmatter into a typed content record (title, description, author, slug, keywords, kind), hashes the file so unchanged content is skipped on the next run, and upserts into a Postgres table. A job reads a YAML source configuration, iterates enabled sources, and runs ingestion in the same binary that serves the rendered pages. The publish step is a CLI command, not a web form. A compliance officer reads the change log with git log against the content directory.
One pipeline serves every content kind. Blog articles, guides, tutorials, and feature pages share one table, distinguished by the typed kind field. The content API returns JSON for machine callers and Markdown for tools that want source, resolved through one content provider the templates and the API share. "Where does content live" has one answer. In the repo, in one table, behind one provider.
- Edit in your editor, merge in your PR — Content is Markdown with YAML frontmatter, committed alongside source. A marketing edit is a pull request. A rollback is a git revert. 'Who edited the homepage on Tuesday' is git log.
- One ingestion pipeline, every kind — Blog posts, documentation, guides, and feature pages flow through the same ingestion. The content hash skips unchanged files. The typed kind field routes the renderer. A new content kind is a config change, not a microservice.
- JSON for machines, Markdown for humans — The content provider exposes get-by-slug, list, and search. The API returns JSON for programmatic callers and raw Markdown for tools that want the source. Templates and API share one provider, so a rendered page and a fetched record are the same record.
- ingestion/mod.rs Ingestion service that scans directories, parses frontmatter, hashes files, and upserts into Postgres.
- content_ingestion.rs Ingestion job reading the YAML source config and iterating enabled sources in-process.
- content_provider.rs Content provider behind templates and the API. Get, list, search, slug resolution.
- content.rs Typed content record shared by every kind, including article, guide, tutorial, feature page.
- blog.rs Blog API handlers. JSON for machine callers, Markdown for tools that want source.
- content_config.rs Content routing trait. Sources, categories, and paths declared in YAML and read at ingestion.
- markdown_content.sql Content storage table. One schema for every kind, exportable as a single SELECT.
Postgres Full-Text Search
Adding Elasticsearch to a content site is another cluster to operate, another index to keep in sync, and another egress path for content that may be under NDA. Adding Algolia puts every visitor search query in front of a third party. systemprompt.io indexes content in the same Postgres instance that holds it, using the database's full-text engine, so a query stays on the same network as the pages it searches.
A token vector is written next to every content row and indexed for query-time scans. A keyword lookup is a single database query. The search repository exposes two paths. A free-text keyword query across title, description, and body. A filtered browse by category. Results carry the fields a listing page needs (slug, title, description, image, source, category) plus a view count joined from the performance table, so popular content surfaces without a second query.
The search service sits behind the same content provider the templates and API use, so a site search box, a documentation filter, and a programmatic query run through one code path. "Which vendor sees our search queries" has one answer. The Postgres cluster you already operate. The ranking logic is a file in the repo.
- No fourth vendor for search — Content indexes in the same Postgres instance that stores it. No Elasticsearch cluster, no Algolia contract, no third party reading visitor queries. Data stays inside the compliance boundary.
- Ranking a search box needs — Results carry slug, title, description, image, source, category, and a view count joined from the performance table, so popular content surfaces first. Ranking logic is a file in the repo, not a vendor tunable.
- One search path, many callers — Site search box, documentation filter, and programmatic job queries hit the same content provider method. One code path to test, one place to fix a bug.
- search/mod.rs (repository) Search repository with keyword and category-filter query paths, both running inside Postgres.
- search.rs (models) Typed search request and response models. Typed filters, typed results.
- search/mod.rs (service) Search service that calls the repository and joins view counts for ranking.
- query.rs HTTP query endpoint the site search box calls, shared with programmatic callers.
- markdown_fts.sql Full-text index table. A token vector per content row, scanned at query time.
- content_performance_metrics.sql Page-performance table joined by the ranker so popular content rises in results.
First-Party Telemetry
Web content has no analytics by default. The usual choices are Google Analytics (every visitor sent to a third party, an opaque script shipped to the browser), paid SaaS (another contract, another export problem), or building it in-house. None let a compliance officer answer "show me every click this user made on every campaign link last quarter" with one SQL query against a database the organisation already owns.
Link tracking, UTM generation, click attribution, and page-level behaviour land in the same Postgres instance as the content, reachable with the same credentials. The link generator writes a campaign-link row with a short code and UTM fields. Specialised wrappers set source, medium, and campaign defaults for social posts, internal cross-links, external CTA links, and external content links, so a report is not a regex over free-form UTMs. The attribution service records session, user, device, country, and a first-click flag. Aggregates roll up into campaign performance and content journey queries without a separate analytics pipeline.
Per-page behaviour writes one row per page view with typed behavioural fields. Scroll depth and reading pattern answer whether visitors finish the page. Rage-click and dead-click flags surface UX bugs without a session-replay vendor. Focus time, blur count, and tab switches separate engaged reads from background tabs. The row lives in your Postgres, so a CISO answers "did this user's device see this page" with one query and no vendor export ticket.
- No third-party analytics script — Every behavioural event lands in your Postgres instance, not a third-party tag. A click history is queryable with the same credentials as the rest of the application, so a compliance export is one SELECT.
- Four link generators, clean UTMs — One base generator and four wrappers (social, internal, external CTA, external content) set UTM source, medium, and campaign defaults. Reports read clean UTM rows, not free-form strings typed into a spreadsheet.
- Typed behavioural fields — Scroll depth and reading pattern answer did visitors finish the page. Rage-click and dead-click flags surface UX bugs without a session-replay vendor. Focus time, blur count, and tab switches separate real reads from background tabs.
- generation.rs L52-L213 Link generator with base plus four wrappers for social, internal, external CTA, external content.
- analytics.rs Attribution service recording session, user, device, country, first-click, with campaign and journey rollups.
- link.rs Typed models for campaign link, click, UTM parameters, and link performance.
- link/analytics.rs (repository) Link analytics repository. Click recording stays inside the same Postgres instance.
- engagement.rs L7-L34 Per-page engagement event with eighteen behavioural fields covering scroll, clicks, focus, and visibility.
- handlers.rs Engagement endpoint resolving page slug and writing event rows with session attribution.
- campaign_links.sql Campaign links table. One row per link with short code, UTM fields, attribution.
- engagement_events.sql Engagement events table. One row per page view with typed columns, directly queryable.
- content_performance_metrics.sql Aggregated page performance. Engagement rolls up here, the search ranker reads from it.
Served By The Same Binary
A governance product whose own marketing site runs on a different stack does not trust its own claim. systemprompt.io (this page, the documentation, the blog, the feature index) is served by the binary in the template repository. No separate marketing site. No WordPress behind a reverse proxy. No Webflow subscription. The HTTP server, template registry, content ingestion, search service, and link analytics described on this page are the instances serving this page.
Every page is configured in YAML. Navigation, feature sections, footer links, and category routing are declared in configuration files and deployed through the CLI publish command. Change the YAML, run publish, the page updates. The content routing trait resolves URLs to slugs. The content provider fetches the record. The template registry renders it. None of it requires a code deploy, so a marketing copy change does not block on the engineering release train.
For a CTO running build-vs-buy, every sibling service collapsed is an attack surface removed, a contract not renewed, a pipeline not maintained. Prerendered pages run at build time through the extension trait's page-prerenderer method, so production serves cached HTML. A staff engineer verifying this opens the configuration directory and binary source named below and reads the same files this site reads.
- YAML pages, no code deploy — Navigation, feature sections, and footer links declare in configuration files and reload with the publish command. A copy change ships without blocking on the engineering release train.
- One process, multiple roles — AI governance, HTTP serving, content ingestion, full-text search, and engagement analytics run inside one binary on one port. One log set, one TLS certificate, one artifact to audit.
- Fewer services, smaller attack surface — Every sibling service removed is a place a scanner cannot reach. Prerendered HTML generates at build time, so production mostly serves cached pages. One process to monitor, one artifact to sign, one vulnerability feed.
- services/web/config/ Site configuration directory. Navigation, feature pages, routing, and branding as YAML in repo.
- content_config.rs Content routing trait resolving URL to content slug for every rendered page.
- content_provider.rs Default content provider fetching records rendered by templates. Shared with API and search.
- server/builder.rs HTTP server serving this website. Same struct serves governance, API, and feature routes.
- registry.rs Template registry rendering this page server-side, inside the governance-gate process.
Founder-led. Self-service first.
No sales team. No demo theatre. The template is free to evaluate — if it solves your problem, we talk.
Who we are
One founder, one binary, full IP ownership. Every line of Rust, every governance rule, every MCP integration — written in-house. Two years of building AI governance infrastructure from first principles. No venture capital dictating roadmap. No advisory board approving features.
How to engage
Evaluate
Clone the template from GitHub. Run it locally with Docker or compile from source. Full governance pipeline.
Talk
Once you have seen the governance pipeline running, book a meeting to discuss your specific requirements — technical implementation, enterprise licensing, or custom integrations.
Deploy
The binary and extension code run on your infrastructure. Perpetual licence, source-available under BSL-1.1, with support and update agreements tailored to your compliance requirements.
One binary. Governance and web serving.
The same process that runs RBAC in front of every tool call publishes your website, blog, and documentation.