High-Level Architecture
Relevant source files
The following files were used as context for generating this wiki page:
PuppyPad architecture provides a single-server hosting environment for static homepages. The system uses a centralized Node.js application to manage user accounts, file storage, and site rendering through host-based routing.
System Components
The architecture consists of three primary layers: the reverse proxy, the application core, and the data persistence layer.
The diagram shows the request flow from the client through Caddy to the Express application, which interacts with local storage and the database.
Sources: PLAN.md:14-38, src/server.js:156-173
Architectural Stack
| Component | Technology | Role |
|---|---|---|
| Proxy | Caddy | TLS termination and Wildcard certificate management via DNS-01 challenge. |
| App Server | Node.js / Express | Handles routing, authentication, and file management logic. |
| Database | SQLite | Stores user metadata, session tokens, and site activity logs. |
| File Storage | Local Disk | Stores member static files under ./data/sites/{name}. |
| Screenshots | Playwright | Renders member homepages in an isolated container to generate previews. |
Sources: PLAN.md:20-40, README.md:100-115
Routing Logic
The application implements host-based routing to differentiate between the primary management portal and individual member sites.
Host-Based Router
The server inspects the Host header of incoming requests. Requests to the base domain (e.g., puppypad.org) utilize the Main Router, while requests to subdomains (e.g., user.puppypad.org) utilize the Site Router.
- Main Router: Manages landing pages, signups, login flows, user dashboards, and administrative tools.
- Site Router: Serves static files directly from the member's directory and handles built-in widgets located at the
/_hw/path.
Sources: src/server.js:156-173, PLAN.md:20-30
The sequence diagram illustrates how the Express middleware selects a router based on the incoming Host header.
Sources: src/server.js:156-180
Data Persistence and Storage
Data is partitioned between a relational database for metadata and the filesystem for static content.
Database Schema
The SQLite database stores structured data including:
- Users: Account details, quota limits, and site configuration (tags, tagline).
- Events: Site updates used for feed generation.
- Activity: Page views, hit counts, and guestbook entries.
- Security: Hashed passwords, session tokens, and API fingerprints.
Sources: PLAN.md:40-45, src/server.js:45-75
Storage Organization
The application maintains three primary directories under ./data:
./data/puppypad.db: The SQLite database file../data/sites/: A directory containing subfolders for every member site../data/shots/: A directory containing.jpgpreview images for every site.
Sources: PLAN.md:30-38, src/server.js:315-325
Screenshot Subsystem
PuppyPad uses a separate containerized worker to capture site previews. This ensures that potentially malicious user-provided JavaScript executes in an isolated environment.
- Dirty Flag: When a member saves a file, the server sets a
shot_dirtyflag in the database. - Polling: The worker polls
/internal/shots/nextwith aSHOT_TOKEN. - Rendering: The worker navigates to the member site and generates a JPEG.
- Submission: The worker posts the image back to
/internal/shots/:name.
Sources: src/server.js:315-342, PLAN.md:35-40
The flowchart demonstrates the asynchronous screenshot generation loop between the application and the Playwright worker.
Sources: src/server.js:321-340, PLAN.md:20-40
Widget and Style Subsystems
Built-in widgets and the style editor provide dynamic functionality to otherwise static sites.
Style Editor Architecture
The style editor generates a standard CSS file named puppypad.css located at the site root. It inserts a metadata block at the top of the file as a JSON comment, allowing the server to read settings back into the editor form without a database dependency for CSS state.
Sources: src/style.js:1-50, src/server.js:550-575
Internal Widget Routing
Member sites access internal features via the /_hw/ path.
- Hit Counter: Served as
/_hw/counter.svg. - Guestbook: Served via an iframe at
/_hw/guestbook. - Site Metadata: Badges like
/_hw/updated.svgdraw fromusers.updated_at.
Sources: src/widgets.js:125-135, src/server.js:200-220
Security and Guardrails
The architecture includes several native enforcement mechanisms:
- Path Safety: The
resolveInfunction prevents path traversal by validating that resolved paths stay within a member's specific directory. - Session Isolation: The
__Host-sidcookie prefix prevents subdomains from reading or overwriting management sessions. - Phishing Detection: The
autoFlagsystem scans saved.html,.js, and.svgfiles for sensitive keywords (e.g., "password", "PayPal") and creates moderation reports. - Bandwidth Guard: Daily byte caps (
BANDWIDTH_MB) and hotlink request limits are enforced in the site router.
Sources: src/server.js:145-155, src/server.js:90-105, PLAN.md:45-55, src/server.js:250-270
Conclusion
PuppyPad utilizes a monolith-inspired architecture designed for efficiency on a single Virtual Private Server (VPS). By leveraging host-based routing and a decoupled screenshot worker, it maintains a separation between administrative functions and user-generated content while keeping the stack simple enough for thousands of members to be served from a single SQLite instance.