Last Updated: 2026-06-28 Status: Active
The complete internal reference for the Vlozi blog system. Everything a developer needs — architecture, schema, deployment, editor internals, SDK — lives here or is one link away.
1. What Is This System?
The Vlozi blog system is a multi-tenant headless blog engine built across three codebases:
| Codebase | Location | Role |
|---|---|---|
| blog-service | apps/blog-service |
Cloudflare Worker — owns all blog data, enforces permissions, serves the API |
| seller-dashboard | apps/seller-dashboard/src/modules/blog |
Next.js dashboard — where tenants write and manage posts |
| blog-sdk | packages/blog-sdk |
Published as @vlozi/blog — what customers embed on their websites |
Two doors in, one data store:
Seller Dashboard ──JWT──► Gateway ──service binding──► blog-service ──► Neon PostgreSQL
Customer Website ──API Key──► Gateway ──────────────────────────────────────────────────►- Admin door (
/blog/admin/*): JWT-authenticated. Full CRUD for posts, categories, and tags. - Public door (
/blog/public/*): API key-authenticated. Read-only, published posts only.
2. Start Here By Role
| Role | Reading Path |
|---|---|
| New to the project | This doc → glossary.md → architecture.md → user-journey.md |
| Backend / blog-service work | architecture.md → database.md → blog-service.md → permissions.md → deployment.md |
| Frontend / dashboard work | architecture.md → frontend-spec.md → editor.md → markdown-import.md |
| SDK integrator | sdk-reference.md → sdk-security-model.md → ai-integration-prompt.md |
| Need a curl example fast | cookbook.md |
| Setting up locally or deploying | deployment.md |
| Debugging a failing request | blog-workflow.md |
3. Doc Index
Core Reference
| Doc | What It Covers |
|---|---|
glossary.md |
Every term used across these docs — tenant, JWT, slug, content_json, hydration, etc. |
architecture.md |
System diagram, all services, request flows, header protocol, permission model, multi-tenant isolation |
database.md |
All 5 tables — column reference, constraints, indexes with rationale, migrations |
billing.md |
Credit billing — publish cost, creditGuard middleware, 30-min grace window, MCP differences |
permissions.md |
Every permission string, what route it gates, role bundles, how to add a new scope |
deployment.md |
Local dev setup, env vars for all 3 services, production deploy, health checks |
blog-service.md |
Full API reference — every endpoint, request/response schemas, middleware pipeline |
cookbook.md |
Runnable curl + fetch recipes for common admin and public operations |
blog-workflow.md |
Step-by-step request traces, common failure modes and fixes, debug checklist |
editor.md |
TipTap extension inventory, custom node views, slash commands, how to add extensions |
markdown-import.md |
The 5-stage .md → draft pipeline: parse → resolve → convert → mirror images → create |
frontend-spec.md |
Dashboard UI — pages, components, API hooks, Redux state, permission-gating |
sdk-reference.md |
Complete @vlozi/blog API — all exports, hooks, components, types, defaults |
sdk-security-model.md |
HTML sanitization, iframe allowlist, XSS prevention, CSP guidance |
testing.md |
Test inventory, how to run, test patterns, coverage gaps |
Context & Process
| Doc | What It Covers |
|---|---|
user-journey.md |
9 end-to-end user journeys for admin authoring and SDK consumption |
backlog.md |
Bugs, gaps, and improvement scope for blog-service — prioritized, with fix guidance |
sdk-backlog.md |
Active bug/improvement queue for @vlozi/blog |
ai-integration-prompt.md |
Paste-ready AI prompt for SDK integration tasks |
Vision (Forward-Looking)
These describe where the product is going — not the current implementation.
| Doc | What It Covers |
|---|---|
vision/blog-engine-vision.md |
Product north star — full editor, management, and integration vision |
vision/layouts-vision.md |
Pre-built blog layout presets |
vision/hosted-blog-vision.md |
Hosted blog pages (no SDK needed) |
vision/integration-friction-vision.md |
Reducing SDK integration friction |
vision/books-vision.md |
Blog → series → book publishing ladder |
4. Codebase Quick Map
apps/
├── blog-service/ Cloudflare Worker · port 8791 (local)
│ ├── src/index.ts Entry point — middleware pipeline + route registration
│ ├── src/db/schema.ts 5 tables: blog_posts, blog_categories, blog_tags, blog_post_tags, blog_overview_snapshots
│ ├── src/routes/admin/ posts.ts · categories.ts · tags.ts
│ ├── src/routes/public/ posts.ts · categories.ts · tags.ts
│ ├── src/middleware/ auth.middleware.ts · db.middleware.ts
│ └── src/utils/tiptap-renderer.ts TipTap JSON → sanitized HTML
│
├── gateway/ Cloudflare Worker · port 8788 (local)
│ └── src/routes/blog.proxy.ts Admin + Public proxy handlers
│
└── seller-dashboard/ Next.js · port 3000 (local)
└── src/modules/blog/ ~54 files across pages, editor, components, store, import
packages/
└── blog-sdk/ Published as @vlozi/blog@2.1.6
└── src/ client · react/ · server/ · next/5. Three Things Every Dev Must Know
1. The gateway is the only trust boundary.
blog-service validates x-gateway-key on every request. Requests missing or bearing an invalid key are rejected 403. There is no direct public access to blog-service. When something is 403 but shouldn't be, check that GATEWAY_SECRET matches in both services.
2. Content is TipTap JSON in the database, HTML on the public API.
All content is stored as JSONB (content_json). The admin API returns raw TipTap JSON for the editor. The public API (/public/posts/:slug) runs it through tiptap-renderer.ts before responding — HTML is generated server-side per request, never persisted.
3. All database queries are scoped by tenant_id.
There is one blog-service serving every tenant. Isolation is enforced at the query level, not by routing or separate databases. Every SELECT, INSERT, UPDATE, and DELETE includes a WHERE tenant_id = ? clause. The requireTenant middleware rejects any request where x-tenant-id is absent.
4. Publishing costs 1 credit — and is the only billed operation.
The creditGuard middleware charges 1 credit from CORE_DATABASE_URL before the publish handler runs. If the handler fails or the post was already published, the credit is automatically refunded. Unpublishing within 30 minutes also refunds the credit. See billing.md.