WordPress, without
WordPress
A Svelte + ASP.NET Core rendering layer that reads WordPress tables — no login, no plugins, no PHP. Your content stays in WordPress. Everything else is yours.
Architecture
| content source | WordPress MySQL database | unchanged, unmodified — no plugin writes |
|---|---|---|
| connection | pengdows.crud | read-only, connection-pooled, parameterized |
| api layer | ASP.NET Core | GraphQL API, per-tenant DI scope |
| rendering | SvelteKit SSR | server-side rendered, SEO-native, no SPA |
| auth surface | none | no login endpoints, no sessions, no cookies |
| deployment | containerized, k3s-ready | db-per-tenant, multi-tenant from one image |
What we read — nothing more
standard WordPress tables
| table | role | |
|---|---|---|
| wp_posts | Published posts, pages, nav menu items, block patterns, templates. Only post_status = 'publish' — no drafts, no revisions, no auto-drafts. |
primary |
| wp_postmeta | Per-post metadata: featured image ID, menu item links, SEO fields, block-related meta. | primary |
| wp_terms | Category and tag names and slugs for taxonomy display and filtering. | taxonomy |
| wp_term_taxonomy | Term type (category vs tag vs nav_menu) and parent relationships for hierarchy. | taxonomy |
| wp_term_relationships | Many-to-many join between posts and their assigned categories, tags, and navigation menus. | taxonomy |
| wp_options | Site title, tagline, and optional custom CSS. Only the specific option names requested — not a full options dump. | config |
| wp_comments | Approved comments only — comment_approved = '1', type comment only. Pingbacks and trackbacks excluded. No writes, no submission endpoint. wp_commentmeta is never accessed. |
read-only |
custom database views
— created by pengdows.cms; wp_users is never queried directly
| view | columns exposed | what it excludes |
|---|---|---|
| wp_user_display | ID, display_name, user_nicename, user_url |
user_login, user_pass, user_email, and session data — excluded at the database level, not just by omission in the query |
| wp_usermeta_display | user_id, meta_key, meta_value |
All usermeta rows except description — the view restricts to the author bio field only |
What you get
// no-write
read-only by construction
The database user has SELECT on these tables and views. No UPDATE, INSERT, or DELETE is possible at the credential level, not just by convention.
// no-auth
zero login surface
No /wp-login.php, no /xmlrpc.php, no REST API. WordPress admin stays on its own origin. pengdows.cms has no authentication endpoints at all.
// ssr
SvelteKit SSR
Full server-side rendering. Pages are HTML from the first byte — cacheable at the edge, no hydration delay, no client-side data fetching on initial load.
// multi-tenant
db-per-tenant isolation
Each tenant resolves to its own WordPress database via pengdows.crud’s tenant registry. One container deployment, full schema isolation per site.
// seo
SEO-first rendering
Title, description, canonical, og:image, and structured data pulled from postmeta at request time. No client-side meta injection.
// comments
approved comments, read-only
Approved comments are rendered from wp_comments. No submission endpoint, no moderation surface, no spam vector. wp_commentmeta is never accessed.
under the hood
c#
// read-only gateway — ResilientGateway wraps TableGateway with retry logic
public class PostGateway : ResilientGateway<WpPosts, ulong>, IPostGateway
{
public async Task<WpPosts?> GetBySlugAsync(IDatabaseContext context, string slug)
{
return await ExecuteWithRetryAsync(async () =>
{
var sc = BuildBaseRetrieve("p", context);
sc.Query.Append(" WHERE ");
sc.Query.Append(sc.WrapObjectName("post_name"));
sc.Query.Append(" = ");
sc.Query.Append(sc.MakeParameterName(
sc.AddParameterWithValue("slug", DbType.String, slug)));
sc.Query.Append(" AND ");
sc.Query.Append(sc.WrapObjectName("post_status"));
sc.Query.Append(" = ");
sc.Query.Append(sc.MakeParameterName(
sc.AddParameterWithValue("status", DbType.String, "publish")));
return await LoadSingleAsync(sc);
});
}
}
ts
import { query } from '$lib/graphql';
import type { PageLoad } from './$types';
import { error } from '@sveltejs/kit';
// PageLoad: runs on server for initial SSR, then client-side during navigation
export const load: PageLoad = async ({ params, fetch }) => {
const slug = params.slug; // URL path segment — not a query string param
const data = await query<{ postBySlug: PostDetail | null }>(
`query($slug: String!) {
postBySlug(slug: $slug) {
id title contentHtml date featuredImageUrl
terms { name slug }
comments { id author date content children { id author date content } }
}
}`,
{ slug },
fetch
);
if (!data.postBySlug) throw error(404, 'Not found');
return { post: data.postBySlug };
};
vs. stock WordPress
| concern | WordPress (default) | pengdows.cms |
|---|---|---|
| login endpoints | wp-login.php, xmlrpc.php exposed | none — no auth layer |
| comment handling | wp_comments write surface: submissions, spam, GDPR exposure | read-only approved comments — no submission endpoint, no moderation surface |
| plugin attack surface | every plugin is an attack surface | zero plugins in render path |
| PHP runtime | required | not involved |
| database access | read + write under one credential | SELECT on specific tables only |
| user data access | wp_users including credentials | custom view — credential columns excluded at the database level |
| SSR HTML delivery | PHP renders on request (no caching by default) | SvelteKit SSR, cacheable at edge |
| multi-tenancy | one install per site | db-per-tenant from one image |
| content editing | WordPress admin — unchanged | WordPress admin — unchanged |
Interested?
Get in touch.
pengdows.cms is not yet publicly available. If you have a use case or want
to be notified when it ships, send us an email.