Napalm Paste – A Fast, Private Paste Service Powered by Cloudflare Workers
Project Overview
Napalm Paste is a lightweight, high‑performance paste service built on Cloudflare’s edge network.
It runs entirely on Cloudflare Workers, using R2 for durable object storage, Workers KV for ultra‑fast caching, and Durable Objects for stateful analytics.
The goal is simple: give developers a fast, secure way to share code snippets, config files, or any text‑based data without the latency of a traditional backend. By rendering the initial page on the edge with Hono SSR and keeping the UI bundle under 50 KB (thanks to Tailwind CSS and minimal JavaScript), Napalm Paste feels instant even on slow connections.
Key Features
- Sub‑10 ms global response – Edge execution and KV caching keep latency near‑zero.
- Private pastes – Authenticated access via Cloudflare Access and optional domain‑wide sharing.
- Burn‑after‑view & time‑based expiration – Auto‑delete after a single view or after 10 min – 30 days.
- Human‑readable IDs – IDs like
happy-river-tiger-magic-blueare easy to remember and share. - Syntax highlighting – Highlight.js automatically detects language and applies styling.
- iOS Shortcuts integration – Share directly from any iOS app via a native Shortcut that calls the query‑parameter API.
- Dark‑mode aware UI – System preference detection with a manual toggle, all CSP‑compliant.
- Real‑time analytics – Durable Objects track request counts and cache hit/miss ratios.
Architecture Overview
A minimal edge‑centric diagram (≤ 10 lines, ≤ 60 chars wide) illustrates the data flow:
+-----------+ +----------------+ +------------+
| Browser /| HTTP | Cloudflare | KV | Workers |
| iOS Short.| -----> | Workers (Hono)| <---- | KV (cache)|
+-----------+ +----------------+ +------------+
|
| R2 (object storage)
v
+---------------+
| R2 Bucket |
+---------------+
Durable Objects (analytics) sit alongside the Workers- Workers (Hono) – Handles routing, SSR, security middleware, and rate limiting.
- Workers KV – Caches recent pastes (default TTL 5 min) for instant reads.
- R2 – Persists the full paste payload; the single source of truth.
- Durable Objects – Maintain counters and analytics across requests.
How It Works
1. Server‑Side Rendering with Hono
The entry point (src/hono-worker.ts) creates a Hono app and renders JSX components directly inside the Worker:
// src/hono-worker.ts
import { Hono } from 'hono';
import { Layout } from './components/Layout';
import { PasteViewer } from './components/PasteViewer';
const app = new Hono();
app.get('/', (c) =>
c.html(
Layout({
title: 'Napalm Paste',
children: `<p>Welcome! Create a new paste below.</p>`
})
)
);
app.get('/p/:id', async (c) => {
const id = c.req.param('id');
const paste = await getPaste(id);
return c.html(
Layout({
title: `Paste – ${id}`,
children: PasteViewer({ content: paste?.content ?? 'Not found' })
})
);
});
export default app;Layout and PasteViewer are tiny JSX components that output static HTML; because they run on the edge, the first paint arrives in milliseconds.
2. Storing and Retrieving Pastes (R2)
All paste bodies are written to an R2 bucket. The helper functions hide the R2 API details:
// src/r2.ts
export const R2_BUCKET = (globalThis as any).R2_BUCKET as R2Bucket; // bound in wrangler.toml
export async function savePaste(id: string, content: string, ttlSeconds: number) {
await R2_BUCKET.put(`${id}.txt`, content, {
httpMetadata: { contentType: 'text/plain' },
customMetadata: { ttl: `${Date.now() + ttlSeconds * 1000}` }
});
}
export async function getPaste(id: string): Promise<string | null> {
const obj = await R2_BUCKET.get(`${id}.txt`);
return obj ? await obj.text() : null;
}- The
customMetadata.ttlfield is later used to enforce expiration.
3. Fast Reads via Workers KV
When a paste is requested, the Worker first checks KV. If a cached version exists, it returns immediately; otherwise it falls back to R2 and populates the cache:
// src/kv.ts
export const KV_NAMESPACE = (globalThis as any).PASTE_KV as KVNamespace;
export async function fetchPaste(id: string): Promise<string | null> {
const cached = await KV_NAMESPACE.get<string>(id);
if (cached) return cached; // cache hit
const fresh = await getPaste(id); // R2 read
if (fresh) await KV_NAMESPACE.put(id, fresh, { expirationTtl: 300 }); // 5 min
return fresh;
}4. Analytics with Durable Objects
A single Durable Object class aggregates request counts and cache‑hit ratios. The Worker binds to it via the DO namespace defined in wrangler.toml.
// src/durable.ts
export class AnalyticsDO {
state: { requests: number; cacheHits: number } = { requests: 0, cacheHits: 0 };
async fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname === '/stats') {
return new Response(JSON.stringify(this.state), { headers: { 'Content-Type': 'application/json' } });
}
// Any other request increments total count
this.state.requests++;
return new Response('OK');
}
// Called from the Worker when a KV hit occurs
async recordCacheHit() {
this.state.cacheHits++;
}
}The main worker can invoke recordCacheHit() after a successful KV read, giving real‑time insight into cache effectiveness.
5. Security Middleware
The Hono router includes two built‑in middlewares:
app.use('*', async (c, next) => {
// CSP with nonce for any inline script
const nonce = crypto.randomUUID();
c.res.headers.set('Content-Security-Policy', `script-src 'nonce-${nonce}'`);
c.set('cspNonce', nonce);
await next();
});
app.use('*', async (c, next) => {
// Simple rate limiting (10 req/s per IP)
const ip = c.req.headers.get('cf-connecting-ip') ?? 'unknown';
const limitKey = `rl:${ip}`;
const count = (await KV_NAMESPACE.get<number>(limitKey)) ?? 0;
if (count > 10) return c.text('Too many requests', 429);
await KV_NAMESPACE.put(limitKey, count + 1, { expirationTtl: 1 });
await next();
});These ensure each response complies with a strict CSP and that abusive traffic is throttled at the edge.
Getting Started
Clone the repo
BASHgit clone https://github.com/xxdesmus/napalm-paste.git cd napalm-pasteInstall dependencies
BASHnpm ciConfigure Cloudflare bindings (see
wrangler.tomlforR2_BUCKET,PASTE_KV, andANALYTICS_DObindings).Run a local dev server – Wrangler emulates the edge environment:
BASHnpm run devDeploy to your Cloudflare account
BASHnpm run deploy
The local server renders the same HTML you’ll see in production, making debugging straightforward.
Recent Developments
The project has recently completed its migration from a React SPA to Hono SSR with Tailwind CSS (commit c373c81). This shift reduced the client bundle from ~212 KB to under 50 KB and introduced edge‑rendered HTML for faster first paints.
All npm audit findings were addressed in a dedicated security commit (fb49b76), and the theme‑toggle component was updated to be fully CSP‑compliant (da739b0). Documentation was refreshed to reflect the new architecture and feature set.
Conclusion
Napalm Paste showcases how a fully serverless stack—Cloudflare Workers, R2, Workers KV, and Durable Objects—can deliver a secure, ultra‑fast paste service with a minimal footprint. By rendering on the edge, caching aggressively, and providing privacy‑first features like private pastes and burn‑after‑view, it offers a compelling alternative to traditional pastebins.
Whether you’re looking to embed a paste service into your own workflow, study a modern edge architecture, or contribute to an open‑source project that pushes the limits of serverless performance, Napalm Paste is ready to copy, paste, and share.
Explore the code, spin up your own instance, and start sharing snippets at the speed of the edge!