TECHNICAL_OVERVIEW
SCP Docs — Latom NodeA full-stack, edge-native documentation portal for the SCP Foundation universe — built on Vue 3, Hono, and the Cloudflare developer platform.OverviewSCP Docs (Latom Node)is a themed intranet portal that brings the SCP Foundation universe to life through a modern, performant web application. It combines a rich content catalog, community features, and an AI-powered assistant into a cohesive experience — all running at the edge.The system is composed of three independently deployable units:UnitDomainStackRoleFrontendnode.scp.latVue 3 VitePrimary user-facing SPABackend APIapi.scp.latHono Cloudflare WorkersServerless API at the edgeAdmin Dashboardadmin.scp.latVue 3 ViteAdministrative control panelEvery layer is written in TypeScript. The frontend and admin are static SPAs deployed to Cloudflare Pages; the backend runs as a Cloudflare Worker with D1 for persistence and Durable Objects for stateful workloads.Architecture┌─────────────────────────────────────────────────────────────────┐ │ Client Layer │ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │ │ │ Frontend SPA │ │ Admin Dashboard │ │ Mobile View │ │ │ │ (Vue 3 Vite) │ │ (Vue 3 Vite) │ │ (Dedicated) │ │ │ └────────┬─────────┘ └────────┬─────────┘ └──────┬───────┘ │ │ │ │ │ │ │ └─────────────┬───────┘ │ │ │ │ DeviceView │ │ │ │ (auto-selects │ │ │ │ desktop/mobile) │ │ └─────────────────────────┼────────────────────────────┼──────────┘ │ HTTPS │ ┌─────────────────────────┼────────────────────────────┼──────────┐ │ Edge Network (Cloudflare) │ │ │ │ │ ┌──────────────────────┴───────────────────────────────────┐ │ │ │ Hono API (Workers) │ │ │ │ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌────────────┐ │ │ │ │ │ Auth │ │ Crawler │ │ Proposals │ │ AI Chat │ │ │ │ │ │ Routes │ │ Routes │ │ Routes │ │ Routes │ │ │ │ │ └────┬────┘ └────┬─────┘ └─────┬─────┘ └─────┬──────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌────┴────┐ ┌────┴─────────────┴──┐ ┌──────┴───────┐ │ │ │ │ │ JWT │ │ D1 Database │ │ Durable │ │ │ │ │ │ Auth │ │ (SQLite, 14 │ │ Objects │ │ │ │ │ │Middleware│ │ tables) │ │ │ │ │ │ │ └─────────┘ └─────────────────────┘ │ ┌──────────┐ │ │ │ │ │ │ │ ScpCrawl │ │ │ │ │ │ │ │ (EN/CN) │ │ │ │ │ │ │ ├──────────┤ │ │ │ │ │ │ │ AiChatDo │ │ │ │ │ │ │ │(per-conv)│ │ │ │ │ │ │ ├──────────┤ │ │ │ │ │ │ │AiQueueDo │ │ │ │ │ │ │ │(per-user)│ │ │ │ │ │ │ └──────────┘ │ │ │ │ │ └──────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌─────┴──────┐ │ │ │ ZhipuAI │ │ │ │ GLM-4-Flash│ │ │ └────────────┘ │ └─────────────────────────────────────────────────────────────────┘Design PrinciplesEdge-first— The API runs on Cloudflare’s global network, placing compute within milliseconds of every user.Mobile as a first-class citizen— Every route has a dedicated mobile component, not just CSS media queries. TheDeviceViewwrapper dynamically selects the appropriate variant at render time.Dark-first design— The interface evokes a classified terminal system: deep navy-black surfaces, gold accents, monospaced data, and a custom crosshair cursor. The light theme is a derived override.Type safety end-to-end— TypeScript across all three packages, with shared type definitions for API contracts and data models.Technology StackFrontendTechnologyVersionRoleVue 33.5UI framework with Composition API andscript setupTypeScript6.0Static type checkingVite8.1Build tool and dev server (HMR on port 8085)Pinia3.0State management (7 stores)Vue Router4.6Client-side routing with HTML5 history modevue-i18n11.4Internationalization (English Chinese)Tailwind CSS4.3Utility-first CSS viatailwindcss/viteVitePWA1.3Service worker, offline caching, installable PWAVitest4.1Unit and integration testing withhappy-domBackendTechnologyVersionRoleHono4.9Lightweight web framework optimized for edgeCloudflare Workers—Serverless edge runtimeCloudflare D1—SQLite database at the edgeCloudflare Durable Objects—Stateful coordination for crawling and AIjose6.0JWT signing and verification (HS256)ZhipuAI GLMGLM-4-FlashAI model powering the chat assistantWrangler4.86Cloudflare development and deployment CLICode QualityToolPurposeESLint 10Static analysis with Vue TypeScript rulesPrettier 3.9Consistent code formattingHusky 9Git hooks for pre-commit checkslint-stagedRun linters only on changed filesFeature Deep DiveSCP Entry CatalogThe catalog is the heart of the application — a browsable, filterable index of SCP objects crawled from the official wiki.Dual-language support— Independent crawlers for the English (scp-wiki.wikidot.com) and Chinese (scp-wiki-cn.wikidot.com) wikis, running on daily alarm schedules at 03:00 UTC.Object class filtering— Safe, Euclid, Keter, Thaumiel, Apollyon, and Neutralized, each with a distinct color code.Series pagination— Entries organized by series (SCP-001 through SCP-7999) with lazy loading.Entry Protocol— An auto-rotating feature on the homepage that cycles through random entries on a configurable timer with a countdown visualization — a thematic “monitoring protocol” effect.Content caching— Individual entry pages are fetched on demand, cleaned of wiki markup, and cached in D1 for instant subsequent loads.AI Chat AssistantA fully integrated AI assistant powered by ZhipuAI’s GLM-4-Flash model, designed to answer questions about the SCP universe.Three-tier architecture:AiQueueDo— A per-user serial queue that ensures only one active AI request at a time, with a 60-second task timeout.AiChatDo— A per-conversation Durable Object with its own SQLite storage, supporting both request-response and SSE streaming modes. Implements a multi-round tool-use loop (up to 5 rounds) and context window management (last 50 messages).GLM Client— Handles communication with the ZhipuAI API, including automatic retry with exponential backoff on transient errors.MCP-style tool integration:The AI has access to three function-calling tools that query the database directly:ToolDescriptionsearch_scp_entriesSearch entries by name or keywordget_scp_entryRetrieve detailed information about a specific SCPlist_scp_entries_by_classFilter entries by containment classAuto-tagging pipeline:When an entry’s content is fetched, a background task sends it to GLM-4-Flash (at temperature 0.3 for deterministic output) to automatically classify it against a 51-tag taxonomy spanning five categories: Object Class, Anomaly Type, Groups of Interest, Narrative Format, and Theme.Authentication SecurityJWT-based sessions— Tokens signed with HS256 via thejoselibrary, containing user ID, codename, role, and clearance level. 24-hour expiry.Password hashing— PBKDF2 with Web Crypto API.Progressive rate limiting— IP-based limits stored in D1 with standardX-RateLimit-*headers. Separate thresholds for registration (10/hour), login (20/15min), and password changes (10/15min).Account lockout— 5 failed login attempts per codenameIP trigger progressive delays (2s → 5s → 10s → 30s).Role-based access—authMiddlewareverifies JWT tokens;adminMiddlewareenforces admin-only routes with 403 responses.SCP-themed error codes— Errors likeERR-401-CLEARANCEandERR-429-THROTTLEmap to i18n translation keys for consistent, localized user-facing messages.Community FeaturesProposals— Authenticated users submit proposals with title, content, and category. Community voting (for/against/abstain) with one vote per user per proposal. Daily submission limits. Admin moderation workflow (open → approved/rejected).Bookmarks History— Save favorite entries and track browsing activity. Unified activity view combining both.Entry Reports— Report content errors, display issues, or special handling requirements for admin review.InternationalizationTwo complete locales: English (en) and Chinese (zh).Automatic browser language detection with localStorage persistence.English fallback for missing translations.Crawler language (EN/CN) mapped from UI locale.All page titles, navigation, error messages, and UI text fully translated.Responsive DesignThe application implements adual-layout architecturewhere every route has both a desktop and mobile component variant:Desktop— Fixed glass-morphism header (60px), 260px sidebar with navigation and system info, centered content area (max 900px).Mobile— Fixed header (52px), bottom tab navigation (5 tabs, 56px safe-area padding), full-width cards, full-screen modals.Breakpoints— Mobile (≤768px), Tablet (769–1024px), Desktop (1024px).Touch targets— Minimum 44×44px for all interactive elements.TheDeviceViewcomponent uses theuseDevicecomposable (with debounced resize detection) to dynamically select the appropriate variant — this is not CSS media queries but entirely separate component trees.Database SchemaCloudflare D1 (SQLite at the edge) with 14 tables:┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ users │ │ scp_entries │ │ entry_tags │ │─────────────────│ │──────────────────│ │─────────────────│ │ id (PK) │ │ id (PK) │ │ entry_id (FK) │ │ codename │ │ scp_number │ │ tag_id (FK) │ │ password_hash │ │ language (en/cn) │ └────────┬────────┘ │ role │ │ name │ │ │ clearance │ │ object_class │ ┌────────┴────────┐ │ created_at │ │ series │ │ tags │ └────────┬────────┘ │ url │ │─────────────────│ │ │ summary │ │ id (PK) │ │ │ content (cached) │ │ category_id(FK) │ │ │ tags (JSON) │ │ name_en │ │ │ created_at │ │ name_zh │ │ └──────────────────┘ │ description │ │ │ ai_keywords │ │ ┌──────────────────────┐ └────────┬────────┘ │ │ tag_categories │ │ │ │──────────────────────│ ┌────────┴────────┐ │ │ id (PK) │ │ crawl_state │ │ │ name_en │ │─────────────────│ │ │ name_zh │ │ language (PK) │ │ └──────────────────────┘ │ status │ │ │ last_crawl │ ┌────┴──────────────┐ │ entries_count │ │ browsing_history │ └─────────────────┘ │───────────────────│ │ id (PK) │ ┌───────────────────┐ │ user_id (FK) │ │ proposals │ │ language │ │───────────────────│ │ scp_number │ │ id (PK) │ │ viewed_at │ │ user_id (FK) │ └───────────────────┘ │ title │ │ content │ ┌───────────────────┐ │ category │ │ bookmarks │ │ status │ │───────────────────│ │ created_at │ │ id (PK) │ └────────┬──────────┘ │ user_id (FK) │ │ │ language │ ┌────────┴──────────┐ │ scp_number │ │ proposal_votes │ │ created_at │ │───────────────────│ └───────────────────┘ │ proposal_id (FK) │ │ user_id (FK) │ ┌───────────────────┐ │ vote │ │ entry_reports │ └───────────────────┘ │───────────────────│ │ id (PK) │ ┌───────────────────┐ │ user_id (FK) │ │ ai_conversations │ │ entry_id (FK) │ │───────────────────│ │ report_type │ │ id (PK) │ │ description │ │ user_id (FK) │ │ status │ │ title │ └───────────────────┘ │ created_at │ └───────────────────┘ ┌───────────────────┐ ┌───────────────────┐ │ system_logs │ │ rate_limits │ │───────────────────│ │───────────────────│ │ id (PK) │ │ key (PK) │ │ level │ │ count │ │ message │ │ window_start │ │ context (JSON) │ └───────────────────┘ │ source │ │ created_at │ ┌───────────────────┐ └───────────────────┘ │ seed_checkpoint │ │───────────────────│ │ id (PK) │ │ last_number │ │ updated_at │ └───────────────────┘The schema includes extensive seed data: 51 tags across 5 categories, each with bilingual names (English/Chinese), descriptions, and AI keyword arrays for the auto-tagging pipeline.API SurfaceThe Hono application exposes 11 route groups:EndpointAuthDescriptionGET /api/health—Health check and uptimePOST /api/auth/register—Account registrationPOST /api/auth/login—Authentication (returns JWT)GET /api/auth/meJWTCurrent user profilePUT /api/auth/profileJWTUpdate profileGET /api/crawler/status—Crawl state per languageGET /api/crawler/entries—Paginated entry list with filtersGET /api/crawler/series—Available series metadataGET /api/crawler/entry/:lang/:number—Single entry contentGET/POST/DELETE /api/bookmarksJWTBookmark CRUDGET/POST/DELETE /api/historyJWTBrowsing history CRUDGET/POST /api/proposalsJWTProposal list and creationPOST /api/proposals/:id/voteJWTCast vote on proposalPOST /api/reportsJWTSubmit entry reportGET /api/reports/checkJWTCheck existing reportsGET /api/tags—Tag categories and tagsPOST /api/ai/streamJWTAI chat (SSE streaming)GET /api/ai/conversationsJWTList conversationsPOST /api/logs—Client log ingestion/api/admin/*AdminDashboard, users, entries, proposals, logs, settings, tagsDurable Objects in DetailScpCrawlerDoTwo singleton instances (SCP_EN_CRAWLERandSCP_CN_CRAWLER) independently manage wiki crawling:Anti-detection— Rotating pool of 10 real browser User-Agent strings, fullSec-Ch-Uaheaders, cookie persistence via a customCookieJar, language-appropriateAccept-Languageheaders, and randomized jitter delays between requests.Incremental updates— Daily alarm at 03:00 UTC triggers a change-detection crawl that only upserts new or modified entries, minimizing D1 writes.Seed checkpoint— Batch crawl progress is tracked inseed_checkpointfor resumable full crawls.Backfill system— Resolves “Unknown” object classes by fetching individual entry pages in the background.Auto-tagging trigger— When entry content is fetched, GLM-4-flash classifies it against the 51-tag taxonomy asynchronously.Log hygiene— Automatic 30-day retention cleanup on each alarm cycle.AiChatDoOne instance per conversation, keyed by conversation ID:Isolated storage— Each instance maintains its own SQLite database for messages and metadata.Streaming— Full SSE streaming support for real-time token delivery.Tool-use loop— Up to 5 rounds of function calling per message, enabling the AI to search entries, fetch details, and filter by class before responding.Context management— Automatic truncation to the last 50 messages to stay within token limits.Regeneration— Users can regenerate the last response with a fresh inference.AiQueueDoPer-user serial queue ensuring fair resource usage:Concurrency control— Only one active AI request per user at a time.Timeout enforcement— 60-second task timeout prevents hung requests.Proxy mode— Transparently forwards to the appropriate AiChatDo instance, supporting both streaming and non-streaming responses.Crawler Pipeline┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ Daily Alarm │────▶│ Fetch Series │────▶│ Parse Entries │ │ (03:00 UTC) │ │ Pages (EN/CN) │ │ from HTML │ └──────────────┘ └──────────────────┘ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Diff Against D1 │ │ (change detect) │ └────────┬─────────┘ │ ┌────────────┴────────────┐ │ │ ▼ ▼ ┌────────────────┐ ┌─────────────────┐ │ Upsert New / │ │ Skip Unchanged │ │ Changed Only │ │ │ └───────┬────────┘ └─────────────────┘ │ ▼ ┌────────────────┐ │ Backfill Pass │ │ (Unknown class│ │ resolution) │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ Auto-Tag │ │ (GLM-4-flash) │ └────────────────┘Client-Side ArchitectureState ManagementSeven Pinia stores manage application state:StoreResponsibilityauthUser authentication, JWT persistence, session restorationcrawlerEntry data, crawl state, language switching, class filtering, paginationsearchCommand-palette search across entries and documentsproposalsProposal list, voting state, daily limitsuserActivityUnified bookmarks and browsing historyhistoryRe-export shim for backward compatibilitybookmarksRe-export shim for backward compatibilityServices LayerA typed API client layer provides:Auto-injected authentication— Bearer token from localStorage on every request.Unified error handling—ApiResultTdiscriminated union (success/failure) with SCP-themed error codes.Streaming support—apiStreamfor SSE-based AI chat responses.Observability— Structured client-side logging with buffered server transmission (30-second flush interval, 50-entry buffer,sendBeaconon page unload for reliable delivery).Performance monitoring— Slow-request warnings for any API call exceeding 3 seconds.ComposablesComposablePurposeuseDeviceResponsive breakpoint detection with debounced resizeuseThemeDark/light theme toggle withdata-themeattributeuseLocalei18n locale managementuseSidebarSidebar collapsed stateuseEntryProtocolAuto-rotating entry display with countdown timerDesign SystemThe visual language is documented inDESIGN.mdand implemented through CSS custom properties:Color PaletteRoleDark ThemeLight ThemePrimary (Gold)#c9a44a#c9a44aAccent (Blue)#4a9eff#4a9effBackground#0a0a0f#f8f8faSurface#12121a#ffffffText Primary#e8e8ec#1a1a2eText Secondary#8888a0#6b7280Object Class ColorsClassColorHexSafe Green#4ade80Euclid Yellow#facc15Keter Red#ef4444Thaumiel Purple#a855f7Apollyon⚫ Dark#1a1a2eNeutralized⚪ Gray#6b7280TypographyUI / Body— Inter, system sans-serifData / Code— JetBrains Mono, monospaceSpacing— 4px base gridCustom CursorA crosshair cursor rendered in brand gold (#c9a44a) reinforces the classified terminal aesthetic.PWA ConfigurationThe frontend is a fully installable Progressive Web App:Service worker— Auto-updating viaregisterType: autoUpdateOffline support— Workbox runtime caching with NetworkFirst strategy for API calls (24h cache, 10s network timeout)Precaching— All static assets (JS, CSS, HTML, SVG, fonts) cached on installManifest— Standalone display mode, dark theme color (#0a0a0f), SVG icons (maskable)Testing StrategyScopeFrameworkCoverageFrontend componentsVitest happy-domUI rendering, props, eventsFrontend storesVitestState mutations, API integrationFrontend servicesVitestAPI client, response normalizationFrontend composablesVitestReactive behavior, side effectsFrontend i18nVitestKey parity between en/zhBackend routesVitestAPI endpoints, auth flowsBackend middlewareVitestJWT verification, admin checksBackend Durable ObjectsVitestCrawler logic, AI chat flowBackend utilsVitestJWT, password hashing, GLM clientIntegrationVitestEnd-to-end auth, admin, proposals, activity flowsTest files follow the__tests__/directory convention with.test.tssuffix. TheMakefileprovides unified commands:makeci# Full pipeline: typecheck lint test buildmaketest# Frontend backend testsmaketypecheck# TypeScript checking for both projectsmakecoverage# Tests with V8 coverageCI/CD PipelineGitHub Actionsci.yml— Runs on every push and PR tomain:┌─────────────────────────────┐ ┌─────────────────────────────┐ │ Frontend Job │ │ Backend Job │ │─────────────────────────────│ │─────────────────────────────│ │ 1. npm ci │ │ 1. cd worker npm ci │ │ 2. npm run typecheck │ │ 2. npm run typecheck │ │ 3. npm run lint │ │ 3. npm run lint │ │ 4. npm test │ │ 4. npm test │ │ 5. npm run build │ │ │ │ 6. Upload build artifact │ │ │ └─────────────────────────────┘ └─────────────────────────────┘deploy.yml— Runs on push tomainor manual trigger:Frontend deployed to Cloudflare PagesBackend deployed to Cloudflare WorkersGetting StartedPrerequisitesNode.js 20npmLocal Development# Frontendnpmcinpmrun dev# → http://localhost:8085# Backendcdworkernpmcinpmrun db:schema:local# Initialize local D1 databasenpmrun dev# → Wrangler dev server# Admin Dashboardcdadminnpmcinpmrun dev# → Admin dev serverFull CI Checkmakeci# typecheck lint test buildProject Structurescp-docs/ ├── src/ # Frontend source │ ├── views/ # Page components (desktop mobile variants) │ │ └── mobile/ # Dedicated mobile views │ ├── components/ │ │ ├── common/ # Reusable UI (Badge, Card, ClassBar, ErrorBoundary) │ │ ├── home/ # Homepage sections (Hero, Stats, RecentEntries) │ │ ├── layout/ # App shell (Header, Sidebar, Footer, SearchModal) │ │ ├── mobile/ # Mobile components (Layout, Header, Nav, Search, AiChat) │ │ └── ai/ # AI chat (Panel, ConversationList, MessageBubble) │ ├── stores/ # Pinia state management │ ├── composables/ # Vue composables (useLocale, useTheme, useDevice, useSidebar) │ ├── services/ # API client layer │ ├── data/ # Static documents │ ├── locales/ # i18n translations (en, zh) │ ├── types/ # TypeScript interfaces │ ├── styles/ # CSS design tokens, base styles │ └── router/ # Vue Router with auth guards ├── worker/ # Backend source │ └── src/ │ ├── routes/ # API endpoints │ │ └── admin/ # Admin API routes │ ├── middleware/ # JWT auth, admin role, logging │ ├── do/ # Durable Objects (crawler, AI chat, AI queue) │ ├── tools/ # AI tool definitions and executor │ ├── utils/ # JWT, password, logger, GLM client │ └── types.ts # Backend type definitions ├── admin/ # Admin dashboard (separate SPA) │ ├── views/ # Admin pages │ ├── stores/ # Admin state management │ ├── services/ # Admin API client │ └── components/ # Admin UI components ├── docs/ # Developer documentation ├── schema.sql # Database schema with seed data ├── Makefile # Unified development commands └── DESIGN.md # Design system specificationLicenseMIT