<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.louiscb.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.louiscb.com/" rel="alternate" type="text/html" /><updated>2025-12-28T18:34:08+00:00</updated><id>https://www.louiscb.com/feed.xml</id><title type="html">Louis Cameron Booth</title><subtitle>Hello
</subtitle><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><entry><title type="html">Prompt and context sharing</title><link href="https://www.louiscb.com/blog/2025/09/04/minnas.html" rel="alternate" type="text/html" title="Prompt and context sharing" /><published>2025-09-04T00:00:00+00:00</published><updated>2025-09-04T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2025/09/04/minnas</id><content type="html" xml:base="https://www.louiscb.com/blog/2025/09/04/minnas.html"><![CDATA[<p>Imagine a modern, AI-forward product team.</p>

<p>The designers use Figma and Lovable. The engineers use Claude Code and Cursor. The team lead uses ChatGPT. Thats a lot of tools, each representing a siloed set of user data.</p>

<p>As models converge in capability, the differentiator is no longer the LLM. It is the quality of the user’s prompt and the relevance of the context they provide. The frontier labs are recognising that their models are becoming commodities and that their moat will be long term memory and context that they build up with their users.</p>

<p>Small teams can often maintain a mental map of the current context of their work. In a brand new startup the design team, engineering team, leadership team, are two people sitting next to each other at a desk. The state of the entire company resides within the two brains of its founders. With the passing of time and as the enterprise scales this becomes much harder. This institutional knowledge is transferred into a more permanent form in the shape of spreadsheets, databases, word documents, pdfs, email threads.</p>

<p>But there’s a disconnect between this vital context and the team’s AI tools.</p>

<p>Manually uploaded context becomes stale in each individual’s AI tool, valuable prompts are trapped in individual chat histories. Overloading the LLMs fails to work as they begin to suffer from “context rot.” As you flood a prompt with more data, the model’s ability to accurately recall specific information actually decreases.</p>

<p>Minnas is a tool that aims to fix this problem: <a href="www.minnas.io">https://minnas.io</a></p>

<p>Minnas is a central hub for a team’s prompts and context. It allows you to store, manage, and share your knowledge in one place.</p>

<p>Instead of copy-pasting context into every new window, Minnas provides a single source of truth. Your team stays aligned, and your AI outputs stay accurate.</p>

<p>I made a demo video below:</p>

<iframe height="500" width="100%" src="https://www.youtube.com/embed/L7GhjxrH8SI?si=IGu9dWnSa31AlUO0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[Imagine a modern, AI-forward product team.]]></summary></entry><entry><title type="html">Stockholm and ambition</title><link href="https://www.louiscb.com/blog/2025/08/29/stockholm.html" rel="alternate" type="text/html" title="Stockholm and ambition" /><published>2025-08-29T00:00:00+00:00</published><updated>2025-08-29T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2025/08/29/stockholm</id><content type="html" xml:base="https://www.louiscb.com/blog/2025/08/29/stockholm.html"><![CDATA[<p>At the end of June I quit my job to become a founder. No concrete idea, no revenue, no traction. All I had was the absolute conviction I was making the right decision, plus a great business partner in Jacob Heden Malm!</p>

<p>The two of us have spent the lion’s share of the summer in Stockholm working on various ideas, launching products, meeting founders and mapping out our future. Urgency is a word that’s often on the lips of those in the startup space. Everything is about high velocity, making quick decisions, failing fast. Those are great soundbites and a useful mentality in many environments, however sometimes you just need to take some time to really fucking think about things. No pressure, no deadlines, just thoughts. Before starting this journey I spoke to a successful founder I know — he runs a tech company in London with 200 employees. I asked him for some advice about my first steps as a founder. <em>“Enjoy the summer”</em> was his wry reply. Now, whilst I didn’t spend the last two months sipping mai tais through an umbrella straw, I made sure I enjoyed myself.</p>

<p>One discussion my cofounder and I have indulged in over the summer is choosing where to found our new enterprise. Armed with European passports and no real sense of loyalty to anywhere, this has been a much more difficult decision than I initially thought. Paul Graham’s 2008 essay <em>“Cities and Ambition”</em> came to mind whilst I compared the pros and cons of the various cities on our shortlist. Truthfully, the first time I read Graham’s essay I dismissed it as an utterly lame and cliché-filled analysis. He makes the unique and clever assessment that in Paris people like to be “stylish” and in London they want to be “aristocratic.” It gave the impression of a tourist who had watched <em>Mary Poppins</em> and eaten a croissant in front of the Eiffel Tower, suddenly feeling qualified to judge the ambitions of tens of millions of people. Yet look past the stereotyping and Graham manages to describe a real phenomenon we have all felt: the city you choose to live and work in matters. Its ambition is palpable. And Stockholm has that in spades.</p>

<p>There’s certainly a buzz in Stockholm that wasn’t present when I lived here a few years ago. A tight-knit community of founders and startups has emerged, and the city of less than two million is competing with much larger players. Dealroom ranked Stockholm as the third best startup hub in Europe, after Paris and London. So far in 2025 Sweden has produced more unicorns than any other EU country, and more than France and Germany put together.</p>

<p>One thing that struck me is how “un-Swedish” this new generation of startup feels. If you’ve spent any time in Stockholm during the summer you’ll be aware of the notorious summer shutdown that occurs. The words sommarstängt (closed for the summer) and på semester (on holiday) are plastered across shopfronts and auto-reply emails. Swedes are normally entitled to four weeks of continuous holiday from June to August, which they take. There’s a general understanding that during the summer if you want to get something done - whether its sales or hiring - you’re better off just waiting until September.</p>

<p>This relaxed approach is definitely not present in the new crop of founders. Slowing down for the summer is off the table and their approach to work seems much more akin to our friends across the pond than the traditional Swedish style. 60 or 70 hour work weeks. Lunch and dinner in the office. Often working weekends. Remote work is the exception. Dropping into to each others office for a face to face chat is the norm.</p>

<p>After all, if you’re building an AI startup, your competition in San Francisco, London or Beijing definitely aren’t taking the summer off.</p>

<p>It’ll be interesting to see where this generational divide takes the city’s tech scene in the next few years, and whether the old school approach will seep into the younger companies as they become more mature.</p>

<p>One rule remains even at hip startups like Lovable, take your shoes off when you enter the office!</p>

<p><img src="/images/lovable.jpeg" alt="Shoes off" /></p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[At the end of June I quit my job to become a founder. No concrete idea, no revenue, no traction. All I had was the absolute conviction I was making the right decision, plus a great business partner in Jacob Heden Malm!]]></summary></entry><entry><title type="html">Using Quora questions to test semantic caching</title><link href="https://www.louiscb.com/blog/2025/06/19/semcache.html" rel="alternate" type="text/html" title="Using Quora questions to test semantic caching" /><published>2025-06-19T00:00:00+00:00</published><updated>2025-06-19T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2025/06/19/semcache</id><content type="html" xml:base="https://www.louiscb.com/blog/2025/06/19/semcache.html"><![CDATA[<p>A frenzied race is underway across all industries to generate maximum value through GenAI and LLMs. It seems to be only a matter of time before providers like OpenAI or Anthropic are as integral in a tech stack as a database or server infrastructure. But as the dust settles and our applications become more reliant on the outputs of these models, new problems begin to emerge:</p>

<ol>
  <li>their cost</li>
  <li>their inherent latency</li>
  <li>rate limited and <a href="https://status.anthropic.com">unreliable APIs</a></li>
</ol>

<p><strong>Semantic caching</strong> is a tool at your disposal. The idea is simple: there are many different ways of asking a question to get the same answer. If you can understand the underlying meaning of a query, you can reuse a response to answer it. By mapping different prompts to existing responses you avoid making an unnecessary API call to your LLM provider, reducing your token usage and cutting the latency to almost zero.</p>

<p>I tested the use case of semantic caching by running an experiment using real world data - questions posed on the website Quora. I present an open-source tool for this called <a href="https://github.com/sensoris/semcache">Semcache</a>, which provides a caching layer between a client and an LLM API.</p>

<p>The <a href="https://www.kaggle.com/datasets/quora/question-pairs-dataset">dataset</a> published by Quora  contains actual questions posted on their website. For example <em>“What is the most populous state in the USA?”</em> and <em>“Which state in the United States has the most people?”</em>. Questions that are semantically equivalent, such as these, are marked as duplicates in the dataset. I like the use of this dataset because it is human and raw (have a look at some of the questions in there). They have spelling mistakes, incorrect grammar and many are generally idiosyncratic.</p>

<h3 id="summary-of-experiment">Summary of experiment</h3>
<ul>
  <li>Requests sent to a LLM API via <strong>Semcache</strong></li>
  <li><strong>Dataset:</strong> <a href="https://www.kaggle.com/datasets/quora/question-pairs-dataset">Quora Question Pairs</a> - ~20,000 questions sent</li>
  <li><strong>28% cache hit rate</strong> (5,432/19,400 requests)</li>
  <li><strong>165x speed improvement</strong> (0.010s vs 1.648s average latency)</li>
  <li><strong>LLM Model:</strong> claude-3-haiku-20240307</li>
</ul>

<h2 id="setup">Setup</h2>

<p>The experiment consists of three components: the client sending requests, the semantic caching proxy and the LLM API.</p>

<p><img src="/images/semcache-diagram.png" alt="LTE Architecture" /></p>

<p>Semcache acts as a middleware layer between applications and LLM providers. It works as a drop-in HTTP proxy, which accepts requests from clients and forwards them to the LLM API. Semcache stores responses from the LLM API and returns them to the client. When a new request comes in it compares the embedding of the text to existing cached prompts. If the similarity is above the user set threshold it is considered a hit. The cache operates entirely in-memory and is cold at the start of the experiment - it is built up over time as requests are received.</p>

<p>We used Anthropic’s Python SDK to send the Quora questions and altered the base url to point to our Semcache instance:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="bp">self</span><span class="p">.</span><span class="n">client</span> <span class="o">=</span> <span class="n">AsyncAnthropic</span><span class="p">(</span>
                <span class="n">api_key</span><span class="o">=</span><span class="n">os</span><span class="p">.</span><span class="n">getenv</span><span class="p">(</span><span class="s">"ANTHROPIC_API_KEY"</span><span class="p">),</span>
                <span class="n">base_url</span><span class="o">=</span><span class="n">semcache_host</span>
              <span class="p">)</span>
</code></pre></div></div>

<p>Semcache was deployed on an AWS t2.micro EC2 instance (running on the free tier) with the benchmark client operating on a separate EC2 instance within the same network. The system used cosine similarity for comparing vector embeddings, with the similarity threshold set to 0.9. For text embedding generation the implementation used the <a href="https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2">sentence-transformers/all-MiniLM-L6-v2</a> model. The client is calling Anthropic’s <strong>claude-3-haiku-20240307</strong> model for answers to its questions.</p>

<h2 id="results">Results</h2>

<iframe src="https://snapshots.raintank.io/dashboard-solo/snapshot/XFe0JC1BkKUgyYalFGA72R6IZK08B4Du?orgId=0&amp;refresh=10s&amp;from=1750272166407&amp;to=1750280223580&amp;panelId=12" width="100%" height="350" frameborder="0"></iframe>

<p>Out of 19,400 total requests the system achieved a <strong>cache hit rate of 28.0%</strong>, serving 5,432 requests from the cache. The performance difference between hits and misses was substantial: cached responses were served with an average latency of just <strong>0.010 seconds</strong>, while cache misses required an average of <strong>1.648 seconds</strong> to process.</p>

<iframe src="https://snapshots.raintank.io/dashboard-solo/snapshot/FYUxCaLiBC1KAK8UHmkqU0BxHhajUXMO?orgId=0&amp;refresh=10s&amp;from=1750272064029&amp;to=1750281063083&amp;panelId=15" width="100%" height="350" frameborder="0"></iframe>

<p>Semcache’s memory footprint grew from <strong>160MB to 201MB</strong> after caching 5,494 unique prompt-response pairs. This works out to approximately <strong>7.5KB per cached entry</strong>. Each entry includes a 384-dimensional vector embedding of the prompt, the entire response from Anthropic, metadata (timestamp, access count). This means a server with 8GB of RAM could theoretically cache over 1 million prompt-response pairs.</p>

<h2 id="accuracy-challenge">Accuracy challenge</h2>

<p>Unlike traditional exact-match caching, semantic caching operates in a fuzzy domain where “similarity” is subjective and context-dependent. In this experiment we observed questions that were considered a cache hit by Semcache but were labelled as “non-duplicates” in the Quora dataset. Defining what should or shouldn’t be considered a cache hit is heavily dependent on the use case of the data. For instance Quora labels the questions below as non-duplicates whilst our system marked them as semantically equivalent:</p>

<ol>
  <li><em>“What is pepperoni made of?”</em></li>
  <li><em>“What is in pepperoni?”</em></li>
</ol>

<p>Now to me as a layman, both of these questions about the contents of spicy Italian sausage seem interchangeable. However clearly there is a definition that Quora is following that means they are not semantically equivalent.</p>

<p>We did observe some false positives in our cache hits that are more questionable. Such as the below example:</p>

<ol>
  <li><em>“What is Elastic demand?”</em></li>
  <li><em>“How do you measure elasticity of demand?”</em></li>
</ol>

<p>The challenge here is aligning the caching system to your application by altering the semantic similarity threshold and the text embedding model. The choice of text embedding model has probably the greatest impact. The <code class="language-plaintext highlighter-rouge">sentence-transformers/all-MiniLM-L6-v2</code> model used here provides a good general model for semantic understanding, but domain-specific models are more likely to yield accurate results.</p>

<h2 id="in-the-real-world">In the real-world</h2>

<p>Semantic caching offers several practical benefits that make it worth implementing in production LLM applications. The most obvious is cost reduction. Fewer tokens sent to LLM providers means lower bills, which matters when you’re processing thousands of queries daily. This becomes even more relevant if you’re using AI orchestration tools that charge based on compute time. When your cached responses return in milliseconds instead of seconds, you’re paying for less execution time.</p>

<p>Perhaps most strategically valuable is the knowledge layer that accumulates over time. Each cached response becomes part of an organisational memory that’s completely agnostic to any specific LLM provider. During our experiment, Anthropic experienced service disruptions that forced us to throttle requests significantly. Applications with robust semantic caching layers can maintain functionality by serving cached responses.</p>

<h2 id="final-thoughts">Final thoughts</h2>

<p>In our setup in this experiment we ended up with a hit rate of 28%, but depending on the source of your prompts this number can be a lot higher. The text embedding model, the similarity threshold, how to host your semantic caching infrastructure are all decisions that need to take into consideration your specific use case. Semantic caching proves itself to be a powerful tool to reduce token usage and latency, but it is not free of complications.</p>

<p>If you’d like to test out Semcache for yourself you can find it on Github: <a href="">https://github.com/sensoris/semcache</a></p>

<p><strong>Interested in learning more?</strong></p>

<p>We’re currently beta testing our cloud-hosted version of Semcache, designed to eliminate the technical complexities of managing semantic caching infrastructure yourself. Instead of being concerned with semantic similarity, embeddings and persisting challenges you focus on building your LLM applications and use Semcache to reduce token usage and latency. Join our waitlist at <a href="https://semcache.io/waitlist">https://semcache.io/waitlist</a> or reach out directly at <a href="mailto:louis@semcache.io">louis@semcache.io</a> to get early access.</p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[A frenzied race is underway across all industries to generate maximum value through GenAI and LLMs. It seems to be only a matter of time before providers like OpenAI or Anthropic are as integral in a tech stack as a database or server infrastructure. But as the dust settles and our applications become more reliant on the outputs of these models, new problems begin to emerge:]]></summary></entry><entry><title type="html">Profiling Elixir with Perf</title><link href="https://www.louiscb.com/blog/2023/05/20/profiling-elixir.html" rel="alternate" type="text/html" title="Profiling Elixir with Perf" /><published>2023-05-20T00:00:00+00:00</published><updated>2023-05-20T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2023/05/20/profiling-elixir</id><content type="html" xml:base="https://www.louiscb.com/blog/2023/05/20/profiling-elixir.html"><![CDATA[<p>In this post we will show you how to profile your Elixir application with perf and visualise its stack trace with <a href="https://www.brendangregg.com/flamegraphs.html">Flamegraphs</a>. We’ll create a basic Phoenix web server with two endpoints which we will call in order to profile and analyse its performance.</p>

<p><strong>Requirements:</strong></p>

<p>A Linux machine that can run the <a href="https://perf.wiki.kernel.org/index.php/Main_Page">perf command</a></p>

<p><strong>TLDR:</strong></p>

<ol>
  <li>Create a <code class="language-plaintext highlighter-rouge">mix release</code> of your application</li>
  <li>Run it in daemon mode with JPperf enabled</li>
  <li>Pass the application’s process id into <code class="language-plaintext highlighter-rouge">perf</code> and record some activity</li>
  <li>Visualise the outputted data file with the <a href="https://github.com/brendangregg/FlameGraph">Flamegraph scripts</a></li>
</ol>

<h1 id="why-use-perf">Why use Perf?</h1>

<p>You might ask: “why should we use the perf tool for profiling Elixir? The BEAM ecosystem has numerous <a href="https://www.erlang.org/doc/efficiency_guide/profiling.html">built-in tools</a> for profiling”. And you would be right. The issue with these tools is that they can <strong>significantly slow down the program they profile</strong>. This might be okay if you want to profile a specific small use case of your program, but in this guide we want to demonstrate a process that works with profiling a production level application.</p>

<h1 id="1-creating-our-web-server">1. Creating our web server</h1>

<p>We’re going to use a Phoenix application as our example for profiling with perf. You can follow what we did step by step below or skip to the next section by simply cloning the server in its <a href="https://github.com/louiscb/slow_server">final form here</a>.</p>

<p>First we want to initiate our <em>no-thrills</em> Phoenix server (get set up with Phoenix <a href="https://hexdocs.pm/phoenix/up_and_running.html">here</a>).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix phx.new slow_server --no-html --no-assets --no-ecto
</code></pre></div></div>

<p>Now we could stop here and profile this application, but that would be pretty boring. So instead we’re going to add two endpoints:</p>

<ol>
  <li>
    <p>POST <code class="language-plaintext highlighter-rouge">/api/fib</code> → calculates and returns the nth <strong>Fibonacci number.</strong></p>
  </li>
  <li>
    <p>GET <code class="language-plaintext highlighter-rouge">/api/desc</code> → Will make a <strong>HTTP request</strong> of its own and return a description of Elixir.</p>
  </li>
</ol>

<p>Firstly, in order to make our HTTP request we are going to use the <a href="https://github.com/wojtekmach/req">Req HTTP client library</a>. So add <code class="language-plaintext highlighter-rouge">{:req, "~&gt; 0.3.0"}</code> as a dependency in your <code class="language-plaintext highlighter-rouge">mix.exs</code> file and <code class="language-plaintext highlighter-rouge">mix deps.get</code></p>

<p>After that we want to edit our <code class="language-plaintext highlighter-rouge">router.ex</code> file to include our new endpoints we are going to call:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">SlowServerWeb</span><span class="o">.</span><span class="no">Router</span> <span class="k">do</span>
  <span class="kn">use</span> <span class="no">SlowServerWeb</span><span class="p">,</span> <span class="ss">:router</span>

  <span class="n">pipeline</span> <span class="ss">:api</span> <span class="k">do</span>
    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="p">[</span><span class="s2">"json"</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="n">scope</span> <span class="s2">"/api"</span><span class="p">,</span> <span class="no">SlowServerWeb</span> <span class="k">do</span>
    <span class="n">pipe_through</span> <span class="ss">:api</span>

    <span class="n">get</span> <span class="s2">"/desc"</span><span class="p">,</span> <span class="no">ApiController</span><span class="p">,</span> <span class="ss">:desc</span>
    <span class="n">post</span> <span class="s2">"/fib"</span><span class="p">,</span> <span class="no">ApiController</span><span class="p">,</span> <span class="ss">:fib</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Then we want to create our <code class="language-plaintext highlighter-rouge">api_controller.ex</code> file that will look something like this:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">SlowServerWeb</span><span class="o">.</span><span class="no">ApiController</span> <span class="k">do</span>
  <span class="kn">use</span> <span class="no">SlowServerWeb</span><span class="p">,</span> <span class="ss">:controller</span>

  <span class="k">def</span> <span class="n">desc</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_params</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">desc</span> <span class="o">=</span> <span class="no">Req</span><span class="o">.</span><span class="n">get!</span><span class="p">(</span><span class="s2">"https://api.github.com/repos/elixir-lang/elixir"</span><span class="p">)</span><span class="o">.</span><span class="n">body</span><span class="p">[</span><span class="s2">"description"</span><span class="p">]</span>
    <span class="n">resp</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="n">desc</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">fib</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="p">%{</span><span class="s2">"n"</span> <span class="o">=&gt;</span> <span class="n">n</span><span class="p">})</span> <span class="k">do</span>
    <span class="n">fib</span> <span class="o">=</span> <span class="no">SlowServer</span><span class="o">.</span><span class="no">Fibonacci</span><span class="o">.</span><span class="n">fib</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="n">resp</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="no">Integer</span><span class="o">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">fib</span><span class="p">))</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>We will leave it up to you to implement the Fibonacci function! Try running the server and making requests to the two new endpoints we’ve just created.</p>

<h1 id="2-getting-production-ready-with-mix-release">2. Getting production ready with mix release</h1>

<p>As stated already, our goal is to profile our application as similar to its production state as possible. This way we can hopefully identify any issues that happen in the real world. In order to do that we will be using <a href="https://hexdocs.pm/mix/Mix.Tasks.Release.html">mix release</a>. This assembles all of our code and the runtime into a single unit.</p>

<p>Firstly we need to run our mix release command with the environment set to production:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MIX_ENV=prod mix release
</code></pre></div></div>

<p>You should see instructions on how to run the built release. You may need to set a secret key to be used by Phoenix which you can set as such <code class="language-plaintext highlighter-rouge">export SECRET_KEY_BASE=1234</code></p>

<p>Run our application with a command similar to the one below:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_build/prod/rel/slow_server/bin/slow_server start
</code></pre></div></div>

<p>Now lets test it by calling our endpoints:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  ~ curl http://localhost:4000/api/desc
Elixir is a dynamic, functional language \
for building scalable and maintainable applications%

➜  ~ curl -X POST -H "Content-Type: application/json" -d '{"n": 6}' \
http://localhost:4000/api/fib
8
</code></pre></div></div>

<p>Great! Now let’s actually profile our application.</p>

<h1 id="3-finally-profiling">3. Finally profiling</h1>

<p>In order to profile our application we need to pass in a <a href="https://www.erlang.org/doc/man/erl.html">specific flag</a> which enables support for perf: <code class="language-plaintext highlighter-rouge">+JPperf true</code>. We’re also going to run our release in daemon mode so that it runs in the background. In order to do this run the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERL_FLAGS="+JPperf true" _build/prod/rel/slow_server/bin/slow_server daemon
</code></pre></div></div>

<p>Next we want to get the process id of our application to profile.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BEAM_PID=$(_build/prod/rel/slow_server/bin/slow_server pid)
</code></pre></div></div>

<p>Now we are going to do the actual profiling with perf! This will output a data file containing the stack traces of our running application once we exit.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>perf record -F 10000 -g -a --pid $BEAM_PID
</code></pre></div></div>

<p>In another terminal call our endpoints with the above curls, then return to our running perf and exit with ctrl-c. You should see some output along the lines of the following and a <code class="language-plaintext highlighter-rouge">perf.data</code> output in the folder the command ran in.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.092 MB perf.data (964 samples) ]
</code></pre></div></div>

<h1 id="4-flamegraphs">4. Flamegraphs</h1>

<p>Now we could stop here, we’ve officially profiled our application! But instead let’s visualise this to really <strong>see</strong> what’s going on. We’re going to clone the <a href="https://github.com/brendangregg/FlameGraph">Flamegraph repo</a> inside of the folder you’ve created your <code class="language-plaintext highlighter-rouge">perf.data</code> and then run these some commands to generate the graph.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/brendangregg/FlameGraph
mv perf.data FlameGraph/perf.data
cd FlameGraph
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl &gt; flame.html
</code></pre></div></div>

<p>This will give use our lovely outputted Flamegraphs, which should look something like this.</p>

<p>Can you figure out what I set to <code class="language-plaintext highlighter-rouge">n</code> to from this Fibonacci flame graph?</p>

<div style="width: 100%; height: 600px;">
    <iframe src="https://www.louiscb.com/images/fib-flame-graph.html" style="width: 100%; height: 100%; border: none;"></iframe>
</div>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[In this post we will show you how to profile your Elixir application with perf and visualise its stack trace with Flamegraphs. We’ll create a basic Phoenix web server with two endpoints which we will call in order to profile and analyse its performance.]]></summary></entry><entry><title type="html">An introduction to the WebAssembly specification</title><link href="https://www.louiscb.com/blog/2022/06/01/wasm.html" rel="alternate" type="text/html" title="An introduction to the WebAssembly specification" /><published>2022-06-01T00:00:00+00:00</published><updated>2022-06-01T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2022/06/01/wasm</id><content type="html" xml:base="https://www.louiscb.com/blog/2022/06/01/wasm.html"><![CDATA[<p>It’s all semantics. A short overview of WebAssembly’s language specification.</p>

<p><img src="/images/macromedia.jpg" alt="Macromedia Shockwave Player loading
screen" /></p>

<p>Before diving into how WebAssembly’s specification was mechanised, it’s
important to understand the backstory of the language and why it was
devised.</p>

<p>If you’ve been a user of the web for longer than a few years you may
remember the loading screen in the above image. Flash, Java Applets, Shockwave
and others were technologies that allowed developers to embed
applications with multimedia features into a website. This could be apps
such as games, videos and other computationally intensive programs.
These technologies were not part of the website’s technology stack of
HTML, CSS and Javascript. Instead they were essentially a separate
entity stuck onto the webpage. As a result there were often performance
and security issues with these applets as they relied on users to
install and update plugins which were owned and developed by third party
companies. As the web became more mature and users’ requirements changed
there was a shift away from these multimedia plugins. Steve Jobs
famously slated Flash in 2010 when Apple decided not to support the
plugin on the iPhone, saying:</p>

<blockquote>
  <p>“Letting a third party layer of software come between the platform and
the developer ultimately results in sub-standard apps and hinders the
enhancement and progress of the platform"</p>
</blockquote>

<p>Jobs continued, stating:</p>

<blockquote>
  <p>“While Adobe’s Flash products are widely available, this does not mean
they are open, since they are controlled entirely by Adobe and available
only from Adobe. By almost any definition, Flash is a closed system."</p>
</blockquote>

<p>Job’s ultimatum was the first nail in the coffin for web software such
as Flash, with its demise finally coming in 2020 when Adobe officially
stopping support the product. This is where WebAssembly comes
in. After JavaScript it is the first language to be implemented across
all major web browsers, and although it is very different it
accomplished similar things to products such as Adobe Flash. WebAssembly
has been developed by several top tech companies including Apple, Google
and Microsoft, with the aim of enabling high-performance programs on web
pages. Due to the fact WebAssembly is an open standard and a joint
venture between several actors the language represents the shared
interests of multiple stakeholders, both individuals and companies.</p>

<p>In this post I will provide a short overview of the mechanisation of
WebAssembly since its announcement in 2015. The development of
WebAssembly has been unique as multiple organisations have come together
to define a collective open standard. This allows for an analysis
of the language from the perspective of its formal methods.</p>

<p>Mechanisation can be considered the process of implementing a formal
definition of a language using a mechanised theorem prover. WebAssembly
is an ideal target for mechanisation due to the fact it is stable, not
large and formally specified - unlike other more mainstream languages.</p>

<h3 id="technical-details">Technical details</h3>

<p><img src="/images/webgl.png" alt="WebGL game engine built using
WebAssembly" /></p>

<p>WebAssembly is a binary instruction format for a stack-based virtual
machine. It is designed as a target for compilation of higher-level
languages like C and Rust, enabling deployment for web applications.
Although designed for the web, the language can be theoretically used
across any operating systems for any number of programs. The primary use
case of WebAssembly is running inside of a web browser’s JavaScript
virtual machine. The WebAssembly function that is called by the host
environment exists within a sandbox environment, unable to directly
access the host’s resources. As previously mentioned the language’s
official specification includes formal semantics, which is normally
reserved for academic work on programming languages and not a
characteristic of industry projects. A goal of the language is that all
undefined behaviour is eliminated and the specification precisely states
the intended type soundness property - before a WebAssembly program is
executed all programs undergo type checking validation.</p>

<h2 id="a-history-of-mechanisation">A history of mechanisation</h2>

<p>In this section I will describe the timeline of WebAssembly and how it
has been mechanised over its short history.</p>

<p>The plan for the development of the WebAssembly project was first
announced in 2015, with several browser vendors coordinating a public
statement. The aim of the project was to replace JavaScript as the the
assembly language of the Web. Brendan Eich, creator of JavaScript and
founder of Mozilla stated on his blog:</p>

<blockquote>
  <p>“Yes, we are aiming to develop the Web’s polyglot-programming-language
object-file format."</p>
</blockquote>

<p>In its initial iteration WebAssembly was to be co-expressive with
asm.js, a subset of JavaScript designed to behave like native code.
Asm.js was the predecessor to WebAssembly and was Mozilla’s attempt at
enabling programming languages such as C to run natively in the web
browser. However in the long term WebAssembly was to diverge from
JavaScript’s semantics in order "to best serve as common object-level
format for multiple source-level programming languages". JavaScript was never intended to compile to
low-level targets, and there were performance issues using the language
for such a purpose.</p>

<p>In 2017 WebAssembly was moved to a minimum viable product status and the
initial draft of the language’s semantics were released.
This draft allowed researchers to mechanise the language according to
its formal rules. The first mechanised formalisation of WebAssembly was
conducted by Conrad Watt, a PhD researcher at Cambridge. Watt utilised the proof assistant Isabelle in order
to mechanise the language’s specification. The W3C claimed that the
language’s type system was sound, implying both type safety and memory
safety with respect to the WebAssembly semantics. While conducting the
proof of this claim, Watt discovered a few key errors which meant that
the type system of the language were unsound. In the next section we
will go into more detail on the specifics of the errors discovered in
the specification.</p>

<p>At the Programming Language Design and Implementation conference in 2017
the language’s formal semantics were released, including details such as
its typing rules, small step reduction rules and abstract syntax. The paper included suggested fixes by Watt
to the errors he discovered, and he was mentioned by name in the paper’s
"Acknowledgements" section. Later on in 2018 Watt officially published
his mechanisation of the language, publicising his type soundness proof
for the improved semantics.</p>

<p>In 2019 WebAssembly 1.0 was released (known as wasm 1.0). The
W3C announced that WebAssembly was now an official web standard, and the
language became the fourth "language" to run natively in the browser
(alongside HTML, CSS and JavaScript). Wasm 1.0 had some slight
refactoring done to it since its initial published 2017 version. The
official specification contained a stronger statement of type soundness,
however didn’t contain any proof, instead citing Watt’s earlier 2019
work as proof that its standard soundness theorems hold.</p>

<p>In 2021 a second paper is published by researchers matching this new
official specification and to prove the stronger statement of type
soundness. This time no errors were found
in the language’s specifications and W3C’s claims were backed up. This
paper contains two Wasm 1.0 mechanised specifications, which together
prove type soundness and include an end-to-end verified interpreter. The
paper’s objective was to replace Watt’s earlier 2018 work and become
"the canonical source for the Wasm 1.0 type soundness proof".</p>

<h2 id="bugs-in-webassemblys-draft-specification">Bugs in WebAssembly’s draft specification</h2>

<p>Conrad Watt’s 2018 paper describes the bugs discovered in WebAssembly’s
initial formal specification. These errors were related to the claim of
type soundness in the language, and meant that the original
specification’s type system was unsound. Here I will briefly describe
two of Watt’s highlighted errors related to the type system.</p>

<h3 id="traps">Traps</h3>

<p>WebAssembly contains traps as a core concept. Similar to software traps
in classic assembly languages, a trap is issued by a program in order to
immediately abort execution and transfer control to the host. When the
trap value is generated in WebAssembly it propagates through the stack
terminating all execution of functions, until it reaches the top of the
stack and stops the program. However in the draft version of WebAssembly
the trap value could become stuck during its reduction progression,
meaning exceptions wouldn’t bubble up to the top of the program and
would potentially be ignored.</p>

<h3 id="return">Return</h3>

<p>According to the specification, Result types classify the result of
executing instructions or functions, which is a sequence of values. In its original iteration the specification defined a return
type simply as a "break" operation, where the break procedure would be
called for the number of nested labels within the current function call.
According to this specification a return operation could occur outside
of a function and still be considered well typed instead of being
rejected.</p>

<h2 id="nothing-left-to-prove">Nothing left to prove</h2>

<p>In April of 2022 the initial draft of WebAssembly 2.0 was released. The new specification contained a number of key changes,
among which are significant alterations to the language’s definition and
types. This includes the addition of three new types: number, vector and
reference. A key update is the inclusion of reference types, as with
this a WebAssembly program can handle references to complex host objects
instead of being limited to integer and floating-point values. The goal
with reference types is to potentially bring more complex features to
WebAssembly such as garbage collection - which has been discussed as a
potential addition since 2015. Despite these changes, this new
2.0 specification points to the same 2018 paper by Watt as proof of its
soundness:</p>

<blockquote>
  <p>A machine-verified version of the formalisation and soundness proof is
described in the following article”</p>
</blockquote>

<p>Researchers such as Watt are working on new and updated mechanisations
of WebAssembly 2.0, as stated in the 2021 paper</p>

<blockquote>
  <p>“we intend to keep our mechanisations abreast of these new features in WebAssembly changes”.</p>
</blockquote>

<h1 id="references">References</h1>

<p><em>– “From asm.js to webassembly”, https://brendaneich.com/2015/06/from-asm-js-to-webassembly/, Brendan Eich, 2015, Jun</em></p>

<p><em>– “Steve Jobs slams adobe flash in open letter - and maybe kills it for mobile”, The Guardian, 2010, Apr</em></p>

<p><em>– “Adobe Flash Player End of life General Information Page”, Adobe, 2020, Dec</em></p>

<p><em>– “WebAssembly is now ready for browsers to use”, InfoWorld, 2017, March</em></p>

<p><em>– “WebAssembly Core Specification 1.0”, W3C, 2019, Dec</em></p>

<p><em>– “Mechanising and Verifying the WebAssembly Specification”, Watt Conrad, 2018,
Association for Computing Machinery, https://doi.org/10.1145/3167082</em></p>

<p><em>– “Bringing the Web up to Speed with WebAssembly”, Haas, Andreas and Rossberg, Andreas and Schuff, Derek L. and Titzer, Ben L. and Holman, Michael and Gohman, Dan and Wagner, Luke and Zakai, Alon and Bastien, JF, 2017, Association for Computing Machinery, https://doi.org/10.1145/3062341.3062363</em></p>

<p><em>– “Two Mechanisations of WebAssembly 1.0”, Watt, Conrad and Rao, Xiaojia and Pichon-Pharabod, Jean and Bodin, Martin and Gardner, Philippa, https://doi.org/10.1007/978-3-030-90870-6_4</em></p>

<p><em>– “WebAssembly Core Specification 2.0”, W3C, 2022, May</em></p>

<p><em>– “WebAssembly Github”, https://github.com/WebAssembly/gc, W3C, 2022, May</em></p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[It’s all semantics. A short overview of WebAssembly’s language specification.]]></summary></entry><entry><title type="html">Incredibly simple ‘Hello World’ program from C to WebAssembly</title><link href="https://www.louiscb.com/blog/2022/05/10/simple-webassembly.html" rel="alternate" type="text/html" title="Incredibly simple ‘Hello World’ program from C to WebAssembly" /><published>2022-05-10T00:00:00+00:00</published><updated>2022-05-10T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2022/05/10/simple-webassembly</id><content type="html" xml:base="https://www.louiscb.com/blog/2022/05/10/simple-webassembly.html"><![CDATA[<p>When dipping my toes into WebAssembly the first thing that struck me was how complicated the supposedly “simple” getting-started examples were. The C “Starting from scratch” <a href="https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_wasm">example</a> on the official WebAssembly website requires Emscripten, which produces a .js file with over 2000 lines and an equally large HTML page as well. Searching for other tutorials online didn’t lead anywhere either.</p>

<p>In this “Hello World!” tutorial we will:</p>
<ul>
  <li>Compile a C program to .wasm and run it on our browser</li>
  <li>Not use Emscripten</li>
  <li>Only use standard tools</li>
  <li>Write less than 100 lines of code in total, all from scratch!</li>
</ul>

<h2 id="code">Code</h2>

<p>You can find all the code on my Github <a href="https://github.com/louiscb/Simple-WebAssembly-Tutorial">here</a>.</p>

<h2 id="requirements">Requirements</h2>

<p>You’ll need to make sure you have:</p>
<ul>
  <li>An up to date version of <a href="https://llvm.org/">Clang</a> (bundled with LLVM) which supports wasm32. You can check this by writing: <code class="language-plaintext highlighter-rouge">llc -version</code> and looking for wasm32</li>
  <li>Python</li>
  <li>A modern web browser</li>
</ul>

<h2 id="getting-started">Getting started</h2>

<p>Here I will run through how to get the program up and running.</p>

<h3 id="c-to-wasm">C to wasm</h3>

<p>Let’s first write our C file and save it as main.c, it will look as simple as this, where we are returning a pointer to memory containing our array of chars.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const char* getString() {
  return "Hello World!";
}
</code></pre></div></div>

<p>Next we want to compile our C program into a .wasm file. We use this Clang command to produce a main.wasm file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang --target=wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -o main.wasm main.c
</code></pre></div></div>

<h3 id="javascript-and-html-glue-code">Javascript and HTML “glue code”</h3>

<p>Our HTML page will just load the Javascript script which actually fetches the .wasm file. We’ve also added an empty placeholder heading element. Save the following as index.html:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;!doctype html&gt;

&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;WebAssembly test!&lt;/title&gt;
  &lt;/head&gt;

  &lt;body&gt;
    &lt;h1 id="heading"&gt;&lt;/h1&gt;

    &lt;script src="script.js"&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre></div></div>

<p>Next we need to create our Javascript file, this is where things get a bit more complex. Due to us returning a memory pointer from our getString() function we need to actually go into the wasm program’s memory and fetch our char values. Then we need to convert it to char from the byte integer we store it as.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var importObject = { imports: { imported_func: arg =&gt; console.log(arg) } };

WebAssembly.instantiateStreaming(fetch('main.wasm'), importObject)
.then(obj =&gt; {
  var charArray = new Int8Array(
    obj.instance.exports.memory.buffer, // WASM's memory
    obj.instance.exports.getString(), // char's pointer
    12                                 // The string's length
  );

  let string = String.fromCharCode.apply(null, charArray) // Convert from ASCII code to char
  console.log(string);

  document.getElementById("heading").innerHTML = string // Set the value of the heading to our string
});
</code></pre></div></div>

<h3 id="running-it">Running it</h3>

<p>Now all we need to do is run the python server, save the code below as <code class="language-plaintext highlighter-rouge">server.py</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import http.server
from http.server import HTTPServer, BaseHTTPRequestHandler

import socketserver

PORT = 8000

Handler = http.server.SimpleHTTPRequestHandler

Handler.extensions_map['.wasm'] = 'application/wasm'

print("Server starting at port:", PORT)

httpd = socketserver.TCPServer(("", PORT), Handler)
httpd.serve_forever()
</code></pre></div></div>

<p>Then run:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python server.py
</code></pre></div></div>

<p>And navigate to <code class="language-plaintext highlighter-rouge">localhost:8000</code> on your modern browser. You should see “Hello world!”</p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[When dipping my toes into WebAssembly the first thing that struck me was how complicated the supposedly “simple” getting-started examples were. The C “Starting from scratch” example on the official WebAssembly website requires Emscripten, which produces a .js file with over 2000 lines and an equally large HTML page as well. Searching for other tutorials online didn’t lead anywhere either.]]></summary></entry><entry><title type="html">Showing the Swedish Royal Family how to hack</title><link href="https://www.louiscb.com/blog/2022/05/03/royal-hacking.html" rel="alternate" type="text/html" title="Showing the Swedish Royal Family how to hack" /><published>2022-05-03T00:00:00+00:00</published><updated>2022-05-03T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2022/05/03/royal-hacking</id><content type="html" xml:base="https://www.louiscb.com/blog/2022/05/03/royal-hacking.html"><![CDATA[<p><img src="/images/royals.jpeg" alt="Royal Hackers." /></p>

<p>Last week I demonstrated how to hack an Android to Sweden’s King, Queen and Crown Princess.</p>

<p>This was during a cyber security <a href="https://www.kth.se/om/nyheter/centrala-nyheter/sarbarhet-och-sakerhet-i-fokus-1.1166119">seminar</a> at KTH for a selected audience of 80 people - comprised of royals, politicians, members of the military and heads of industry. During the event there were talks and panel discussions on the current state of Sweden’s cyber defence, as well as what the future may bring. Looming in the background, of course, is the brown bear in the room. Russia’s invasion of Ukraine has brought up fierce debate in Sweden over whether to join NATO, and whilst conflict in the Baltic may look ever slightly more likely now, an increase in cyber attacks originating from Russia is looking inevitable. Many of the speakers mentioned cases such as the Coop attack of 2021 which caused a majority of stores of one of Sweden’s largest supermarkets to completely shutdown. The origin of the attack was a Russian ransomware gang called REvil. Whilst the Russian government claims to not harbour cyber criminals and in fact has said has “dismantled” REvil, many groups like REvil don’t target Russian organisations. The broader question to ask is in the current political climate will Russian police hunt down cyber criminals residing in Russian purely to assist western investigations? I think we know the answer to that. Sweden may be particularly vulnerable to cyber attacks as it is one of, if not, the most digitalised societies in the world. By being effectively cashless and with a transition to tech and service industries in recent years there are many more vectors for attacks. On stage Micael Byden, the Swedish military’s Supreme Commander, stated that Sweden was well aware of these threats and had been working on improving its cyber defence. During his talk he even gave a shout out to a so-called “<a href="https://jobb.forsvarsmakten.se/sv/utbildning/befattningsguiden/gu-befattningar/cybersoldat/">cyber soldier</a>” graduate sitting right next to me in the audience as an example of the strides being made. I later spoke to the nameless cyber soldier (who was dressed very much like a real soldier) who told me around 30 people graduated from the program in his year. Most of them didn’t carry on to work for the military.</p>

<p>My part in this event was representing KTH’s <a href="https://nse.digital/">Cyber Security Lab</a> where I work part-time whilst doing my Master’s in Computer Science. Our department decided to demonstrate a few hacks, one of which being the ES File Explorer <a href="https://github.com/fs0c131y/ESFileExplorerOpenPortVuln">vulnerability</a>. The ES File Explorer app was an Android app that brought rich file exploring features to Android devices and was hugely popular in the late 2010s, reaching over 500 million users. The vulnerability in the app is that as part of its local networking features it exposes a port on the user’s phone which essentially allows an external actor full access to the user’s file system. If you had the app open and were connected to a Wi-Fi network, anyone else on the network could have complete read and write access to your device in a matter of seconds. I walked through this with the Royals and alongside my fellow NSE employee Viktor we demonstrated how this hack worked. I took a selfie with the King and Princess and then we showed how that selfie can be stolen. As the Princess herself said, I should probably not give up the hacking to be a photographer.</p>

<p><img src="/images/selfie-1.jpeg" alt="Royal Hackers." /></p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Watch my cameo in KTH Innovation’s promo video</title><link href="https://www.louiscb.com/blog/2021/12/01/kth-innovation-promo.html" rel="alternate" type="text/html" title="Watch my cameo in KTH Innovation’s promo video" /><published>2021-12-01T00:00:00+00:00</published><updated>2021-12-01T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2021/12/01/kth-innovation-promo</id><content type="html" xml:base="https://www.louiscb.com/blog/2021/12/01/kth-innovation-promo.html"><![CDATA[<p><a href="https://www.linkedin.com/posts/kth-innovation_kthinnovation-itallstartshere-activity-6867115698762657792-iW9D/" title=" It starts with you."><img src="/images/kth-promo.jpg" alt="It starts with you." /></a></p>

<p>See if you can spot me in this promo <a href="https://www.linkedin.com/posts/kth-innovation_kthinnovation-itallstartshere-activity-6867115698762657792-iW9D/">video</a> for KTH Innovation. It’s the first modelling role I’ve taken in my career - I think I’ll stick with the day job.</p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Busybee has graduated!</title><link href="https://www.louiscb.com/blog/2021/11/14/busybee-graduated.html" rel="alternate" type="text/html" title="Busybee has graduated!" /><published>2021-11-14T00:00:00+00:00</published><updated>2021-11-14T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2021/11/14/busybee-graduated</id><content type="html" xml:base="https://www.louiscb.com/blog/2021/11/14/busybee-graduated.html"><![CDATA[<iframe height="500" width="100%" src="https://www.youtube-nocookie.com/embed/9x_5QQdi2IQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Last Thursday we attended Demo Day, the finale of the year long pre-incubator program at KTH Innovation. At the event we exhibited our ideas along with the other start-ups in batch 12. I got to go on stage with my co-founder, Matay, and discuss Busybee in front of an audience of about 150 investors, fellow entrepreneurs and journalists. It was a great experience and a fun way to end the program at KTH Innovation, who have been a great help. Regardless of what happens with Busybee the things I’ve learned by attempting to build a tech company from the ground up will stay with me for the rest of my career. Below you can watch our demo day pitch.</p>

<p><img src="/images/matay.jpg" alt="Matay showing Busybee app" />
<img src="/images/jacob.jpg" alt="Jacob at the stand" />
<img src="/images/jenny.jpg" alt="Jenny at the stand" /></p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Read my interview with KTH about working and studying in tech</title><link href="https://www.louiscb.com/blog/2021/01/25/kth-news.html" rel="alternate" type="text/html" title="Read my interview with KTH about working and studying in tech" /><published>2021-01-25T00:00:00+00:00</published><updated>2021-01-25T00:00:00+00:00</updated><id>https://www.louiscb.com/blog/2021/01/25/kth-news</id><content type="html" xml:base="https://www.louiscb.com/blog/2021/01/25/kth-news.html"><![CDATA[<p>Last week I was privileged enough to be interviewed by Håkan Soold at KTH, the university where I completed my Bachelor’s degree. We discussed how the program at KTH prepared me for my role at Klarna (which incidentally, I was offered a while before I graduated - although they don’t mention that in this article). You can read the article <a href="https://www.kth.se/en/aktuellt/nyheter/louis-jobbar-som-mjukvaruutvecklare-pa-klarna-1.1042824">here</a>.</p>]]></content><author><name>{&quot;email&quot;=&gt;&quot;hello@louiscb.com&quot;}</name><email>hello@louiscb.com</email></author><category term="blog" /><summary type="html"><![CDATA[Last week I was privileged enough to be interviewed by Håkan Soold at KTH, the university where I completed my Bachelor’s degree. We discussed how the program at KTH prepared me for my role at Klarna (which incidentally, I was offered a while before I graduated - although they don’t mention that in this article). You can read the article here.]]></summary></entry></feed>