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

PostGateway.cs — reading posts via pengdows.crud
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);
        });
    }
}
src/routes/[…slug]/+page.ts — SvelteKit load function
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.

✉ software@pengdows.com