<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-CA">
    <title>nove.dev</title>
    <subtitle>devon&#x27;s personal website</subtitle>
    <link rel="self" type="application/atom+xml" href="https://nove.dev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://nove.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-12-01T12:06:00-08:00</updated>
    <id>https://nove.dev/atom.xml</id>
    <entry xml:lang="en-CA">
        <title>advent of code 2025</title>
        <published>2025-12-01T12:06:00-08:00</published>
        <updated>2025-12-01T12:06:00-08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nove.dev/blog/aoc-2025/"/>
        <id>https://nove.dev/blog/aoc-2025/</id>
        
        <content type="html" xml:base="https://nove.dev/blog/aoc-2025/">&lt;p&gt;last year i never did any aoc, though i&#x27;ve been &lt;a href=&quot;https:&#x2F;&#x2F;tangled.org&#x2F;nove.dev&#x2F;aoc-2024&#x2F;&quot;&gt;retroactively working&lt;&#x2F;a&gt; on some of the problems. this year i&#x27;ll do it!&lt;&#x2F;p&gt;
&lt;p&gt;i always aspire to complete the whole month and get stymied partway through. hopefully this year will be different :) i will show more persistence and do more background research on the compsci fundamentals; maybe i&#x27;ll write some abstracted algorithms or data structures to do frequent tasks. the recommended reading below is mostly for myself!&lt;&#x2F;p&gt;
&lt;p&gt;i&#x27;m putting &lt;a href=&quot;https:&#x2F;&#x2F;tangled.org&#x2F;nove.dev&#x2F;aoc-2025&#x2F;&quot;&gt;my solutions&lt;&#x2F;a&gt; on tangled, writing in rust, using jujutsu for version control, and a nix flake for a dev environment. it&#x27;s a good mix of low-friction while still using new tools that i want to be better at. plus, i don&#x27;t use rust at work much anymore, so i need to keep the proverbial rust off.&lt;&#x2F;p&gt;
&lt;p&gt;recommended reading:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;c&#x2F;6lunsz&#x2F;&quot;&gt;this comment&lt;&#x2F;a&gt; on &lt;a href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;&quot;&gt;lobste.rs&lt;&#x2F;a&gt;, which discusses strategies if you find yourself stuck, common data structurs &amp;amp; algorithms, and textbooks to read for more of a theory background&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;elly.town&#x2F;d&#x2F;blog&#x2F;2025-09-01-advent-of-code-500.html&quot;&gt;elly&#x27;s post&lt;&#x2F;a&gt;, elly&#x27;s solved every aoc out there and has good advice&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en-CA">
        <title>thoughts on the latest cloudflare outage</title>
        <published>2025-11-21T20:06:00-08:00</published>
        <updated>2025-11-21T20:06:00-08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nove.dev/blog/cloudflare/"/>
        <id>https://nove.dev/blog/cloudflare/</id>
        
        <content type="html" xml:base="https://nove.dev/blog/cloudflare/">&lt;p&gt;against my best instincts, i will put forward my opinion on the recent cloudflare outage. you should probably read their &lt;a href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;18-november-2025-outage&#x2F;&quot;&gt;postmortem&lt;&#x2F;a&gt; for context.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;intro-and-background&quot;&gt;intro and background&lt;&#x2F;h2&gt;
&lt;p&gt;tl;dr is that a quarter of the internet went down because a database change caused oversized config files to be propagated to a critical component of their core proxy. this component used a fixed amount of preallocated memory as an optimization, and nothing validated the size of the config it was parsing. when the oversized config was loaded, the application crashed, which translated into an http 500 server error, which blocked all traffic attempting to traverse that component. in other words:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cloudflare:&lt;&#x2F;span&gt;&lt;span&gt; i would like to parse dynamic config generated by an external service
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;me:&lt;&#x2F;span&gt;&lt;span&gt; fair enough
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cloudflare:&lt;&#x2F;span&gt;&lt;span&gt; i would like to restrict the size of this config, for performance
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;me:&lt;&#x2F;span&gt;&lt;span&gt; fair enough
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;me:&lt;&#x2F;span&gt;&lt;span&gt; what will you do if the config is too big?
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cloudflare:&lt;&#x2F;span&gt;&lt;span&gt; die instantly
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;me:&lt;&#x2F;span&gt;&lt;span&gt; ah.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;spooky-scary-sloppy-rust-code&quot;&gt;spooky scary sloppy rust code&lt;&#x2F;h2&gt;
&lt;p&gt;the snippet of code provided in the postmortem is as follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fetch_features&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;dyn BotsInput,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; Features,
&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; Result&amp;lt;(), (ErrorFlags, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    features.checksum &amp;amp;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0xFFFF_FFFF_0000_0000&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    features.checksum |= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;::from(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.config.checksum);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(feature_values, _) = features
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;append_with_names&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.config.feature_names)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;the problematic part is that last line: &lt;code&gt;.unwrap()&lt;&#x2F;code&gt;. in rust, errors are handled using sum types, namely, &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;&#x2F;code&gt;. this &lt;code&gt;Result&lt;&#x2F;code&gt; object can then be dealt with in various ways: either explicitly with &lt;code&gt;match&lt;&#x2F;code&gt;, &lt;code&gt;if let&lt;&#x2F;code&gt;, or &lt;code&gt;let ... else&lt;&#x2F;code&gt;; automatically with &lt;code&gt;?&lt;&#x2F;code&gt; or &lt;code&gt;.unwrap_or_else()&lt;&#x2F;code&gt;; dangerously with &lt;code&gt;.unwrap()&lt;&#x2F;code&gt;; or &lt;code&gt;unsafe&lt;&#x2F;code&gt;ly with &lt;code&gt;.unwrap_unchecked()&lt;&#x2F;code&gt;. here&#x27;s how each would have looked; don&#x27;t mind the owl references, we&#x27;ll get to it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;we don&amp;#39;t know the actual return type of .append_with_names(), 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;so this is a guesstimate:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; res: Result&amp;lt;(Vec&amp;lt;FeatureValues&amp;gt;, Unknown), AppendError&amp;gt; 
&lt;&#x2F;span&gt;&lt;span&gt;    = features.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;append_with_names&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.config.feature_names);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;handle the error directly
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; res {
&lt;&#x2F;span&gt;&lt;span&gt;    Ok((feature_values, _)) =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*do whatever you needed to do here*&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Err(error) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        log_error!(error, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;failed to append features when loading config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;draw the rest of the owl
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;find a safe default
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(feature_values, _) = res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap_or_else&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;| {
&lt;&#x2F;span&gt;&lt;span&gt;    log_error!(error, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;failed to append features when loading config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    (vec![], Default::default()) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;draw the rest of the owl
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;bubble it up - let the caller draw the rest of the owl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;Ok((feature_values, _)) = res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    log_error!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;failed to append features when loading config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;Err((ErrorFlags::AppendError, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;120&lt;&#x2F;span&gt;&lt;span&gt;)) 
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;who knows what this error return should be
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;another way to bubble it up, more conveniently but without a local log 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;(assuming the error type is convertible)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(feature_values, _) = res?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;the bare minimum - cloudflare&amp;#39;s actual solution.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;crash the program if you can&amp;#39;t append
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(feature_values, _) = res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;more or less equivalent to the above, with slightly better debuggability
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(feature_values, _) = res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;there should be a max of 200 feature values&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;abdicate all responsibility. perform undefined behaviour if you can&amp;#39;t append. 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;here there be memory corruption. summon the nasal demons.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;thankfully cloudflare had the sense not to do this
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(feature_values, _) = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;unsafe &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap_unchecked&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;what-s-wrong-with-unwrapping&quot;&gt;what&#x27;s wrong with unwrapping&lt;&#x2F;h2&gt;
&lt;p&gt;unwraps are to be used in one of two&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-third-1&quot;&gt;&lt;a href=&quot;#fn-third&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; scenarios:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;you do not care about your code very much. if it crashes, who cares&lt;&#x2F;li&gt;
&lt;li&gt;the error is believed to be completely impossible. there is an invariant being upheld that rust&#x27;s type system is insufficiently smart to prove. if the value is an error, something has gone catastrophically wrong and no assumptions can be made about the state of the program. the only safe response is to panic, unwind the stack and kill the entire thread. we can only pray that is enough.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;this unwrap falls in neither of these. cloudflare&#x27;s core proxy is critical infrastructure and thus must make every effort not to crash, eliminating the first point. the second is where cloudflare-positive discourse seems to have placed them - the engineer and code reviewer must have believed it impossible to fail here. i disagree - in a situation where you are parsing input provided from an external system, generated by a database, across a network boundary, no assumptions can be made. operate as if it is, if not actively malicious, at least highly suspicious.&lt;&#x2F;p&gt;
&lt;p&gt;cloudflare&#x27;s core proxy code should probably have zero potential panics in it. this makes it more difficult to write rust, i understand that. if they must write panicking code, at the &lt;em&gt;very least&lt;&#x2F;em&gt; it should have safety-style comments above each potential panic indicating why the engineer believes a panic is impossible.&lt;&#x2F;p&gt;
&lt;p&gt;additionally, this component of the core proxy was a filter designed to keep llm scrapers and other (semi-)malicious bots from accessing a client site. in the event of a catastrophic failure like this, the component should &lt;em&gt;obviously&lt;&#x2F;em&gt; fail open - bot management is not a reliable security boundary, and increasing load &#x2F; visibility is far superior to taking the website off the internet altogether. present one of those horrible little javascript challenges on each page load if you must (i&#x27;m thinking of ticket-sellers looking to avoid being bought out by bots), but don&#x27;t break the web. different components have different threat models; i&#x27;m sure e.g. their zero trust product would prefer failing closed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;crux&quot;&gt;crux&lt;&#x2F;h2&gt;
&lt;p&gt;the core of the issue is an unstated, unvalidated assumption - the config will never exceed 200 features. rust did its best to warn the coder about this issue; the coder responded by explicitly ignoring the concern, and this outage is the result. a sufficiently conscientious programmer and reviewer would have looked at this &lt;code&gt;Result&lt;&#x2F;code&gt;, thought &quot;how might i handle this properly&quot;, and pursued a proper solution.&lt;&#x2F;p&gt;
&lt;p&gt;now we finally return to &quot;draw the rest of the owl&quot;; it is not obvious what this solution should have been. i have several ideas: there could have been a validator in the config generator or distributor, plus a &lt;code&gt;&#x2F;&#x2F;SAFETY&lt;&#x2F;code&gt; comment immediately before this unwrap; that would have prevented the outage. i&#x27;m hesitant about this, though - if the validator fails, we&#x27;re back where we started. the solution could have also been a configuration manager that stores past versions, so the proxy could revert to a previous known-good config in the event of a misconfiguration like this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-i-would-have-fixed-this&quot;&gt;how i would have fixed this&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;configuration is about defining a point in multidimensional space&lt;&#x2F;strong&gt;. this point is fed to your program to define its initial state and rules about how it handles input and output. if configuration fails, a critically important program should continue to work at all costs, in this case by statically choosing an acceptable default point in the state-space, inhabiting it, and logging a maximum-severity error message. ideally, such a message would trigger an alert and page an on-call engineer to fix the problem immediately, but until then, nothing is fundamentally broken. for this program, the sensible static default is &quot;allow every request through&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;postscriptum-response-to-this-is-rust-s-fault&quot;&gt;postscriptum - response to &quot;this is rust&#x27;s fault&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;if this had been c, cloudflare would have failed to check the error code and proceeded to perform undefined behaviour. whee, memory safety cves! if this had been java, javascript, python, go, haskell, c#, kotlin, lisp, etc., this program would have been ripped out and rewritten in rust; if cloudflare is so memory-constrained as to only allow 200 entries in an array, a garbage-collected language would have been right out. if this had been c++, it would have thrown an unchecked exception, same as the panic, and the outage would have been exactly the same. maybe zig solves this; i haven&#x27;t written any zig.&lt;&#x2F;p&gt;
&lt;p&gt;rust is not a perfect language. the naming of &lt;code&gt;.unwrap()&lt;&#x2F;code&gt; is suboptimal; error handling in tutorials and educational work is often brushed under the rug with it. maybe in rust 2.0, which will never happen, we can rename unwrap. until then, it is the professional responsibility of engineers, at least those who create programs which must have extremely high reliability, to understand the language in which they work and not litter their code with potential crashes.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-third&quot;&gt;
&lt;p&gt;there is a third&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-fourth-1&quot;&gt;&lt;a href=&quot;#fn-fourth&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, false option: if you handle panics with &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;panic&#x2F;fn.catch_unwind.html&quot;&gt;&lt;code&gt;catch_unwind&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; or a panic hook, and you will convert the panic into an error 500 or something. cloudflare probably had this, it&#x27;s common in web frameworks. this is not something you want to rely on. panic catching is dangerous; if you panic within a panic (either in a &lt;code&gt;Drop&lt;&#x2F;code&gt; implementation or the panic handler) it hard-aborts your program immediately. also, responding with a 500 is not actually the correct response in this case! as i said above, this filter should have failed open, not responded to every request with a 500. listen to the rust documentation: &quot;it is not recommended to use this [...] for a general try&#x2F;catch mechanism&quot; &lt;a href=&quot;#fr-third-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-fourth&quot;&gt;
&lt;p&gt;another, even less defensible idea: you are allowed to panic if there is no sensible way of responding to such an error. i disagree with this - in a webserver, you want to respond to the client no matter what; in a user-driven app with a gui, tui, or cli, splatting a panic stacktrace is not a friendly user experience; in a library you should &lt;em&gt;really&lt;&#x2F;em&gt; never panic but always return some indication of failure, etc. &lt;a href=&quot;#fr-fourth-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="en-CA">
        <title>on correctness</title>
        <published>2025-11-17T13:00:00-08:00</published>
        <updated>2025-11-17T13:00:00-08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nove.dev/blog/correctness/"/>
        <id>https://nove.dev/blog/correctness/</id>
        
        <content type="html" xml:base="https://nove.dev/blog/correctness/">&lt;p&gt;correctness must be (normative) baseline in software. luckily, correctness is a mathematical property of an algorithm, and thus we can use computers to help humans build correct software. millions of lines of code have been written for the express purpose of helping humans prove subsets of correctness. i&#x27;m thinking of (ranked by how much assurance they provide, in decreasing order): formal verification, type systems, linters, property testing, unit testing, fuzzers, autoformatters, syntax highlighters.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;formal verification is the highest standard of correct software. i do not have direct experience with it. to my understanding, it converts many programming errors into much fewer specification errors, which tend to be easier to solve.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;type systems, when implemented kindly, are the best tool for correctness. they enable local reasoning, tooling support, invariants. when implemented with the sharp edge against the programmer, the programmer learns to hate them. the difference between a kind type system and a sharp type system is how the programmer feels about the type system. if the programmer writhes in its grasp, flailing against the oppressive all-seeing eye of &quot;computer says no&quot;, the type system is not helping achieve correct software. if the programmer uses the type system as a tool, sketching their software out in broad strokes, knowing with certainty the potential states and being assured of the correctness of composition, it&#x27;s good.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;i have a distaste for gradual typing systems, as the flow goes something like this: working, highly dynamic program -&amp;gt; overlaid type system incorrectly proclaims incorrectness -&amp;gt; programmer develops hatred for type systems&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;linters are a good way to soften the blow of a compiler error. moving from &quot;computer says no&quot; to &quot;computer says you&#x27;re wrong, but here&#x27;s an escape hatch labelled &#x27;do not use&#x27;&quot;. the downside of this approach is obvious, however: programmers are creatures of infinite creativity and chaotic alignments. when deadlines are imposed, the programmer is burnt out, or years of useless lints have jaded their saliency checker, lints are ignored and software is built incorrectly. equally, escape hatches allow lint writers to be more careless about false positives, pushing the vicious cycle one step further&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;property testing is interesting to me. i have used it but lightly, and never in anger. from my perspective, property testing gives you 80% of the assurance of formal verification with 20% of the effort. of course, an 80% correct program is rarely good enough. the core conceit is to formalize properties about your functions, formalize ways of generating input to said functions, and machine-checking the whole shebang.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;i have described unit testing suites as brittle exoskeletons attached to the nerves of a program. with the slightest twitch, a change to a program can shatter its exoskeleton to dust while self-inflicting great pain. the result of this, and thus the purpose of unit testing systems, is to disincentivise change of any kind. have fun migrating off .net 6.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;however, i have had the immense displeasure of refactoring a complicated program with no test suite whatsoever. combined with the total lack of documentation, &lt;a href=&quot;https:&#x2F;&#x2F;www.hyrumslaw.com&#x2F;&quot;&gt;hyrum&lt;&#x2F;a&gt; ensured that any minor modification would break something with no warning. therefore, i consider unit testing to be something of a necessary evil, and keep hope that a better world is possible.&lt;&#x2F;li&gt;
&lt;li&gt;property testing suffers from the same problem in theory, yet in practice the properties are sufficiently abstract as to allow for enough program movement so as to not atrophy.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;fuzzers are a reduced form of property testing applied to an entire program, usually with some level of codepath-aware analysis to minimize breadth-first-search-itis. the exoskeleton is kept at a safe distance from the slithering muscles beneath, which allows for major refactors that don&#x27;t change the basic api. however, they don&#x27;t check correctness. fuzzers can root out memory safety bugs, edge case crashes, and input mishandling, for which they are invaluable, but a fuzzer on its own isn&#x27;t good enough.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;autoformatters, like type systems, are best when they are human-focused and not impositional. an autoformatter offloads mental effort from the programmer by keeping a program in a consistent style - the writer doesn&#x27;t have to fuss about indentation and the reader can load the program into their mental approximation without being distracted by a format different from the one they use. it serves the same purpose as typesetting rules at a scientific journal. the formatter has to be universally applied. it must be consistent and disrespect programmer intentions to be valuable, but its style must be smart enough to not cause anger.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;autoformatters can also catch errors themselves, usually by papering over language-level problems. &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unreachable_code#goto_fail_bug&quot;&gt;goto fail&lt;&#x2F;a&gt;, while indicative of a half-dozen problems with the c language (unreachable code is legal, if statements don&#x27;t require braces, goto in general, rampant mutability, &lt;a href=&quot;https:&#x2F;&#x2F;joeduffyblog.com&#x2F;2016&#x2F;02&#x2F;07&#x2F;the-error-model&#x2F;#error-codes&quot;&gt;error codes&lt;&#x2F;a&gt;), would have nevertheless been caught by a human reviewer if the code was autoformatted.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;syntax highlighting instantly catches syntax-level errors without requiring a full compiler pass or linter run. it&#x27;s the fastest feedback we have available - misspelled a keyword? it&#x27;s the wrong colour, your brain &lt;em&gt;knows&lt;&#x2F;em&gt; something&#x27;s wrong before you can consciously respond to it. i consider consciousness a slow and error-prone&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-consciousness-1&quot;&gt;&lt;a href=&quot;#fn-consciousness&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; method of cognition, and short-circuiting it lets you write better code faster.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-consciousness&quot;&gt;
&lt;p&gt;this requires some explanation. when you&#x27;re doing your best work, you &quot;lose yourself&quot; in it. no one considers a self-conscious or self-aware coding style any good. our consciousness lies, makes up narratives, and generally runs interference. of course, preconscious cognition is much less sophisticated, and so conscious composition is indispensable. for a longer-form meditation on consciousness as a liability, try reading &lt;a href=&quot;https:&#x2F;&#x2F;www.rifters.com&#x2F;real&#x2F;Blindsight.htm&quot;&gt;blindsight&lt;&#x2F;a&gt; &lt;a href=&quot;#fr-consciousness-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="en-CA">
        <title>software engineers don&#x27;t exist</title>
        <published>2025-11-14T21:10:00-08:00</published>
        <updated>2025-11-18T21:17:00-08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nove.dev/blog/engineering/"/>
        <id>https://nove.dev/blog/engineering/</id>
        
        <content type="html" xml:base="https://nove.dev/blog/engineering/">&lt;p&gt;my employer calls me a software engineer. i disagree.&lt;&#x2F;p&gt;
&lt;p&gt;i think software engineering is a misnomer, not because what we do is materially different from what engineers do, but because we lack the professional and legal institutions engineers have. when a software developer can refuse to write unsafe code, whistleblow to an oversight agency, and be free from fear of retribution, then they can call themselves an engineer. when the legally enshrined organization pairs new grads with experienced mentors, with whom they must work for multiple years before they receive a government-recognized certification, then we can call what we do engineering. when we base technical decisions on scientific data instead of vibes and shoddy undergraduate studies. when our field reads and understands our own history instead of chasing vc whims and riding bubbles.&lt;&#x2F;p&gt;
&lt;p&gt;i spent a couple years in an engineering program at university. i have no misconceptions that &quot;real&quot; engineers are any more ethical than software developers. in my first year, we received a lecture on engineering ethics from a man who worked on weapons programs for the u.s. department of &quot;defense&quot;, which disillusioned me from the entire discipline. but i still believe a better world is possible, and until software stops killing people we need to move slow and correct things.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;update 2025-11-18: i recently encountered this article from the acm, whose area is rather relevant to the subject at hand. &lt;a href=&quot;https:&#x2F;&#x2F;cacm.acm.org&#x2F;opinion&#x2F;software-professionals-malpractice-law-and-codes-of-ethics&#x2F;&quot;&gt;recommended reading&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;update 2026-01-19: found another &lt;a href=&quot;https:&#x2F;&#x2F;alexwennerberg.com&#x2F;blog&#x2F;2025-11-28-engineering.html&quot;&gt;recommended post&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en-CA">
        <title>catalogue of personal computation</title>
        <published>2025-11-14T20:41:00-08:00</published>
        <updated>2025-11-14T20:41:00-08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nove.dev/blog/computer-catalogue/"/>
        <id>https://nove.dev/blog/computer-catalogue/</id>
        
        <content type="html" xml:base="https://nove.dev/blog/computer-catalogue/">&lt;p&gt;my desktop is a decommissioned compute server that my old employer was giving away after they got acquired and the new parent pushed everything into aws. it has a 48-core threadripper and 256gb of ram, 128 of which is in a drawer of my desk because it makes my overclock unstable. it&#x27;s wildly overkill 95% of the time, underpowered 10% of the time (single-core performance was never threadripper&#x27;s strong suit, and the state of the art has gotten a lot better since zen 2), and very appreciated 5% of the time: i was repeatedly recompiling ffmpeg the other day and task manager claimed i hit 101% cpu usage. it runs windows because elden ring holds me hostage; all my development is done in a wsl2 vm running either nixos or arch. i think nixos solves more or less all linux problems and replaces them with nixos problems, which is a mixed blessing. i use lix instead of cppnix for ideological reasons.&lt;&#x2F;p&gt;
&lt;p&gt;i have a pair of b&amp;amp;w passive bookshelf speakers and a b&amp;amp;w active subwoofer, which i power with a fosi audio v3 amp. i recommend all of these very highly; i listen to a lot of music on them. i plan to get a dac, headphone amp, maybe microphone setup someday.&lt;&#x2F;p&gt;
&lt;p&gt;my home server is composed of extremely sketchy hardware, notwithstanding the redundant zfs storage pool. the side panel won&#x27;t go back on ever since i swapped the motherboard for one with a hyper 212 evo, which is too tall for the micro-atx case. the power button hasn&#x27;t worked for 5 years; i use a screwdriver to bridge the pins on the motherboard. two of the four hard drives are failing, which is why i have the other two. the psu fan failed in 2025, so i cannibalized my old gaming pc for its evga (rip) psu and swapped it in. honestly i&#x27;m impressed that the 400w psu cooler master included with its bottom-of-the-line case in 2018 lasted 7 years without lighting on fire. the server&#x27;s main purpose is to run the unifi controller, which i manage with a bizarrely bulletproof bash script, and act as an smb server. i haven&#x27;t had any issues with the software in years. i ran freenas as a kid but got annoyed with freebsd software availability, then i ran truenas scale but got annoyed by the unfinished betaness of it all, so i gave up on semicustom distributions and installed ubuntu server lts.&lt;&#x2F;p&gt;
&lt;p&gt;my personal laptop is a lenovo yoga i got in 2019 when i was starting my degree, for which it was perfect. it has a touch screen, stylus, and 360 degree hinge with an i7 and 8gb of ram. power use while sleeping is abysmal, performance is terrible when using modern software, 8gb of ram is no longer enough. it can still run wsl2 with arch and a text editor, so i try to use it mostly for that. the speakers and fingerprint reader do not work in linux. the drivers are not open source and no one has managed to reverse engineer them, otherwise i would have put arch on it years ago. eventually, i see myself getting a macbook air.&lt;&#x2F;p&gt;
&lt;p&gt;my work laptop is a macbook pro 16&quot; with an m3 pro and 36gb of ram. battery life and performance are very good, though i think my desktop can still beat it in a multicore drag race.&lt;&#x2F;p&gt;
&lt;p&gt;i have a raspberry pi collecting dust in the closet. i used to have an arm vm on oracle&#x27;s free plan. it ran a minecraft server for a while, then the backend for a web toy i made, and nothing since they closed it for inactivity in 2024.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en-CA">
        <title>blogpost about static site generation</title>
        <published>2025-11-14T19:40:00-08:00</published>
        <updated>2025-11-14T19:40:00-08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nove.dev/blog/meta/"/>
        <id>https://nove.dev/blog/meta/</id>
        
        <content type="html" xml:base="https://nove.dev/blog/meta/">&lt;p&gt;as is tradition, now that i&#x27;ve stripped my old website to the bones, my first post must be about the  technology that made the new website. if i am to follow in my priors&#x27; footsteps, this will also be the last post on this blog. i endeavour to not.&lt;&#x2F;p&gt;
&lt;p&gt;both this site and my old site use the zola static site generator. my old site was laden with plenty of custom css, unpopular ideas about fonts, and altogether too much self-importance. this current site uses the stock terminimal theme with no adjustments, so hopefully i can actually write posts instead of screwing around with css. &lt;s&gt;at least i&#x27;ve kept the theme colour&lt;&#x2F;s&gt; nope, green looks better.&lt;&#x2F;p&gt;
&lt;details&gt;
    &lt;summary&gt;aside&lt;&#x2F;summary&gt;
    &lt;p&gt;i originally used pure CMYK magenta, on the hubristic idea that one day my resume would be printed on a colour printer. i don&#x27;t think that has ever happened, and it&#x27;s weird for a computer professional to have a dead-trees-focused resume. it also was (is) too bombastic. after i&#x27;m happy with this site, i&#x27;ll rewrite my resume in typst with a basic theme. it&#x27;ll be linked here, eventually.&lt;&#x2F;p&gt;
    &lt;details&gt;
        &lt;summary&gt;aside aside&lt;&#x2F;summary&gt;
        &lt;p&gt;i lightly wish terminimal had asides. i&#x27;m not convinced they work on mobile, at all - it requires a certain width of reading area that mobile lacks completely. still, they work in a similar way to my brain. this is a nested aside, not that it renders that way. i&#x27;m polyfilling with details tags. i wonder how i expect a doubly-nested aside to work&lt;&#x2F;p&gt;
    &lt;&#x2F;details&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;zola&#x27;s pretty good, if a little obtuse with its sections-and-pages systematization. it could use better error messages, but it really didn&#x27;t take too long to refamiliarize myself with after not touching it for years, and now it&#x27;s mostly staying out of my way. i like the terminimal theme, partially from association with other blogs i respect. i appreciate the lack of javascript.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
