A modern Desktop application (built with Electron & Node.js) to manage local Ghost blogs per project, ingest custom main website designs, auto-tag affiliate links, encrypt protected posts, and deploy static HTML pages to GitHub Pages, Netlify, Cloudflare Pages, Render, or Vercel for 100% free hosting.
- β¨ Features
- π Prerequisites & System Requirements
- π οΈ Quick Start & Testing
- π First-Time Ghost Admin Setup (Mode A)
- π Ghost Content API Key Setup
- π¨ Design Ingestion & Custom
layout.htmlGuide - π° Affiliate Link Ingestion & Automation
- π¬ Giscus Comments Integration & Setup
- βοΈ Newsletter Subscriptions on a Static Blog
- π Custom Signup, Contact & Beta Forms on Static Sites
- π Deployment Architecture: Subfolder vs Subdomain
- π Security Architecture & Credentials Management
- π‘οΈ Security Disclaimer & Token Best Practices
- π€ Standing on the Shoulders of Giants
- π Giving Back to Open Source
- π License & Collaboration
- π Multi-Project Profiles: Manage multiple independent project blogs (e.g. Project A, Project B) each with their own Docker container, volume, port, design layout, logo, and GitHub repository.
- π³ Mode A: Docker & Cloudflare Tunnel: Run Ghost locally in a dedicated Docker container (
ghost:5-alpine) with isolated persistent volumes, and manage files instantly via the built-in 1-click Web File Browser. - β‘ Mode B: Static HTML Exporter: Convert Ghost posts into zero-cost, static HTML pages ready for GitHub Pages.
- π¨ Custom Design & Logo Ingestion: Import your main website's
layout.htmlfile so blog posts share your exact header, footer, navbar, logos, and CSS styling. - π° Affiliate Link Automation: Auto-tags product links (
amazon.com,bestbuy.com, etc.) withrel="sponsored nofollow noopener"andtarget="_blank"for Google SEO compliance and link monetization tracking. - π Build-Time AES-256 Password Protection: Encrypts password-protected posts at export time. Unencrypted text is never exposed on GitHub Pages; visitors decrypt posts in-browser with a password prompt.
- π Pagefind Static Full-Text Search: Builds an instant client-side search index (
/search/search-index.js) without requiring a search server. - π¬ Giscus GitHub Comments: Embeds free GitHub Discussions commenting into static posts.
- π‘ RSS & Sitemap XML: Automatically generates valid
rss.xmlandsitemap.xmlfiles. - π Multi-Cloud Publisher: Deploy directly to GitHub Pages, Netlify, Cloudflare Pages, Render, or Vercel.
Before running the application, make sure you have the following installed on your machine:
| Tool | Required For | Required? | Download Link |
|---|---|---|---|
| Node.js (v18+) | Running the desktop application (npm start) |
Required | nodejs.org |
| Git for Windows / Mac / Linux | 1-Click Publishing to GitHub Pages | Required (for Mode B) | git-scm.com |
| GitHub Account | Free static site hosting on GitHub Pages | Required (for Mode B) | github.com |
| Docker Desktop | Running local containerized Ghost instances per project | Optional (Only needed for Mode A local Ghost) | docker.com |
Cloudflare CLI (cloudflared) |
Public HTTPS Tunnels for Mode A | Optional (Only for Mode A Tunnels) | developers.cloudflare.com |
| Netlify / Vercel / Render Account | Alternative high-performance static hosting platforms | Optional (If not using GitHub Pages) | netlify.com / vercel.com / render.com |
| Form Endpoint / Newsletter API Key | Contact forms, newsletter subscriptions, Tally widgets | Optional (If using interactive newsletter/forms) | formspree.io / buttondown.email |
-
Clone the repository:
git clone https://github.com/Swapp-Technologies-LLC/staticGhost.git cd staticGhost -
Install dependencies:
npm install
-
Run verification tests:
npm test -
Run in Development Mode: Launch the desktop interface locally from source:
npm start
-
Run in Production Mode (Install Standalone App): For standard everyday use (launching directly from your Start Menu or Desktop shortcut without needing Node or terminal commands):
- Build the Windows executable installer:
npm run dist
- Open the generated
dist/directory and run theStaticGhost Setup 1.0.0.exewizard to install it natively.
- Build the Windows executable installer:
When running a local Ghost instance via Mode A (Docker):
- Start the Container: In the StaticGhost dashboard under Mode A: Docker + Tunnel, click Start This Project Container.
- Open Ghost Admin: Open your web browser and navigate to:
(Replace
http://localhost:2368/ghost2368with your project's allocated port if using a custom port). - Complete First-Time Setup: Follow the on-screen Ghost setup wizard to create your admin account (email, password, and site title).
- Generate Content API Key (Required for Mode B Exporter):
- Inside Ghost Admin, navigate to Settings (βοΈ) β Integrations.
- Scroll down to the bottom and click + Add custom integration.
- Name it (e.g.
StaticGhost) and click Create. - Copy the generated Content API Key and paste it into the StaticGhost app under Overview Connection Test or Mode B Exporter.
To fetch and compile posts from your local Ghost container, the StaticGhost Exporter requires a valid Content API Key. Without it, the exporter will fall back to exporting the template mock posts.
Follow these steps to generate and connect your API key:
- Open Ghost Admin: Navigate to
http://localhost:2368/ghostin your browser (make sure your project container is running). - Access Integrations: Click the Settings (βοΈ) gear icon in the bottom-left sidebar, then select Integrations under the Advanced menu.
- Create Custom Integration:
- Scroll to the bottom of the page and click + Add custom integration.
- Set the name to
StaticGhostand click Create.
- Copy the Key: Locate the Content API key field on the screen and copy the long hexadecimal string.
- Connect to Desktop App:
- Open the StaticGhost desktop app and select the Overview & Mode tab.
- Paste the copied key into the Content API Key input inside the Local Ghost Connection Test block.
- Click Test Connection to confirm a successful link.
Your Content API key is automatically saved in your active project profile database (profiles.json) so you only need to enter it once.
The Design & Logo Ingestion Manager lets you wrap raw Ghost article content inside your own website's design layout (header, footer, navbars, logos, and custom styles) during static exporting.
Your custom layout.html file must contain these HTML comment tags where you want content to be injected dynamically:
| Placeholder Tag | Description | Typical Placement |
|---|---|---|
<!-- GHOST_PAGE_TITLE --> |
Replaced with the individual post's title. | Inside the <title> tag. |
<!-- GHOST_META_TAGS --> |
Replaced with automatically generated social OpenGraph cards, description, and analytics tracking code. | Inside the <head> block. |
<!-- GHOST_SITE_TITLE --> |
Replaced with the overall blog name. | Logo headers, navbar text, or footers. |
<!-- GHOST_CONTENT --> |
Replaced with the post body, author credentials, reading time calculation, and Giscus comments. | Inside the <main> wrapper. |
Below is a sample blueprint you can copy, save as layout.html, and upload to the desktop app:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><!-- GHOST_PAGE_TITLE --></title>
<!-- GHOST_META_TAGS -->
<link rel="icon" type="image/x-icon" href="./favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="./favicon.png">
<style>
/* Add your custom website CSS styling here */
body { font-family: sans-serif; background: #0f172a; color: #fff; margin: 0; }
.container { max-width: 800px; margin: 2rem auto; padding: 0 1.5rem; }
nav { padding: 1rem; border-bottom: 1px solid #334155; }
a { color: #6366f1; }
</style>
</head>
<body>
<nav>
<a href="./index.html"><strong><!-- GHOST_SITE_TITLE --></strong></a>
</nav>
<main class="container">
<!-- GHOST_CONTENT -->
</main>
</body>
</html>To automate link monetization and maintain Google SEO compliance (preventing search rank penalties for paid/sponsored links):
Under the Affiliate Links & Tags tab, enter a comma-separated list of target domains or subdomains:
amazon.com, amzn.to, bestbuy.com, partnerstack.com
Note: The app automatically splits names by commas and trims whitespace during export.
When exporting static pages:
- The builder scans all anchor links (
<a href="...">) in your posts' HTML. - If the link's destination matches any domain in your list (e.g.,
bestbuy.com):- It appends or overwrites the link's
relattribute with:rel="sponsored nofollow noopener"
- It appends
target="_blank"so the affiliate offer opens in a new tab without taking visitors away from your blog.
- It appends or overwrites the link's
- All other non-matching external or internal links are left completely untouched.
The static site exporter supports free, secure, and zero-database comments powered by Giscus, which stores comments directly inside your repository's GitHub Discussions.
- Target Repository Requirements:
- The target GitHub repository must be public (private repositories do not support Giscus comments for anonymous/outside visitors).
- The repository must have Discussions enabled. (Go to your repository Settings β General β Features and check the Discussions checkbox).
- Authorize Giscus App:
- Go to github.com/apps/giscus and click Install.
- Grant it access to your target blog repository.
- Configure input in the Desktop App:
- Enter your repository name in the format
Owner/Repository(e.g.username/my-blog-repo) inside the Giscus GitHub Comments Repo input field. - When visitors load your static posts, a clean commenting interface will render automatically at the bottom, syncing discussion threads directly to GitHub!
- Enter your repository name in the format
Because static sites (Mode B hosted on GitHub Pages) do not run a live database, you cannot use Ghost's native Portal membership signup form directly unless your Ghost instance is hosted publicly 24/7.
Instead, to collect email addresses and handle user newsletter subscriptions for free, we recommend embedding a third-party subscription form directly into your custom layout.html file:
- Choose a Newsletter Provider: Sign up for an account with email marketing platforms like:
- Buttondown (minimalist, highly popular for developer/markdown blogs).
- Beehiiv or Substack (great for newsletters & writers).
- ConvertKit or Mailchimp (classic marketing tools).
- Retrieve the Embed Code: In your newsletter provider's dashboard, navigate to Integrations / Sharing and copy their HTML form embed snippet.
- Insert into layout.html: Paste the snippet inside your custom template file (e.g., in the footer or sidebar wrapper).
- Custom Forms (Advanced): If you prefer to code your own custom CSS form, use services like Formspree or Getform to handle submission logic and route emails directly to Google Sheets or a webhook.
If you want to add contact forms, user feedback inputs, or beta signup forms on your static blog without running a backend:
Instead of editing form targets manually after each export, you can automate this directly in the Desktop App:
- Go to the Affiliate Links & Automation tab in the sidebar.
- Tick Auto-redirect form actions to custom endpoint.
- Input your target URL (e.g. your Formspree form link or Buttondown endpoint).
During export, the suite automatically inspects your Ghost HTML posts, rewrites any
<form>tags to direct submissions to your target URL, and enforcesmethod="POST".
If your static site is hosted on Netlify, you can build plain HTML forms and collect responses directly in your Netlify dashboard with zero JS:
- Write a standard HTML form.
- Add the attribute
data-netlify="true"to your<form>element:<form name="beta-registration" method="POST" data-netlify="true"> <input type="text" name="username" placeholder="Name" required> <input type="email" name="email" placeholder="Email" required> <button type="submit">Join Waitlist</button> </form>
If hosting on GitHub Pages, use a serverless form endpoint to capture submissions:
- Create a free account on Formspree.io, Getform.io, or Basin.
- Paste the provided endpoint URL directly into your form's
actionattribute:<form action="https://formspree.io/f/your-form-id" method="POST"> <input type="email" name="email" placeholder="Enter email" required> <button type="submit">Submit</button> </form>
Use visual form creators like Tally.so, Google Forms, or Typeform and paste their <iframe> widget codes directly into your posts.
When hosting a static blog alongside your main marketing website on platforms like GitHub Pages, we recommend the following structural layout:
Always keep your static blog files in a separate repository (e.g. my-blog-repo) rather than merging it with your main website's codebase:
- Clean Git Logs: Re-generating static files, RSS feeds, and Pagefind search indexes creates high commit frequency. A separate repository prevents bloat in your core website's Git history.
- Security & Scope: Your Personal Access Tokens (PATs) or deployment credentials only need write permission for the blog repository, isolating security risks.
- Build Efficiency: Prevents your main website's build pipeline (e.g. Netlify/Vercel) from triggering useless deployments every time a post is exported.
-
Subfolder (
example.com/blog) β Best for SEO (Recommended):- SEO Benefit: Directly consolidates search rank and domain authority under your main website. Backlinks to your blog posts automatically improve your core domain's SEO.
- GitHub Pages Setup: Create a separate repository named
blogunder the exact same GitHub account/organization that controls your main site repository (which is mapped to custom domainexample.com). GitHub Pages will natively resolve it atexample.com/blogwithout requiring reverse proxies. - Cross-Platform Setup (GitHub Pages Main + Netlify Blog): GitHub Pages cannot natively proxy subfolders to external hosts. If you host the main site on GitHub and the blog on Netlify (e.g. to use Netlify Forms), you must route your domain through Cloudflare and use a Cloudflare Worker to proxy
/blog/*requests to your Netlify app. Alternatively, move both sites to Netlify and add a_redirectsfile to your main site to rewrite/blog/*to your blog project with a200status code (e.g./blog/* https://my-blog.netlify.app/:splat 200).
-
Subdomain (
blog.example.com) β Easiest Setup:- SEO Benefit: Lower. Search engines treat subdomains as separate web properties, meaning you must build domain authority for the blog independently.
- GitHub Pages Setup: Configure a custom CNAME DNS record for
blogpointing to your user page (e.g.username.github.io) and save it under your repository settings.
If you host your static blog using GitHub Pages, keep in mind the following official platform constraints:
- Size Limits: Source repositories and published websites are recommended to be under 1 GB in total size.
- Traffic Bandwidth: Sites are subject to a soft bandwidth limit of 100 GB per month.
- Build Rate Limit: GitHub enforces a soft build limit of 10 builds per hour. Since StaticGhost compiles and builds files locally before pushing a complete directory commit, you generally will not hit this limit, but avoid continuous rapid exports.
Tip
If your blog expects heavy media usage (e.g., gigabytes of raw images/video uploads) or massive monthly traffic exceeding 100 GB, we recommend deploying to Netlify, Vercel, or Cloudflare Pages which offer larger free-tier bandwidth allocations.
The StaticGhost Desktop Suite is designed with security-first principles to keep your local credentials, database content, and publishing tokens completely safe:
Any sensitive credentials you enter into the application (such as your GitHub Personal Access Tokens and Ghost API keys) are encrypted before being saved to disk:
- Windows: Encrypted via the Data Protection API (DPAPI), locking access specifically to your Windows user account.
- macOS: Encrypted via the native macOS Keychain Services. Even if configuration files are copied or stolen, they cannot be decrypted on other computers or user accounts.
Your local Ghost databases and media files are stored inside isolated Docker volumes inside WSL 2 (Windows Subsystem for Linux):
- This isolates your blogging files from normal Windows user profiles, preventing desktop applications or scripting utilities from accessing your raw database directly.
- Local Port Scoping: Ports are bound to the loopback interface (
127.0.0.1), preventing local network scanning or unauthorized access from other devices on the same Wi-Fi.
When exposing your local container via Mode A Cloudflare Tunnels, traffic is securely proxied:
- The Cloudflare tunnel creates an outbound connection to Cloudflare's edge servers.
- You do not need to open any incoming router ports or expose your home IP address, protecting your network from port scanners and DDOS attacks.
For newsletters or user registrations on your static website, double opt-in is enforced by default:
- Even if an unauthorized person had physical access to your keyboard or submitted inputs, they cannot finalize newsletter signup without direct access to the recipient's personal email inbox to click the verification links.
Warning
Local Device & Credential Security:
- All saved access tokens are automatically encrypted on disk using your operating system's native keychain (
safeStoragevia Windows DPAPI / macOS Keychain / Linux Secret Service). - However, if an unauthorized 3rd party gains physical access or remote control of your unlocked local computer, stored credentials could potentially be accessed.
- Recommended Best Practice: Always use Fine-Grained Personal Access Tokens (or SSH keys) scoped strictly to the target blog repository with minimum required permissions (
Contents: Read and write). Never use account-wide admin tokens.
This project is made possible thanks to the incredible open-source projects, tools, and platforms that form its foundation:
- Ghost Foundation: For building the world-class open-source Ghost CMS, Handlebars theme engine, and Content API.
- Electron & Node.js: For providing the cross-platform desktop application framework and runtime.
- Docker & Cloudflare: For containerized local hosting and high-security edge tunneling (
cloudflared). - Pagefind: For static client-side full-text search indexing with zero backend.
- Giscus: For powering free, zero-database blog comments via GitHub Discussions.
- GitHub Pages, Netlify, Cloudflare Pages, Render & Vercel: For providing generous, high-performance static hosting for developers worldwide.
- CryptoJS: For client-side AES-256 cryptographic decryption.
We strongly believe in supporting the open-source ecosystem that makes this tool possible.
20% of all sponsorship funds received via GitHub Sponsors, Open Collective, and Buy Me a Coffee are automatically re-donated back to the Ghost Foundation, Electron, Pagefind, and our core open-source dependency maintainers!
Created by Swapp Technologies LLC in collaboration with Google Antigravity AI. Licensed under the MIT License. Feel free to use, modify, and share!
