e11

Blog · A branded blog deployed to your domain

A renderer, not a CMS.

The blog is the consumer end of a content pipeline. A thin Next.js renderer with Prisma + SQLite, deployed to your domain, that accepts signed publishes from a content engine you also own. No editor in the product, no content team required to keep it fed.

Your domain. Your database volume. Your HMAC secret to rotate.

Surface signal

Status

LIVE

Tenancy

Per-domain

Reference

blog.eleven11.pro

Why this exists

A branded blog should not require a CMS, and it should not require a content team.

Most of what gets sold as a "branded blog" is one of two things: a managed CMS that locks you into someone else's editor and database, or a static-site repo that quietly demands a content team to keep it fed. Neither shape fits a serious operating company that wants a blog on its own domain without inheriting a second product to administer.

The blog you are reading about is the consumer end of a content pipeline. The producer (e11-pr) writes; the consumer renders. The split is the point — a thin renderer is a small surface to own, and a content engine that targets it is what removes the content team.

Self-sustained by design

Owned, not rented.

A branded blog is a load-bearing marketing surface. It should not be the kind of thing whose pricing model, plugin store, or auth provider can change under you on a Tuesday.

01

Your domain, your database

Deployed to your hostname. SQLite volume mounted into your container, owned by your filesystem. The reader sees your brand; the operator sees your storage.

02

A secret you can rotate

Every signed publish carries an HMAC-SHA256 over the raw body. Rotate `PR_PUBLISH_SECRET` (or the row in `SiteSettings`) and the producer is locked out until you re-share. Sovereignty is a function of which key you hold.

03

No editor, no surprise

There is no in-product compose UI, no draft autosave, no plugin surface. Posts arrive over a single signed endpoint. That is the whole write path. Smaller surface, fewer ways to be surprised.

04

SEO is yours to edit

`metaTitle`, `metaDescription`, `canonicalUrl`, `keywords`, `tags`, JSON-LD blocks — all editable through a bearer-gated admin endpoint and preserved across re-publishes via `preserveBlogSeo`. The renderer holds the authoritative SEO state.

05

A renderer you can read in an afternoon

Next.js App Router, Prisma, two ingest routes, one upsert path, one schema renderer. No queue, no worker, no headless platform. The whole consumer fits in your head.

The primitive

Three things you can name. Typed.

The blog is built on a small, opinionated trio — a row to store the post, a signed endpoint to write it, a public route to render it. There is no fourth thing. Anything that looks like a fourth thing is a server action that mutates one of those three.

01 · Source of record

Post

BlogPost (Prisma)

One row per slug. Body, excerpt, cover, status — plus the SEO columns and the cached score. Indexed on status and publishedAt because the list query is the hot path.

02 · Signed write

Ingest

POST /api/internal/pr-dispatch

Two routes, one upsert. `pr-dispatch` takes the publish-worker shape; `pr-publish` keeps the flat-shape legacy path. Both verify `X-Pr-Signature: sha256=<hex>` against the raw body and refuse anything else.

03 · Public surface

Render

GET /blog/[slug]

Markdown or HTML in, JSON-LD graph out. Internal-link rewriting, related posts, cover image, RSS at `/feed.xml`, sitemap and robots as Next metadata routes. Cached at the edge where it makes sense.

How it fits the fleet

Half a pipeline by itself. The other half is pr.

The blog renders. pr produces. Sold and operated as a system, the two repos are how a customer ends up with editorial output on their domain without hiring a content team or buying a CMS. The other tools in the fleet wire into the same pipeline.

pr

The engine. Profile + factBundle + LLM compose the post; the publish worker signs the body and POSTs `pr-dispatch`. pr is the producer — this blog is the consumer end of that pipeline. Sold and operated as one system.

operator

Where the blog is administered. Per-instance HMAC visibility, secret rotation, target wiring. Operator is how a deployed blog gets configured without SSH-ing into the box for every change.

alerts

`blog.ingest.signature_invalid` is the highest-severity event the renderer emits — a wrong-key publish reads as either a misconfigured rotation or a probe. `blog.ingest.received` and `blog.ingest.failed` are the per-post heartbeat.

discovery

When the blog is part of a customer site rebuild, discovery's intelligence layer can read the published surface back as a tech fingerprint — so the audit engine knows what the rebuild actually shipped.

architect

Posts that originate from architect matters carry the `publishIntentId` back through the pipeline, so a draft locked into a session has a traceable line to the URL it eventually became.

phoenix

When phoenix rebuilds a WordPress site into a static Astro shell, the editorial stream that used to live in WP can move here — same domain, same canonical URLs, no more plugin attack surface.

Surfaces & contracts

Six things you actually call.

Three reader-facing routes, two signed ingest paths, one bearer-gated admin patch. The smallest surface that runs a serious branded blog.

GET /blog

Post list

Newest first, status-indexed, paginated when it has to be. The default reader's entry point.

GET /blog/[slug]

Post detail

Markdown/HTML render, JSON-LD schemas in document order, internal-link rewriting against known slugs, related posts (up to four), cover image with OG and Twitter cards.

GET /feed.xml

RSS feed

Hand-rolled RSS, fifty most recent posts, one-hour edge cache. Readers and aggregators that don't want to crawl the HTML get a clean stream.

POST /api/internal/pr-dispatch

Signed ingest (native)

Publish-worker shape — top-level `publishIntentId`/`runId`/`artifactId`, post fields nested under `payload`. HMAC-verified, idempotent on slug, emits an alert on every outcome.

POST /api/internal/pr-publish

Signed ingest (flat)

The flat-shape legacy path. Same HMAC, same upsert, same response contract — kept for publishers that haven't moved to the wrapped envelope yet.

PATCH /api/admin/blog/[slug]

SEO admin

Bearer-gated edits to the SEO columns and JSON-LD blocks, recomputing the cached SEO score on every write. Combined with `preserveBlogSeo: true` on ingest, edits survive re-publishes.

Senior engineering, visible

The proofs are in the substrate.

Five decisions visible in the ingest code, the secret resolution path, the entrypoint script, and the absence of a logging line where one would have been convenient — not adjectives, design choices.

Constant-time HMAC

`verifyPrPublishSignature` uses constant-time comparison. Signature checks should not be where a timing oracle lives.

DB-first secret resolution

`getPrPublishSecret()` reads `SiteSettings.prPublishSecret` first, then falls back to the env var. Rotation is a runtime PATCH, not a container restart and a redeploy.

Migrations run at boot

`prisma migrate deploy` runs in `docker-entrypoint.sh` before the server binds. The build stage runs the same against a throwaway DB so Prisma Client compiles. There is no manual schema step on the customer host.

Two ingest paths, one upsert

`pr-dispatch` and `pr-publish` parse different envelopes and call the same `upsertBlogPostFromIngestPayload`. Behavioral parity is enforced by code share, not by hope.

Body never logged

Neither ingest endpoint writes the request body to the application log. Drafts and unpublished content do not leak into your logging stack while the pipeline is in flight.

Who this is for

Teams whose marketing surface is load-bearing.

The blog earns its keep when search traffic and editorial reputation are operating assets — and when the cost of administering someone else's CMS starts to look like the cost of owning the renderer instead.

Operating companies whose marketing surface needs to live on their own domain — not a sub-path of a vendor's blog product.
Teams that want editorial output without hiring or contracting a content team — the content engine produces, this renderer publishes.
Founders rebuilding a legacy WordPress site who want the editorial stream out of WP without losing canonical URLs or feed subscribers.
Compliance-sensitive teams who need their CMS and database to be artifacts they administer, not services someone else holds.
Engineering-led companies that would rather own a thin renderer than rent a managed platform whose pricing changes annually.

FAQ

Final friction, reduced.

Where does the content come from if there is no editor?

From e11-pr, the content engine. pr composes posts from a profile and a fact bundle, signs the body, and POSTs to the renderer. The producer/consumer split is the point — sold and operated as one pipeline.

What if a publish should not be accepted?

Rotate `PR_PUBLISH_SECRET` or clear the `prPublishSecret` row in `SiteSettings`. Every subsequent publish fails the HMAC check until you re-share. The customer holds the kill switch.

Can SEO survive when pr re-publishes the same slug?

Yes. With `preserveBlogSeo: true` on ingest, the upsert keeps existing `metaTitle`, `metaDescription`, `canonicalUrl`, `keywords`, `tags`, `category`, `author`, and JSON-LD blocks — and only rewrites body, title, excerpt, and cover. Your SEO manager's edits are not overwritten by the engine.

Why SQLite and not Postgres?

Blog scale is hundreds of posts, not millions. SQLite gives a single file the customer can back up, restore, and inspect with one tool. There is no second service to administer, no connection pool to tune, and the renderer stays a thin shell.

Discuss the blog

A branded blog on your domain, fed by an engine you own.

The blog is sold and operated as the consumer half of the pr pipeline. Talk to us about a deployed blog on your domain, migrating off a legacy CMS, or wiring the producer side into an existing editorial workflow.

Direct line

Consultation requests stay owned. We reply from e11 after reviewing fit and timing.