“Do you remember that tool you shared six months ago? What was its name?”

I get this question constantly, and it makes sense. After six+ years of curating cloud security knowledge through CloudSecList (launched in 2019  ) and CloudSecDocs (which followed in 2020  ), I’ve built a vast knowledge base that’s become challenging to navigate.

So I had an idea: What if you could actually chat with your newsletter?

This post shows how I’ve made CloudSecList conversational, turning years of newsletters and articles into something you can query directly. I’ll walk through the technical approach, building on my previous article “Building a Serverless Mailing List in AWS  ”.

The End Goal: Converse with your Knowledge

Newsletters are great, but they’re too static; they pile up and become unsearchable quickly. Wikis have similar issues too; although they offer basic keyword search, they can’t synthesize information across multiple sources to answer complex questions.

I wanted the ability to ask questions like “Open source container scanners” or “What tool can I use to check my OIDC configuration in GitHub is secure?” and get practical answers from my knowledge base.

So here’s what I built in the end:

Ask CloudSecList
Ask CloudSecList

Instead of scrolling through old issues or maintaining bookmarks you’ll forget, you can now query years of curated content conversationally.

Sample Query Sample Query Sample Query
You can try the final solution here: cloudseclist.com/ask/

Next, let’s take a look at how this is implemented.

Under The Hood

The current setup looks like the diagram below.

High-Level Overview
High-Level Overview

To simplify the discussion, we can break it down into five main areas, covered next:

  1. AI - RAG Pipeline
  2. AI - Query & Retrieval
  3. Website (Static Hosting)
  4. Deployment (CI/CD)
  5. Security Guardrails

AI - RAG Pipeline

I needed a retrieval-augmented generation (RAG) pipeline that could handle years of markdown content without having to manage vector databases directly. After evaluating a few options, I chose AutoRAG, a new service recently announced by Cloudflare, mainly for three reasons:

  1. Zero infrastructure overhead: AutoRAG abstracts away the complexity of having to manage and scale a vector database.
  2. Automatic content processing: It handles chunking and embedding without custom code.
  3. Already integrated with my existing Cloudflare stack: I already had CI/CD and monitoring in place for Cloudflare.

I won’t go into the inner workings of AutoRAG here (please check the links above if you want to see a full explanation), but, at a high-level, its indexing process works as follows:

  • Markdown conversion: AutoRAG converts all files into structured Markdown.
  • Chunking: The extracted text is chunked into smaller pieces to improve retrieval granularity.
  • Embedding: Each chunk is embedded to transform the content into vectors.
  • Vector storage: The resulting vectors are stored in a Vectorize database.
AutoRag Indexing. Courtesy of Cloudflare.
AutoRag Indexing. Courtesy of Cloudflare.

Unfortunately, there is no Terraform support for AutoRAG yet, so the setup process is fully manual:

  1. Connect data source: AutoRAG only integrates with R2 buckets as data source, so before starting I had to create a new bucket.
  2. Generate indexes: Next, you need to select an embedding model to convert your data to vector representation. Here I left the recommended Smart Default.
  3. Generation model: You also need to select an LLM to use to generate your responses. Here I left the recommended Smart Default.
  4. Connect an AI Gateway: Required by Cloudflare to monitor and control your model usage. We will look at the Gateway later in this post.
AutoRAG Creation Process AutoRAG Creation Process AutoRAG Creation Process

As soon as you add content to the R2 bucket, AutoRAG will automatically pick it up and start indexing it.

AutoRAG Indexing Result
AutoRAG Indexing Result

AI - Query & Retrieval

With the knowledge base indexed, the next step is building the query interface.

First of all, Cloudflare requires you to create an AI Gateway. I’m not totally sure why this is required, but I can see it as a choke point where you can configure logging, caching, rate limiting, and even authentication. Plus, it provides you with analytics around usage of your AI models.

AI Gateway Analytics
AI Gateway Analytics

With the gateway setup, the query processing itself runs on an Cloudflare Worker with AI bindings. The core implementation is straightforward:

  const AutoRAG = env.RAG_NAME;
  const ragResult = await env.AI.autorag(AutoRAG).aiSearch({
      query: fullQuery,
      rewrite_query: true,
      max_num_results: RAG_MAX_NUM_RESULTS,
      ranking_options: {
          score_threshold: RAG_SCORE_THRESHOLD,
      },
  });

The Worker receives user queries and forwards them to the AutoRAG instance through the AI binding. The rewrite_query parameter enables automatic query optimization, while score_threshold filters out low-relevance results.

If you want an example to start with, checkout Create a simple search engine and the Workers Binding page on the Cloudflare docs website.

When you send a request to the AutoRAG’s .aiSearch() endpoint, the query workflow starts and AutoRAG takes care of:

  • Query rewriting: Improve retrieval quality by transforming the original query into a more effective search query.
  • Embedding the query: The rewritten query is transformed into a vector so that it can be compared against your vectorized data to find the most relevant matches.
  • Vector search in Vectorize: The query vector is searched against stored vectors in the associated Vectorize database.
  • Content retrieval: Vectorize returns the most relevant chunks and their metadata. The original content is then retrieved from the R2 bucket.
  • Response generation: A text-generation model from Workers AI is used to generate a response using the retrieved content and the original user’s query.
AutoRag Querying. Courtesy of Cloudflare.
AutoRag Querying. Courtesy of Cloudflare.

Website (Static Hosting)

Now we have an indexed knowledge base and a Worker API endpoint that can query it.

The challenge is integrating this dynamic functionality into CloudSecList’s static website architecture. The CloudSecList website, infact, is a static website generated via Jekyll and deployed to an S3 bucket configured for static web hosting.

In the end, the Worker itself enabled for a hybrid approach: the Worker provides the dynamic backend while the frontend remains static HTML, CSS, and JavaScript.

In the frontend, the implementation consisted of two main components:

  • Chat interface: A responsive UI that feels native to the existing site design. Its HTML and CSS have been created with a one-shot prompt with Cursor (and I’m quite impressed with the result).
  • JavaScript client: Handles user interactions, API calls to the Worker endpoint, and response rendering. This keeps the static site generator completely unaware of the AI functionality.
The Frontend
The Frontend

Deployment (CI/CD)

If you recall from Building a Serverless Mailing List in AWS   and My Blogging Stack  , all my static websites are deployed via GitHub actions, which allow me to automatically push the generated HTML content to S3 every time I push to the main branch.

Here, I had to add a step to the CI/CD pipeline to automatically push the generated HTML content of both CloudSecList and CloudSecDocs not only to S3, but also to the newly created R2 bucket supporting AutoRAG.

Updated CD Pipeline for CloudSecList
Updated CD Pipeline for CloudSecList

This way, every time I update one of these websites, the updated content will be added to the knowledge base and automatically indexed by AutoRAG, making it available for querying.

Security Guardrails

Before going live, I wanted to be sure the AI model wasn’t going to be abused, especially in a way that could’ve leaked the original source of any of my websites.

While Cloudflare AI Gateway offers built-in Guardrails, I decided to test Lakera Guard for this implementation.

Disclaimer

At the time of writing, I’m working at Lakera.

Regardless, I want to clarify that this is NOT a sponsored post, and that I’m not receiving any compensation from neither Cloudflare nor Lakera for writing it.

As always, my blog posts contain my unbiased personal experiences and learnings.

This meant slightly extending the code of the Worker to verify whether the user request is either malicious or constitutes a prompt attack:

const LAKERA_GUARD_API_KEY = await env.KV_NAMESPACE.get("LAKERA_GUARD_API_KEY");
const LAKERA_PROJECT_ID = env.LAKERA_PROJECT_ID;

const lakeraMessages = [{ content: userQuery, role: "user" }];
const lakeraRequestBody = {
    messages: lakeraMessages,
    project_id: LAKERA_PROJECT_ID,
};

const lakeraResponse = await fetch(LAKERA_GUARD_API_URL, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${LAKERA_GUARD_API_KEY}`,
    },
    body: JSON.stringify(lakeraRequestBody),
});

const lakeraResult = await lakeraResponse.json();
if (lakeraResult.flagged) {
    console.warn("Lakera Guard identified a threat:", lakeraResult);
    return new Response(JSON.stringify({ error: "Malicious query detected" }), {
        status: 403,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
    });
}

Oh boy if I was right. Within literally three minutes of announcing the project on Twitter, the first prompt injection attempts arrived.

An attempted prompt attack An attempted prompt attack

Conclusions

Building a conversational interface for six+ years of curated content proved more straightforward than expected, thanks to Cloudflare’s AutoRAG handling the complex RAG pipeline infrastructure.

In addition, I got first-hand proof that security guardrails aren’t optional for public AI interfaces. The immediate attack attempts after launch confirmed that protection needs to be built in from day one, not added later.

As next steps, I’m exploring query refinement based on user feedback patterns and considering extending this approach to other content collections. The foundation scales well beyond newsletters to any curated knowledge base.

If you implement something similar or run into interesting challenges with RAG systems, I’d be interested to hear about it: 🐣 Twitter or 📢 feedback.marcolancini.it.