<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<atom:link href="https://seanbehan.ca/posts/tag/cloudflare/rss.xml" rel="self" type="application/rss+xml" />
		<title>Sean Behan — Cloudflare</title>
		<link>https://seanbehan.ca/posts/tag/cloudflare</link>
		<description>Posts tagged “Cloudflare”.</description>
		<language>en-CA</language>
		<!-- RSS wants an address here and readers show the name beside it. -->
		<managingEditor>sean@seanbehan.ca (Sean Behan)</managingEditor>
		<webMaster>sean@seanbehan.ca (Sean Behan)</webMaster>
		<lastBuildDate>Sun, 13 Sep 2026 03:14:55 GMT</lastBuildDate>
		<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/how-i-survived-a-ddos-flood</guid>
		<title><![CDATA[How I survived a DDoS Flood]]></title>
		<description><![CDATA[CloudFlare L3/L4 is amazing. Today I not only survived a DDoS attack. My site didn’t even go down. W Cloudflare!]]></description>
		<link>https://seanbehan.ca/posts/how-i-survived-a-ddos-flood</link>
		<pubDate>Fri, 28 Aug 2026 04:16:26 GMT</pubDate>
		<category>Cloudflare</category><category>EmDash</category><category>Networking</category>
		<content:encoded><![CDATA[<p>CloudFlare L3/L4 is amazing. Today I not only survived a DDoS attack. My site didn’t even go down. W Cloudflare!</p><img src="https://seanbehan.ca/_emdash/api/media/file/01M2CC3Z965YHFVQZ4Y7X3SFFN.png" alt="Analytics graph of total requests during the DDoS flood, peaking near 58 million." width="518" height="267" /><p>How you ask? By hosting the entire site on Cloudflare’s Edge network. It allows me to host a CMS (shoutout to <a href="https://github.com/emdash-cms/emdash">EmDash</a> by Cloudflare). While also being basically immune to DDoS.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/emdash-and-cloudflare-workers</guid>
		<title><![CDATA[EmDash and Cloudflare Workers]]></title>
		<description><![CDATA[A post explaining that this blog has migrated from SvelteKit to EmDash]]></description>
		<link>https://seanbehan.ca/posts/emdash-and-cloudflare-workers</link>
		<pubDate>Mon, 24 Aug 2026 23:53:36 GMT</pubDate>
		<category>Cloudflare</category><category>EmDash</category>
		<content:encoded><![CDATA[<p>Today I migrated from SvelteKit to EmDash.</p><p></p><p>EmDash is a full-stack TypeScript CMS built on Astro and Cloudflare. This will be my first post made using the platform.</p><p></p><p>It works by hosting posts on Cloudflare D1 and images on R2. So it requires an active billing account. It also requires Workers Paid due to the size constraints of Workers Free (free Workers can only be up to 3MB).</p><p></p><p>So far it’s working great. I can publish posts directly from the admin panel, and it’s all still cached on Edge.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/email-bot</guid>
		<title><![CDATA[Email bot]]></title>
		<description><![CDATA[Cloudflare email routing plus Workers AI: an inbox that answers its own mail.]]></description>
		<link>https://seanbehan.ca/posts/email-bot</link>
		<pubDate>Wed, 04 Dec 2024 20:29:41 GMT</pubDate>
		<category>Cloudflare</category><category>serverless</category><category>TypeScript</category>
		<content:encoded><![CDATA[<h3>How it Works</h3><p>In this post I&#x27;m just going to show you something cool I made using cloudflare workers.</p><pre><code class="language-typescript">import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';

export default {
	async email(message, env, ctx) {
		const content = await new Response(message.raw).text();
		const messages = [
			{ role: 'system', content: 'you are an email responder, reply to the given message' },
			{ role: 'user', content }
		];
		// @ts-expect-error broken bindings
		const { response } = await env.AI.run('@cf/meta/llama-3.2-11b-vision-instruct', { messages });
		const msg = createMimeMessage();
		msg.setHeader('In-Reply-To', message.headers.get('Message-ID') as string);
		msg.setSender({ name: 'Sean Behan', addr: 'contact@seanbehan.ca' });
		msg.setRecipient(message.from);
		msg.setSubject('RE');
		msg.addMessage({
			contentType: 'text/plain',
			data: `${response}\n\nThis was an automated message. If this is urgent, message Sean on Telegram https://t.me/codebam`
		});

		const replyMessage = new EmailMessage('contact@seanbehan.ca', message.from, msg.asRaw());

		await message.reply(replyMessage);
		await message.forward('codebam@riseup.net');
	}
} satisfies ExportedHandler&lt;Env&gt;;</code></pre><p>This small amount of code is all it takes to automatically reply to all my emails with llama3.2!</p><p>Feel free to copy it and use it yourself. The source code is on <a href="https://github.com/codebam/email-bot">GitHub</a>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/cf-workers-telegram-bot</guid>
		<title><![CDATA[Cloudflare Workers Telegram Bot]]></title>
		<description><![CDATA[A tour of the rewritten cf-workers-telegram-bot API for running a Telegram bot on Cloudflare Workers.]]></description>
		<link>https://seanbehan.ca/posts/cf-workers-telegram-bot</link>
		<pubDate>Wed, 15 May 2024 07:11:06 GMT</pubDate>
		<category>Cloudflare</category><category>TypeScript</category>
		<content:encoded><![CDATA[<p>This past week I spent rewriting <a href="https://github.com/codebam/cf-workers-telegram-bot">`cf-workers-telegram-bot`</a>.</p><p>Here I&#x27;m going to show you the newly updated API that anyone can use to run a Telegram Bot on Cloudflare Workers or any other serverless platform.</p><h3>Project Setup</h3><p>First you need to set up a new Cloudflare Workers project using wrangler.</p><pre><code class="language-shell">npm create cloudflare@latest</code></pre><p>This will prompt you for a project name first. Choose a name for your new project or use the default.</p><p>Next you&#x27;ll be prompted for the type of application you want to deploy. You should choose a &quot;Hello World&quot; Worker.</p><p>After that you&#x27;ll be asked a few more questions, like if you want to use TypeScript, and Git (optional, but recommended). You don&#x27;t need to deploy just yet.</p><h3>Adding Your Bot Token</h3><p>Next you can add the token you get from <code>@BotFather</code> on Telegram as a secret so you don&#x27;t have to keep it in the code.</p><pre><code class="language-shell">npx wrangler secret put TOKEN</code></pre><p>Once you&#x27;ve created your project and created your secret you&#x27;ll want to install the <code>cf-workers-telegram-bot</code> library to start coding.</p><pre><code class="language-shell">cd your-new-project
npm i @codebam/cf-workers-telegram-bot</code></pre><p>This will give you access to my <code>TelegramBot</code> class for creating a Telegram Bot.</p><h3>Writing the Bot</h3><p>To get started you can open <code>src/index.ts</code> in your favorite editor such as vscode or vim. Then you can import like this.</p><pre><code class="language-javascript">import TelegramBot, { TelegramExecutionContext } from '@codebam/cf-workers-telegram-bot';</code></pre><p>Then you can simply write your bot like this.</p><pre><code class="language-javascript">export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise&lt;Response&gt; {
    const bot = new TelegramBot(env.TOKEN);
    await bot.on('start', async function (context: TelegramExecutionContext) {
      switch (context.update_type) {
	case 'message':
	  await context.reply('Hello from Cloudflare workers');
	  break;

	default:
	  break;
      }
    }).handle(request.clone());
    return new Response('ok');
  },
};</code></pre><h3>Deployment and Webhook</h3><p>Now you&#x27;re ready to deploy.</p><pre><code class="language-shell">npx wrangler deploy</code></pre><p>You can set your webhook by going to the URL you&#x27;ve been given plus your token and <code>?command=set</code>.</p><p>Like this. <code>https://telegram-bot.username.workers.dev/YOURTOKENHERE?command=set </code>Now when you send your bot a message on Telegram it should respond with <code>Hello from Cloudflare workers</code>.</p><h3>Starter Template</h3><p>If you&#x27;ve got this far and you don&#x27;t want to code it all by yourself, you can fork my repository <a href="https://github.com/codebam/cwtb-consumer">here</a> to get started with a working copy.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/svelte-search</guid>
		<title><![CDATA[Search bar in Sveltekit]]></title>
		<description><![CDATA[The fuzzy search on this site, built with fuse.js.]]></description>
		<link>https://seanbehan.ca/posts/svelte-search</link>
		<pubDate>Wed, 06 Dec 2023 20:29:41 GMT</pubDate>
		<category>Cloudflare</category><category>JavaScript</category><category>serverless</category><category>Svelte</category><category>SvelteKit</category><category>TypeScript</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>In this post I&#x27;m going to show you how I made the search bar on the posts page and main page of my website.</p><h3>Implementing the Search Bar</h3><p>I used a library called <code>fuse.js</code> for fuzzy searching for titles, here&#x27;s how it works.</p><pre><code class="language-typescript">&lt;script lang="ts"&gt;
	import { Heading } from 'flowbite-svelte';
	import Post from '$lib/components/Post.svelte';
	import Fuse from 'fuse.js';
	import { afterUpdate } from 'svelte';
	export let posts: { path: string; meta: { title: string; date: string } }[];
	let query = '*';
	let results = posts;
	const options = { keys: ['meta.title'] };
	const fuse = new Fuse(posts, options);
	afterUpdate(() =&gt; {
		let results_ = fuse.search(query);
		results = results_.map((result) =&gt; ({ path: result.item.path, meta: result.item.meta }));
	});
&lt;/script&gt;

&lt;Heading
	tag="h4"
	style="display: inline"
	class="ml-8 my-4 text-secondary dark:text-dark-secondary w-auto"&gt;Posts&lt;/Heading
&gt;
&lt;input class="text-secondary" bind:value={query} /&gt;
&lt;ul&gt;
	{#each results as post}
		&lt;Post {post} /&gt;
	{/each}
	{#if results.length === 0}
		{#each posts as post}
			&lt;Post {post} /&gt;
		{/each}
	{/if}
&lt;/ul&gt;</code></pre><h3>How it Works</h3><p>You can see I initialize the query to <code>&#x27;*&#x27;</code>. All this really does is sets the search field to <code>*</code> instead of being empty. Then in the input element I bind to the value of query so that when the user types in the field the value of query will change.</p><p>I do some more initialization of <code>fuse.js</code> to search and then I register <code>afterUpdate()</code>. This allows me to search only when the page changes.</p><p>Finally I added some logic to display all the posts if there are no results, like for example when the page loads.</p><p>All this together looks like the search bar you see on <code>/posts</code> and <code>/</code>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/svelte-auth</guid>
		<title><![CDATA[Auth and Databases in Sveltekit]]></title>
		<description><![CDATA[HTTP Basic Auth with a session cookie in SvelteKit, backed by Cloudflare D1.]]></description>
		<link>https://seanbehan.ca/posts/svelte-auth</link>
		<pubDate>Sun, 12 Nov 2023 05:40:33 GMT</pubDate>
		<category>Cloudflare</category><category>JavaScript</category><category>serverless</category><category>Svelte</category><category>SvelteKit</category><category>TypeScript</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Recently I implemented HTTP Basic Auth using a session cookie in SvelteKit on Cloudflare Pages using Cloudflare D1 as a database backend. Here I&#x27;ll show some code snippets so you can do the same.</p><h3>Project Setup</h3><p>First you would want to set up your project according to <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site">this</a>.</p><p>So you can have a basic project set up.</p><h3>Database Setup</h3><p>Next you&#x27;ll want to add the database to your project. It&#x27;s easy to create using wrangler. Just remember to link it to your project in your cloudflare dash under settings &gt; functions.</p><pre><code class="language-sh">wrangler d1 create your-database-name</code></pre><p>Then you can copy it into your <a href="https://github.com/codebam/svelte-auth/blob/master/wrangler.toml">`wrangler.toml` </a>so your local dev works with it. To run with support for D1 you can use this oneliner.</p><pre><code class="language-sh">npm run build &amp;&amp; wrangler pages dev .svelte-kit/cloudflare</code></pre><p>My final schema looks like this. Yours might be a bit different, but mine allows for adding songs to a playlist, which was the purpose of <a href="https://github.com/codebam/svelte-auth">that project</a>.</p><pre><code class="language-sql">CREATE TABLE IF NOT EXISTS Users (id TEXT PRIMARY KEY, password TEXT);
CREATE TABLE IF NOT EXISTS Playlist (id TEXT PRIMARY KEY, email TEXT, url TEXT, date TEXT);</code></pre><p>You&#x27;ll want to create these tables using wrangler both locally (on your dev server) and remotely.</p><pre><code class="language-sh">wrangler d1 execute svelte-auth --local --file=./schema.sql
wrangler d1 execute svelte-auth --file=./schema.sql</code></pre><p>Now you can add it to your <a href="https://github.com/codebam/svelte-auth/blob/master/src/app.d.ts">`app.d.ts` </a>like this.</p><pre><code class="language-typescript">declare global {
	namespace App {
		interface Platform {
			env?: {
				DB: D1Database;
			};
			context: {
				waitUntil(promise: Promise&lt;any&gt;): void;
			};
			caches: CacheStorage &amp; { default: Cache };
		}
	}
}

export {};</code></pre><h3>Authentication Implementation</h3><p>Now you&#x27;re ready to set up authentication. It&#x27;s probably easier to just show you how I did it. I imported <code>sha256</code> which is just a simple function I wrote to call web crypto to get the sha256sum of a string.</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/login/%2Bpage.server.ts">Here </a>is my code.</p><p>I added <a href="https://kit.svelte.dev/docs/form-actions">form actions</a>. These allow you to quickly write code that will work with HTML <code>&lt;form&gt;</code>&#x27;s.</p><pre><code class="language-typescript">import { redirect } from '@sveltejs/kit';
import sha256 from '$lib/sha256';

export const actions = {
	register: async (event) =&gt; {
		const data = await event.request.formData();
		const email = data.get('email');
		const password = await sha256(data.get('password')?.toString() ?? '');
		const { success } = await event.platform?.env?.DB.prepare('INSERT INTO Users VALUES (?, ?)')
			.bind(email, password)
			.all();
		if (success) {
			event.cookies.set('session', JSON.stringify({ email, password }));
			throw redirect(303, '/');
		}
	},
	login: async (event) =&gt; {
		const data = await event.request.formData();
		const email = data.get('email');
		const password = await sha256(data.get('password')?.toString() ?? '');
		const results = await event.platform?.env?.DB.prepare('SELECT password FROM Users WHERE id=?')
			.bind(email)
			.all();
		if (results.results[0].password === password) {
			event.cookies.set('session', JSON.stringify({ email, password }));
			throw redirect(303, '/');
		}
	}
};</code></pre><h3>Checking Authentication Status</h3><p>Now this is enough to save a cookie with the user auth, but to re-auth on a page we need to check if their cookie is valid. So how I do this is I wrote another function called <a href="https://github.com/codebam/svelte-auth/blob/master/src/lib/tryLogin.ts">`tryLogin.ts`</a>.</p><pre><code class="language-typescript">import type { D1Database } from '@cloudflare/workers-types';

const tryLogin = async (session_cookie: string | undefined, DB: D1Database) =&gt; {
	if (session_cookie) {
		const { email, password } = JSON.parse(session_cookie);
		const results = await DB.prepare('SELECT password FROM Users WHERE id=?').bind(email).all();
		if (results.results[0].password === password) {
			return true;
		}
	}
	return false;
};

export default tryLogin;</code></pre><p>Now I can call this inside <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.server.ts">`+page.server.ts`</a>.</p><pre><code class="language-typescript">import tryLogin from '$lib/tryLogin';
import getCurrentSong from '$lib/getCurrentSong';

export const load = async (event) =&gt; ({
	auth: await tryLogin(event.cookies.get('session'), event.platform?.env?.DB),
	song: await getCurrentSong(event.platform?.env?.DB)
});</code></pre><p>Using it inside the <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.svelte">`+page.svelte` </a>is as simple as this one line.</p><pre><code>export let data: {auth: boolean, song: {id: string, url: string}};</code></pre><h3>Playlist Management</h3><p>Now I wrote more form actions for adding and removing songs from the playlist <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/playlist/%2Bpage.server.ts">here</a>.</p><pre><code class="language-typescript">import { redirect } from '@sveltejs/kit';
import tryLogin from '$lib/tryLogin';

export const actions = {
	add: async (event) =&gt; {
		if (await tryLogin(event.cookies.get('session'), event.platform?.env?.DB)) {
			const session = JSON.parse(event.cookies.get('session'));
			const email = session.email;
			const formdata = await event.request.formData();
			const v = new URL(formdata.get('url')).searchParams.get('v');
			await event.platform?.env?.DB.prepare('INSERT INTO Playlist VALUES (?, ?, ?, ?)')
				.bind(crypto.randomUUID(), email, v, Math.floor(new Date().getTime()))
				.all();
		}
		throw redirect(303, '/');
	},
	remove: async (event) =&gt; {
		if (await tryLogin(event.cookies.get('session'), event.platform?.env?.DB)) {
			const formdata = await event.request.formData();
			const id = formdata.get('id');
			await event.platform?.env?.DB.prepare('DELETE FROM Playlist WHERE id=?').bind(id).all();
		}
		throw redirect(303, '/');
	}
};</code></pre><h3>User Interface</h3><p>Now this allows us to add songs, we just need the interface for it. So I wrote the <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.svelte">main page </a>to allow logged in users to submit songs.</p><pre><code class="language-svelte">&lt;script lang="ts"&gt;
	import { enhance } from '$app/forms';
	import YouTube from '$lib/svelte-youtube.svelte';
	export let data: { auth: boolean; song: { id: string; url: string } };
&lt;/script&gt;

{#if !data.auth}
	&lt;p&gt;Visit &lt;a href="https://seanbehan.ca/login"&gt;login&lt;/a&gt;&lt;/p&gt;
{/if}

&lt;YouTube
	on:end={() =&gt; document.getElementById('remove_song')?.click()}
	videoId={data.song.url}
	options={{ playerVars: { autoplay: 1 } }}
/&gt;

&lt;form style="display: none;" method="POST" action="/playlist?/remove" use:enhance&gt;
	&lt;input name="id" value={data.song.id} /&gt;
	&lt;button id="remove_song"&gt;remove&lt;/button&gt;
&lt;/form&gt;

{#if data.auth}
	&lt;form method="POST" action="/playlist?/add" use:enhance&gt;
		&lt;label
			&gt;Submit a song
			&lt;input name="url" type="url" placeholder="paste a youtube link" pattern=".*v=.*" /&gt;
		&lt;/label&gt;
		&lt;button&gt;Submit&lt;/button&gt;
	&lt;/form&gt;
	&lt;p&gt;&lt;a href="https://seanbehan.ca/logout"&gt;Log out&lt;/a&gt;&lt;/p&gt;
{/if}</code></pre><h3>Conclusion</h3><p>If you have any problems or you want to see the entire repo, the code is <a href="https://github.com/codebam/svelte-auth">here</a>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/llama2</guid>
		<title><![CDATA[Using Llama2 on Cloudflare Workers]]></title>
		<description><![CDATA[Wiring Workers AI into a Telegram bot: the AI binding, the model call, and getting a reply back.]]></description>
		<link>https://seanbehan.ca/posts/llama2</link>
		<pubDate>Thu, 12 Oct 2023 18:21:27 GMT</pubDate>
		<category>Cloudflare</category><category>serverless</category><category>TypeScript</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>You might know I&#x27;ve made a Chat Bot <a href="https://github.com/codebam/cf-workers-telegram-bot">cf-workers-telegram-bot</a>.</p><p>Recently I added AI to it using Cloudflare AI. Here I&#x27;m going to show you how and how you too can make your own chatbot using Cloudflare AI.</p><h3>Cloudflare Workers AI Setup</h3><p>First you&#x27;ll want to add the AI section to your wrangler.toml.</p><pre><code class="language-toml">\[ai\]
binding = "AI"</code></pre><h3>Using the AI Model</h3><p>Then you can simply import</p><pre><code class="language-javascript">import { Ai } from '@cloudflare/ai';</code></pre><p>And you can use it like this</p><pre><code class="language-javascript">const ai = new Ai(env.AI);
prompt = 'hello world';
const result = await ai.run('@cf/meta/llama-2-7b-chat-int8', {
	prompt
});</code></pre><h3>Other Models and Tips</h3><p>That&#x27;s it!</p><p>There are other models you can use as well on Cloudflare AI so you should check them out here for other cool things you can do https://developers.cloudflare.com/workers-ai/models/ I&#x27;m hoping for a 70B model soon :) If you want to use this to make a chat bot make sure when you restore the prompt you wrap user input in <code>[INST] [/INST]</code> tags so llama2 knows what was user input and what it said.</p><p>If you want to try out my Chat Bot it&#x27;s on <a href="https://t.me/TuxRobot">Telegram</a>.</p>]]></content:encoded>
	</item>
	</channel>
</rss>